From 40882e16529fbd9ca61d8dae019b0ffd511dd4c7 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Mon, 1 Dec 2025 08:39:24 -0800 Subject: [PATCH 01/31] Enable sandboxing for terminal commands --- .vscode/launch.json | 16 +- package-lock.json | 80 ++++++++- package.json | 1 + .../electron-main/storageMainService.test.ts | 1 + src/vs/platform/terminal/common/terminal.ts | 41 +++++ .../common/terminalPlatformConfiguration.ts | 45 +++++ .../userData/common/fileUserDataProvider.ts | 1 + .../userDataProfile/common/userDataProfile.ts | 3 + .../server/node/serverEnvironmentService.ts | 2 + .../test/browser/editSessions.test.ts | 1 + .../terminal.chatAgentTools.contribution.ts | 55 +++++- .../browser/tools/runInTerminalTool.ts | 169 +++++++++++++++++- .../common/terminal.chatAgentTools.ts | 1 + .../terminalChatAgentToolsConfiguration.ts | 81 +++++++++ .../runInTerminalTool.test.ts | 1 - .../test/browser/storageService.test.ts | 3 +- .../userDataProfileImportExportService.ts | 3 +- .../workingCopyBackupService.test.ts | 3 +- 18 files changed, 481 insertions(+), 26 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 07407e53ab60b..a7a15cc31a6c3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -282,7 +282,7 @@ // To debug observables you also need the extension "ms-vscode.debug-value-editor" "type": "chrome", "request": "launch", - "name": "Launch VS Code Internal (Dev Debug)", + "name": "Launch VS Code Internal (Hot Reload)", "windows": { "runtimeExecutable": "${workspaceFolder}/scripts/code.bat" }, @@ -298,7 +298,10 @@ "VSCODE_EXTHOST_WILL_SEND_SOCKET": null, "VSCODE_SKIP_PRELAUNCH": "1", "VSCODE_DEV_DEBUG": "1", + "VSCODE_DEV_SERVER_URL": "http://localhost:5199/build/vite/workbench-vite-electron.html", + "DEV_WINDOW_SRC": "http://localhost:5199/build/vite/workbench-vite-electron.html", "VSCODE_DEV_DEBUG_OBSERVABLES": "1", + "VSCODE_DEV": "1" }, "cleanUp": "wholeBrowser", "runtimeArgs": [ @@ -322,6 +325,7 @@ "presentation": { "hidden": true, }, + "preLaunchTask": "Launch Monaco Editor Vite" }, { "type": "node", @@ -591,7 +595,7 @@ "name": "Monaco Editor - Playground", "type": "chrome", "request": "launch", - "url": "https://microsoft.github.io/monaco-editor/playground.html?source=http%3A%2F%2Flocalhost%3A5199%2Fbuild%2Fmonaco-editor-playground%2Findex.ts%3Fesm#example-creating-the-editor-hello-world", + "url": "https://microsoft.github.io/monaco-editor/playground.html?source=http%3A%2F%2Flocalhost%3A5199%2Fbuild%2Fvite%2Findex.ts%3Fesm#example-creating-the-editor-hello-world", "preLaunchTask": "Launch Monaco Editor Vite", "presentation": { "group": "monaco", @@ -602,7 +606,7 @@ "name": "Monaco Editor - Self Contained Diff Editor", "type": "chrome", "request": "launch", - "url": "http://localhost:5199/build/monaco-editor-playground/index.html", + "url": "http://localhost:5199/build/vite/index.html", "preLaunchTask": "Launch Monaco Editor Vite", "presentation": { "group": "monaco", @@ -613,7 +617,7 @@ "name": "Monaco Editor - Workbench", "type": "chrome", "request": "launch", - "url": "http://localhost:5199/build/monaco-editor-playground/workbench-vite.html", + "url": "http://localhost:5199/build/vite/workbench-vite.html", "preLaunchTask": "Launch Monaco Editor Vite", "presentation": { "group": "monaco", @@ -638,10 +642,10 @@ } }, { - "name": "VS Code (Debug Observables)", + "name": "VS Code (Hot Reload)", "stopAll": true, "configurations": [ - "Launch VS Code Internal (Dev Debug)", + "Launch VS Code Internal (Hot Reload)", "Attach to Main Process", "Attach to Extension Host", "Attach to Shared Process", diff --git a/package-lock.json b/package-lock.json index ff7180b2fe834..85ad3c1577a75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@anthropic-ai/sandbox-runtime": "^0.0.13", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", "@parcel/watcher": "parcel-bundler/watcher#1ca032aa8339260a8a3bcf825c3a1a71e3e43542", @@ -179,6 +180,35 @@ "node": ">=6.0.0" } }, + "node_modules/@anthropic-ai/sandbox-runtime": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.13.tgz", + "integrity": "sha512-rjjmp3MVDn3Yl4ywNVM2Dr7+Q/KzsvsmLl8prP10Mi/+3F3Gs997UiFx3WXqESJtnYkznOuYUp0VhFDfOyJFPw==", + "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", @@ -1736,6 +1766,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", @@ -2056,6 +2092,21 @@ "@types/node": "*" } }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "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.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -15521,10 +15578,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", @@ -18493,6 +18556,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.7.0", "resolved": "https://registry.npmjs.org/zx/-/zx-8.7.0.tgz", diff --git a/package.json b/package.json index 7fb7d71297eb0..d5e62de718e21 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "update-build-ts-version": "npm install -D typescript@next @typescript/native-preview && (cd build && npm run compile)" }, "dependencies": { + "@anthropic-ai/sandbox-runtime": "^0.0.13", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", "@parcel/watcher": "parcel-bundler/watcher#1ca032aa8339260a8a3bcf825c3a1a71e3e43542", diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index ff72087bd53cc..b1e41708dc333 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -48,6 +48,7 @@ suite('StorageMainService', function () { promptsHome: joinPath(inMemoryProfileRoot, 'promptsHome'), extensionsResource: joinPath(inMemoryProfileRoot, 'extensionsResource'), cacheHome: joinPath(inMemoryProfileRoot, 'cache'), + sandboxSettingsResource: joinPath(inMemoryProfileRoot, 'sandbox-settings.json') }; class TestStorageMainService extends StorageMainService { diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index e4f056aee7910..1540a7449eb24 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -19,6 +19,22 @@ import type { IAction } from '../../../base/common/actions.js'; import type { IDisposable } from '../../../base/common/lifecycle.js'; import type { SingleOrMany } from '../../../base/common/types.js'; +/** + * Local type definition for sandbox runtime configuration to avoid importing external package + * in the common layer. The actual type should match @anthropic-ai/sandbox-runtime. + */ +export interface ISandboxRuntimeConfig { + network?: { + allowedDomains?: string[]; + deniedDomains?: string[]; + }; + filesystem?: { + denyRead?: string[]; + allowWrite?: string[]; + denyWrite?: string[]; + }; +} + export const enum TerminalSettingPrefix { AutomationProfile = 'terminal.integrated.automationProfile.', DefaultProfile = 'terminal.integrated.defaultProfile.', @@ -667,8 +683,28 @@ export interface IShellLaunchConfig { * This allows extensions to control shell integration for terminals they create. */ shellIntegrationNonce?: string; + + /** Whether to launch the terminal in a sandboxed environment. */ + sandboxed?: boolean; + + /** Sandbox settings to use when launching the terminal process in a sandboxed environment. */ + sandboxSettings?: ISandboxRuntimeConfig; +} + +export interface ISandboxTerminalSettings { + enabled?: boolean; + network?: { + allowedDomains?: string[]; + deniedDomains?: string[]; + }; + filesystem?: { + denyRead?: string[]; + allowWrite?: string[]; + denyWrite?: string[]; + }; } + export interface ITerminalTabAction { id: string; label: string; @@ -709,6 +745,8 @@ export interface IShellLaunchConfigDto { isFeatureTerminal?: boolean; tabActions?: ITerminalTabAction[]; shellIntegrationEnvironmentReporting?: boolean; + sandboxed?: boolean; + sandboxSettings?: ISandboxRuntimeConfig; } /** @@ -726,6 +764,7 @@ export interface ITerminalProcessOptions { environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined; workspaceFolder: IWorkspaceFolder | undefined; isScreenReaderOptimized: boolean; + sandboxSettings?: ISandboxRuntimeConfig; } export interface ITerminalEnvironment { @@ -917,6 +956,8 @@ export interface ITerminalProfile { overrideName?: boolean; color?: string; icon?: ThemeIcon | URI | { light: URI; dark: URI }; + sandboxed?: boolean; + sandboxSettings?: ISandboxRuntimeConfig; } export interface ITerminalDimensionsOverride extends Readonly { diff --git a/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts b/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts index 30339af1ef050..192e461956ca7 100644 --- a/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts +++ b/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts @@ -59,6 +59,51 @@ export const terminalProfileBaseProperties: IJSONSchemaMap = { } }; +export const terminalSandboxPropertySchema: IJSONSchemaMap = { + filesystem: { + type: 'object', + description: localize('terminalSandbox.fileSystem', "Controls file system access in the terminal sandbox."), + properties: { + allowWrite: { + type: 'array', + description: localize('terminalSandbox.fileSystem.allowedPaths', "List of allowed file system paths."), + items: { type: 'string' }, + default: [] + }, + denyRead: { + type: 'array', + description: localize('terminalSandbox.fileSystem.deniedPaths', "List of denied file system paths."), + items: { type: 'string' }, + default: [] + }, + denyWrite: { + type: 'array', + description: localize('terminalSandbox.fileSystem.deniedWritePaths', "List of denied file system write paths."), + items: { type: 'string' }, + default: [] + } + } + }, + network: { + type: 'object', + description: localize('terminalSandbox.network', "Controls network access in the terminal sandbox."), + properties: { + allowedDomains: { + type: 'array', + description: localize('terminalSandbox.network.allowedHosts', "List of allowed network hosts."), + items: { type: 'string' }, + default: [] + }, + deniedDomains: { + type: 'array', + description: localize('terminalSandbox.network.deniedDomains', "List of denied network domains."), + items: { type: 'string' }, + default: [] + } + } + } +}; + const terminalProfileSchema: IJSONSchema = { type: 'object', required: ['path'], diff --git a/src/vs/platform/userData/common/fileUserDataProvider.ts b/src/vs/platform/userData/common/fileUserDataProvider.ts index efa390b0ad2cd..079c54eb0a4a6 100644 --- a/src/vs/platform/userData/common/fileUserDataProvider.ts +++ b/src/vs/platform/userData/common/fileUserDataProvider.ts @@ -65,6 +65,7 @@ export class FileUserDataProvider extends Disposable implements this.atomicReadWriteResources.add(profile.keybindingsResource); this.atomicReadWriteResources.add(profile.tasksResource); this.atomicReadWriteResources.add(profile.extensionsResource); + this.atomicReadWriteResources.add(profile.sandboxSettingsResource); } } diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 5d17d17ba0374..6afafefba42ee 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -56,6 +56,7 @@ export interface IUserDataProfile { readonly useDefaultFlags?: UseDefaultProfileFlags; readonly isTransient?: boolean; readonly workspaces?: readonly URI[]; + readonly sandboxSettingsResource: URI; } export function isUserDataProfile(thing: unknown): thing is IUserDataProfile { @@ -145,6 +146,7 @@ export function reviveProfile(profile: UriDto, scheme: string) useDefaultFlags: profile.useDefaultFlags, isTransient: profile.isTransient, workspaces: profile.workspaces?.map(w => URI.revive(w)), + sandboxSettingsResource: URI.revive(profile.sandboxSettingsResource).with({ scheme }) }; } @@ -167,6 +169,7 @@ export function toUserDataProfile(id: string, name: string, location: URI, profi useDefaultFlags: options?.useDefaultFlags, isTransient: options?.transient, workspaces: options?.workspaces, + sandboxSettingsResource: joinPath(location, 'sandbox-settings.json') }; } diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index ab7659a7ca50c..ef423dc80fb78 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -83,6 +83,7 @@ export const serverOptions: OptionDescriptions> = { 'enable-remote-auto-shutdown': { type: 'boolean' }, 'remote-auto-shutdown-without-delay': { type: 'boolean' }, + 'inspect-ptyhost': { type: 'string', allowEmptyValue: true }, 'use-host-proxy': { type: 'boolean' }, 'without-browser-env-var': { type: 'boolean' }, @@ -212,6 +213,7 @@ export interface ServerParsedArgs { 'enable-remote-auto-shutdown'?: boolean; 'remote-auto-shutdown-without-delay'?: boolean; + 'inspect-ptyhost'?: string; 'use-host-proxy'?: boolean; 'without-browser-env-var'?: boolean; diff --git a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts index 12be1cfe7d4b4..e47d9828bc3d5 100644 --- a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts +++ b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts @@ -166,6 +166,7 @@ suite('Edit session sync', () => { promptsHome: URI.file('promptsHome'), extensionsResource: URI.file('extensionsResource'), cacheHome: URI.file('cacheHome'), + sandboxSettingsResource: URI.file('sandbox-settings.json') }; }); 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 aef70202c5882..f1b2a119eea93 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 @@ -7,15 +7,16 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { isNumber } from '../../../../../base/common/types.js'; -import { localize } from '../../../../../nls.js'; -import { MenuId } from '../../../../../platform/actions/common/actions.js'; +import { localize, localize2 } from '../../../../../nls.js'; +import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { TerminalSettingId } from '../../../../../platform/terminal/common/terminal.js'; import { registerWorkbenchContribution2, WorkbenchPhase, type IWorkbenchContribution } from '../../../../common/contributions.js'; import { IChatWidgetService } from '../../../chat/browser/chat.js'; import { ChatContextKeys } from '../../../chat/common/chatContextKeys.js'; +import { IChatService } from '../../../chat/common/chatService.js'; import { ILanguageModelToolsService, ToolDataSource, VSCodeToolReference } from '../../../chat/common/languageModelToolsService.js'; import { registerActiveInstanceAction, sharedWhenClause } from '../../../terminal/browser/terminalActions.js'; import { TerminalContextMenuGroup } from '../../../terminal/browser/terminalMenus.js'; @@ -57,6 +58,7 @@ class ChatAgentToolsContribution extends Disposable implements IWorkbenchContrib constructor( @IInstantiationService instantiationService: IInstantiationService, @ILanguageModelToolsService toolsService: ILanguageModelToolsService, + @IConfigurationService configurationService: IConfigurationService, ) { super(); @@ -115,8 +117,55 @@ registerWorkbenchContribution2(ChatAgentToolsContribution.ID, ChatAgentToolsCont // #endregion Contributions + // #region Actions +// Retry action for sandboxed terminal commands (invoked via command link in toolResultMessage) +registerAction2(class RetryWithoutSandboxingAction extends Action2 { + constructor() { + super({ + id: TerminalChatAgentToolsCommandId.RetryWithoutSandboxing, + title: localize2('terminal.retryWithoutSandboxing', "Retry without Sandboxing"), + f1: false + }); + } + + async run(accessor: ServicesAccessor, failedCommandDetails: { lastFailedCommandId: string }): Promise { + const chatService = accessor.get(IChatService); + const chatWidgetService = accessor.get(IChatWidgetService); + + const widget = chatWidgetService.lastFocusedWidget; + if (!widget) { + return; + } + const model = widget.viewModel?.model; + if (!model) { + return; + } + const requests = model.getRequests(); + const lastRequest = requests?.[requests.length - 1]; + if (!lastRequest) { + return; + } + + try { + // Disable sandboxing + RunInTerminalTool.updateSandboxingForCommandExecution(false, failedCommandDetails.lastFailedCommandId); + const languageModelId = widget?.input.currentLanguageModel; + + // Retry the request + await chatService.resendRequest(lastRequest, { + userSelectedModelId: languageModelId, + attempt: (lastRequest?.attempt ?? -1) + 1, + ...widget?.getModeRequestOptions(), + }); + } finally { + // Re-enable sandboxing + RunInTerminalTool.updateSandboxingForCommandExecution(undefined, failedCommandDetails.lastFailedCommandId); + } + } +}); + registerActiveInstanceAction({ id: TerminalChatAgentToolsCommandId.ChatAddTerminalSelection, title: localize('addTerminalSelection', 'Add Terminal Selection to Chat'), 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 55292ebf37b27..03f3c10df815b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -12,15 +12,15 @@ import { Event } from '../../../../../../base/common/event.js'; import { MarkdownString, type IMarkdownString } from '../../../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; import { basename } from '../../../../../../base/common/path.js'; -import { OperatingSystem, OS } from '../../../../../../base/common/platform.js'; +import { isWindows, OperatingSystem, OS } from '../../../../../../base/common/platform.js'; import { count } from '../../../../../../base/common/strings.js'; import { generateUuid } from '../../../../../../base/common/uuid.js'; import { localize } from '../../../../../../nls.js'; -import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { ConfigurationTarget, IConfigurationChangeEvent, 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 { ITerminalLogService, ITerminalProfile } from '../../../../../../platform/terminal/common/terminal.js'; +import { ISandboxTerminalSettings, ITerminalLogService, ITerminalProfile } from '../../../../../../platform/terminal/common/terminal.js'; import { IRemoteAgentService } from '../../../../../services/remote/common/remoteAgentService.js'; import { TerminalToolConfirmationStorageKeys } from '../../../../chat/browser/chatContentParts/toolInvocationParts/chatTerminalToolConfirmationSubPart.js'; import { IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService.js'; @@ -53,6 +53,11 @@ import { IHistoryService } from '../../../../../services/history/common/history. import { TerminalCommandArtifactCollector } from './terminalCommandArtifactCollector.js'; import { isNumber, isString } from '../../../../../../base/common/types.js'; import { ChatConfiguration } from '../../../../chat/common/constants.js'; +import { IFileService } from '../../../../../../platform/files/common/files.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { IUserDataProfileService } from '../../../../../services/userDataProfile/common/userDataProfile.js'; +import { TerminalChatAgentToolsCommandId } from '../../common/terminal.chatAgentTools.js'; // #region Tool data @@ -261,6 +266,13 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { private readonly _telemetry: RunInTerminalToolTelemetry; private readonly _commandArtifactCollector: TerminalCommandArtifactCollector; protected readonly _profileFetcher: TerminalProfileFetcher; + private _sandboxConfigPath: string | undefined; + private _lastFailedCommandId: string | undefined; + private static _isSandboxedForCommandExecution: boolean | undefined; + private static _lastFailedCommandIdList: Set = new Set(); + + + private readonly _commandLineRewriters: ICommandLineRewriter[]; private readonly _commandLineAnalyzers: ICommandLineAnalyzer[]; @@ -291,6 +303,8 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { @ITerminalLogService private readonly _logService: ITerminalLogService, @ITerminalService private readonly _terminalService: ITerminalService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @IFileService private readonly _fileService: IFileService, + @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService, ) { super(); @@ -318,8 +332,24 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); } } + // check for sandbox setting changes + if (e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandbox)) { + const eventDetails: IConfigurationChangeEvent = e; + const sandboxSetting = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandbox); + //update the config file if needed based on where the setting is update. + if (this._isSandboxedTerminal()) { + const inspected = this._configurationService.inspect(TerminalChatAgentToolsSettingId.TerminalSandbox); + if (eventDetails.source === ConfigurationTarget.WORKSPACE && inspected?.workspaceValue) { + // create a config file inside .vscode folder. + this._createConfigFileForSandboxingAtWorkspaceLevel(sandboxSetting); + } else { + this._createConfigForSandboxingAtApplicationLevel(sandboxSetting); + } + } + } })); + // Restore terminal associations from storage this._restoreTerminalAssociations(); this._register(this._terminalService.onDidDisposeInstance(e => { @@ -399,6 +429,12 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { }; } + if (this._isSandboxedTerminal()) { + 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. @@ -478,13 +514,12 @@ 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 chatSessionId = invocation.context?.sessionId ?? 'no-chat-session'; - 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 @@ -495,6 +530,11 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { toolSpecificData.commandLine.toolEdited !== toolSpecificData.commandLine.original ); + if (this._isSandboxedTerminal()) { + this._logService.info(`RunInTerminalTool: Sandboxing is enabled, wrapping command with srt.`); + command = `srt --settings "${this._sandboxConfigPath}" "${command}"`; // sandboxing + } + if (token.isCancellationRequested) { throw new CancellationError(); } @@ -508,6 +548,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { const store = new DisposableStore(); + this._logService.debug(`RunInTerminalTool: Creating ${args.isBackground ? 'background' : 'foreground'} terminal. termId=${termId}, chatSessionId=${chatSessionId}`); const toolTerminal = await (args.isBackground ? this._initBackgroundTerminal(chatSessionId, termId, terminalToolSessionId, token) @@ -515,6 +556,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._handleTerminalVisibility(toolTerminal); + const timingConnectMs = Date.now() - timingStart; const xterm = await toolTerminal.instance.xtermReadyPromise; @@ -661,6 +703,22 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } terminalResult = resultArr.join('\n\n'); + //if sandboxed and there is error, display a hint message + if (exitCode !== 0 && this._isSandboxedTerminal()) { + this._lastFailedCommandId = commandId; + const mdTrustSettings = { + isTrusted: true + }; + const args = { lastFailedCommandId: this._lastFailedCommandId }; + + toolResultMessage = new MarkdownString( + `$(warning) [Retry](command:${TerminalChatAgentToolsCommandId.RetryWithoutSandboxing}?${encodeURIComponent(JSON.stringify(args))}) without sandboxing.`, + mdTrustSettings + ); + } else { + this._lastFailedCommandId = undefined; + } + } catch (e) { this._logService.debug(`RunInTerminalTool: Threw exception`); toolTerminal.instance.dispose(); @@ -707,16 +765,28 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { return { toolResultMessage, toolMetadata: { - exitCode: exitCode + exitCode: exitCode, + sandboxingFailed: exitCode !== 0 && this._isSandboxedTerminal() }, content: [{ kind: 'text', - value: resultText.join(''), + value: resultText.join('') }] }; } } + // This is called when the user clicks the "Retry without sandboxing" link. + public static updateSandboxingForCommandExecution(enabled: boolean | undefined, lastFailedCommandId: string): void { + RunInTerminalTool._isSandboxedForCommandExecution = enabled; + if (enabled === false && lastFailedCommandId) { + RunInTerminalTool._lastFailedCommandIdList.add(lastFailedCommandId); + } else { + RunInTerminalTool._lastFailedCommandIdList.delete(lastFailedCommandId); + } + + } + private _handleTerminalVisibility(toolTerminal: IToolTerminal) { if (this._configurationService.getValue(TerminalChatAgentToolsSettingId.OutputLocation) === 'terminal') { this._terminalService.setActiveInstance(toolTerminal.instance); @@ -724,12 +794,92 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } + + /** + * Creates a sandbox settings configuration file at the application level. + * The file is stored in the user profile directory (e.g., ~/.config/Code/User/sandbox-settings.json) + * This allows sandbox settings to be persisted across all workspaces for the user. + * @param settings The sandbox settings to write to the file + */ + private async _createConfigForSandboxingAtApplicationLevel(settings: ISandboxTerminalSettings): Promise { + if (!settings?.enabled) { + return; + } + // Get the user profile's sandboxSettings resource URI + const sandboxSettingsUri = this._userDataProfileService.currentProfile.sandboxSettingsResource; + await this._createSandboxConfig(sandboxSettingsUri, settings); + } + + private _isSandboxedTerminal(): boolean { + if (isWindows) { + return false; + } + const settings = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandbox); + const isEnabled = settings?.enabled === true; + + if (!isEnabled) { + this._sandboxConfigPath = undefined; + return false; + } + + // if its disabled at command execution time, return false + if (RunInTerminalTool._isSandboxedForCommandExecution === false && this._lastFailedCommandId && RunInTerminalTool._lastFailedCommandIdList.has(this._lastFailedCommandId)) { + this._sandboxConfigPath = undefined; + return false; + } + + // Determine config path based on configuration level + const inspected = this._configurationService.inspect(TerminalChatAgentToolsSettingId.TerminalSandbox); + if (inspected?.workspaceValue) { + // Workspace-level setting: use workspace .vscode folder + const workSpaceFolder = this._workspaceContextService.getWorkspace().folders[0]?.uri.fsPath; + if (workSpaceFolder) { + this._sandboxConfigPath = URI.file(workSpaceFolder).with({ + path: `${workSpaceFolder}/.vscode/sandbox-settings.json` + }).fsPath; + } + } else { + // User-level setting: use user profile + this._sandboxConfigPath = this._userDataProfileService.currentProfile.sandboxSettingsResource.fsPath; + } + return isEnabled; + } + + private _createConfigFileForSandboxingAtWorkspaceLevel(settings: ISandboxTerminalSettings): void { + const workSpaceFolder = this._workspaceContextService.getWorkspace().folders[0]?.uri.fsPath; + if (!workSpaceFolder) { + return; + } + const sandboxSettingsUri = URI.file(workSpaceFolder).with({ + path: `${workSpaceFolder}/.vscode/sandbox-settings.json` + }); + + this._createSandboxConfig(sandboxSettingsUri, settings); + } + + private async _createSandboxConfig(sandboxSettingsUri: URI, settings: ISandboxTerminalSettings): Promise { + const sandboxSettings = { + network: { + allowedDomains: settings.network?.allowedDomains || [], + deniedDomains: settings.network?.deniedDomains || [] + }, + filesystem: { + denyRead: settings.filesystem?.denyRead || [], + allowWrite: settings.filesystem?.allowWrite || ['.'], + denyWrite: settings.filesystem?.denyWrite || [] + } + }; + this._sandboxConfigPath = sandboxSettingsUri.fsPath; + this._fileService.createFile(sandboxSettingsUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true }); + } + // #region Terminal init private async _initBackgroundTerminal(chatSessionId: string, termId: string, terminalToolSessionId: string | undefined, token: CancellationToken): Promise { this._logService.debug(`RunInTerminalTool: Creating background terminal with ID=${termId}`); const profile = await this._profileFetcher.getCopilotProfile(); const toolTerminal = await this._terminalToolCreator.createTerminal(profile, token); + this._terminalChatService.registerTerminalInstanceWithToolSession(terminalToolSessionId, toolTerminal.instance); this._terminalChatService.registerTerminalInstanceWithChatSession(chatSessionId, toolTerminal.instance); this._registerInputListener(toolTerminal); @@ -742,6 +892,8 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { return toolTerminal; } + + private async _initForegroundTerminal(chatSessionId: string, termId: string, terminalToolSessionId: string | undefined, token: CancellationToken): Promise { const cachedTerminal = this._sessionTerminalAssociations.get(chatSessionId); if (cachedTerminal) { @@ -882,7 +1034,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } } - // #endregion } diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts index d6417b4259af0..8757a7074ba64 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts @@ -5,4 +5,5 @@ export const enum TerminalChatAgentToolsCommandId { ChatAddTerminalSelection = 'workbench.action.terminal.chat.addTerminalSelection', + RetryWithoutSandboxing = 'workbench.action.terminal.retryWithoutSandboxing' } diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index afbeb57cc74ab..e862296a08ad2 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -20,6 +20,7 @@ export const enum TerminalChatAgentToolsSettingId { ShellIntegrationTimeout = 'chat.tools.terminal.shellIntegrationTimeout', AutoReplyToPrompts = 'chat.tools.terminal.autoReplyToPrompts', OutputLocation = 'chat.tools.terminal.outputLocation', + TerminalSandbox = 'chat.tools.terminal.sandbox', TerminalProfileLinux = 'chat.tools.terminal.terminalProfile.linux', TerminalProfileMacOs = 'chat.tools.terminal.terminalProfile.osx', @@ -63,6 +64,60 @@ const terminalChatAgentProfileSchema: IJSONSchema = { } }; +export const terminalSandboxPropertySchema: IJSONSchema = { + type: 'object', + required: ['enabled'], + properties: { + enabled: { + type: 'boolean', + description: localize('terminalSandbox.enabled', "Controls whether to run commands in a sandboxed terminal for the run in terminal tool."), + }, + fileSystem: { + type: 'object', + description: localize('terminalSandbox.fileSystem', "Controls file system access in the terminal sandbox."), + properties: { + denyRead: { + type: 'array', + description: localize('terminalSandbox.fileSystem.denyRead', "List of denied file system paths for reading."), + items: { type: 'string' }, + default: [] + }, + allowWrite: { + type: 'array', + description: localize('terminalSandbox.fileSystem.allowWrite', "List of allowed file system paths for writing."), + items: { type: 'string' }, + default: [] + }, + denyWrite: { + type: 'array', + description: localize('terminalSandbox.fileSystem.denyWrite', "List of denied file system paths for writing."), + items: { type: 'string' }, + default: [] + } + } + }, + network: { + type: 'object', + description: localize('terminalSandbox.network', "Controls network access in the terminal sandbox."), + properties: { + allowedDomains: { + type: 'array', + description: localize('terminalSandbox.network.allowedDomains', "List of allowed network domains."), + items: { type: 'string' }, + default: [] + }, + deniedDomains: { + type: 'array', + description: localize('terminalSandbox.network.deniedDomains', "List of denied network domains."), + items: { type: 'string' }, + default: [] + } + } + } + } +}; + + export const terminalChatAgentToolsConfiguration: IStringDictionary = { [TerminalChatAgentToolsSettingId.EnableAutoApprove]: { description: localize('autoApproveMode.description', "Controls whether to allow auto approval in the run in terminal tool."), @@ -419,6 +474,32 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary { instance: mockTerminal2, shellIntegrationQuality: ShellIntegrationQuality.None }); - ok(runInTerminalTool.sessionTerminalAssociations.has(sessionId1), 'Session 1 terminal association should exist'); ok(runInTerminalTool.sessionTerminalAssociations.has(sessionId2), 'Session 2 terminal association should exist'); diff --git a/src/vs/workbench/services/storage/test/browser/storageService.test.ts b/src/vs/workbench/services/storage/test/browser/storageService.test.ts index 2531f78ca5daf..b5f0381aa6575 100644 --- a/src/vs/workbench/services/storage/test/browser/storageService.test.ts +++ b/src/vs/workbench/services/storage/test/browser/storageService.test.ts @@ -46,7 +46,8 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS snippetsHome: joinPath(inMemoryExtraProfileRoot, 'snippetsHome'), promptsHome: joinPath(inMemoryExtraProfileRoot, 'promptsHome'), extensionsResource: joinPath(inMemoryExtraProfileRoot, 'extensionsResource'), - cacheHome: joinPath(inMemoryExtraProfileRoot, 'cache') + cacheHome: joinPath(inMemoryExtraProfileRoot, 'cache'), + sandboxSettingsResource: joinPath(inMemoryExtraProfileRoot, 'sandboxSettings.json') }; const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, disposables.add(new UserDataProfileService(inMemoryExtraProfile)), logService)); diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts index c3aff7cf4c5f7..3ea26eb0518f8 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts @@ -756,7 +756,8 @@ class UserDataProfileExportState extends UserDataProfileImportExportState { extensionsResource: profile.extensionsResource, cacheHome: profile.cacheHome, useDefaultFlags: profile.useDefaultFlags, - isTransient: profile.isTransient + isTransient: profile.isTransient, + sandboxSettingsResource: profile.sandboxSettingsResource.with({ scheme: USER_DATA_PROFILE_EXPORT_SCHEME }) }; } diff --git a/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts index 6b7307e586363..bb487bfc0cdc5 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts @@ -51,7 +51,8 @@ const NULL_PROFILE = { snippetsHome: joinPath(homeDir, 'snippets'), promptsHome: joinPath(homeDir, 'prompts'), extensionsResource: joinPath(homeDir, 'extensions.json'), - cacheHome: joinPath(homeDir, 'cache') + cacheHome: joinPath(homeDir, 'cache'), + sandboxSettingsResource: joinPath(homeDir, 'sandbox-settings.json') }; const TestNativeWindowConfiguration: INativeWindowConfiguration = { From 0b15f7ee9b2e108f301423cbe89be9f9b5cde9e1 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 4 Dec 2025 11:48:19 -0800 Subject: [PATCH 02/31] removing unused types --- src/vs/platform/terminal/common/terminal.ts | 6 ------ .../common/terminalChatAgentToolsConfiguration.ts | 10 +++++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 1540a7449eb24..8b951019189ab 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -683,12 +683,6 @@ export interface IShellLaunchConfig { * This allows extensions to control shell integration for terminals they create. */ shellIntegrationNonce?: string; - - /** Whether to launch the terminal in a sandboxed environment. */ - sandboxed?: boolean; - - /** Sandbox settings to use when launching the terminal process in a sandboxed environment. */ - sandboxSettings?: ISandboxRuntimeConfig; } export interface ISandboxTerminalSettings { diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index e862296a08ad2..3a6891107b643 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -72,25 +72,25 @@ export const terminalSandboxPropertySchema: IJSONSchema = { type: 'boolean', description: localize('terminalSandbox.enabled', "Controls whether to run commands in a sandboxed terminal for the run in terminal tool."), }, - fileSystem: { + filesystem: { type: 'object', - description: localize('terminalSandbox.fileSystem', "Controls file system access in the terminal sandbox."), + description: localize('terminalSandbox.filesystem', "Controls file system access in the terminal sandbox."), properties: { denyRead: { type: 'array', - description: localize('terminalSandbox.fileSystem.denyRead', "List of denied file system paths for reading."), + description: localize('terminalSandbox.filesystem.denyRead', "List of denied file system paths for reading."), items: { type: 'string' }, default: [] }, allowWrite: { type: 'array', - description: localize('terminalSandbox.fileSystem.allowWrite', "List of allowed file system paths for writing."), + description: localize('terminalSandbox.filesystem.allowWrite', "List of allowed file system paths for writing."), items: { type: 'string' }, default: [] }, denyWrite: { type: 'array', - description: localize('terminalSandbox.fileSystem.denyWrite', "List of denied file system paths for writing."), + description: localize('terminalSandbox.filesystem.denyWrite', "List of denied file system paths for writing."), items: { type: 'string' }, default: [] } From 1084d72a1554d0713669b2ff90261f08fbf365d2 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Mon, 5 Jan 2026 16:51:00 -0800 Subject: [PATCH 03/31] code review comments update --- .../terminal.chatAgentTools.contribution.ts | 2 +- .../browser/tools/runInTerminalTool.ts | 252 ++++++++++++------ .../terminalChatAgentToolsConfiguration.ts | 14 +- 3 files changed, 171 insertions(+), 97 deletions(-) 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 f1b2a119eea93..d339a759dbc44 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 @@ -17,7 +17,7 @@ import { registerWorkbenchContribution2, WorkbenchPhase, type IWorkbenchContribu import { IChatWidgetService } from '../../../chat/browser/chat.js'; import { ChatContextKeys } from '../../../chat/common/chatContextKeys.js'; import { IChatService } from '../../../chat/common/chatService.js'; -import { ILanguageModelToolsService, ToolDataSource, VSCodeToolReference } from '../../../chat/common/languageModelToolsService.js'; +import { ILanguageModelToolsService } from '../../../chat/common/languageModelToolsService.js'; import { registerActiveInstanceAction, sharedWhenClause } from '../../../terminal/browser/terminalActions.js'; import { TerminalContextMenuGroup } from '../../../terminal/browser/terminalMenus.js'; import { TerminalContextKeys } from '../../../terminal/common/terminalContextKey.js'; 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 03f3c10df815b..fc1b0da19938d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -11,19 +11,22 @@ import { CancellationError } from '../../../../../../base/common/errors.js'; import { Event } from '../../../../../../base/common/event.js'; import { MarkdownString, type IMarkdownString } from '../../../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; -import { basename } from '../../../../../../base/common/path.js'; +import { basename, dirname, join } from '../../../../../../base/common/path.js'; +import { FileAccess } from '../../../../../../base/common/network.js'; import { isWindows, OperatingSystem, OS } from '../../../../../../base/common/platform.js'; +import { joinPath } from '../../../../../../base/common/resources.js'; import { count } from '../../../../../../base/common/strings.js'; import { generateUuid } from '../../../../../../base/common/uuid.js'; import { localize } from '../../../../../../nls.js'; -import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { IEnvironmentService, type INativeEnvironmentService } from '../../../../../../platform/environment/common/environment.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 { ISandboxTerminalSettings, ITerminalLogService, ITerminalProfile } from '../../../../../../platform/terminal/common/terminal.js'; import { IRemoteAgentService } from '../../../../../services/remote/common/remoteAgentService.js'; import { TerminalToolConfirmationStorageKeys } from '../../../../chat/browser/chatContentParts/toolInvocationParts/chatTerminalToolConfirmationSubPart.js'; -import { IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService.js'; +import { ElicitationState, IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService.js'; import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolInvocationPresentation, ToolProgress } from '../../../../chat/common/languageModelToolsService.js'; import { ITerminalChatService, ITerminalService, type ITerminalInstance } from '../../../../terminal/browser/terminal.js'; import type { XtermTerminal } from '../../../../terminal/browser/xterm/xtermTerminal.js'; @@ -54,10 +57,10 @@ import { TerminalCommandArtifactCollector } from './terminalCommandArtifactColle import { isNumber, isString } from '../../../../../../base/common/types.js'; import { ChatConfiguration } from '../../../../chat/common/constants.js'; import { IFileService } from '../../../../../../platform/files/common/files.js'; -import { URI } from '../../../../../../base/common/uri.js'; import { VSBuffer } from '../../../../../../base/common/buffer.js'; -import { IUserDataProfileService } from '../../../../../services/userDataProfile/common/userDataProfile.js'; -import { TerminalChatAgentToolsCommandId } from '../../common/terminal.chatAgentTools.js'; +import { IChatWidgetService } from '../../../../chat/browser/chat.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { ChatElicitationRequestPart } from '../../../../chat/browser/chatElicitationRequestPart.js'; // #region Tool data @@ -270,8 +273,9 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { private _lastFailedCommandId: string | undefined; private static _isSandboxedForCommandExecution: boolean | undefined; private static _lastFailedCommandIdList: Set = new Set(); - - + private static _needsForceUpdateConfigFile = true; + private static _tempDir: URI | undefined; + private static _sandboxSettingsId: string | undefined; private readonly _commandLineRewriters: ICommandLineRewriter[]; @@ -294,6 +298,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { constructor( @IChatService private readonly _chatService: IChatService, @IConfigurationService private readonly _configurationService: IConfigurationService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IHistoryService private readonly _historyService: IHistoryService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILanguageModelToolsService private readonly _languageModelToolsService: ILanguageModelToolsService, @@ -304,7 +309,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { @ITerminalService private readonly _terminalService: ITerminalService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IFileService private readonly _fileService: IFileService, - @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService, + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, ) { super(); @@ -325,6 +330,8 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._register(this._instantiationService.createInstance(CommandLineAutoApproveAnalyzer, this._treeSitterCommandParser, this._telemetry, (message, args) => this._logService.info(`RunInTerminalTool#CommandLineAutoApproveAnalyzer: ${message}`, args))), ]; + this._setTempDir(); + // Clear out warning accepted state if the setting is disabled this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { if (!e || e.affectsConfiguration(TerminalChatAgentToolsSettingId.EnableAutoApprove)) { @@ -332,24 +339,12 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); } } - // check for sandbox setting changes + // if terminal sandbox setting changed, set tmp dir. if (e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandbox)) { - const eventDetails: IConfigurationChangeEvent = e; - const sandboxSetting = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandbox); - //update the config file if needed based on where the setting is update. - if (this._isSandboxedTerminal()) { - const inspected = this._configurationService.inspect(TerminalChatAgentToolsSettingId.TerminalSandbox); - if (eventDetails.source === ConfigurationTarget.WORKSPACE && inspected?.workspaceValue) { - // create a config file inside .vscode folder. - this._createConfigFileForSandboxingAtWorkspaceLevel(sandboxSetting); - } else { - this._createConfigForSandboxingAtApplicationLevel(sandboxSetting); - } - } + this._setTempDir(); } })); - // Restore terminal associations from storage this._restoreTerminalAssociations(); this._register(this._terminalService.onDidDisposeInstance(e => { @@ -531,8 +526,12 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { ); if (this._isSandboxedTerminal()) { + this._getSandboxConfigPath(); this._logService.info(`RunInTerminalTool: Sandboxing is enabled, wrapping command with srt.`); - command = `srt --settings "${this._sandboxConfigPath}" "${command}"`; // sandboxing + // Construct full path to srt binary in node_modules/.bin + const appRoot = dirname(FileAccess.asFileUri('').fsPath); + const srtPath = join(appRoot, 'node_modules', '.bin', 'srt'); + command = `"${srtPath}" --settings "${this._sandboxConfigPath}" "${command}"`; // sandboxing } if (token.isCancellationRequested) { @@ -703,18 +702,105 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } terminalResult = resultArr.join('\n\n'); - //if sandboxed and there is error, display a hint message + //if sandboxed and there is error, retry without sandboxing. if (exitCode !== 0 && this._isSandboxedTerminal()) { - this._lastFailedCommandId = commandId; - const mdTrustSettings = { - isTrusted: true - }; - const args = { lastFailedCommandId: this._lastFailedCommandId }; + const sessionResource = invocation.context?.sessionResource; + const session = sessionResource ? this._chatService.getSession(sessionResource) : undefined; + + // Use the unsandboxed command line (no `srt` wrapper), preserving any user/tool edits. + const commandToRetryWithoutSandboxing = toolSpecificData.commandLine.userEdited ?? toolSpecificData.commandLine.toolEdited ?? toolSpecificData.commandLine.original; + + let shouldRetryWithoutSandboxing = false; + if (session?.lastRequest) { + const lastRequest = session.lastRequest; + let didResolve = false; + const decisionPromise = new Promise(resolve => { + const toolElicitation = new ChatElicitationRequestPart( + localize('terminal.sandbox.retry.title', 'Retry Without Sandboxing?'), + new MarkdownString().appendText(localize( + 'terminal.sandbox.retry.message', + 'This command failed due to sandboxing restrictions. Retry without sandboxing?' + )), + localize('terminal.sandbox.retry.subtitle', 'Terminal'), + localize('terminal.sandbox.retry.yes', 'Yes'), + localize('terminal.sandbox.retry.no', 'No'), + async () => { + if (!didResolve) { + didResolve = true; + resolve(true); + } + return ElicitationState.Accepted; + }, + async () => { + if (!didResolve) { + didResolve = true; + resolve(false); + } + return ElicitationState.Rejected; + }, + ); + + this._chatService.appendProgress(lastRequest, toolElicitation); + }); + + const cancellationPromise = new Promise((_resolve, reject) => token.onCancellationRequested(() => reject(new CancellationError()))); + shouldRetryWithoutSandboxing = await Promise.race([decisionPromise, cancellationPromise]); + } + this._lastFailedCommandId = commandId; toolResultMessage = new MarkdownString( - `$(warning) [Retry](command:${TerminalChatAgentToolsCommandId.RetryWithoutSandboxing}?${encodeURIComponent(JSON.stringify(args))}) without sandboxing.`, - mdTrustSettings + `$(warning) ${localize('terminal.sandbox.failed', 'Command failed due to sandboxing restrictions.')}`, + { supportThemeIcons: true } ); + + if (shouldRetryWithoutSandboxing) { + const commandDetectionRetry = toolTerminal.instance.capabilities.get(TerminalCapability.CommandDetection); + let retryStrategy: ITerminalExecuteStrategy; + switch (toolTerminal.shellIntegrationQuality) { + case ShellIntegrationQuality.None: + retryStrategy = this._instantiationService.createInstance(NoneExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false); + break; + case ShellIntegrationQuality.Basic: + retryStrategy = this._instantiationService.createInstance(BasicExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false, commandDetectionRetry!); + break; + case ShellIntegrationQuality.Rich: + retryStrategy = this._instantiationService.createInstance(RichExecuteStrategy, toolTerminal.instance, commandDetectionRetry!); + break; + } + + const retryCommandId = `tool-retry-${generateUuid()}`; + const retryResult = await retryStrategy.execute(commandToRetryWithoutSandboxing, token, retryCommandId); + toolTerminal.receivedUserInput = false; + + await this._commandArtifactCollector.capture(toolSpecificData, toolTerminal.instance, retryCommandId); + { + const state = toolSpecificData.terminalCommandState ?? {}; + state.timestamp = state.timestamp ?? timingStart; + if (retryResult.exitCode !== undefined) { + state.exitCode = retryResult.exitCode; + if (state.timestamp !== undefined) { + state.duration = state.duration ?? Math.max(0, Date.now() - state.timestamp); + } + } + toolSpecificData.terminalCommandState = state; + } + + outputLineCount = retryResult.output === undefined ? 0 : count(retryResult.output.trim(), '\n') + 1; + exitCode = retryResult.exitCode; + error = retryResult.error; + + const retryResultArr: string[] = []; + if (retryResult.output !== undefined) { + retryResultArr.push(retryResult.output); + } + if (retryResult.additionalInformation) { + retryResultArr.push(retryResult.additionalInformation); + } + terminalResult = retryResultArr.join('\n\n'); + + // Retry path: clear failed state + this._lastFailedCommandId = undefined; + } } else { this._lastFailedCommandId = undefined; } @@ -794,83 +880,56 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } - - /** - * Creates a sandbox settings configuration file at the application level. - * The file is stored in the user profile directory (e.g., ~/.config/Code/User/sandbox-settings.json) - * This allows sandbox settings to be persisted across all workspaces for the user. - * @param settings The sandbox settings to write to the file - */ - private async _createConfigForSandboxingAtApplicationLevel(settings: ISandboxTerminalSettings): Promise { - if (!settings?.enabled) { - return; - } - // Get the user profile's sandboxSettings resource URI - const sandboxSettingsUri = this._userDataProfileService.currentProfile.sandboxSettingsResource; - await this._createSandboxConfig(sandboxSettingsUri, settings); - } - private _isSandboxedTerminal(): boolean { if (isWindows) { return false; } const settings = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandbox); const isEnabled = settings?.enabled === true; - if (!isEnabled) { this._sandboxConfigPath = undefined; return false; } - // if its disabled at command execution time, return false if (RunInTerminalTool._isSandboxedForCommandExecution === false && this._lastFailedCommandId && RunInTerminalTool._lastFailedCommandIdList.has(this._lastFailedCommandId)) { this._sandboxConfigPath = undefined; return false; } - - // Determine config path based on configuration level - const inspected = this._configurationService.inspect(TerminalChatAgentToolsSettingId.TerminalSandbox); - if (inspected?.workspaceValue) { - // Workspace-level setting: use workspace .vscode folder - const workSpaceFolder = this._workspaceContextService.getWorkspace().folders[0]?.uri.fsPath; - if (workSpaceFolder) { - this._sandboxConfigPath = URI.file(workSpaceFolder).with({ - path: `${workSpaceFolder}/.vscode/sandbox-settings.json` - }).fsPath; - } - } else { - // User-level setting: use user profile - this._sandboxConfigPath = this._userDataProfileService.currentProfile.sandboxSettingsResource.fsPath; - } return isEnabled; } - private _createConfigFileForSandboxingAtWorkspaceLevel(settings: ISandboxTerminalSettings): void { - const workSpaceFolder = this._workspaceContextService.getWorkspace().folders[0]?.uri.fsPath; - if (!workSpaceFolder) { - return; - } - const sandboxSettingsUri = URI.file(workSpaceFolder).with({ - path: `${workSpaceFolder}/.vscode/sandbox-settings.json` - }); + private async _getSandboxConfigPath(forceRefresh: boolean = false): Promise { - this._createSandboxConfig(sandboxSettingsUri, settings); + if (!this._sandboxConfigPath || forceRefresh || RunInTerminalTool._needsForceUpdateConfigFile) { + this._sandboxConfigPath = await this._createSandboxConfig(); + RunInTerminalTool._needsForceUpdateConfigFile = false; + } + return this._sandboxConfigPath; } - private async _createSandboxConfig(sandboxSettingsUri: URI, settings: ISandboxTerminalSettings): Promise { - const sandboxSettings = { - network: { - allowedDomains: settings.network?.allowedDomains || [], - deniedDomains: settings.network?.deniedDomains || [] - }, - filesystem: { - denyRead: settings.filesystem?.denyRead || [], - allowWrite: settings.filesystem?.allowWrite || ['.'], - denyWrite: settings.filesystem?.denyWrite || [] + private async _createSandboxConfig(): Promise { + const sandboxSetting = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandbox); + if (sandboxSetting.enabled) { + if (!RunInTerminalTool._sandboxSettingsId) { + RunInTerminalTool._sandboxSettingsId = generateUuid(); } - }; - this._sandboxConfigPath = sandboxSettingsUri.fsPath; - this._fileService.createFile(sandboxSettingsUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true }); + const configFileUri = joinPath(RunInTerminalTool._tempDir!, `vscode-sandbox-settings-${RunInTerminalTool._sandboxSettingsId}.json`); + const sandboxSettings = { + network: { + allowedDomains: sandboxSetting.network?.allowedDomains || [], + deniedDomains: sandboxSetting.network?.deniedDomains || [] + }, + filesystem: { + denyRead: sandboxSetting.filesystem?.denyRead || [], + allowWrite: sandboxSetting.filesystem?.allowWrite || ['.'], + denyWrite: sandboxSetting.filesystem?.denyWrite || [] + } + }; + this._sandboxConfigPath = configFileUri.fsPath; + await this._fileService.createFile(configFileUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true }); + return this._sandboxConfigPath; + } + return undefined; } // #region Terminal init @@ -1013,6 +1072,25 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } + private _setTempDir(): void { + if (this._isSandboxedTerminal()) { + RunInTerminalTool._needsForceUpdateConfigFile = true; + const nativeEnv = this._environmentService as Partial; + const tmpDir = nativeEnv.tmpDir; + if (!tmpDir) { + this._logService.warn('RunInTerminalTool: Cannot create sandbox settings file because no tmpDir is available in this environment'); + return; + } + RunInTerminalTool._tempDir = tmpDir; + this._fileService.exists(RunInTerminalTool._tempDir).then(exists => { + if (!exists) { + this._logService.warn(`RunInTerminalTool: tmp directory is not present at ${RunInTerminalTool._tempDir}`); + } + }); + } + } + + private _cleanupSessionTerminals(sessionId: string): void { const toolTerminal = this._sessionTerminalAssociations.get(sessionId); if (toolTerminal) { diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index 3a6891107b643..0236795b7c3c9 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -78,19 +78,19 @@ export const terminalSandboxPropertySchema: IJSONSchema = { properties: { denyRead: { type: 'array', - description: localize('terminalSandbox.filesystem.denyRead', "List of denied file system paths for reading."), + description: localize('terminalSandbox.fileSystem.denyRead', "Array of paths to deny read access. Empty array = full read access."), items: { type: 'string' }, default: [] }, allowWrite: { type: 'array', - description: localize('terminalSandbox.filesystem.allowWrite', "List of allowed file system paths for writing."), + description: localize('terminalSandbox.fileSystem.allowWrite', "Array of paths to allow write access. Empty array = no write access."), items: { type: 'string' }, default: [] }, denyWrite: { type: 'array', - description: localize('terminalSandbox.filesystem.denyWrite', "List of denied file system paths for writing."), + description: localize('terminalSandbox.fileSystem.denyWrite', "Array of paths to deny write access within allowed paths (takes precedence over allowWrite)."), items: { type: 'string' }, default: [] } @@ -102,13 +102,13 @@ export const terminalSandboxPropertySchema: IJSONSchema = { properties: { allowedDomains: { type: 'array', - description: localize('terminalSandbox.network.allowedDomains', "List of allowed network domains."), + description: localize('terminalSandbox.network.allowedDomains', "Array of allowed domains (supports wildcards like *.example.com). Empty array = no network access."), items: { type: 'string' }, default: [] }, deniedDomains: { type: 'array', - description: localize('terminalSandbox.network.deniedDomains', "List of denied network domains."), + description: localize('terminalSandbox.network.deniedDomains', "Array of denied domains (checked first, takes precedence over allowedDomains)."), items: { type: 'string' }, default: [] } @@ -493,10 +493,6 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary Date: Fri, 9 Jan 2026 16:34:58 -0800 Subject: [PATCH 04/31] refactored the code and added utility for sandboxing --- .../chatTerminalToolProgressPart.ts | 5 +- .../terminal.chatAgentTools.contribution.ts | 59 +---- .../browser/tools/runInTerminalTool.ts | 242 ++---------------- 3 files changed, 32 insertions(+), 274 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts index c3f6c7a565d0f..86b13e7690029 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts @@ -49,6 +49,8 @@ import { removeAnsiEscapeCodes } from '../../../../../../../base/common/strings. import { PANEL_BACKGROUND } from '../../../../../../common/theme.js'; import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; +import { ISandboxUtility } from '../../../../common/sandboxUtility.js'; + const MIN_OUTPUT_ROWS = 1; const MAX_OUTPUT_ROWS = 10; @@ -242,6 +244,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, @IKeybindingService private readonly _keybindingService: IKeybindingService, + @ISandboxUtility private readonly _sandboxUtility: ISandboxUtility, ) { super(toolInvocation); @@ -453,7 +456,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart showOutputAction = this._instantiationService.createInstance(ToggleChatTerminalOutputAction, () => this._toggleOutputFromAction()); this._showOutputAction.value = showOutputAction; const exitCode = resolvedCommand?.exitCode ?? this._terminalData.terminalCommandState?.exitCode; - if (exitCode) { + if (exitCode && !this._sandboxUtility.isEnabled()) { this._toggleOutput(true); } } 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 20daffd776011..be0e13666c241 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 @@ -6,16 +6,15 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { isNumber } from '../../../../../base/common/types.js'; -import { localize, localize2 } from '../../../../../nls.js'; -import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { localize } from '../../../../../nls.js'; +import { MenuId } from '../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { TerminalSettingId } from '../../../../../platform/terminal/common/terminal.js'; import { registerWorkbenchContribution2, WorkbenchPhase, type IWorkbenchContribution } from '../../../../common/contributions.js'; import { IChatWidgetService } from '../../../chat/browser/chat.js'; import { ChatContextKeys } from '../../../chat/common/actions/chatContextKeys.js'; -import { IChatService } from '../../../chat/common/chatService/chatService.js'; import { ILanguageModelToolsService } from '../../../chat/common/tools/languageModelToolsService.js'; import { registerActiveInstanceAction, sharedWhenClause } from '../../../terminal/browser/terminalActions.js'; import { TerminalContextMenuGroup } from '../../../terminal/browser/terminalMenus.js'; @@ -30,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 { ISandboxUtility, SandboxUtility } from '../../../chat/common/sandboxUtility.js'; + +// #region Services + +registerSingleton(ISandboxUtility, SandboxUtility, InstantiationType.Delayed); + +// #endregion Services class ShellIntegrationTimeoutMigrationContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'terminal.shellIntegrationTimeoutMigration'; @@ -110,51 +117,7 @@ registerWorkbenchContribution2(ChatAgentToolsContribution.ID, ChatAgentToolsCont // #region Actions -// Retry action for sandboxed terminal commands (invoked via command link in toolResultMessage) -registerAction2(class RetryWithoutSandboxingAction extends Action2 { - constructor() { - super({ - id: TerminalChatAgentToolsCommandId.RetryWithoutSandboxing, - title: localize2('terminal.retryWithoutSandboxing', "Retry without Sandboxing"), - f1: false - }); - } - async run(accessor: ServicesAccessor, failedCommandDetails: { lastFailedCommandId: string }): Promise { - const chatService = accessor.get(IChatService); - const chatWidgetService = accessor.get(IChatWidgetService); - - const widget = chatWidgetService.lastFocusedWidget; - if (!widget) { - return; - } - const model = widget.viewModel?.model; - if (!model) { - return; - } - const requests = model.getRequests(); - const lastRequest = requests?.[requests.length - 1]; - if (!lastRequest) { - return; - } - - try { - // Disable sandboxing - RunInTerminalTool.updateSandboxingForCommandExecution(false, failedCommandDetails.lastFailedCommandId); - const languageModelId = widget?.input.currentLanguageModel; - - // Retry the request - await chatService.resendRequest(lastRequest, { - userSelectedModelId: languageModelId, - attempt: (lastRequest?.attempt ?? -1) + 1, - ...widget?.getModeRequestOptions(), - }); - } finally { - // Re-enable sandboxing - RunInTerminalTool.updateSandboxingForCommandExecution(undefined, failedCommandDetails.lastFailedCommandId); - } - } -}); registerActiveInstanceAction({ id: TerminalChatAgentToolsCommandId.ChatAddTerminalSelection, 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 60f7e218708c4..7f2af31038a06 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -11,19 +11,16 @@ import { CancellationError } from '../../../../../../base/common/errors.js'; import { Event } from '../../../../../../base/common/event.js'; import { MarkdownString, type IMarkdownString } from '../../../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; -import { basename, dirname, join } from '../../../../../../base/common/path.js'; -import { FileAccess } from '../../../../../../base/common/network.js'; -import { isWindows, OperatingSystem, OS } from '../../../../../../base/common/platform.js'; -import { joinPath } from '../../../../../../base/common/resources.js'; +import { basename } from '../../../../../../base/common/path.js'; +import { OperatingSystem, OS } from '../../../../../../base/common/platform.js'; import { count } from '../../../../../../base/common/strings.js'; import { generateUuid } from '../../../../../../base/common/uuid.js'; import { localize } from '../../../../../../nls.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; -import { IEnvironmentService, type INativeEnvironmentService } from '../../../../../../platform/environment/common/environment.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 { ISandboxTerminalSettings, ITerminalLogService, ITerminalProfile } from '../../../../../../platform/terminal/common/terminal.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'; import { ElicitationState, IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService/chatService.js'; @@ -47,6 +44,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 { ISandboxUtility } from '../../../../chat/common/sandboxUtility.js'; import { LocalChatSessionUri } from '../../../../chat/common/model/chatUri.js'; import type { ICommandLineRewriter } from './commandLineRewriter/commandLineRewriter.js'; import { CommandLineCdPrefixRewriter } from './commandLineRewriter/commandLineCdPrefixRewriter.js'; @@ -57,10 +55,7 @@ import { IHistoryService } from '../../../../../services/history/common/history. import { TerminalCommandArtifactCollector } from './terminalCommandArtifactCollector.js'; import { isNumber, isString } from '../../../../../../base/common/types.js'; import { ChatConfiguration } from '../../../../chat/common/constants.js'; -import { IFileService } from '../../../../../../platform/files/common/files.js'; -import { VSBuffer } from '../../../../../../base/common/buffer.js'; import { IChatWidgetService } from '../../../../chat/browser/chat.js'; -import { URI } from '../../../../../../base/common/uri.js'; import { ChatElicitationRequestPart } from '../../../../chat/common/model/chatProgressTypes/chatElicitationRequestPart.js'; import { TerminalChatCommandId } from '../../../chat/browser/terminalChat.js'; @@ -276,13 +271,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { private readonly _telemetry: RunInTerminalToolTelemetry; private readonly _commandArtifactCollector: TerminalCommandArtifactCollector; protected readonly _profileFetcher: TerminalProfileFetcher; - private _sandboxConfigPath: string | undefined; - private _lastFailedCommandId: string | undefined; - private static _isSandboxedForCommandExecution: boolean | undefined; - private static _lastFailedCommandIdList: Set = new Set(); - private static _needsForceUpdateConfigFile = true; - private static _tempDir: URI | undefined; - private static _sandboxSettingsId: string | undefined; private readonly _commandLineRewriters: ICommandLineRewriter[]; @@ -305,7 +293,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { constructor( @IChatService protected readonly _chatService: IChatService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IHistoryService private readonly _historyService: IHistoryService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILanguageModelToolsService private readonly _languageModelToolsService: ILanguageModelToolsService, @@ -316,7 +303,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { @ITerminalService private readonly _terminalService: ITerminalService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, - @IFileService private readonly _fileService: IFileService, + @ISandboxUtility private readonly _sandboxUtility: ISandboxUtility, ) { super(); @@ -338,8 +325,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._register(this._instantiationService.createInstance(CommandLineAutoApproveAnalyzer, this._treeSitterCommandParser, this._telemetry, (message, args) => this._logService.info(`RunInTerminalTool#CommandLineAutoApproveAnalyzer: ${message}`, args))), ]; - this._setTempDir(); - // Clear out warning accepted state if the setting is disabled this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { if (!e || e.affectsConfiguration(TerminalChatAgentToolsSettingId.EnableAutoApprove)) { @@ -347,9 +332,9 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); } } - // if terminal sandbox setting changed, set tmp dir. + // if terminal sandbox setting changed, update sandbox config. if (e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandbox)) { - this._setTempDir(); + this._sandboxUtility.setNeedsForceUpdateConfigFile(); } })); @@ -434,7 +419,8 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { }; } - if (this._isSandboxedTerminal()) { + toolSpecificData.autoApproveInfo = new MarkdownString(localize('autoApprove.sandbox', 'In sandbox mode')); + if (this._sandboxUtility.isEnabled()) { return { toolSpecificData }; @@ -566,14 +552,10 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { toolSpecificData.commandLine.toolEdited !== toolSpecificData.commandLine.original ); - if (this._isSandboxedTerminal()) { - this._getSandboxConfigPath(); + if (this._sandboxUtility.isEnabled()) { + await this._sandboxUtility.getSandboxConfigPath(); this._logService.info(`RunInTerminalTool: Sandboxing is enabled, wrapping command with srt.`); - // Construct full path to srt binary in node_modules/.bin - const appRoot = dirname(FileAccess.asFileUri('').fsPath); - const srtPath = join(appRoot, 'node_modules', '.bin', 'srt'); - command = `"${srtPath}" --debug --settings "${this._sandboxConfigPath}" "${command}"`; // sandboxing with debug - // command = `"${srtPath}" --settings "${this._sandboxConfigPath}" "${command}"`; // sandboxing + command = this._sandboxUtility.wrapCommand(command); } if (token.isCancellationRequested) { @@ -780,7 +762,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } //if sandboxed and there is error, retry without sandboxing. - if (exitCode !== 0 && this._isSandboxedTerminal()) { + if (exitCode !== 0 && this._sandboxUtility.isEnabled()) { const sessionResource = invocation.context?.sessionResource; const session = sessionResource ? this._chatService.getSession(sessionResource) : undefined; @@ -794,11 +776,8 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { const decisionPromise = new Promise(resolve => { const toolElicitation = new ChatElicitationRequestPart( localize('terminal.sandbox.retry.title', 'Retry Without Sandboxing?'), - new MarkdownString().appendText(localize( - 'terminal.sandbox.retry.message', - 'This command failed due to sandboxing restrictions. Retry without sandboxing?' - )), - localize('terminal.sandbox.retry.subtitle', 'Terminal'), + '', + '', localize('terminal.sandbox.retry.yes', 'Yes'), localize('terminal.sandbox.retry.no', 'No'), async () => { @@ -824,7 +803,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { shouldRetryWithoutSandboxing = await Promise.race([decisionPromise, cancellationPromise]); } - this._lastFailedCommandId = commandId; toolResultMessage = new MarkdownString( `$(warning) ${localize('terminal.sandbox.failed', 'Command failed due to sandboxing restrictions.')}`, { supportThemeIcons: true } @@ -875,116 +853,11 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } terminalResult = retryResultArr.join('\n\n'); - // Retry path: clear failed state - this._lastFailedCommandId = undefined; - } - } else { - this._lastFailedCommandId = undefined; - } - - //if sandboxed and there is error, retry without sandboxing. - if (exitCode !== 0 && this._isSandboxedTerminal()) { - const sessionResource = invocation.context?.sessionResource; - const session = sessionResource ? this._chatService.getSession(sessionResource) : undefined; - - // Use the unsandboxed command line (no `srt` wrapper), preserving any user/tool edits. - const commandToRetryWithoutSandboxing = toolSpecificData.commandLine.userEdited ?? toolSpecificData.commandLine.toolEdited ?? toolSpecificData.commandLine.original; - let shouldRetryWithoutSandboxing = false; - if (session?.lastRequest) { - const lastRequest = session.lastRequest; - let didResolve = false; - const decisionPromise = new Promise(resolve => { - const toolElicitation = new ChatElicitationRequestPart( - localize('terminal.sandbox.retry.title', 'Retry Without Sandboxing?'), - new MarkdownString().appendText(localize( - 'terminal.sandbox.retry.message', - 'This command failed due to sandboxing restrictions. Retry without sandboxing?' - )), - localize('terminal.sandbox.retry.subtitle', 'Terminal'), - localize('terminal.sandbox.retry.yes', 'Yes'), - localize('terminal.sandbox.retry.no', 'No'), - async () => { - if (!didResolve) { - didResolve = true; - resolve(true); - } - return ElicitationState.Accepted; - }, - async () => { - if (!didResolve) { - didResolve = true; - resolve(false); - } - return ElicitationState.Rejected; - }, - ); - - this._chatService.appendProgress(lastRequest, toolElicitation); - }); - - const cancellationPromise = new Promise((_resolve, reject) => token.onCancellationRequested(() => reject(new CancellationError()))); - shouldRetryWithoutSandboxing = await Promise.race([decisionPromise, cancellationPromise]); } - - this._lastFailedCommandId = commandId; - toolResultMessage = new MarkdownString( - `$(warning) ${localize('terminal.sandbox.failed', 'Command failed due to sandboxing restrictions.')}`, - { supportThemeIcons: true } - ); - - if (shouldRetryWithoutSandboxing) { - const commandDetectionRetry = toolTerminal.instance.capabilities.get(TerminalCapability.CommandDetection); - let retryStrategy: ITerminalExecuteStrategy; - switch (toolTerminal.shellIntegrationQuality) { - case ShellIntegrationQuality.None: - retryStrategy = this._instantiationService.createInstance(NoneExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false); - break; - case ShellIntegrationQuality.Basic: - retryStrategy = this._instantiationService.createInstance(BasicExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false, commandDetectionRetry!); - break; - case ShellIntegrationQuality.Rich: - retryStrategy = this._instantiationService.createInstance(RichExecuteStrategy, toolTerminal.instance, commandDetectionRetry!); - break; - } - - const retryCommandId = `tool-retry-${generateUuid()}`; - const retryResult = await retryStrategy.execute(commandToRetryWithoutSandboxing, token, retryCommandId); - toolTerminal.receivedUserInput = false; - - await this._commandArtifactCollector.capture(toolSpecificData, toolTerminal.instance, retryCommandId); - { - const state = toolSpecificData.terminalCommandState ?? {}; - state.timestamp = state.timestamp ?? timingStart; - if (retryResult.exitCode !== undefined) { - state.exitCode = retryResult.exitCode; - if (state.timestamp !== undefined) { - state.duration = state.duration ?? Math.max(0, Date.now() - state.timestamp); - } - } - toolSpecificData.terminalCommandState = state; - } - - outputLineCount = retryResult.output === undefined ? 0 : count(retryResult.output.trim(), '\n') + 1; - exitCode = retryResult.exitCode; - error = retryResult.error; - - const retryResultArr: string[] = []; - if (retryResult.output !== undefined) { - retryResultArr.push(retryResult.output); - } - if (retryResult.additionalInformation) { - retryResultArr.push(retryResult.additionalInformation); - } - terminalResult = retryResultArr.join('\n\n'); - - // Retry path: clear failed state - this._lastFailedCommandId = undefined; - } - } else { - this._lastFailedCommandId = undefined; } + } catch (e) { this._logService.debug(`RunInTerminalTool: Threw exception`); toolTerminal.instance.dispose(); @@ -1035,8 +908,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { return { toolResultMessage, toolMetadata: { - exitCode: exitCode, - sandboxingFailed: exitCode !== 0 && this._isSandboxedTerminal() + exitCode: exitCode }, content: [{ kind: 'text', @@ -1046,16 +918,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } - // This is called when the user clicks the "Retry without sandboxing" link. - public static updateSandboxingForCommandExecution(enabled: boolean | undefined, lastFailedCommandId: string): void { - RunInTerminalTool._isSandboxedForCommandExecution = enabled; - if (enabled === false && lastFailedCommandId) { - RunInTerminalTool._lastFailedCommandIdList.add(lastFailedCommandId); - } else { - RunInTerminalTool._lastFailedCommandIdList.delete(lastFailedCommandId); - } - - } private _handleTerminalVisibility(toolTerminal: IToolTerminal, chatSessionId: string) { const chatSessionOpenInWidget = !!this._chatWidgetService.getWidgetBySessionResource(LocalChatSessionUri.forSession(chatSessionId)); @@ -1065,58 +927,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } - private _isSandboxedTerminal(): boolean { - if (isWindows) { - return false; - } - const settings = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandbox); - const isEnabled = settings?.enabled === true; - if (!isEnabled) { - this._sandboxConfigPath = undefined; - return false; - } - // if its disabled at command execution time, return false - if (RunInTerminalTool._isSandboxedForCommandExecution === false && this._lastFailedCommandId && RunInTerminalTool._lastFailedCommandIdList.has(this._lastFailedCommandId)) { - this._sandboxConfigPath = undefined; - return false; - } - return isEnabled; - } - - private async _getSandboxConfigPath(forceRefresh: boolean = false): Promise { - - if (!this._sandboxConfigPath || forceRefresh || RunInTerminalTool._needsForceUpdateConfigFile) { - this._sandboxConfigPath = await this._createSandboxConfig(); - RunInTerminalTool._needsForceUpdateConfigFile = false; - } - return this._sandboxConfigPath; - } - - private async _createSandboxConfig(): Promise { - const sandboxSetting = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandbox); - if (sandboxSetting.enabled) { - if (!RunInTerminalTool._sandboxSettingsId) { - RunInTerminalTool._sandboxSettingsId = generateUuid(); - } - const configFileUri = joinPath(RunInTerminalTool._tempDir!, `vscode-sandbox-settings-${RunInTerminalTool._sandboxSettingsId}.json`); - const sandboxSettings = { - network: { - allowedDomains: sandboxSetting.network?.allowedDomains || [], - deniedDomains: sandboxSetting.network?.deniedDomains || [] - }, - filesystem: { - denyRead: sandboxSetting.filesystem?.denyRead || [], - allowWrite: sandboxSetting.filesystem?.allowWrite || ['.'], - denyWrite: sandboxSetting.filesystem?.denyWrite || [] - } - }; - this._sandboxConfigPath = configFileUri.fsPath; - await this._fileService.createFile(configFileUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true }); - return this._sandboxConfigPath; - } - return undefined; - } - // #region Terminal init private async _initBackgroundTerminal(chatSessionId: string, termId: string, terminalToolSessionId: string | undefined, token: CancellationToken): Promise { @@ -1259,24 +1069,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } - private _setTempDir(): void { - if (this._isSandboxedTerminal()) { - RunInTerminalTool._needsForceUpdateConfigFile = true; - const nativeEnv = this._environmentService as Partial; - const tmpDir = nativeEnv.tmpDir; - if (!tmpDir) { - this._logService.warn('RunInTerminalTool: Cannot create sandbox settings file because no tmpDir is available in this environment'); - return; - } - RunInTerminalTool._tempDir = tmpDir; - this._fileService.exists(RunInTerminalTool._tempDir).then(exists => { - if (!exists) { - this._logService.warn(`RunInTerminalTool: tmp directory is not present at ${RunInTerminalTool._tempDir}`); - } - }); - } - } - private _cleanupSessionTerminals(sessionId: string): void { const toolTerminal = this._sessionTerminalAssociations.get(sessionId); From a7ce30e5ae5148858b576fa337bac5f626abeff6 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Fri, 9 Jan 2026 18:49:59 -0800 Subject: [PATCH 05/31] refactored the code and added utility for sandboxing --- .../contrib/chat/common/sandboxUtility.ts | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/vs/workbench/contrib/chat/common/sandboxUtility.ts diff --git a/src/vs/workbench/contrib/chat/common/sandboxUtility.ts b/src/vs/workbench/contrib/chat/common/sandboxUtility.ts new file mode 100644 index 0000000000000..3ce0717ace23f --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/sandboxUtility.ts @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isWindows } from '../../../../base/common/platform.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { ISandboxTerminalSettings, ITerminalLogService } from '../../../../platform/terminal/common/terminal.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { dirname, join } from '../../../../base/common/path.js'; +import { FileAccess } from '../../../../base/common/network.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { joinPath } from '../../../../base/common/resources.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { IEnvironmentService, type INativeEnvironmentService } from '../../../../platform/environment/common/environment.js'; + +export const ISandboxUtility = createDecorator('sandboxUtility'); + +export interface ISandboxUtility { + readonly _serviceBrand: undefined; + isEnabled(): boolean; + wrapCommand(command: string): string; + getSandboxConfigPath(forceRefresh?: boolean): Promise; + getTempDir(): URI | undefined; + setNeedsForceUpdateConfigFile(): void; +} + +export class SandboxUtility implements ISandboxUtility { + readonly _serviceBrand: undefined; + private static _srtPath: string; + private static _sandboxConfigPath: string | undefined; + private _needsForceUpdateConfigFile = true; + private static _tempDir: URI | undefined; + private static _sandboxSettingsId: string | undefined; + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IFileService private readonly _fileService: IFileService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @ITerminalLogService private readonly _logService: ITerminalLogService, + ) { + const appRoot = dirname(FileAccess.asFileUri('').fsPath); + SandboxUtility._srtPath = SandboxUtility._srtPath ?? join(appRoot, 'node_modules', '.bin', 'srt'); + SandboxUtility._sandboxSettingsId = generateUuid(); + this._initTempDir(); + } + + public isEnabled(): boolean { + if (isWindows) { + return false; + } + const settings = this._configurationService.getValue('chat.tools.terminal.sandbox'); + const isEnabled = settings?.enabled === true; + if (!isEnabled) { + return false; + } + return true; + } + + public wrapCommand(command: string): string { + if (!SandboxUtility._sandboxConfigPath || !SandboxUtility._tempDir) { + throw new Error('Sandbox config path or temp dir not initialized'); + } + return `"${SandboxUtility._srtPath}" TMPDIR=${SandboxUtility._tempDir.fsPath} --settings "${SandboxUtility._sandboxConfigPath}" "${command}"`; + } + + public getTempDir(): URI | undefined { + return SandboxUtility._tempDir; + } + + public setNeedsForceUpdateConfigFile(): void { + this._needsForceUpdateConfigFile = true; + } + + public async getSandboxConfigPath(forceRefresh: boolean = false): Promise { + if (!SandboxUtility._sandboxConfigPath || forceRefresh || this._needsForceUpdateConfigFile) { + SandboxUtility._sandboxConfigPath = await this._createSandboxConfig(); + this._needsForceUpdateConfigFile = false; + } + return SandboxUtility._sandboxConfigPath; + } + + private async _createSandboxConfig(): Promise { + const sandboxSetting = this._configurationService.getValue('chat.tools.terminal.sandbox'); + if (sandboxSetting.enabled) { + const configFileUri = joinPath(SandboxUtility._tempDir!, `vscode-sandbox-settings-${SandboxUtility._sandboxSettingsId}.json`); + const sandboxSettings = { + network: { + allowedDomains: sandboxSetting.network?.allowedDomains || [], + deniedDomains: sandboxSetting.network?.deniedDomains || [] + }, + filesystem: { + denyRead: sandboxSetting.filesystem?.denyRead || [], + allowWrite: sandboxSetting.filesystem?.allowWrite || ['.'], + denyWrite: sandboxSetting.filesystem?.denyWrite || [] + } + }; + SandboxUtility._sandboxConfigPath = configFileUri.fsPath; + await this._fileService.createFile(configFileUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true }); + return SandboxUtility._sandboxConfigPath; + } + return undefined; + } + + private _initTempDir(): void { + if (this.isEnabled()) { + this._needsForceUpdateConfigFile = true; + const nativeEnv = this._environmentService as Partial; + const tmpDir = nativeEnv.tmpDir; + if (!tmpDir) { + this._logService.warn('SandboxUtility: Cannot create sandbox settings file because no tmpDir is available in this environment'); + return; + } + SandboxUtility._tempDir = tmpDir; + this._fileService.exists(SandboxUtility._tempDir).then(exists => { + if (!exists) { + this._logService.warn(`SandboxUtility: tmp directory is not present at ${SandboxUtility._tempDir}`); + } + }); + } + } +} From 63cac43490a2431440c42bbea35e1cbdbb09b535 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Fri, 9 Jan 2026 18:56:03 -0800 Subject: [PATCH 06/31] refactored the code and added utility for sandboxing --- src/vs/workbench/contrib/chat/common/sandboxUtility.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/sandboxUtility.ts b/src/vs/workbench/contrib/chat/common/sandboxUtility.ts index 3ce0717ace23f..62ab5e8cc56af 100644 --- a/src/vs/workbench/contrib/chat/common/sandboxUtility.ts +++ b/src/vs/workbench/contrib/chat/common/sandboxUtility.ts @@ -34,6 +34,7 @@ export class SandboxUtility implements ISandboxUtility { private _needsForceUpdateConfigFile = true; private static _tempDir: URI | undefined; private static _sandboxSettingsId: string | undefined; + private static readonly TERMINAL_SANDBOX_SETTING_ID = 'chat.tools.terminal.sandbox'; constructor( @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -51,7 +52,7 @@ export class SandboxUtility implements ISandboxUtility { if (isWindows) { return false; } - const settings = this._configurationService.getValue('chat.tools.terminal.sandbox'); + const settings = this._configurationService.getValue(SandboxUtility.TERMINAL_SANDBOX_SETTING_ID); const isEnabled = settings?.enabled === true; if (!isEnabled) { return false; @@ -83,7 +84,7 @@ export class SandboxUtility implements ISandboxUtility { } private async _createSandboxConfig(): Promise { - const sandboxSetting = this._configurationService.getValue('chat.tools.terminal.sandbox'); + const sandboxSetting = this._configurationService.getValue(SandboxUtility.TERMINAL_SANDBOX_SETTING_ID); if (sandboxSetting.enabled) { const configFileUri = joinPath(SandboxUtility._tempDir!, `vscode-sandbox-settings-${SandboxUtility._sandboxSettingsId}.json`); const sandboxSettings = { From caf2c45a765598a9f86cfd62df347ca0fd441109 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Sun, 11 Jan 2026 21:24:52 -0800 Subject: [PATCH 07/31] fixing build error --- .../workbench/contrib/chat/common/sandboxUtility.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/sandboxUtility.ts b/src/vs/workbench/contrib/chat/common/sandboxUtility.ts index 62ab5e8cc56af..b6d84c6cecac5 100644 --- a/src/vs/workbench/contrib/chat/common/sandboxUtility.ts +++ b/src/vs/workbench/contrib/chat/common/sandboxUtility.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isWindows } from '../../../../base/common/platform.js'; +import { isNative, isWindows } from '../../../../base/common/platform.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { ISandboxTerminalSettings, ITerminalLogService } from '../../../../platform/terminal/common/terminal.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -14,7 +14,7 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { joinPath } from '../../../../base/common/resources.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { IEnvironmentService, type INativeEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; export const ISandboxUtility = createDecorator('sandboxUtility'); @@ -85,7 +85,7 @@ export class SandboxUtility implements ISandboxUtility { private async _createSandboxConfig(): Promise { const sandboxSetting = this._configurationService.getValue(SandboxUtility.TERMINAL_SANDBOX_SETTING_ID); - if (sandboxSetting.enabled) { + if (sandboxSetting.enabled && SandboxUtility._tempDir) { const configFileUri = joinPath(SandboxUtility._tempDir!, `vscode-sandbox-settings-${SandboxUtility._sandboxSettingsId}.json`); const sandboxSettings = { network: { @@ -106,10 +106,9 @@ export class SandboxUtility implements ISandboxUtility { } private _initTempDir(): void { - if (this.isEnabled()) { + if (this.isEnabled() && isNative) { this._needsForceUpdateConfigFile = true; - const nativeEnv = this._environmentService as Partial; - const tmpDir = nativeEnv.tmpDir; + const tmpDir = (this._environmentService as IEnvironmentService & { tmpDir: URI }).tmpDir; if (!tmpDir) { this._logService.warn('SandboxUtility: Cannot create sandbox settings file because no tmpDir is available in this environment'); return; From 5c216914a4866f054ee4f74d993af8f90c2472d6 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Tue, 13 Jan 2026 17:07:26 -0800 Subject: [PATCH 08/31] review suggestions --- package.json | 2 +- .../electron-main/storageMainService.test.ts | 3 +- src/vs/platform/terminal/common/terminal.ts | 13 +- .../common/terminalPlatformConfiguration.ts | 45 ----- .../userData/common/fileUserDataProvider.ts | 1 - .../userDataProfile/common/userDataProfile.ts | 7 +- .../chatTerminalToolProgressPart.ts | 7 +- .../contrib/chat/common/sandboxUtility.ts | 124 -------------- .../terminal/terminalContribExports.ts | 6 +- .../terminal.chatAgentTools.contribution.ts | 7 +- .../browser/tools/runInTerminalTool.ts | 86 +++++----- .../terminalChatAgentToolsConfiguration.ts | 162 ++++++++++-------- .../runInTerminalTool.test.ts | 1 + .../test/browser/storageService.test.ts | 3 +- .../userDataProfileImportExportService.ts | 3 +- 15 files changed, 147 insertions(+), 323 deletions(-) delete mode 100644 src/vs/workbench/contrib/chat/common/sandboxUtility.ts diff --git a/package.json b/package.json index e4216017d3076..e3b75ffcb4b34 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,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.13", + "@anthropic-ai/sandbox-runtime": "0.0.26", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", "@types/semver": "^7.5.8", diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index b1e41708dc333..a79d841341e78 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -47,8 +47,7 @@ suite('StorageMainService', function () { snippetsHome: joinPath(inMemoryProfileRoot, 'snippetsHome'), promptsHome: joinPath(inMemoryProfileRoot, 'promptsHome'), extensionsResource: joinPath(inMemoryProfileRoot, 'extensionsResource'), - cacheHome: joinPath(inMemoryProfileRoot, 'cache'), - sandboxSettingsResource: joinPath(inMemoryProfileRoot, 'sandbox-settings.json') + cacheHome: joinPath(inMemoryProfileRoot, 'cache') }; class TestStorageMainService extends StorageMainService { diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index f3e78077a72c9..1e468cdbdf76d 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -686,17 +686,8 @@ export interface IShellLaunchConfig { shellIntegrationNonce?: string; } -export interface ISandboxTerminalSettings { +export interface ITerminalSandboxSettings extends ISandboxRuntimeConfig { enabled?: boolean; - network?: { - allowedDomains?: string[]; - deniedDomains?: string[]; - }; - filesystem?: { - denyRead?: string[]; - allowWrite?: string[]; - denyWrite?: string[]; - }; } @@ -740,8 +731,6 @@ export interface IShellLaunchConfigDto { isFeatureTerminal?: boolean; tabActions?: ITerminalTabAction[]; shellIntegrationEnvironmentReporting?: boolean; - sandboxed?: boolean; - sandboxSettings?: ISandboxRuntimeConfig; } /** diff --git a/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts b/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts index fcdf374d01536..27fd88b46d6cd 100644 --- a/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts +++ b/src/vs/platform/terminal/common/terminalPlatformConfiguration.ts @@ -59,51 +59,6 @@ export const terminalProfileBaseProperties: IJSONSchemaMap = { } }; -export const terminalSandboxPropertySchema: IJSONSchemaMap = { - filesystem: { - type: 'object', - description: localize('terminalSandbox.fileSystem', "Controls file system access in the terminal sandbox."), - properties: { - allowWrite: { - type: 'array', - description: localize('terminalSandbox.fileSystem.allowedPaths', "List of allowed file system paths."), - items: { type: 'string' }, - default: [] - }, - denyRead: { - type: 'array', - description: localize('terminalSandbox.fileSystem.deniedPaths', "List of denied file system paths."), - items: { type: 'string' }, - default: [] - }, - denyWrite: { - type: 'array', - description: localize('terminalSandbox.fileSystem.deniedWritePaths', "List of denied file system write paths."), - items: { type: 'string' }, - default: [] - } - } - }, - network: { - type: 'object', - description: localize('terminalSandbox.network', "Controls network access in the terminal sandbox."), - properties: { - allowedDomains: { - type: 'array', - description: localize('terminalSandbox.network.allowedHosts', "List of allowed network hosts."), - items: { type: 'string' }, - default: [] - }, - deniedDomains: { - type: 'array', - description: localize('terminalSandbox.network.deniedDomains', "List of denied network domains."), - items: { type: 'string' }, - default: [] - } - } - } -}; - const terminalProfileSchema: IJSONSchema = { type: 'object', required: ['path'], diff --git a/src/vs/platform/userData/common/fileUserDataProvider.ts b/src/vs/platform/userData/common/fileUserDataProvider.ts index 079c54eb0a4a6..efa390b0ad2cd 100644 --- a/src/vs/platform/userData/common/fileUserDataProvider.ts +++ b/src/vs/platform/userData/common/fileUserDataProvider.ts @@ -65,7 +65,6 @@ export class FileUserDataProvider extends Disposable implements this.atomicReadWriteResources.add(profile.keybindingsResource); this.atomicReadWriteResources.add(profile.tasksResource); this.atomicReadWriteResources.add(profile.extensionsResource); - this.atomicReadWriteResources.add(profile.sandboxSettingsResource); } } diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 6afafefba42ee..9f2b991408681 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -56,7 +56,6 @@ export interface IUserDataProfile { readonly useDefaultFlags?: UseDefaultProfileFlags; readonly isTransient?: boolean; readonly workspaces?: readonly URI[]; - readonly sandboxSettingsResource: URI; } export function isUserDataProfile(thing: unknown): thing is IUserDataProfile { @@ -145,8 +144,7 @@ export function reviveProfile(profile: UriDto, scheme: string) cacheHome: URI.revive(profile.cacheHome).with({ scheme }), useDefaultFlags: profile.useDefaultFlags, isTransient: profile.isTransient, - workspaces: profile.workspaces?.map(w => URI.revive(w)), - sandboxSettingsResource: URI.revive(profile.sandboxSettingsResource).with({ scheme }) + workspaces: profile.workspaces?.map(w => URI.revive(w)) }; } @@ -168,8 +166,7 @@ export function toUserDataProfile(id: string, name: string, location: URI, profi cacheHome: joinPath(profilesCacheHome, id), useDefaultFlags: options?.useDefaultFlags, isTransient: options?.transient, - workspaces: options?.workspaces, - sandboxSettingsResource: joinPath(location, 'sandbox-settings.json') + workspaces: options?.workspaces }; } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts index 86b13e7690029..a62648b3fc295 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts @@ -49,8 +49,7 @@ import { removeAnsiEscapeCodes } from '../../../../../../../base/common/strings. import { PANEL_BACKGROUND } from '../../../../../../common/theme.js'; import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; -import { ISandboxUtility } from '../../../../common/sandboxUtility.js'; - +import { ISandboxService } from '../../../../common/sandboxService.js'; const MIN_OUTPUT_ROWS = 1; const MAX_OUTPUT_ROWS = 10; @@ -244,7 +243,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @ISandboxUtility private readonly _sandboxUtility: ISandboxUtility, + @ISandboxService private readonly _sandboxService: ISandboxService, ) { super(toolInvocation); @@ -456,7 +455,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart showOutputAction = this._instantiationService.createInstance(ToggleChatTerminalOutputAction, () => this._toggleOutputFromAction()); this._showOutputAction.value = showOutputAction; const exitCode = resolvedCommand?.exitCode ?? this._terminalData.terminalCommandState?.exitCode; - if (exitCode && !this._sandboxUtility.isEnabled()) { + if (exitCode && !this._sandboxService.isEnabled()) { this._toggleOutput(true); } } diff --git a/src/vs/workbench/contrib/chat/common/sandboxUtility.ts b/src/vs/workbench/contrib/chat/common/sandboxUtility.ts deleted file mode 100644 index b6d84c6cecac5..0000000000000 --- a/src/vs/workbench/contrib/chat/common/sandboxUtility.ts +++ /dev/null @@ -1,124 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { isNative, isWindows } from '../../../../base/common/platform.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { ISandboxTerminalSettings, ITerminalLogService } from '../../../../platform/terminal/common/terminal.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { dirname, join } from '../../../../base/common/path.js'; -import { FileAccess } from '../../../../base/common/network.js'; -import { URI } from '../../../../base/common/uri.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { VSBuffer } from '../../../../base/common/buffer.js'; -import { joinPath } from '../../../../base/common/resources.js'; -import { generateUuid } from '../../../../base/common/uuid.js'; -import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; - -export const ISandboxUtility = createDecorator('sandboxUtility'); - -export interface ISandboxUtility { - readonly _serviceBrand: undefined; - isEnabled(): boolean; - wrapCommand(command: string): string; - getSandboxConfigPath(forceRefresh?: boolean): Promise; - getTempDir(): URI | undefined; - setNeedsForceUpdateConfigFile(): void; -} - -export class SandboxUtility implements ISandboxUtility { - readonly _serviceBrand: undefined; - private static _srtPath: string; - private static _sandboxConfigPath: string | undefined; - private _needsForceUpdateConfigFile = true; - private static _tempDir: URI | undefined; - private static _sandboxSettingsId: string | undefined; - private static readonly TERMINAL_SANDBOX_SETTING_ID = 'chat.tools.terminal.sandbox'; - - constructor( - @IConfigurationService private readonly _configurationService: IConfigurationService, - @IFileService private readonly _fileService: IFileService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @ITerminalLogService private readonly _logService: ITerminalLogService, - ) { - const appRoot = dirname(FileAccess.asFileUri('').fsPath); - SandboxUtility._srtPath = SandboxUtility._srtPath ?? join(appRoot, 'node_modules', '.bin', 'srt'); - SandboxUtility._sandboxSettingsId = generateUuid(); - this._initTempDir(); - } - - public isEnabled(): boolean { - if (isWindows) { - return false; - } - const settings = this._configurationService.getValue(SandboxUtility.TERMINAL_SANDBOX_SETTING_ID); - const isEnabled = settings?.enabled === true; - if (!isEnabled) { - return false; - } - return true; - } - - public wrapCommand(command: string): string { - if (!SandboxUtility._sandboxConfigPath || !SandboxUtility._tempDir) { - throw new Error('Sandbox config path or temp dir not initialized'); - } - return `"${SandboxUtility._srtPath}" TMPDIR=${SandboxUtility._tempDir.fsPath} --settings "${SandboxUtility._sandboxConfigPath}" "${command}"`; - } - - public getTempDir(): URI | undefined { - return SandboxUtility._tempDir; - } - - public setNeedsForceUpdateConfigFile(): void { - this._needsForceUpdateConfigFile = true; - } - - public async getSandboxConfigPath(forceRefresh: boolean = false): Promise { - if (!SandboxUtility._sandboxConfigPath || forceRefresh || this._needsForceUpdateConfigFile) { - SandboxUtility._sandboxConfigPath = await this._createSandboxConfig(); - this._needsForceUpdateConfigFile = false; - } - return SandboxUtility._sandboxConfigPath; - } - - private async _createSandboxConfig(): Promise { - const sandboxSetting = this._configurationService.getValue(SandboxUtility.TERMINAL_SANDBOX_SETTING_ID); - if (sandboxSetting.enabled && SandboxUtility._tempDir) { - const configFileUri = joinPath(SandboxUtility._tempDir!, `vscode-sandbox-settings-${SandboxUtility._sandboxSettingsId}.json`); - const sandboxSettings = { - network: { - allowedDomains: sandboxSetting.network?.allowedDomains || [], - deniedDomains: sandboxSetting.network?.deniedDomains || [] - }, - filesystem: { - denyRead: sandboxSetting.filesystem?.denyRead || [], - allowWrite: sandboxSetting.filesystem?.allowWrite || ['.'], - denyWrite: sandboxSetting.filesystem?.denyWrite || [] - } - }; - SandboxUtility._sandboxConfigPath = configFileUri.fsPath; - await this._fileService.createFile(configFileUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true }); - return SandboxUtility._sandboxConfigPath; - } - return undefined; - } - - private _initTempDir(): void { - if (this.isEnabled() && isNative) { - this._needsForceUpdateConfigFile = true; - const tmpDir = (this._environmentService as IEnvironmentService & { tmpDir: URI }).tmpDir; - if (!tmpDir) { - this._logService.warn('SandboxUtility: Cannot create sandbox settings file because no tmpDir is available in this environment'); - return; - } - SandboxUtility._tempDir = tmpDir; - this._fileService.exists(SandboxUtility._tempDir).then(exists => { - if (!exists) { - this._logService.warn(`SandboxUtility: tmp directory is not present at ${SandboxUtility._tempDir}`); - } - }); - } - } -} diff --git a/src/vs/workbench/contrib/terminal/terminalContribExports.ts b/src/vs/workbench/contrib/terminal/terminalContribExports.ts index 59f7bae295e0d..42c00ae2be1c1 100644 --- a/src/vs/workbench/contrib/terminal/terminalContribExports.ts +++ b/src/vs/workbench/contrib/terminal/terminalContribExports.ts @@ -43,7 +43,11 @@ export const enum TerminalContribSettingId { AutoApprove = TerminalChatAgentToolsSettingId.AutoApprove, EnableAutoApprove = TerminalChatAgentToolsSettingId.EnableAutoApprove, ShellIntegrationTimeout = TerminalChatAgentToolsSettingId.ShellIntegrationTimeout, - OutputLocation = TerminalChatAgentToolsSettingId.OutputLocation + OutputLocation = TerminalChatAgentToolsSettingId.OutputLocation, + TerminalSandboxEnabled = TerminalChatAgentToolsSettingId.TerminalSandboxEnabled, + TerminalSandboxNetwork = TerminalChatAgentToolsSettingId.TerminalSandboxNetwork, + TerminalSandboxLinuxFileSystem = TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem, + TerminalSandboxMacFileSystem = TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem, } // 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 be0e13666c241..61de8079aceb7 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 @@ -30,11 +30,11 @@ import { CreateAndRunTaskTool, CreateAndRunTaskToolData } from './tools/task/cre 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 { ISandboxUtility, SandboxUtility } from '../../../chat/common/sandboxUtility.js'; +import { ISandboxService, SandboxService } from '../../../chat/common/sandboxService.js'; // #region Services -registerSingleton(ISandboxUtility, SandboxUtility, InstantiationType.Delayed); +registerSingleton(ISandboxService, SandboxService, InstantiationType.Delayed); // #endregion Services @@ -64,7 +64,6 @@ class ChatAgentToolsContribution extends Disposable implements IWorkbenchContrib constructor( @IInstantiationService instantiationService: IInstantiationService, @ILanguageModelToolsService toolsService: ILanguageModelToolsService, - @IConfigurationService configurationService: IConfigurationService, ) { super(); @@ -117,8 +116,6 @@ registerWorkbenchContribution2(ChatAgentToolsContribution.ID, ChatAgentToolsCont // #region Actions - - registerActiveInstanceAction({ id: TerminalChatAgentToolsCommandId.ChatAddTerminalSelection, title: localize('addTerminalSelection', 'Add Terminal Selection to Chat'), 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 7f2af31038a06..ef97463980c65 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -19,7 +19,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'; @@ -44,7 +44,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 { ISandboxUtility } from '../../../../chat/common/sandboxUtility.js'; +import { ISandboxService } from '../../../../chat/common/sandboxService.js'; import { LocalChatSessionUri } from '../../../../chat/common/model/chatUri.js'; import type { ICommandLineRewriter } from './commandLineRewriter/commandLineRewriter.js'; import { CommandLineCdPrefixRewriter } from './commandLineRewriter/commandLineCdPrefixRewriter.js'; @@ -270,18 +270,14 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { private readonly _treeSitterCommandParser: TreeSitterCommandParser; private readonly _telemetry: RunInTerminalToolTelemetry; private readonly _commandArtifactCollector: TerminalCommandArtifactCollector; - protected readonly _profileFetcher: TerminalProfileFetcher; - - private readonly _commandLineRewriters: ICommandLineRewriter[]; private readonly _commandLineAnalyzers: ICommandLineAnalyzer[]; - + private static readonly _backgroundExecutions = new Map(); + protected readonly _profileFetcher: TerminalProfileFetcher; protected readonly _sessionTerminalAssociations: Map = new Map(); - // Immutable window state protected readonly _osBackend: Promise; - private static readonly _backgroundExecutions = new Map(); public static getBackgroundOutput(id: string): string { const backgroundExecution = RunInTerminalTool._backgroundExecutions.get(id); if (!backgroundExecution) { @@ -303,7 +299,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { @ITerminalService private readonly _terminalService: ITerminalService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, - @ISandboxUtility private readonly _sandboxUtility: ISandboxUtility, + @ISandboxService private readonly _sandboxService: ISandboxService, ) { super(); @@ -332,9 +328,14 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); } } - // if terminal sandbox setting changed, update sandbox config. - if (e?.affectsConfiguration(TerminalChatAgentToolsSettingId.TerminalSandbox)) { - this._sandboxUtility.setNeedsForceUpdateConfigFile(); + // 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(); } })); @@ -420,7 +421,9 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } toolSpecificData.autoApproveInfo = new MarkdownString(localize('autoApprove.sandbox', 'In sandbox mode')); - if (this._sandboxUtility.isEnabled()) { + // 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()) { return { toolSpecificData }; @@ -552,10 +555,10 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { toolSpecificData.commandLine.toolEdited !== toolSpecificData.commandLine.original ); - if (this._sandboxUtility.isEnabled()) { - await this._sandboxUtility.getSandboxConfigPath(); + if (this._sandboxService.isEnabled()) { + await this._sandboxService.getSandboxConfigPath(); this._logService.info(`RunInTerminalTool: Sandboxing is enabled, wrapping command with srt.`); - command = this._sandboxUtility.wrapCommand(command); + command = this._sandboxService.wrapCommand(command); } if (token.isCancellationRequested) { @@ -686,21 +689,9 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { let altBufferResult: IToolResult | undefined; const executeCancellation = store.add(new CancellationTokenSource(token)); 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 +753,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } //if sandboxed and there is error, retry without sandboxing. - if (exitCode !== 0 && this._sandboxUtility.isEnabled()) { + if (exitCode !== 0 && this._sandboxService.isEnabled()) { const sessionResource = invocation.context?.sessionResource; const session = sessionResource ? this._chatService.getSession(sessionResource) : undefined; @@ -810,19 +801,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { if (shouldRetryWithoutSandboxing) { const commandDetectionRetry = toolTerminal.instance.capabilities.get(TerminalCapability.CommandDetection); - let retryStrategy: ITerminalExecuteStrategy; - switch (toolTerminal.shellIntegrationQuality) { - case ShellIntegrationQuality.None: - retryStrategy = this._instantiationService.createInstance(NoneExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false); - break; - case ShellIntegrationQuality.Basic: - retryStrategy = this._instantiationService.createInstance(BasicExecuteStrategy, toolTerminal.instance, () => toolTerminal.receivedUserInput ?? false, commandDetectionRetry!); - break; - case ShellIntegrationQuality.Rich: - retryStrategy = this._instantiationService.createInstance(RichExecuteStrategy, toolTerminal.instance, commandDetectionRetry!); - break; - } - + const retryStrategy = this._getExecuteStrategy(toolTerminal.shellIntegrationQuality, toolTerminal, commandDetectionRetry!); const retryCommandId = `tool-retry-${generateUuid()}`; const retryResult = await retryStrategy.execute(commandToRetryWithoutSandboxing, token, retryCommandId); toolTerminal.receivedUserInput = false; @@ -1069,7 +1048,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } - private _cleanupSessionTerminals(sessionId: string): void { const toolTerminal = this._sessionTerminalAssociations.get(sessionId); if (toolTerminal) { @@ -1091,6 +1069,22 @@ 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 05622572a280a..f242598fe24b1 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -21,7 +21,10 @@ export const enum TerminalChatAgentToolsSettingId { ShellIntegrationTimeout = 'chat.tools.terminal.shellIntegrationTimeout', AutoReplyToPrompts = 'chat.tools.terminal.autoReplyToPrompts', OutputLocation = 'chat.tools.terminal.outputLocation', - TerminalSandbox = 'chat.tools.terminal.sandbox', + 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', TerminalProfileLinux = 'chat.tools.terminal.terminalProfile.linux', @@ -66,60 +69,6 @@ const terminalChatAgentProfileSchema: IJSONSchema = { } }; -export const terminalSandboxPropertySchema: IJSONSchema = { - type: 'object', - required: ['enabled'], - properties: { - enabled: { - type: 'boolean', - description: localize('terminalSandbox.enabled', "Controls whether to run commands in a sandboxed terminal for the run in terminal tool."), - }, - filesystem: { - type: 'object', - description: localize('terminalSandbox.filesystem', "Controls file system access in the terminal sandbox."), - properties: { - denyRead: { - type: 'array', - description: localize('terminalSandbox.fileSystem.denyRead', "Array of paths to deny read access. Empty array = full read access."), - items: { type: 'string' }, - default: [] - }, - allowWrite: { - type: 'array', - description: localize('terminalSandbox.fileSystem.allowWrite', "Array of paths to allow write access. Empty array = no write access."), - items: { type: 'string' }, - default: [] - }, - denyWrite: { - type: 'array', - description: localize('terminalSandbox.fileSystem.denyWrite', "Array of paths to deny write access within allowed paths (takes precedence over allowWrite)."), - items: { type: 'string' }, - default: [] - } - } - }, - network: { - type: 'object', - description: localize('terminalSandbox.network', "Controls network access in the terminal sandbox."), - properties: { - allowedDomains: { - type: 'array', - description: localize('terminalSandbox.network.allowedDomains', "Array of allowed domains (supports wildcards like *.example.com). Empty array = no network access."), - items: { type: 'string' }, - default: [] - }, - deniedDomains: { - type: 'array', - description: localize('terminalSandbox.network.deniedDomains', "Array of denied domains (checked first, takes precedence over allowedDomains)."), - items: { type: 'string' }, - default: [] - } - } - } - } -}; - - export const terminalChatAgentToolsConfiguration: IStringDictionary = { [TerminalChatAgentToolsSettingId.EnableAutoApprove]: { description: localize('autoApproveMode.description', "Controls whether to allow auto approval in the run in terminal tool."), @@ -515,27 +464,94 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary { instance: mockTerminal2, shellIntegrationQuality: ShellIntegrationQuality.None }); + ok(runInTerminalTool.sessionTerminalAssociations.has(sessionId1), 'Session 1 terminal association should exist'); ok(runInTerminalTool.sessionTerminalAssociations.has(sessionId2), 'Session 2 terminal association should exist'); diff --git a/src/vs/workbench/services/storage/test/browser/storageService.test.ts b/src/vs/workbench/services/storage/test/browser/storageService.test.ts index b5f0381aa6575..2531f78ca5daf 100644 --- a/src/vs/workbench/services/storage/test/browser/storageService.test.ts +++ b/src/vs/workbench/services/storage/test/browser/storageService.test.ts @@ -46,8 +46,7 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS snippetsHome: joinPath(inMemoryExtraProfileRoot, 'snippetsHome'), promptsHome: joinPath(inMemoryExtraProfileRoot, 'promptsHome'), extensionsResource: joinPath(inMemoryExtraProfileRoot, 'extensionsResource'), - cacheHome: joinPath(inMemoryExtraProfileRoot, 'cache'), - sandboxSettingsResource: joinPath(inMemoryExtraProfileRoot, 'sandboxSettings.json') + cacheHome: joinPath(inMemoryExtraProfileRoot, 'cache') }; const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, disposables.add(new UserDataProfileService(inMemoryExtraProfile)), logService)); diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts index 3ea26eb0518f8..c3aff7cf4c5f7 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts @@ -756,8 +756,7 @@ class UserDataProfileExportState extends UserDataProfileImportExportState { extensionsResource: profile.extensionsResource, cacheHome: profile.cacheHome, useDefaultFlags: profile.useDefaultFlags, - isTransient: profile.isTransient, - sandboxSettingsResource: profile.sandboxSettingsResource.with({ scheme: USER_DATA_PROFILE_EXPORT_SCHEME }) + isTransient: profile.isTransient }; } From cd711564e8be17973cdcc3536ab6b39e8404fe76 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Tue, 13 Jan 2026 17:07:50 -0800 Subject: [PATCH 09/31] review suggestions --- .../contrib/chat/common/sandboxService.ts | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/vs/workbench/contrib/chat/common/sandboxService.ts diff --git a/src/vs/workbench/contrib/chat/common/sandboxService.ts b/src/vs/workbench/contrib/chat/common/sandboxService.ts new file mode 100644 index 0000000000000..0a7a95d20671b --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/sandboxService.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isLinux, isMacintosh, isNative, isWindows } from '../../../../base/common/platform.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { ITerminalLogService, ITerminalSandboxSettings } from '../../../../platform/terminal/common/terminal.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { dirname, join } from '../../../../base/common/path.js'; +import { FileAccess } from '../../../../base/common/network.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { joinPath } from '../../../../base/common/resources.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { TerminalContribSettingId } from '../../terminal/terminalContribExports.js'; + +export const ISandboxService = createDecorator('sandboxService'); + +export interface ISandboxService { + readonly _serviceBrand: undefined; + isEnabled(): boolean; + wrapCommand(command: string): string; + getSandboxConfigPath(forceRefresh?: boolean): Promise; + getTempDir(): URI | undefined; + setNeedsForceUpdateConfigFile(): void; +} + +export class SandboxService implements ISandboxService { + readonly _serviceBrand: undefined; + private _srtPath: string; + private _sandboxConfigPath: string | undefined; + private _needsForceUpdateConfigFile = true; + private _tempDir: URI | undefined; + private _sandboxSettingsId: string | undefined; + + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IFileService private readonly _fileService: IFileService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @ITerminalLogService private readonly _logService: ITerminalLogService, + ) { + const appRoot = dirname(FileAccess.asFileUri('').fsPath); + this._srtPath = join(appRoot, 'node_modules', '.bin', 'srt'); + this._sandboxSettingsId = generateUuid(); + this._initTempDir(); + } + + public isEnabled(): boolean { + if (isWindows) { + return false; + } + const enabledSetting = this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxEnabled); + const isEnabled = enabledSetting === true; + if (!isEnabled) { + return false; + } + return true; + } + + 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(TerminalContribSettingId.TerminalSandboxNetwork) ?? {}; + const linuxFileSystemSetting = isLinux + ? this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxLinuxFileSystem) ?? {} + : {}; + const macFileSystemSetting = isMacintosh + ? this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxMacFileSystem) ?? {} + : {}; + const configFileUri = joinPath(this._tempDir, `vscode-sandbox-settings-${this._sandboxSettingsId}.json`); + const sandboxSettings = { + network: { + allowedDomains: networkSetting.allowedDomains ?? [], + deniedDomains: networkSetting.deniedDomains ?? [] + }, + filesystem: { + denyRead: isMacintosh ? macFileSystemSetting.denyRead : linuxFileSystemSetting.denyRead, + allowWrite: isMacintosh ? macFileSystemSetting.allowWrite : linuxFileSystemSetting.allowWrite, + denyWrite: isMacintosh ? 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 tmpDir = (this._environmentService as IEnvironmentService & { tmpDir: URI }).tmpDir; + if (!tmpDir) { + this._logService.warn('SandboxService: Cannot create sandbox settings file because no tmpDir is available in this environment'); + return; + } + this._tempDir = tmpDir; + this._fileService.exists(this._tempDir).then(exists => { + if (!exists) { + this._logService.warn(`SandboxService: tmp directory is not present at ${this._tempDir}`); + } + }); + } + } +} From 51472871ec0a547b7f92a068aa13283e542c5316 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Wed, 14 Jan 2026 14:51:35 -0800 Subject: [PATCH 10/31] changes for retry --- .../storage/test/electron-main/storageMainService.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index a79d841341e78..ff72087bd53cc 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -47,7 +47,7 @@ suite('StorageMainService', function () { snippetsHome: joinPath(inMemoryProfileRoot, 'snippetsHome'), promptsHome: joinPath(inMemoryProfileRoot, 'promptsHome'), extensionsResource: joinPath(inMemoryProfileRoot, 'extensionsResource'), - cacheHome: joinPath(inMemoryProfileRoot, 'cache') + cacheHome: joinPath(inMemoryProfileRoot, 'cache'), }; class TestStorageMainService extends StorageMainService { From 697f4f5043ffbf3fd90fe27a278b47f88230c5ae Mon Sep 17 00:00:00 2001 From: dileepyavan <52841896+dileepyavan@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:45:52 -0800 Subject: [PATCH 11/31] Update src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../chatAgentTools/common/terminalChatAgentToolsConfiguration.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index 7f2c2cb3fcbe7..2532ab57eeaf7 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -610,7 +610,6 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary Date: Wed, 14 Jan 2026 15:46:18 -0800 Subject: [PATCH 12/31] Update src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7c401c69a2093..452693162d0de 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -925,7 +925,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { }, content: [{ kind: 'text', - value: resultText.join('') + value: resultText.join(''), }] }; } From b6341381339477311c291715450f12685fd5911d Mon Sep 17 00:00:00 2001 From: dileepyavan <52841896+dileepyavan@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:52:39 -0800 Subject: [PATCH 13/31] Update src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../chatAgentTools/common/terminalChatAgentToolsConfiguration.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index 2532ab57eeaf7..8b0ce1f137d96 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -586,7 +586,6 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary Date: Wed, 14 Jan 2026 15:53:33 -0800 Subject: [PATCH 14/31] Update src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 2 ++ 1 file changed, 2 insertions(+) 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 452693162d0de..75ad3563bc03d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -331,6 +331,8 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { // Clear out warning accepted state if the setting is disabled this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { + this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { + // Clear out warning accepted state if the setting is disabled if (!e || e.affectsConfiguration(TerminalChatAgentToolsSettingId.EnableAutoApprove)) { if (this._configurationService.getValue(TerminalChatAgentToolsSettingId.EnableAutoApprove) !== true) { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); From a8b410c304f24a0d2b4c5c095ff891c21e7650cd Mon Sep 17 00:00:00 2001 From: dileepyavan <52841896+dileepyavan@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:56:58 -0800 Subject: [PATCH 15/31] Update src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 1 - 1 file changed, 1 deletion(-) 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 75ad3563bc03d..9f04f609baf20 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -583,7 +583,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { const store = new DisposableStore(); - this._logService.debug(`RunInTerminalTool: Creating ${args.isBackground ? 'background' : 'foreground'} terminal. termId=${termId}, chatSessionId=${chatSessionId}`); const toolTerminal = await (args.isBackground ? this._initBackgroundTerminal(chatSessionResource, termId, terminalToolSessionId, token) From 49aeae9738d5f91958ab5f57df8176d5e7537cd9 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Wed, 14 Jan 2026 17:05:55 -0800 Subject: [PATCH 16/31] updating anthropic sandbox runtime to 0.0.23 --- package-lock.json | 531 ++++++++++++++---- package.json | 37 +- .../browser/tools/runInTerminalTool.ts | 1 - 3 files changed, 432 insertions(+), 137 deletions(-) diff --git a/package-lock.json b/package-lock.json index ba1b4901e4fa6..b7256379a53d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { "name": "code-oss-dev", - "version": "1.108.0", + "version": "1.109.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-oss-dev", - "version": "1.108.0", + "version": "1.109.0", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@anthropic-ai/sandbox-runtime": "^0.0.13", + "@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", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.1", @@ -20,24 +21,23 @@ "@vscode/proxy-agent": "^0.36.0", "@vscode/ripgrep": "^1.15.13", "@vscode/spdlog": "^0.15.2", - "@vscode/sqlite3": "5.1.10-vscode", - "@vscode/sudo-prompt": "9.3.1", + "@vscode/sqlite3": "5.1.11-vscode", + "@vscode/sudo-prompt": "9.3.2", "@vscode/tree-sitter-wasm": "^0.3.0", "@vscode/vscode-languagedetection": "1.0.21", - "@vscode/watcher": "bpasero/watcher#8ecffb4a57df24ac3e6946aa095b9b1f14f8bba9", "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.3.0-beta.89", - "@xterm/addon-image": "^0.10.0-beta.89", - "@xterm/addon-ligatures": "^0.11.0-beta.89", - "@xterm/addon-progress": "^0.3.0-beta.89", - "@xterm/addon-search": "^0.17.0-beta.89", - "@xterm/addon-serialize": "^0.15.0-beta.89", - "@xterm/addon-unicode11": "^0.10.0-beta.89", - "@xterm/addon-webgl": "^0.20.0-beta.88", - "@xterm/headless": "^6.1.0-beta.89", - "@xterm/xterm": "^6.1.0-beta.89", + "@xterm/addon-clipboard": "^0.3.0-beta.101", + "@xterm/addon-image": "^0.10.0-beta.101", + "@xterm/addon-ligatures": "^0.11.0-beta.101", + "@xterm/addon-progress": "^0.3.0-beta.101", + "@xterm/addon-search": "^0.17.0-beta.101", + "@xterm/addon-serialize": "^0.15.0-beta.101", + "@xterm/addon-unicode11": "^0.10.0-beta.101", + "@xterm/addon-webgl": "^0.20.0-beta.100", + "@xterm/headless": "^6.1.0-beta.101", + "@xterm/xterm": "^6.1.0-beta.101", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.4", @@ -47,14 +47,14 @@ "native-is-elevated": "0.8.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "^1.1.0-beta43", + "node-pty": "^1.2.0-beta.6", "open": "^10.1.2", "tas-client": "0.3.1", "undici": "^7.9.0", "v8-inspect-profiler": "^0.1.1", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", - "vscode-textmate": "^9.3.0", + "vscode-textmate": "^9.3.1", "yauzl": "^3.0.0", "yazl": "^2.4.3" }, @@ -180,9 +180,9 @@ } }, "node_modules/@anthropic-ai/sandbox-runtime": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.13.tgz", - "integrity": "sha512-rjjmp3MVDn3Yl4ywNVM2Dr7+Q/KzsvsmLl8prP10Mi/+3F3Gs997UiFx3WXqESJtnYkznOuYUp0VhFDfOyJFPw==", + "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", @@ -1679,6 +1679,313 @@ "node": ">=8.0.0" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.4.tgz", + "integrity": "sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.4", + "@parcel/watcher-darwin-arm64": "2.5.4", + "@parcel/watcher-darwin-x64": "2.5.4", + "@parcel/watcher-freebsd-x64": "2.5.4", + "@parcel/watcher-linux-arm-glibc": "2.5.4", + "@parcel/watcher-linux-arm-musl": "2.5.4", + "@parcel/watcher-linux-arm64-glibc": "2.5.4", + "@parcel/watcher-linux-arm64-musl": "2.5.4", + "@parcel/watcher-linux-x64-glibc": "2.5.4", + "@parcel/watcher-linux-x64-musl": "2.5.4", + "@parcel/watcher-win32-arm64": "2.5.4", + "@parcel/watcher-win32-ia32": "2.5.4", + "@parcel/watcher-win32-x64": "2.5.4" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.4.tgz", + "integrity": "sha512-hoh0vx4v+b3BNI7Cjoy2/B0ARqcwVNrzN/n7DLq9ZB4I3lrsvhrkCViJyfTj/Qi5xM9YFiH4AmHGK6pgH1ss7g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.4.tgz", + "integrity": "sha512-kphKy377pZiWpAOyTgQYPE5/XEKVMaj6VUjKT5VkNyUJlr2qZAn8gIc7CPzx+kbhvqHDT9d7EqdOqRXT6vk0zw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.4.tgz", + "integrity": "sha512-UKaQFhCtNJW1A9YyVz3Ju7ydf6QgrpNQfRZ35wNKUhTQ3dxJ/3MULXN5JN/0Z80V/KUBDGa3RZaKq1EQT2a2gg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.4.tgz", + "integrity": "sha512-Dib0Wv3Ow/m2/ttvLdeI2DBXloO7t3Z0oCp4bAb2aqyqOjKPPGrg10pMJJAQ7tt8P4V2rwYwywkDhUia/FgS+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.4.tgz", + "integrity": "sha512-I5Vb769pdf7Q7Sf4KNy8Pogl/URRCKu9ImMmnVKYayhynuyGYMzuI4UOWnegQNa2sGpsPSbzDsqbHNMyeyPCgw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.4.tgz", + "integrity": "sha512-kGO8RPvVrcAotV4QcWh8kZuHr9bXi9a3bSZw7kFarYR0+fGliU7hd/zevhjw8fnvIKG3J9EO5G6sXNGCSNMYPQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.4.tgz", + "integrity": "sha512-KU75aooXhqGFY2W5/p8DYYHt4hrjHZod8AhcGAmhzPn/etTa+lYCDB2b1sJy3sWJ8ahFVTdy+EbqSBvMx3iFlw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.4.tgz", + "integrity": "sha512-Qx8uNiIekVutnzbVdrgSanM+cbpDD3boB1f8vMtnuG5Zau4/bdDbXyKwIn0ToqFhIuob73bcxV9NwRm04/hzHQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.4.tgz", + "integrity": "sha512-UYBQvhYmgAv61LNUn24qGQdjtycFBKSK3EXr72DbJqX9aaLbtCOO8+1SkKhD/GNiJ97ExgcHBrukcYhVjrnogA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.4.tgz", + "integrity": "sha512-YoRWCVgxv8akZrMhdyVi6/TyoeeMkQ0PGGOf2E4omODrvd1wxniXP+DBynKoHryStks7l+fDAMUBRzqNHrVOpg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.4.tgz", + "integrity": "sha512-iby+D/YNXWkiQNYcIhg8P5hSjzXEHaQrk2SLrWOUD7VeC4Ohu0WQvmV+HDJokZVJ2UjJ4AGXW3bx7Lls9Ln4TQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.4.tgz", + "integrity": "sha512-vQN+KIReG0a2ZDpVv8cgddlf67J8hk1WfZMMP7sMeZmJRSmEax5xNDNWKdgqSe2brOKTQQAs3aCCUal2qBHAyg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.4.tgz", + "integrity": "sha512-3A6efb6BOKwyw7yk9ro2vus2YTt2nvcd56AuzxdMiVOxL9umDyN5PKkKfZ/gZ9row41SjVmTVQNWQhaRRGpOKw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2035,9 +2342,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "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": { @@ -2619,10 +2926,11 @@ ] }, "node_modules/@vscode/deviceid": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.1.tgz", - "integrity": "sha512-ErpoMeKKNYAkR1IT3zxB5RtiTqEECdh8fxggupWvzuxpTAX77hwOI2NdJ7um+vupnXRBZVx4ugo0+dVHJWUkag==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.4.tgz", + "integrity": "sha512-3u705VptsQhKMcHvUMJzaOn9fBrKEQHsl7iibRRVQ8kUNV+cptki7bQXACPNsGtJ5Dh4/7A7W1uKtP3z39GUQg==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "fs-extra": "^11.2.0", "uuid": "^9.0.1" @@ -3002,9 +3310,9 @@ } }, "node_modules/@vscode/policy-watcher": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@vscode/policy-watcher/-/policy-watcher-1.3.5.tgz", - "integrity": "sha512-k1n9gaDBjyVRy5yJLABbZCnyFwgQ8OA4sR3vXmXnmB+mO9JA0nsl/XOXQfVCoLasBu3UHCOfAnDWGn2sRzCR+A==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@vscode/policy-watcher/-/policy-watcher-1.3.7.tgz", + "integrity": "sha512-OvIczTbtGLZs7YU0ResbjM0KEB2ORBnlJ4ICxaB9fKHNVBwNVp4i2qIkDQGp3UBGtu7P8/+eg4/ZKk2oJGFcug==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3064,9 +3372,9 @@ } }, "node_modules/@vscode/spdlog": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@vscode/spdlog/-/spdlog-0.15.4.tgz", - "integrity": "sha512-NmFasVWjn/6BjHMAjqalsbG2srQCt8yfC0EczP5wzNQFawv74rhvuarhWi44x3St9LB8bZBxrpbT7igPaTJwcw==", + "version": "0.15.6", + "resolved": "https://registry.npmjs.org/@vscode/spdlog/-/spdlog-0.15.6.tgz", + "integrity": "sha512-s0ei7I0rLrNlsGTa8EVoAXe4qvbsfXrHebQ5dNbu7dc1Zs/DbnJNSADpHUy8vtNvTJukBWjOXFhAYUfXxGk+Bg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3076,9 +3384,9 @@ } }, "node_modules/@vscode/sqlite3": { - "version": "5.1.10-vscode", - "resolved": "https://registry.npmjs.org/@vscode/sqlite3/-/sqlite3-5.1.10-vscode.tgz", - "integrity": "sha512-sCJozBr1jItK4eCtbibX3Vi8BXfNyDsPCplojm89OuydoSxwP+Z3gSgzsTXWD5qYyXpTvVaT3LtHLoH2Byv8oA==", + "version": "5.1.11-vscode", + "resolved": "https://registry.npmjs.org/@vscode/sqlite3/-/sqlite3-5.1.11-vscode.tgz", + "integrity": "sha512-x2vBjFRZj/34Ji46lrxotjUtgljistPZU3cbxpckml3bMwF+Z0zbJYiplIeskHLo2g0Kj3kvR8MRRJ+o2nxNug==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -3096,9 +3404,10 @@ } }, "node_modules/@vscode/sudo-prompt": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@vscode/sudo-prompt/-/sudo-prompt-9.3.1.tgz", - "integrity": "sha512-9ORTwwS74VaTn38tNbQhsA5U44zkJfcb0BdTSyyG6frP4e8KMtHuTXYmwefe5dpL8XB1aGSIVTaLjD3BbWb5iA==" + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@vscode/sudo-prompt/-/sudo-prompt-9.3.2.tgz", + "integrity": "sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw==", + "license": "MIT" }, "node_modules/@vscode/telemetry-extractor": { "version": "1.10.2", @@ -3277,26 +3586,6 @@ "node": ">= 16" } }, - "node_modules/@vscode/watcher": { - "version": "2.5.1-vscode", - "resolved": "git+ssh://git@github.com/bpasero/watcher.git#8ecffb4a57df24ac3e6946aa095b9b1f14f8bba9", - "integrity": "sha512-7F4REbtMh5JAtdPpBCyPq7yLgcqnZV5L+uzuT4IDaZUyCKvIqi9gDiNPyoKpvCtrw6funLmrAncFHHWoDI+S4g==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@vscode/windows-ca-certs": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@vscode/windows-ca-certs/-/windows-ca-certs-0.3.3.tgz", @@ -3322,9 +3611,9 @@ } }, "node_modules/@vscode/windows-mutex": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@vscode/windows-mutex/-/windows-mutex-0.5.2.tgz", - "integrity": "sha512-O9CNYVl2GmFVbiHiz7tyFrKIdXVs3qf8HnyWlfxyuMaKzXd1L35jSTNCC1oAVwr8F0O2P4o3C/jOSIXulUCJ7w==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@vscode/windows-mutex/-/windows-mutex-0.5.3.tgz", + "integrity": "sha512-hWNmD+AzINR57jWuc/iW53kA+BghI4iOuicxhAEeeJLPOeMm9X5IUD0ttDwJFEib+D8H/2T9pT/8FeB/xcqbRw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3333,9 +3622,9 @@ } }, "node_modules/@vscode/windows-process-tree": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@vscode/windows-process-tree/-/windows-process-tree-0.6.2.tgz", - "integrity": "sha512-uzyUuQ93m7K1jSPrB/72m4IspOyeGpvvghNwFCay/McZ+y4Hk2BnLdZPb6EJ8HLRa3GwCvYjH/MQZzcnLOVnaQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@vscode/windows-process-tree/-/windows-process-tree-0.6.3.tgz", + "integrity": "sha512-mjirLbtgjv7P6fwD8gx7iaY961EfGqUExGvfzsKl3spLfScg57ejlMi+7O1jfJqpM2Zly9DTSxyY4cFsDN6c9Q==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3343,9 +3632,9 @@ } }, "node_modules/@vscode/windows-registry": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@vscode/windows-registry/-/windows-registry-1.1.2.tgz", - "integrity": "sha512-/eDRmGNe6g11wHckOyiVLvK/mEE5UBZFeoRlBosIL343LDrSKUL5JDAcFeAZqOXnlTtZ3UZtj5yezKiAz99NcA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@vscode/windows-registry/-/windows-registry-1.1.3.tgz", + "integrity": "sha512-si8+b+2Wh0x2X6W2+kgDyLJD9hyGIrjUo1X/7RWlvsxyI5+Pg+bpdHJrVYtIW4cHOPVB0FYFaN1UZndbUbU5lQ==", "hasInstallScript": true, "license": "MIT" }, @@ -3572,30 +3861,30 @@ } }, "node_modules/@xterm/addon-clipboard": { - "version": "0.3.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.89.tgz", - "integrity": "sha512-1weXTZ0dSvO94gsP++7XwaIVA2+6EC92EEiWI+FpBiGQCz261DjMdo4+8dBwmkovuxBvSdy0L6bTB9EJv94haw==", + "version": "0.3.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.101.tgz", + "integrity": "sha512-xuEqMUlvC6UR4HEa1OHSgF0LUEH7K5rS0fYjMJ9Tj/9Fsb84Z9LWwk5O5kYB4njEToX+mbm78Dhy7huXUcs8Ug==", "license": "MIT", "dependencies": { "js-base64": "^3.7.5" }, "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/addon-image": { - "version": "0.10.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.89.tgz", - "integrity": "sha512-BnLB551sgyH8BH3H723olQKCqVpVofWuQUuO+w++D2F+YQ53pIR3Aa7YcktQ0sCW0AaCDsuFblG/fA+uUtdpPg==", + "version": "0.10.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.101.tgz", + "integrity": "sha512-Gd8ZpfyzvisG+08+mXynufQHfaWWxGhhtRMSQXV2FyPNa3MNXNrowgjeXhpaRObOOsxSZnAlB8qDW8OHTjzG6A==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/addon-ligatures": { - "version": "0.11.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.89.tgz", - "integrity": "sha512-UbADhN/PBC0GpjXm8rKH3ZQhm4PVGwOO2DBi8ZemIK+3BfD9QEF+S1ukoKROmAODhQ5xnjus6DkUVJD92ELhbA==", + "version": "0.11.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.101.tgz", + "integrity": "sha512-FIO3S/f3K1nnmQs/oJ3ILI9p1Vb4sSK7J4UhROBj5JOyZtmRwhdUFr9MozfPVdc2VZPRJJJJq6vaPDdLioeJyQ==", "license": "MIT", "dependencies": { "lru-cache": "^6.0.0", @@ -3605,7 +3894,7 @@ "node": ">8.0.0" }, "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/addon-ligatures/node_modules/lru-cache": { @@ -3627,63 +3916,63 @@ "license": "ISC" }, "node_modules/@xterm/addon-progress": { - "version": "0.3.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.89.tgz", - "integrity": "sha512-8MhqLgoPcMLRpntaL4bAaHwZ4cMtfy15QNrwkzWskyP///bAra+LrUrBFYun/aSdSSeyMXTvqoXGmMa8LEEKOA==", + "version": "0.3.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.101.tgz", + "integrity": "sha512-QsJLvH3tc9KywOXb9O/mxkPoYmL2cCvKUa/YckriuZmEh5WMw+3cgNa+BC0aA73htdWE8UMUBvigE786KErajA==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/addon-search": { - "version": "0.17.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.89.tgz", - "integrity": "sha512-LvGWGNeHetQ7Gqr2uNANJ4sflWLiUxAIqEMdLTFr/zNBOT2SdJveK3hdhhOvMeYW6I0GHcFEeqEh5yBVgBchOA==", + "version": "0.17.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.101.tgz", + "integrity": "sha512-Y1yWv2baZdqP6AH/aKmMXSs6VNr7FGrpOe+DKNKq77H+QZbUoUXfgI/qKZpqlByl7FGuo8OT9g0AqH3zykkkUw==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/addon-serialize": { - "version": "0.15.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.89.tgz", - "integrity": "sha512-CRehvDVdFAG9WtZYpBP3xkUOvycUPyJggpmqvaVL2bEBHBRi6on6S/hcoMcMo2ZEmAjJ7hgWo6HnmHc8D1CT7Q==", + "version": "0.15.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.101.tgz", + "integrity": "sha512-NJXY4WBV9tG2IG85xMlmUX6YFIa63NO0qF+JzDM9+3AaNDEmTXd+Lg5ucWtJjZRDW3AHUOLAQos4osLvBfyhYQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/addon-unicode11": { - "version": "0.10.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.89.tgz", - "integrity": "sha512-P/XnHwrQ0faKGNpiLxfWPqAB03SfZ3R+HfzGjMBeQu+O27kwBUrRXRE1DasQsfUL/BU0O/nEJTLF7HoKYd0KZg==", + "version": "0.10.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.101.tgz", + "integrity": "sha512-WM9Rs8ZBQ1nY5nb4OxXsOLVIZ2DQvGmtSIKkUueDB9mSQOg05mz2dHbEdW63MrX8sMQAbEx+o/kyxvl7oFDS/A==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/addon-webgl": { - "version": "0.20.0-beta.88", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.88.tgz", - "integrity": "sha512-0IcDtPmISWxXZLjRUDAnbCXIYceSo/IqIJRwNXxIwiYtGRQP2Z2UgOhtWu7gsvGjseh4Rwvd6V7Wtv4HT6HvGQ==", + "version": "0.20.0-beta.100", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.100.tgz", + "integrity": "sha512-h17XiyERE+LpuYEPUAm2ux6g2Iy34BT/tfwxOckZ+RrhjM8bZMeN3u6/q28viBqAKWhhD3JbxlcDfKMN8sE3sg==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^6.1.0-beta.89" + "@xterm/xterm": "^6.1.0-beta.101" } }, "node_modules/@xterm/headless": { - "version": "6.1.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.1.0-beta.89.tgz", - "integrity": "sha512-Z2AVChT1yzS/RC3KGLPReVV/PcyoUYqo0i2ZkYprcrdyTTt5pPgGNRu91I3gjcJ6UPlUlkHIZNYO2G3aoZH63g==", + "version": "6.1.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.1.0-beta.101.tgz", + "integrity": "sha512-m+5Gyiy72wry8wJQPueUojcF8bMzK983owwOCyFp0I6qrHk+VuKh84FQXAvq5Gu9C0irL99iP5K54xGjDZ7Zkg==", "license": "MIT", "workspaces": [ "addons/*" ] }, "node_modules/@xterm/xterm": { - "version": "6.1.0-beta.89", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.89.tgz", - "integrity": "sha512-rs8PdUQUzEzr4aVDdQMSFsbUSU3Gs0CA2RgYJzxSoCpcb/ByPBk9Yilk/MCh4EhK2v/zBv7WiHJ1xeT+LDHmrw==", + "version": "6.1.0-beta.101", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.101.tgz", + "integrity": "sha512-fINmTdz6WkLkMkwwpwuWu4h1gn4uQVnnJJsdKYy+Wwr7mzazx2uK22Lrgc6B/2AZZjw+CfK2mkK5+AuR5J2YRw==", "license": "MIT", "workspaces": [ "addons/*" @@ -4525,6 +4814,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -7270,6 +7560,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -10650,6 +10941,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -11571,9 +11863,9 @@ "dev": true }, "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "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": { @@ -12060,6 +12352,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -12583,9 +12876,9 @@ "license": "MIT" }, "node_modules/native-keymap": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/native-keymap/-/native-keymap-3.3.7.tgz", - "integrity": "sha512-07n5kF0L9ERC9pilqEFucnhs1XG4WttbHAMWhhOSqQYXhB8mMNTSCzP4psTaVgDSp6si2HbIPhTIHuxSia6NPQ==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/native-keymap/-/native-keymap-3.3.9.tgz", + "integrity": "sha512-d/ydQ5x+GM5W0dyAjFPwexhtc9CDH1g/xWZESS5CXk16ThyFzSBLvlBJq1+FyzUIFf/F2g1MaHdOpa6G9150YQ==", "hasInstallScript": true, "license": "MIT" }, @@ -12714,9 +13007,9 @@ } }, "node_modules/node-pty": { - "version": "1.1.0-beta43", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta43.tgz", - "integrity": "sha512-CYyIQogRs97Rfjo0WKyku8V56Bm4WyWUijrbWDs5LJ+ZmsUW2gqbVAEpD+1gtA7dEZ6v1A08GzfqsDuIl/eRqw==", + "version": "1.2.0-beta.6", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.6.tgz", + "integrity": "sha512-0ArHUpsE5y6nSRSkbY36l+bjyuZNMjww0pdsBKCbiw/HTFCikJlsbUuyZc60KPdgH/9YhAiqD2BM8a0AOUVrsw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -13693,6 +13986,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -16584,6 +16878,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -17464,9 +17759,9 @@ } }, "node_modules/vscode-textmate": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.3.0.tgz", - "integrity": "sha512-zHiZZOdb9xqj5/X1C4a29sbgT2HngdWxPLSl3PyHRQF+5visI4uNM020OHiLJjsMxUssyk/pGVAg/9LCIobrVg==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.3.1.tgz", + "integrity": "sha512-U19nFkCraZF9/bkQKQYsb9mRqM9NwpToQQFl40nGiioZTH9gRtdtCHwp48cubayVfreX3ivnoxgxQgNwrTVmQg==", "license": "MIT" }, "node_modules/vscode-uri": { diff --git a/package.json b/package.json index e3b75ffcb4b34..98f15da14ddf1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.108.0", - "distro": "6fae31d1e4b6cf064fb9ca6b450e1eaa0b50013f", + "version": "1.109.0", + "distro": "b570759d1928b4b2f34a86c40da42b1a2b6d3796", "author": { "name": "Microsoft Corporation" }, @@ -42,6 +42,7 @@ "mixin-telemetry-docs": "node build/npm/mixin-telemetry-docs.ts", "smoketest": "node build/lib/preLaunch.ts && cd test/smoke && npm run compile && node test/index.js", "smoketest-no-compile": "cd test/smoke && node test/index.js", + "sanity-test": "cd test/sanity && npm run compile && node out/index.js", "download-builtin-extensions": "node build/lib/builtInExtensions.ts", "download-builtin-extensions-cg": "node build/lib/builtInExtensionsCG.ts", "monaco-compile-check": "tsgo --project src/tsconfig.monaco.json --noEmit", @@ -72,9 +73,10 @@ "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.26", + "@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", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.1", @@ -82,24 +84,23 @@ "@vscode/proxy-agent": "^0.36.0", "@vscode/ripgrep": "^1.15.13", "@vscode/spdlog": "^0.15.2", - "@vscode/sqlite3": "5.1.10-vscode", - "@vscode/sudo-prompt": "9.3.1", + "@vscode/sqlite3": "5.1.11-vscode", + "@vscode/sudo-prompt": "9.3.2", "@vscode/tree-sitter-wasm": "^0.3.0", "@vscode/vscode-languagedetection": "1.0.21", - "@vscode/watcher": "bpasero/watcher#8ecffb4a57df24ac3e6946aa095b9b1f14f8bba9", "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.3.0-beta.89", - "@xterm/addon-image": "^0.10.0-beta.89", - "@xterm/addon-ligatures": "^0.11.0-beta.89", - "@xterm/addon-progress": "^0.3.0-beta.89", - "@xterm/addon-search": "^0.17.0-beta.89", - "@xterm/addon-serialize": "^0.15.0-beta.89", - "@xterm/addon-unicode11": "^0.10.0-beta.89", - "@xterm/addon-webgl": "^0.20.0-beta.88", - "@xterm/headless": "^6.1.0-beta.89", - "@xterm/xterm": "^6.1.0-beta.89", + "@xterm/addon-clipboard": "^0.3.0-beta.101", + "@xterm/addon-image": "^0.10.0-beta.101", + "@xterm/addon-ligatures": "^0.11.0-beta.101", + "@xterm/addon-progress": "^0.3.0-beta.101", + "@xterm/addon-search": "^0.17.0-beta.101", + "@xterm/addon-serialize": "^0.15.0-beta.101", + "@xterm/addon-unicode11": "^0.10.0-beta.101", + "@xterm/addon-webgl": "^0.20.0-beta.100", + "@xterm/headless": "^6.1.0-beta.101", + "@xterm/xterm": "^6.1.0-beta.101", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.4", @@ -109,14 +110,14 @@ "native-is-elevated": "0.8.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "^1.1.0-beta43", + "node-pty": "^1.2.0-beta.6", "open": "^10.1.2", "tas-client": "0.3.1", "undici": "^7.9.0", "v8-inspect-profiler": "^0.1.1", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", - "vscode-textmate": "^9.3.0", + "vscode-textmate": "^9.3.1", "yauzl": "^3.0.0", "yazl": "^2.4.3" }, 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 9f04f609baf20..2ce01bfb20939 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -330,7 +330,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { ]; // Clear out warning accepted state if the setting is disabled - this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { // Clear out warning accepted state if the setting is disabled if (!e || e.affectsConfiguration(TerminalChatAgentToolsSettingId.EnableAutoApprove)) { From 31ea4be58330c853e315deda924ba6fb46840bcb Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Wed, 14 Jan 2026 19:08:12 -0800 Subject: [PATCH 17/31] fixing tests for runInTerminalTool --- .../test/electron-browser/runInTerminalTool.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) 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..e4336d20578c1 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 { ISandboxService } from '../../../../chat/common/sandboxService.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(ISandboxService, { + _serviceBrand: undefined, + isEnabled: () => false, + wrapCommand: command => command, + getSandboxConfigPath: async () => undefined, + getTempDir: () => undefined, + setNeedsForceUpdateConfigFile: () => { } + }); const treeSitterLibraryService = store.add(instantiationService.createInstance(TreeSitterLibraryService)); treeSitterLibraryService.isTest = true; From 10ce464b10b251fb9225b1a94ca67df1f0e7c3b2 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Wed, 14 Jan 2026 19:43:00 -0800 Subject: [PATCH 18/31] refactoring changes --- src/vs/platform/userDataProfile/common/userDataProfile.ts | 4 ++-- .../chatAgentTools/common/terminal.chatAgentTools.ts | 1 - .../test/electron-browser/workingCopyBackupService.test.ts | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 9f2b991408681..5d17d17ba0374 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -144,7 +144,7 @@ export function reviveProfile(profile: UriDto, scheme: string) cacheHome: URI.revive(profile.cacheHome).with({ scheme }), useDefaultFlags: profile.useDefaultFlags, isTransient: profile.isTransient, - workspaces: profile.workspaces?.map(w => URI.revive(w)) + workspaces: profile.workspaces?.map(w => URI.revive(w)), }; } @@ -166,7 +166,7 @@ export function toUserDataProfile(id: string, name: string, location: URI, profi cacheHome: joinPath(profilesCacheHome, id), useDefaultFlags: options?.useDefaultFlags, isTransient: options?.transient, - workspaces: options?.workspaces + workspaces: options?.workspaces, }; } diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts index 8757a7074ba64..d6417b4259af0 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminal.chatAgentTools.ts @@ -5,5 +5,4 @@ export const enum TerminalChatAgentToolsCommandId { ChatAddTerminalSelection = 'workbench.action.terminal.chat.addTerminalSelection', - RetryWithoutSandboxing = 'workbench.action.terminal.retryWithoutSandboxing' } diff --git a/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts index bb487bfc0cdc5..6b7307e586363 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-browser/workingCopyBackupService.test.ts @@ -51,8 +51,7 @@ const NULL_PROFILE = { snippetsHome: joinPath(homeDir, 'snippets'), promptsHome: joinPath(homeDir, 'prompts'), extensionsResource: joinPath(homeDir, 'extensions.json'), - cacheHome: joinPath(homeDir, 'cache'), - sandboxSettingsResource: joinPath(homeDir, 'sandbox-settings.json') + cacheHome: joinPath(homeDir, 'cache') }; const TestNativeWindowConfiguration: INativeWindowConfiguration = { From 02fec9be32d441fdf19d77aa06b48579d5529e01 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 15 Jan 2026 09:33:46 -0800 Subject: [PATCH 19/31] review changes --- src/vs/platform/terminal/common/terminal.ts | 8 ++++---- .../toolInvocationParts/chatTerminalToolProgressPart.ts | 4 ++-- src/vs/workbench/contrib/chat/common/sandboxService.ts | 6 +++--- .../browser/terminal.chatAgentTools.contribution.ts | 4 ++-- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 4 ++-- .../test/electron-browser/runInTerminalTool.test.ts | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index a58036304331b..f0df17a7b4c48 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -23,7 +23,7 @@ import type { SingleOrMany } from '../../../base/common/types.js'; * Local type definition for sandbox runtime configuration to avoid importing external package * in the common layer. The actual type should match @anthropic-ai/sandbox-runtime. */ -export interface ISandboxRuntimeConfig { +export interface ITerminalSandboxRuntimeConfig { network?: { allowedDomains?: string[]; deniedDomains?: string[]; @@ -689,7 +689,7 @@ export interface IShellLaunchConfig { shellIntegrationNonce?: string; } -export interface ITerminalSandboxSettings extends ISandboxRuntimeConfig { +export interface ITerminalSandboxSettings extends ITerminalSandboxRuntimeConfig { enabled?: boolean; } @@ -751,7 +751,7 @@ export interface ITerminalProcessOptions { environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined; workspaceFolder: IWorkspaceFolder | undefined; isScreenReaderOptimized: boolean; - sandboxSettings?: ISandboxRuntimeConfig; + sandboxSettings?: ITerminalSandboxRuntimeConfig; } export interface ITerminalEnvironment { @@ -944,7 +944,7 @@ export interface ITerminalProfile { color?: string; icon?: ThemeIcon | URI | { light: URI; dark: URI }; sandboxed?: boolean; - sandboxSettings?: ISandboxRuntimeConfig; + sandboxSettings?: ITerminalSandboxRuntimeConfig; } export interface ITerminalDimensionsOverride extends Readonly { diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts index 0ee1bcc3b56e6..31043c1bd76c7 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts @@ -49,7 +49,7 @@ import { removeAnsiEscapeCodes } from '../../../../../../../base/common/strings. import { PANEL_BACKGROUND } from '../../../../../../common/theme.js'; import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; -import { ISandboxService } from '../../../../common/sandboxService.js'; +import { IChatSandboxService } from '../../../../common/sandboxService.js'; const MIN_OUTPUT_ROWS = 1; const MAX_OUTPUT_ROWS = 10; @@ -245,7 +245,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @ISandboxService private readonly _sandboxService: ISandboxService, + @IChatSandboxService private readonly _sandboxService: IChatSandboxService, ) { super(toolInvocation); diff --git a/src/vs/workbench/contrib/chat/common/sandboxService.ts b/src/vs/workbench/contrib/chat/common/sandboxService.ts index 0a7a95d20671b..f1c9dc5e05a7d 100644 --- a/src/vs/workbench/contrib/chat/common/sandboxService.ts +++ b/src/vs/workbench/contrib/chat/common/sandboxService.ts @@ -17,9 +17,9 @@ import { generateUuid } from '../../../../base/common/uuid.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; import { TerminalContribSettingId } from '../../terminal/terminalContribExports.js'; -export const ISandboxService = createDecorator('sandboxService'); +export const IChatSandboxService = createDecorator('sandboxService'); -export interface ISandboxService { +export interface IChatSandboxService { readonly _serviceBrand: undefined; isEnabled(): boolean; wrapCommand(command: string): string; @@ -28,7 +28,7 @@ export interface ISandboxService { setNeedsForceUpdateConfigFile(): void; } -export class SandboxService implements ISandboxService { +export class SandboxService implements IChatSandboxService { readonly _serviceBrand: undefined; private _srtPath: string; private _sandboxConfigPath: string | undefined; 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 17bdb38395cac..734f80d3d3b06 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 @@ -30,11 +30,11 @@ import { CreateAndRunTaskTool, CreateAndRunTaskToolData } from './tools/task/cre 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 { ISandboxService, SandboxService } from '../../../chat/common/sandboxService.js'; +import { IChatSandboxService, SandboxService } from '../../../chat/common/sandboxService.js'; // #region Services -registerSingleton(ISandboxService, SandboxService, InstantiationType.Delayed); +registerSingleton(IChatSandboxService, SandboxService, InstantiationType.Delayed); // #endregion Services 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 2ce01bfb20939..6033305836ac5 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -45,7 +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 { ISandboxService } from '../../../../chat/common/sandboxService.js'; +import { IChatSandboxService } from '../../../../chat/common/sandboxService.js'; import { chatSessionResourceToId, LocalChatSessionUri } from '../../../../chat/common/model/chatUri.js'; import { URI } from '../../../../../../base/common/uri.js'; import type { ICommandLineRewriter } from './commandLineRewriter/commandLineRewriter.js'; @@ -307,7 +307,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { @ITerminalService private readonly _terminalService: ITerminalService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, - @ISandboxService private readonly _sandboxService: ISandboxService, + @IChatSandboxService private readonly _sandboxService: IChatSandboxService, ) { super(); 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 e4336d20578c1..a6c95794117cd 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,7 +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 { ISandboxService } from '../../../../chat/common/sandboxService.js'; +import { IChatSandboxService } from '../../../../chat/common/sandboxService.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'; @@ -92,7 +92,7 @@ suite('RunInTerminalTool', () => { instantiationService.stub(IHistoryService, { getLastActiveWorkspaceRoot: () => undefined }); - instantiationService.stub(ISandboxService, { + instantiationService.stub(IChatSandboxService, { _serviceBrand: undefined, isEnabled: () => false, wrapCommand: command => command, From 22c6a80e1067b5858a365fbd965f3695db32a0a3 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 15 Jan 2026 10:36:48 -0800 Subject: [PATCH 20/31] review changes --- .../chatTerminalToolProgressPart.ts | 4 +- ...andboxService.ts => chatSandboxService.ts} | 29 +++--- .../terminal.chatAgentTools.contribution.ts | 4 +- .../browser/tools/runInTerminalTool.ts | 93 +------------------ .../runInTerminalTool.test.ts | 2 +- 5 files changed, 24 insertions(+), 108 deletions(-) rename src/vs/workbench/contrib/chat/common/{sandboxService.ts => chatSandboxService.ts} (76%) diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts index 31043c1bd76c7..b36a2b71901cc 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts @@ -49,7 +49,6 @@ import { removeAnsiEscapeCodes } from '../../../../../../../base/common/strings. import { PANEL_BACKGROUND } from '../../../../../../common/theme.js'; import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; -import { IChatSandboxService } from '../../../../common/sandboxService.js'; const MIN_OUTPUT_ROWS = 1; const MAX_OUTPUT_ROWS = 10; @@ -245,7 +244,6 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IChatSandboxService private readonly _sandboxService: IChatSandboxService, ) { super(toolInvocation); @@ -457,7 +455,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart showOutputAction = this._instantiationService.createInstance(ToggleChatTerminalOutputAction, () => this._toggleOutputFromAction()); this._showOutputAction.value = showOutputAction; const exitCode = resolvedCommand?.exitCode ?? this._terminalData.terminalCommandState?.exitCode; - if (exitCode && !this._sandboxService.isEnabled()) { + if (exitCode) { this._toggleOutput(true); } } diff --git a/src/vs/workbench/contrib/chat/common/sandboxService.ts b/src/vs/workbench/contrib/chat/common/chatSandboxService.ts similarity index 76% rename from src/vs/workbench/contrib/chat/common/sandboxService.ts rename to src/vs/workbench/contrib/chat/common/chatSandboxService.ts index f1c9dc5e05a7d..38b8a4b6ff450 100644 --- a/src/vs/workbench/contrib/chat/common/sandboxService.ts +++ b/src/vs/workbench/contrib/chat/common/chatSandboxService.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isLinux, isMacintosh, isNative, isWindows } from '../../../../base/common/platform.js'; +import { isNative, OperatingSystem, OS } from '../../../../base/common/platform.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { ITerminalLogService, ITerminalSandboxSettings } from '../../../../platform/terminal/common/terminal.js'; +import { ITerminalSandboxSettings } from '../../../../platform/terminal/common/terminal.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { dirname, join } from '../../../../base/common/path.js'; import { FileAccess } from '../../../../base/common/network.js'; @@ -16,6 +17,7 @@ import { joinPath } from '../../../../base/common/resources.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; import { TerminalContribSettingId } from '../../terminal/terminalContribExports.js'; +import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; export const IChatSandboxService = createDecorator('sandboxService'); @@ -28,29 +30,32 @@ export interface IChatSandboxService { setNeedsForceUpdateConfigFile(): void; } -export class SandboxService implements IChatSandboxService { +export class ChatSandboxService implements IChatSandboxService { 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, - @ITerminalLogService private readonly _logService: ITerminalLogService, + @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 (isWindows) { + if (this._os === OperatingSystem.Windows) { return false; } const enabledSetting = this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxEnabled); @@ -91,10 +96,10 @@ export class SandboxService implements IChatSandboxService { } if (this._tempDir) { const networkSetting = this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxNetwork) ?? {}; - const linuxFileSystemSetting = isLinux + const linuxFileSystemSetting = this._os === OperatingSystem.Linux ? this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxLinuxFileSystem) ?? {} : {}; - const macFileSystemSetting = isMacintosh + const macFileSystemSetting = this._os === OperatingSystem.Macintosh ? this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxMacFileSystem) ?? {} : {}; const configFileUri = joinPath(this._tempDir, `vscode-sandbox-settings-${this._sandboxSettingsId}.json`); @@ -104,9 +109,9 @@ export class SandboxService implements IChatSandboxService { deniedDomains: networkSetting.deniedDomains ?? [] }, filesystem: { - denyRead: isMacintosh ? macFileSystemSetting.denyRead : linuxFileSystemSetting.denyRead, - allowWrite: isMacintosh ? macFileSystemSetting.allowWrite : linuxFileSystemSetting.allowWrite, - denyWrite: isMacintosh ? macFileSystemSetting.denyWrite : linuxFileSystemSetting.denyWrite, + 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; @@ -121,13 +126,13 @@ export class SandboxService implements IChatSandboxService { this._needsForceUpdateConfigFile = true; const tmpDir = (this._environmentService as IEnvironmentService & { tmpDir: URI }).tmpDir; if (!tmpDir) { - this._logService.warn('SandboxService: Cannot create sandbox settings file because no tmpDir is available in this environment'); + this._logService.warn('ChatSandboxService: Cannot create sandbox settings file because no tmpDir is available in this environment'); return; } this._tempDir = tmpDir; this._fileService.exists(this._tempDir).then(exists => { if (!exists) { - this._logService.warn(`SandboxService: tmp directory is not present at ${this._tempDir}`); + this._logService.warn(`ChatSandboxService: tmp directory is not present at ${this._tempDir}`); } }); } 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 734f80d3d3b06..e1a141b020937 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 @@ -30,11 +30,11 @@ import { CreateAndRunTaskTool, CreateAndRunTaskToolData } from './tools/task/cre 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 { IChatSandboxService, SandboxService } from '../../../chat/common/sandboxService.js'; +import { IChatSandboxService, ChatSandboxService } from '../../../chat/common/chatSandboxService.js'; // #region Services -registerSingleton(IChatSandboxService, SandboxService, InstantiationType.Delayed); +registerSingleton(IChatSandboxService, ChatSandboxService, InstantiationType.Delayed); // #endregion Services 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 6033305836ac5..50d9760116286 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -9,7 +9,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../../../ba import { Codicon } from '../../../../../../base/common/codicons.js'; import { CancellationError } from '../../../../../../base/common/errors.js'; import { Event } from '../../../../../../base/common/event.js'; -import { createCommandUri, MarkdownString, type IMarkdownString } from '../../../../../../base/common/htmlContent.js'; +import { MarkdownString, type IMarkdownString } from '../../../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../../../base/common/map.js'; import { basename } from '../../../../../../base/common/path.js'; @@ -24,7 +24,7 @@ import { ICommandDetectionCapability, TerminalCapability } from '../../../../../ 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'; -import { ElicitationState, IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService/chatService.js'; +import { IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService/chatService.js'; import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolInvocationPresentation, ToolProgress } from '../../../../chat/common/tools/languageModelToolsService.js'; import { ITerminalChatService, ITerminalService, type ITerminalInstance } from '../../../../terminal/browser/terminal.js'; import type { XtermTerminal } from '../../../../terminal/browser/xterm/xtermTerminal.js'; @@ -45,7 +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 { IChatSandboxService } from '../../../../chat/common/sandboxService.js'; +import { IChatSandboxService } from '../../../../chat/common/chatSandboxService.js'; import { chatSessionResourceToId, LocalChatSessionUri } from '../../../../chat/common/model/chatUri.js'; import { URI } from '../../../../../../base/common/uri.js'; import type { ICommandLineRewriter } from './commandLineRewriter/commandLineRewriter.js'; @@ -58,7 +58,6 @@ import { TerminalCommandArtifactCollector } from './terminalCommandArtifactColle import { isNumber, isString } from '../../../../../../base/common/types.js'; import { ChatConfiguration } from '../../../../chat/common/constants.js'; import { IChatWidgetService } from '../../../../chat/browser/chat.js'; -import { ChatElicitationRequestPart } from '../../../../chat/common/model/chatProgressTypes/chatElicitationRequestPart.js'; import { TerminalChatCommandId } from '../../../chat/browser/terminalChat.js'; // #region Tool data @@ -775,92 +774,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } terminalResult = resultArr.join('\n\n'); } - - //if sandboxed and there is error, retry without sandboxing. - if (exitCode !== 0 && this._sandboxService.isEnabled()) { - const sessionResource = invocation.context?.sessionResource; - const session = sessionResource ? this._chatService.getSession(sessionResource) : undefined; - - // Use the unsandboxed command line (no `srt` wrapper), preserving any user/tool edits. - const commandToRetryWithoutSandboxing = toolSpecificData.commandLine.userEdited ?? toolSpecificData.commandLine.toolEdited ?? toolSpecificData.commandLine.original; - - let shouldRetryWithoutSandboxing = false; - if (session?.lastRequest) { - const lastRequest = session.lastRequest; - let didResolve = false; - const decisionPromise = new Promise(resolve => { - const settingsUri = createCommandUri('workbench.action.openSettings', { query: 'sandbox' }); - const settingsLink = new MarkdownString( - `[${localize('terminal.sandbox.retry.settings', 'Update Settings')}](${settingsUri.toString()} "${localize('terminal.sandbox.retry.settings.tooltip', 'Open settings and search for sandbox')}")`, - { isTrusted: { enabledCommands: ['workbench.action.openSettings'] } } - ); - const toolElicitation = new ChatElicitationRequestPart( - localize('terminal.sandbox.retry.title', 'Failed due to sandboxing restrictions'), - 'Retry without sandboxing?', - settingsLink, - localize('terminal.sandbox.retry.yes', 'Yes'), - localize('terminal.sandbox.retry.no', 'No'), - async () => { - if (!didResolve) { - didResolve = true; - resolve(true); - } - return ElicitationState.Accepted; - }, - async () => { - if (!didResolve) { - didResolve = true; - resolve(false); - } - return ElicitationState.Rejected; - }, - ); - - this._chatService.appendProgress(lastRequest, toolElicitation); - }); - - const cancellationPromise = new Promise((_resolve, reject) => token.onCancellationRequested(() => reject(new CancellationError()))); - shouldRetryWithoutSandboxing = await Promise.race([decisionPromise, cancellationPromise]); - } - - if (shouldRetryWithoutSandboxing) { - const commandDetectionRetry = toolTerminal.instance.capabilities.get(TerminalCapability.CommandDetection); - const retryStrategy = this._getExecuteStrategy(toolTerminal.shellIntegrationQuality, toolTerminal, commandDetectionRetry!); - const retryCommandId = `tool-retry-${generateUuid()}`; - const retryResult = await retryStrategy.execute(commandToRetryWithoutSandboxing, token, retryCommandId); - toolTerminal.receivedUserInput = false; - - await this._commandArtifactCollector.capture(toolSpecificData, toolTerminal.instance, retryCommandId); - { - const state = toolSpecificData.terminalCommandState ?? {}; - state.timestamp = state.timestamp ?? timingStart; - if (retryResult.exitCode !== undefined) { - state.exitCode = retryResult.exitCode; - if (state.timestamp !== undefined) { - state.duration = state.duration ?? Math.max(0, Date.now() - state.timestamp); - } - } - toolSpecificData.terminalCommandState = state; - } - - outputLineCount = retryResult.output === undefined ? 0 : count(retryResult.output.trim(), '\n') + 1; - exitCode = retryResult.exitCode; - error = retryResult.error; - - const retryResultArr: string[] = []; - if (retryResult.output !== undefined) { - retryResultArr.push(retryResult.output); - } - if (retryResult.additionalInformation) { - retryResultArr.push(retryResult.additionalInformation); - } - terminalResult = retryResultArr.join('\n\n'); - - - } - } - - } catch (e) { // Handle timeout case - get output collected so far and return it if (didTimeout && e instanceof CancellationError) { 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 a6c95794117cd..8ba4396620471 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,7 +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 { IChatSandboxService } from '../../../../chat/common/sandboxService.js'; +import { IChatSandboxService } from '../../../../chat/common/chatSandboxService.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'; From 544d616f1c96047a61a2a8753bb9844e123d4664 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 15 Jan 2026 10:58:33 -0800 Subject: [PATCH 21/31] review changes --- .../browser/tools/runInTerminalTool.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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 50d9760116286..0bd4bb6db5bdd 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -277,14 +277,18 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { private readonly _treeSitterCommandParser: TreeSitterCommandParser; private readonly _telemetry: RunInTerminalToolTelemetry; private readonly _commandArtifactCollector: TerminalCommandArtifactCollector; + protected readonly _profileFetcher: TerminalProfileFetcher; + private readonly _commandLineRewriters: ICommandLineRewriter[]; private readonly _commandLineAnalyzers: ICommandLineAnalyzer[]; - private static readonly _backgroundExecutions = new Map(); - protected readonly _profileFetcher: TerminalProfileFetcher; + protected readonly _sessionTerminalAssociations = new ResourceMap(); + // Immutable window state protected readonly _osBackend: Promise; + private static readonly _backgroundExecutions = new Map(); + public static getBackgroundOutput(id: string): string { const backgroundExecution = RunInTerminalTool._backgroundExecutions.get(id); if (!backgroundExecution) { @@ -426,6 +430,8 @@ 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. toolSpecificData.autoApproveInfo = new MarkdownString(localize('autoApprove.sandbox', 'In sandbox mode')); // If in sandbox mode, skip confirmation logic. In sandbox mode, commands are run in a restricted environment and explicit // user confirmation is not required. @@ -545,6 +551,7 @@ 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 | IMarkdownString | undefined; @@ -873,8 +880,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { return toolTerminal; } - - private async _initForegroundTerminal(chatSessionResource: URI, termId: string, terminalToolSessionId: string | undefined, token: CancellationToken): Promise { const cachedTerminal = this._sessionTerminalAssociations.get(chatSessionResource); if (cachedTerminal) { From 5b01c730dc4f9b3e0f648e8ab11b72477ebdccca Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 15 Jan 2026 11:43:18 -0800 Subject: [PATCH 22/31] review changes --- .../terminal.chatAgentTools.contribution.ts | 1 - .../terminalChatAgentToolsConfiguration.ts | 26 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) 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 e1a141b020937..21bb042d38765 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 @@ -129,7 +129,6 @@ registerWorkbenchContribution2(ChatAgentToolsContribution.ID, ChatAgentToolsCont // #endregion Contributions - // #region Actions registerActiveInstanceAction({ diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index 6a2d79d733e49..fd6cb45f4178f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -511,7 +511,8 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary Date: Thu, 15 Jan 2026 11:47:35 -0800 Subject: [PATCH 23/31] review changes --- .../contrib/editSessions/test/browser/editSessions.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts index e47d9828bc3d5..12be1cfe7d4b4 100644 --- a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts +++ b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts @@ -166,7 +166,6 @@ suite('Edit session sync', () => { promptsHome: URI.file('promptsHome'), extensionsResource: URI.file('extensionsResource'), cacheHome: URI.file('cacheHome'), - sandboxSettingsResource: URI.file('sandbox-settings.json') }; }); From 4742604ae87f2316e46bfe59c317098947c02eed Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 15 Jan 2026 12:33:50 -0800 Subject: [PATCH 24/31] review changes --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b7256379a53d7..f2c0285665aa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@anthropic-ai/sandbox-runtime": "^0.0.23", + "@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", diff --git a/package.json b/package.json index 98f15da14ddf1..32aef3605e73e 100644 --- a/package.json +++ b/package.json @@ -73,7 +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", + "@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", From 4adb2a6f418e0e096599f7828fe8bc1114db3b43 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 15 Jan 2026 22:31:39 -0800 Subject: [PATCH 25/31] review changes --- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 0bd4bb6db5bdd..7f5bd1117076d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -335,6 +335,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { // Clear out warning accepted state if the setting is disabled this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { // Clear out warning accepted state if the setting is disabled + if (!e || e.affectsConfiguration(TerminalChatAgentToolsSettingId.EnableAutoApprove)) { if (this._configurationService.getValue(TerminalChatAgentToolsSettingId.EnableAutoApprove) !== true) { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); @@ -430,12 +431,10 @@ 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. - toolSpecificData.autoApproveInfo = new MarkdownString(localize('autoApprove.sandbox', 'In sandbox mode')); // 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 }; From aa3945ee484bde11190041c6bb0411418224f689 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Thu, 15 Jan 2026 23:46:13 -0800 Subject: [PATCH 26/31] update to sandboxservice --- .../common/terminalSandboxService.ts} | 32 +++++++------------ .../terminal.chatAgentTools.contribution.ts | 4 +-- .../browser/tools/runInTerminalTool.ts | 4 +-- .../runInTerminalTool.test.ts | 4 +-- 4 files changed, 17 insertions(+), 27 deletions(-) rename src/vs/workbench/contrib/{chat/common/chatSandboxService.ts => terminal/common/terminalSandboxService.ts} (81%) diff --git a/src/vs/workbench/contrib/chat/common/chatSandboxService.ts b/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts similarity index 81% rename from src/vs/workbench/contrib/chat/common/chatSandboxService.ts rename to src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts index 38b8a4b6ff450..f28acbb8b25ef 100644 --- a/src/vs/workbench/contrib/chat/common/chatSandboxService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts @@ -15,13 +15,13 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { joinPath } from '../../../../base/common/resources.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; -import { TerminalContribSettingId } from '../../terminal/terminalContribExports.js'; +import { IEnvironmentService, INativeEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { TerminalContribSettingId } from '../terminalContribExports.js'; import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; -export const IChatSandboxService = createDecorator('sandboxService'); +export const ITerminalSandboxService = createDecorator('terminalSandboxService'); -export interface IChatSandboxService { +export interface ITerminalSandboxService { readonly _serviceBrand: undefined; isEnabled(): boolean; wrapCommand(command: string): string; @@ -30,7 +30,7 @@ export interface IChatSandboxService { setNeedsForceUpdateConfigFile(): void; } -export class ChatSandboxService implements IChatSandboxService { +export class TerminalSandboxService implements ITerminalSandboxService { readonly _serviceBrand: undefined; private _srtPath: string; private _sandboxConfigPath: string | undefined; @@ -43,7 +43,7 @@ export class ChatSandboxService implements IChatSandboxService { constructor( @IConfigurationService private readonly _configurationService: IConfigurationService, @IFileService private readonly _fileService: IFileService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IEnvironmentService private readonly _environmentService: INativeEnvironmentService, @ILogService private readonly _logService: ILogService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, ) { @@ -58,12 +58,7 @@ export class ChatSandboxService implements IChatSandboxService { if (this._os === OperatingSystem.Windows) { return false; } - const enabledSetting = this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxEnabled); - const isEnabled = enabledSetting === true; - if (!isEnabled) { - return false; - } - return true; + return this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxEnabled); } public wrapCommand(command: string): string { @@ -124,17 +119,12 @@ export class ChatSandboxService implements IChatSandboxService { private _initTempDir(): void { if (this.isEnabled() && isNative) { this._needsForceUpdateConfigFile = true; - const tmpDir = (this._environmentService as IEnvironmentService & { tmpDir: URI }).tmpDir; - if (!tmpDir) { - this._logService.warn('ChatSandboxService: Cannot create sandbox settings file because no tmpDir is available in this environment'); + const environmentService = this._environmentService; + 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; } - this._tempDir = tmpDir; - this._fileService.exists(this._tempDir).then(exists => { - if (!exists) { - this._logService.warn(`ChatSandboxService: tmp directory is not present at ${this._tempDir}`); - } - }); } } } 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 21bb042d38765..2f10f5c83efc1 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 @@ -30,11 +30,11 @@ import { CreateAndRunTaskTool, CreateAndRunTaskToolData } from './tools/task/cre 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 { IChatSandboxService, ChatSandboxService } from '../../../chat/common/chatSandboxService.js'; +import { ITerminalSandboxService, TerminalSandboxService } from '../../../terminal/common/terminalSandboxService.js'; // #region Services -registerSingleton(IChatSandboxService, ChatSandboxService, InstantiationType.Delayed); +registerSingleton(ITerminalSandboxService, TerminalSandboxService, InstantiationType.Delayed); // #endregion Services 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 7f5bd1117076d..a5e92189ff002 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -45,7 +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 { IChatSandboxService } from '../../../../chat/common/chatSandboxService.js'; +import { ITerminalSandboxService } from '../../../../terminal/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'; @@ -310,7 +310,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { @ITerminalService private readonly _terminalService: ITerminalService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, - @IChatSandboxService private readonly _sandboxService: IChatSandboxService, + @ITerminalSandboxService private readonly _sandboxService: ITerminalSandboxService, ) { super(); 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 8ba4396620471..e8ba12c8ab1d2 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,7 +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 { IChatSandboxService } from '../../../../chat/common/chatSandboxService.js'; +import { ITerminalSandboxService } from '../../../../terminal/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'; @@ -92,7 +92,7 @@ suite('RunInTerminalTool', () => { instantiationService.stub(IHistoryService, { getLastActiveWorkspaceRoot: () => undefined }); - instantiationService.stub(IChatSandboxService, { + instantiationService.stub(ITerminalSandboxService, { _serviceBrand: undefined, isEnabled: () => false, wrapCommand: command => command, From 1e046d4fa8efc66c77ed87d0ae0c2fc998ee275f Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Fri, 16 Jan 2026 00:07:27 -0800 Subject: [PATCH 27/31] update to sandboxservice --- .../contrib/terminal/common/terminalSandboxService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts b/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts index f28acbb8b25ef..5ecc542e69029 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts @@ -15,7 +15,7 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { joinPath } from '../../../../base/common/resources.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { IEnvironmentService, INativeEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; import { TerminalContribSettingId } from '../terminalContribExports.js'; import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; @@ -43,7 +43,7 @@ export class TerminalSandboxService implements ITerminalSandboxService { constructor( @IConfigurationService private readonly _configurationService: IConfigurationService, @IFileService private readonly _fileService: IFileService, - @IEnvironmentService private readonly _environmentService: INativeEnvironmentService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, @ILogService private readonly _logService: ILogService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, ) { @@ -119,7 +119,7 @@ export class TerminalSandboxService implements ITerminalSandboxService { private _initTempDir(): void { if (this.isEnabled() && isNative) { this._needsForceUpdateConfigFile = true; - const environmentService = this._environmentService; + 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'); From e35e56f81ac6d7c6f67e548444c94b0516b68ffe Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Fri, 16 Jan 2026 09:14:21 -0800 Subject: [PATCH 28/31] update to sandboxservice --- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 2 -- 1 file changed, 2 deletions(-) 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 a5e92189ff002..3cc99d8e43f6f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -850,7 +850,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { } } - private _handleTerminalVisibility(toolTerminal: IToolTerminal, chatSessionResource: URI) { const chatSessionOpenInWidget = !!this._chatWidgetService.getWidgetBySessionResource(chatSessionResource); if (this._configurationService.getValue(TerminalChatAgentToolsSettingId.OutputLocation) === 'terminal' && chatSessionOpenInWidget) { @@ -866,7 +865,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { const profile = await this._profileFetcher.getCopilotProfile(); const os = await this._osBackend; const toolTerminal = await this._terminalToolCreator.createTerminal(profile, os, token); - this._terminalChatService.registerTerminalInstanceWithToolSession(terminalToolSessionId, toolTerminal.instance); this._terminalChatService.registerTerminalInstanceWithChatSession(chatSessionResource, toolTerminal.instance); this._registerInputListener(toolTerminal); From 2ed1d8d0570f041864d18ce577e1bb314f1394ca Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Fri, 16 Jan 2026 09:17:49 -0800 Subject: [PATCH 29/31] removing extra line --- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 1 - 1 file changed, 1 deletion(-) 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 3cc99d8e43f6f..a03c97ea1d429 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -702,7 +702,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { let timeoutPromise: CancelablePromise | undefined; const executeCancellation = store.add(new CancellationTokenSource(token)); - // Set up timeout if provided and the setting is enabled if (args.timeout !== undefined && args.timeout > 0) { const shouldEnforceTimeout = this._configurationService.getValue(TerminalChatAgentToolsSettingId.EnforceTimeoutFromModel) === true; From 46534e1ac8a363f7f855887f106c88a1b4b188fc Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Fri, 16 Jan 2026 09:43:35 -0800 Subject: [PATCH 30/31] formatting PR suggested by copilot --- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 3 --- 1 file changed, 3 deletions(-) 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 a03c97ea1d429..6ce932b153bc5 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -288,7 +288,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { protected readonly _osBackend: Promise; private static readonly _backgroundExecutions = new Map(); - public static getBackgroundOutput(id: string): string { const backgroundExecution = RunInTerminalTool._backgroundExecutions.get(id); if (!backgroundExecution) { @@ -334,8 +333,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { // Clear out warning accepted state if the setting is disabled this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { - // Clear out warning accepted state if the setting is disabled - if (!e || e.affectsConfiguration(TerminalChatAgentToolsSettingId.EnableAutoApprove)) { if (this._configurationService.getValue(TerminalChatAgentToolsSettingId.EnableAutoApprove) !== true) { this._storageService.remove(TerminalToolConfirmationStorageKeys.TerminalAutoApproveWarningAccepted, StorageScope.APPLICATION); From 07dc6dfc801a7078283e3ef7cf336cf730801b35 Mon Sep 17 00:00:00 2001 From: Dileep Yavanmandha Date: Fri, 16 Jan 2026 11:14:33 -0800 Subject: [PATCH 31/31] files location change suggested for review --- src/vs/platform/terminal/common/terminal.ts | 24 ------------ .../terminal/terminalContribExports.ts | 4 -- .../terminal.chatAgentTools.contribution.ts | 2 +- .../browser/tools/runInTerminalTool.ts | 2 +- .../chatAgentTools/common/terminalSandbox.ts | 24 ++++++++++++ .../common/terminalSandboxService.ts | 39 +++++++++---------- .../runInTerminalTool.test.ts | 2 +- 7 files changed, 46 insertions(+), 51 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalSandbox.ts rename src/vs/workbench/contrib/{terminal => terminalContrib/chatAgentTools}/common/terminalSandboxService.ts (74%) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index f0df17a7b4c48..f21babec2fa0c 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -19,22 +19,6 @@ import type { IAction } from '../../../base/common/actions.js'; import type { IDisposable } from '../../../base/common/lifecycle.js'; import type { SingleOrMany } from '../../../base/common/types.js'; -/** - * Local type definition for sandbox runtime configuration to avoid importing external package - * in the common layer. The actual type should match @anthropic-ai/sandbox-runtime. - */ -export interface ITerminalSandboxRuntimeConfig { - network?: { - allowedDomains?: string[]; - deniedDomains?: string[]; - }; - filesystem?: { - denyRead?: string[]; - allowWrite?: string[]; - denyWrite?: string[]; - }; -} - export const enum TerminalSettingPrefix { AutomationProfile = 'terminal.integrated.automationProfile.', DefaultProfile = 'terminal.integrated.defaultProfile.', @@ -689,11 +673,6 @@ export interface IShellLaunchConfig { shellIntegrationNonce?: string; } -export interface ITerminalSandboxSettings extends ITerminalSandboxRuntimeConfig { - enabled?: boolean; -} - - export interface ITerminalTabAction { id: string; label: string; @@ -751,7 +730,6 @@ export interface ITerminalProcessOptions { environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined; workspaceFolder: IWorkspaceFolder | undefined; isScreenReaderOptimized: boolean; - sandboxSettings?: ITerminalSandboxRuntimeConfig; } export interface ITerminalEnvironment { @@ -943,8 +921,6 @@ export interface ITerminalProfile { overrideName?: boolean; color?: string; icon?: ThemeIcon | URI | { light: URI; dark: URI }; - sandboxed?: boolean; - sandboxSettings?: ITerminalSandboxRuntimeConfig; } export interface ITerminalDimensionsOverride extends Readonly { diff --git a/src/vs/workbench/contrib/terminal/terminalContribExports.ts b/src/vs/workbench/contrib/terminal/terminalContribExports.ts index 42c00ae2be1c1..1fd3bb600a07d 100644 --- a/src/vs/workbench/contrib/terminal/terminalContribExports.ts +++ b/src/vs/workbench/contrib/terminal/terminalContribExports.ts @@ -44,10 +44,6 @@ export const enum TerminalContribSettingId { EnableAutoApprove = TerminalChatAgentToolsSettingId.EnableAutoApprove, ShellIntegrationTimeout = TerminalChatAgentToolsSettingId.ShellIntegrationTimeout, OutputLocation = TerminalChatAgentToolsSettingId.OutputLocation, - TerminalSandboxEnabled = TerminalChatAgentToolsSettingId.TerminalSandboxEnabled, - TerminalSandboxNetwork = TerminalChatAgentToolsSettingId.TerminalSandboxNetwork, - TerminalSandboxLinuxFileSystem = TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem, - TerminalSandboxMacFileSystem = TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem, } // 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 2f10f5c83efc1..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 @@ -30,7 +30,7 @@ import { CreateAndRunTaskTool, CreateAndRunTaskToolData } from './tools/task/cre 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 '../../../terminal/common/terminalSandboxService.js'; +import { ITerminalSandboxService, TerminalSandboxService } from '../common/terminalSandboxService.js'; // #region Services 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 6ce932b153bc5..594fa998d6bee 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -45,7 +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 '../../../../terminal/common/terminalSandboxService.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'; diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalSandbox.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalSandbox.ts new file mode 100644 index 0000000000000..113cf02d20257 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalSandbox.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Local type definition for sandbox runtime configuration to avoid importing external package + * in the common layer. The actual type should match @anthropic-ai/sandbox-runtime. + */ +export interface ITerminalSandboxRuntimeConfig { + network?: { + allowedDomains?: string[]; + deniedDomains?: string[]; + }; + filesystem?: { + denyRead?: string[]; + allowWrite?: string[]; + denyWrite?: string[]; + }; +} + +export interface ITerminalSandboxSettings extends ITerminalSandboxRuntimeConfig { + enabled?: boolean; +} diff --git a/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalSandboxService.ts similarity index 74% rename from src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts rename to src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalSandboxService.ts index 5ecc542e69029..1f93088394f1a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalSandboxService.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalSandboxService.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isNative, OperatingSystem, OS } from '../../../../base/common/platform.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { ITerminalSandboxSettings } from '../../../../platform/terminal/common/terminal.js'; -import { ILogService } from '../../../../platform/log/common/log.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { dirname, join } from '../../../../base/common/path.js'; -import { FileAccess } from '../../../../base/common/network.js'; -import { URI } from '../../../../base/common/uri.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { VSBuffer } from '../../../../base/common/buffer.js'; -import { joinPath } from '../../../../base/common/resources.js'; -import { generateUuid } from '../../../../base/common/uuid.js'; -import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; -import { TerminalContribSettingId } from '../terminalContribExports.js'; -import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; +import { VSBuffer } from '../../../../../base/common/buffer.js'; +import { FileAccess } from '../../../../../base/common/network.js'; +import { dirname, join } from '../../../../../base/common/path.js'; +import { isNative, OperatingSystem, OS } from '../../../../../base/common/platform.js'; +import { joinPath } from '../../../../../base/common/resources.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { generateUuid } from '../../../../../base/common/uuid.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ILogService } from '../../../../../platform/log/common/log.js'; +import { ITerminalSandboxSettings } from './terminalSandbox.js'; +import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js'; +import { TerminalChatAgentToolsSettingId } from './terminalChatAgentToolsConfiguration.js'; export const ITerminalSandboxService = createDecorator('terminalSandboxService'); @@ -39,7 +39,6 @@ export class TerminalSandboxService implements ITerminalSandboxService { private _sandboxSettingsId: string | undefined; private _os: OperatingSystem = OS; - constructor( @IConfigurationService private readonly _configurationService: IConfigurationService, @IFileService private readonly _fileService: IFileService, @@ -58,7 +57,7 @@ export class TerminalSandboxService implements ITerminalSandboxService { if (this._os === OperatingSystem.Windows) { return false; } - return this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxEnabled); + return this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled); } public wrapCommand(command: string): string { @@ -90,12 +89,12 @@ export class TerminalSandboxService implements ITerminalSandboxService { this._initTempDir(); } if (this._tempDir) { - const networkSetting = this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxNetwork) ?? {}; + const networkSetting = this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxNetwork) ?? {}; const linuxFileSystemSetting = this._os === OperatingSystem.Linux - ? this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxLinuxFileSystem) ?? {} + ? this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem) ?? {} : {}; const macFileSystemSetting = this._os === OperatingSystem.Macintosh - ? this._configurationService.getValue(TerminalContribSettingId.TerminalSandboxMacFileSystem) ?? {} + ? this._configurationService.getValue(TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem) ?? {} : {}; const configFileUri = joinPath(this._tempDir, `vscode-sandbox-settings-${this._sandboxSettingsId}.json`); const sandboxSettings = { 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 e8ba12c8ab1d2..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,7 +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 '../../../../terminal/common/terminalSandboxService.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';