copilotShellTools.ts ×23

Frontier kind: Code frontier

unlabeled · c_d685b3249da6

453 tests · 27998 LOC · 131 files · introduces 0 tests · 343 LOC · 3 files

Introduces — evidence that enters the hierarchy at this concept

Code
37 ranges343 lines · 3 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2289 ranges27998 lines · 131 files · Browse complete extent
All tests (intent)
453 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

3 files ranked by introduced lines: 343 introduced LOC across 37 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/copilot/copilotShellTools.ts 176 introduced LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- copilotShellTools.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type { Tool, ToolResultObject } from '@github/copilot-sdk';
7 > import { generateUuid } from '../../../../base/common/uuid.js';
8 > import { URI } from '../../../../base/common/uri.js';
9 > import { Disposable, DisposableStore, type IReference, toDisposable } from '../../../../base/common/lifecycle.js';
10 > import { Emitter, Event } from '../../../../base/common/event.js';
11 > import { IEnvironmentService } from '../../../environment/common/environment.js';
12 > import { IInstantiationService } from '../../../instantiation/common/instantiation.js';
13 > import { ILogService } from '../../../log/common/log.js';
14 > import { IProductService } from '../../../product/common/productService.js';
15 > import { ISandboxHelperService } from '../../../sandbox/common/sandboxHelperService.js';
16 > import type { ITerminalSandboxResolvedNetworkDomains } from '../../../sandbox/common/terminalSandboxService.js';
17 > import { TerminalSandboxEngine } from '../../../sandbox/common/terminalSandboxEngine.js';
18 > import { TerminalClaimKind, type TerminalSessionClaim } from '../../common/state/protocol/state.js';
19 > import { isZsh } from '../agentHostShellUtils.js';
20 > import { IAgentHostTerminalManager } from '../agentHostTerminalManager.js';
21 > import { createAgentHostSandboxEngine } from './agentHostSandboxEngine.js';
22 > import { IAgentConfigurationService } from '../agentConfigurationService.js';
23 > import { DEFAULT_SHELL_COMMAND_TIMEOUT_MS, executeShellCommand, isMultilineCommand, prefixForHistorySuppression, prepareOutputForModel, shellTypeForExecutable, type IShellCommandResult, type ShellType } from '../shared/shellCommandExecution.js';
24 >
25 > // Re-exported for consumers (and tests) that historically imported these
26 > // shell helpers from this module. Their canonical home is the shared,
27 > // agent-agnostic shellCommandExecution module.
28 > export { isMultilineCommand, prefixForHistorySuppression, shellTypeForExecutable };
29 > export type { ShellType };
30 >
31 > /**
32 > * Message returned to the model when a command switches to the terminal's
33 > * alternate buffer (typically an interactive full-screen UI).
34 > */
35 > const ALT_BUFFER_MESSAGE = 'The command opened the alternate buffer and is still running in the terminal. It likely launched an interactive terminal UI. Use write_bash/write_powershell to interact with it, or shutdown the shell to stop it.';
36 >
37 > /**
38 > * Tracks a single persistent shell instance backed by a managed PTY terminal.
39 > */
40 > interface IManagedShell {
41 > readonly id: string;
42 > readonly terminalUri: string;
43 > readonly shellType: ShellType;
44 > readonly executable: string;
45 > }
46 >
47 > // ---------------------------------------------------------------------------
48 > // ShellManager
49 > // ---------------------------------------------------------------------------
50 >
51 > /**
52 > * Per-session manager for persistent shell instances. Each shell is backed by
53 > * a {@link IAgentHostTerminalManager} terminal and participates in AHP terminal
54 > * claim semantics.
55 > *
56 > * Created via {@link IInstantiationService} once per session and disposed when
57 > * the session ends.
58 > */
59 > export class ShellManager extends Disposable {
60 >
61 > private readonly _shells = new Map<string, IManagedShell>();
62 > private readonly _toolCallShells = new Map<string, string>();
63 > private _resolvedExecutable: Promise<string> | undefined;
64 > private _sandboxEngine: TerminalSandboxEngine | undefined;
65 > /** Set of shell ids currently executing a command and unsafe to share. */
66 > private readonly _busyShellIds = new Set<string>();
67 > /** Release listeners for shells held after a tool returns while the command is still running. */
68 > private readonly _heldShellReleaseListeners = new Map<string, DisposableStore>();
69 >
70 > private readonly _onDidAssociateTerminal = this._register(new Emitter<{ toolCallId: string; terminalUri: string; displayName: string }>());
71 > readonly onDidAssociateTerminal: Event<{ toolCallId: string; terminalUri: string; displayName: string }> = this._onDidAssociateTerminal.event;
72 >
73 > constructor(
74 private readonly _sessionUri: URI,
75 public readonly workingDirectory: URI | undefined,
99 }));
100 }
102 > /**
103 > * Resolves the session's shell executable via {@link IAgentHostTerminalManager.getDefaultShell}
104 > * and caches it so every tool call in the session uses the same binary
105 > * (keeps `shellType`, sentinel format, and history suppression consistent).
106 > */
107 > getResolvedExecutable(): Promise<string> {
108 if (!this._resolvedExecutable) {
109 this._resolvedExecutable = this._terminalManager.getDefaultShell();
111 return this._resolvedExecutable;
112 }
114 > /**
115 > * Lazily constructs the per-session {@link TerminalSandboxEngine}. The engine
116 > * is registered for disposal alongside the {@link ShellManager}; its temp dir
117 > * is cleaned up best-effort on dispose.
118 > */
119 > getOrCreateSandboxEngine(): TerminalSandboxEngine {
120 if (!this._sandboxEngine) {
121 const sessionId = this._sessionUri.path.split('/').pop() ?? generateUuid();
137 return this._sandboxEngine;
138 }
140 > /**
141 > * Acquire a shell of the given type for executing a single command. The
142 > * returned reference holds the shell exclusively — its terminal will not
143 > * be handed out to another concurrent caller until the reference is
144 > * disposed. If no idle shell of the requested type exists, a new one is
145 > * created.
146 > */
147 > async getOrCreateShell(
148 shellType: ShellType,
149 turnId: string,
198 return this._makeReference(shell);
199 }
201 > private _makeReference(shell: IManagedShell): IReference<IManagedShell> {
202 let disposed = false;
203 return {
212 };
213 }
215 > holdShellUntilCommandFinishes(shell: IManagedShell): void {
216 if (this._heldShellReleaseListeners.has(shell.id)) {
217 return;
228 this._heldShellReleaseListeners.set(shell.id, store);
229 }
231 > private _trackToolCall(toolCallId: string, shellId: string): void {
232 this._toolCallShells.set(toolCallId, shellId);
233 const shell = this._shells.get(shellId);
237 }
238 }
240 > getTerminalUriForToolCall(toolCallId: string): string | undefined {
241 const shellId = this._toolCallShells.get(toolCallId);
242 if (!shellId) {
245 return this._shells.get(shellId)?.terminalUri;
246 }
248 > getShell(id: string): IManagedShell | undefined {
249 return this._shells.get(id);
250 }
252 > listShells(): IManagedShell[] {
253 const result: IManagedShell[] = [];
254 for (const shell of this._shells.values()) {
259 return result;
260 }
262 > shutdownShell(id: string): boolean {
263 const shell = this._shells.get(id);
264 if (!shell) {
273 return true;
274 }
276 >
277 > // ---------------------------------------------------------------------------
278 > // Tool implementations
279 > // ---------------------------------------------------------------------------
280 >
281 > interface IShellExecutionResult {
282 > readonly toolResult: ToolResultObject;
283 > readonly keepShellBusy?: boolean;
284 > }
285 >
286 function makeSuccessResult(text: string): ToolResultObject {
287 return { textResultForLlm: text, resultType: 'success' };
288 }
290 function makeFailureResult(text: string, error?: string): ToolResultObject {
291 return { textResultForLlm: text, resultType: 'failure', error };
292 }
294 function makeExecutionResult(toolResult: ToolResultObject, options?: { keepShellBusy?: boolean }): IShellExecutionResult {
295 return { toolResult, keepShellBusy: options?.keepShellBusy };
296 }
298 > /**
299 > * Maps the neutral {@link IShellCommandResult} produced by the shared shell
300 > * executor to the Copilot SDK {@link ToolResultObject} shape expected by the
301 > * shell tools.
302 > */
303 function shellCommandResultToExecutionResult(result: IShellCommandResult, timeoutMs: number): IShellExecutionResult {
304 switch (result.status) {
324 }
325 }
327 async function executeCommandInShell(
328 shell: IManagedShell,
344 };
345 }
347 > // ---------------------------------------------------------------------------
348 > // Public factory
349 > // ---------------------------------------------------------------------------
350 >
351 > interface IShellToolArgs {
352 > command: string;
353 > timeout?: number;
354 > requestUnsandboxedExecution?: boolean;
355 > requestUnsandboxedExecutionReason?: string;
356 > }
357 >
358 > export interface IUnsandboxedCommandConfirmationRequest {
359 > readonly toolCallId: string;
360 > readonly toolName: string;
361 > readonly shellExecutable: string;
362 > readonly command: string;
363 > readonly reason?: string;
364 > readonly blockedDomains?: readonly string[];
365 > }
366 >
367 > export type UnsandboxedCommandConfirmationHandler = (request: IUnsandboxedCommandConfirmationRequest) => Promise<boolean>;
368 >
369 > interface IWriteShellArgs {
370 > command: string;
371 > }
372 >
373 > interface IReadShellArgs {
374 > shell_id?: string;
375 > }
376 >
377 > interface IShutdownShellArgs {
378 > shell_id?: string;
379 > }
380 >
381 > /**
382 > * Builds the SDK {@link Tool} set that overrides the Copilot SDK's two
383 > * built-in shells (`bash` and `powershell`) with PTY-backed implementations,
384 > * plus companion tools (read, write, shutdown, list).
385 > */
386 export async function createShellTools(
387 shellManager: ShellManager,
638 return [primaryTool, readTool, writeTool, shutdownTool, listTool, redirectTool];
639 }
641 function isWindowsPowerShell(envShell: string): boolean {
642 return envShell.endsWith('System32\\WindowsPowerShell\\v1.0\\powershell.exe');
643 }
645 function createPowerShellModelDescription(shellType: string, shellPath: string, isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string {
646 const isWinPwsh = isWindowsPowerShell(shellPath);
704 return parts.join('\n');
705 }
707 function createSandboxLines(networkDomains?: ITerminalSandboxResolvedNetworkDomains): string[] {
708 const lines = [
731 return lines;
732 }
734 function createGenericDescription(shellType: string, isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string {
735 const parts = [`
784 return parts.join('');
785 }
787 function createBashModelDescription(isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string {
788 return [
794 ].join('\n');
795 }
797 function createZshModelDescription(isSandboxEnabled: boolean, networkDomains?: ITerminalSandboxResolvedNetworkDomains): string {
798 return [
src/vs/platform/sandbox/common/sandboxHelperService.ts 102 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- sandboxHelperService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { createDecorator } from '../../instantiation/common/instantiation.js';
7 >
8 > export const ISandboxHelperService = createDecorator<ISandboxHelperService>('sandboxHelperService');
9 >
10 > export interface ISandboxDependencyStatus {
11 > readonly bubblewrapInstalled: boolean;
12 > readonly bubblewrapUsable: boolean;
13 > readonly socatInstalled: boolean;
14 > readonly bubblewrapError?: string;
15 > readonly dependencyInstallCommand?: string;
16 > }
17 >
18 > export interface IWindowsMxcFilesystemPolicy {
19 > readonly readonlyPaths: string[];
20 > readonly readwritePaths: string[];
21 > }
22 >
23 > /** Sandbox policy passed to the Windows MXC helper process. */
24 > export interface IWindowsMxcSandboxPolicy {
25 > version: string;
26 > filesystem?: {
27 > readwritePaths?: string[];
28 > readonlyPaths?: string[];
29 > deniedPaths?: string[];
30 > clearPolicyOnExit?: boolean;
31 > };
32 > network?: {
33 > allowOutbound?: boolean;
34 > allowLocalNetwork?: boolean;
35 > allowedHosts?: string[];
36 > blockedHosts?: string[];
37 > proxy?: { builtinTestServer: true } | { localhost: number } | { url: string };
38 > };
39 > ui?: {
40 > allowWindows?: boolean;
41 > clipboard?: 'none' | 'read' | 'write' | 'all';
42 > allowInputInjection?: boolean;
43 > };
44 > timeoutMs?: number;
45 > }
46 >
47 > /** MXC payload returned by the Windows sandbox helper. */
48 > export interface IWindowsMxcConfig {
49 > version: string;
50 > containerId?: string;
51 > containment?: IWindowsMxcPolicyContainment;
52 > lifecycle?: {
53 > destroyOnExit?: boolean;
54 > preservePolicy?: boolean;
55 > };
56 > process?: {
57 > commandLine: string;
58 > cwd?: string;
59 > env?: string[];
60 > timeout?: number;
61 > };
62 > processContainer?: {
63 > leastPrivilege?: boolean;
64 > capabilities?: string[];
65 > ui?: {
66 > isolation: 'desktop' | 'handles' | 'atoms' | 'container';
67 > desktopSystemControl: boolean;
68 > systemSettings: string;
69 > ime: boolean;
70 > };
71 > };
72 > filesystem?: {
73 > readwritePaths?: string[];
74 > readonlyPaths?: string[];
75 > deniedPaths?: string[];
76 > clearPolicyOnExit?: boolean;
77 > };
78 > network?: {
79 > enforcementMode?: 'capabilities' | 'firewall' | 'both';
80 > defaultPolicy?: 'allow' | 'block';
81 > allowLocalNetwork?: boolean;
82 > allowedHosts?: string[];
83 > blockedHosts?: string[];
84 > proxy?: { builtinTestServer: true } | { localhost: number } | { url: string };
85 > removeRulesOnExit?: boolean;
86 > };
87 > ui?: {
88 > disable: boolean;
89 > clipboard: 'none' | 'read' | 'write' | 'all';
90 > injection: boolean;
91 > };
92 > }
93 >
94 > export type IWindowsMxcPolicyContainment = 'process' | 'vm' | 'microvm' | 'processcontainer' | 'windows_sandbox' | 'wslc' | 'lxc' | 'hyperlight' | 'seatbelt' | 'isolation_session' | 'bubblewrap';
95 >
96 > export interface ISandboxHelperService {
97 > readonly _serviceBrand: undefined;
98 > checkSandboxDependencies(): Promise<ISandboxDependencyStatus | undefined>;
99 > getWindowsMxcFilesystemPolicy(): Promise<IWindowsMxcFilesystemPolicy | undefined>;
100 > getWindowsMxcEnvironment(): Promise<string[] | undefined>;
101 > buildWindowsMxcSandboxPayload(commandLine: string, policy: IWindowsMxcSandboxPolicy, workingDirectory?: string, containerName?: string, containment?: IWindowsMxcPolicyContainment): Promise<IWindowsMxcConfig | undefined>;
102 > }
src/vs/platform/agentHost/node/copilot/agentHostSandboxEngine.ts 65 introduced LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- agentHostSandboxEngine.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Event } from '../../../../base/common/event.js';
7 > import { FileAccess } from '../../../../base/common/network.js';
8 > import { dirname } from '../../../../base/common/path.js';
9 > import { OS, OperatingSystem } from '../../../../base/common/platform.js';
10 > import { URI } from '../../../../base/common/uri.js';
11 > import { createHash } from 'crypto';
12 > import { IEnvironmentService, INativeEnvironmentService } from '../../../environment/common/environment.js';
13 > import { IInstantiationService } from '../../../instantiation/common/instantiation.js';
14 > import { IProductService } from '../../../product/common/productService.js';
15 > import { ISandboxHelperService, type ISandboxDependencyStatus, type IWindowsMxcPolicyContainment, type IWindowsMxcSandboxPolicy } from '../../../sandbox/common/sandboxHelperService.js';
16 > import { ITerminalSandboxEngineHost, ITerminalSandboxRuntimeInfo, TerminalSandboxEngine } from '../../../sandbox/common/terminalSandboxEngine.js';
17 > import { IAgentConfigurationService } from '../agentConfigurationService.js';
18 > import { getAppNodeModulesDirName } from '../appNodeModules.js';
19 > import { AgentHostSandboxConfigKey, sandboxConfigSchema, sandboxSettingIdToAgentHostKey } from '../../common/sandboxConfigSchema.js';
20 >
21 > /** Subdirectory under the user home + product data folder where the engine creates its temp dir. */
22 > const SANDBOX_TEMP_DIR_NAME = 'tmp';
23 >
24 > /**
25 > * Host adapter that bridges agent-host environment data into the shared
26 > * {@link TerminalSandboxEngine}. One instance per session, wired up via
27 > * {@link createAgentHostSandboxEngine}.
28 > */
29 > class AgentHostTerminalSandboxHost implements ITerminalSandboxEngineHost {
30 > readonly onDidChangeRoots = Event.None;
31 > readonly onDidChangeSandboxSettings: Event<void>;
32 > private readonly _sandboxHelper: ISandboxHelperService;
33 >
34 > constructor(
35 private readonly _sessionId: string,
36 private readonly _workingDirectory: URI | undefined,
43 this.onDidChangeSandboxSettings = this._agentConfigurationService.onDidRootConfigChange;
44 }
46 > async getOS(): Promise<OperatingSystem> {
47 return OS;
48 }
50 > async getRuntimeInfo(): Promise<ITerminalSandboxRuntimeInfo> {
51 const appRoot = dirname(FileAccess.asFileUri('').path);
52 const runAsNode = !!process.versions['electron'];
58 return { appRoot, execPath: process.execPath, runAsNode, nativeModulesDir };
59 }
61 > async getUserHome(): Promise<URI | undefined> {
62 return this._environmentService.userHome;
63 }
65 > async getSandboxTempDir(): Promise<URI | undefined> {
66 const userHome = this._environmentService.userHome;
67 if (!userHome) {
80 return URI.joinPath(sandboxRoot, sessionLeaf);
81 }
83 > async getWorkspaceStorageReadRoot(): Promise<URI | undefined> {
84 // The agent host has no workspace-storage equivalent today.
85 return undefined;
86 }
88 > getWriteRoots(): readonly URI[] {
89 return this._workingDirectory ? [this._workingDirectory] : [];
90 }
92 > async checkSandboxDependencies(): Promise<ISandboxDependencyStatus | undefined> {
93 return this._sandboxHelper.checkSandboxDependencies();
94 }
96 > async getWindowsMxcFilesystemPolicy() {
97 return this._sandboxHelper.getWindowsMxcFilesystemPolicy();
98 }
100 > async getWindowsMxcEnvironment() {
101 return this._sandboxHelper.getWindowsMxcEnvironment();
102 }
104 > async buildWindowsMxcSandboxPayload(commandLine: string, policy: IWindowsMxcSandboxPolicy, workingDirectory?: string, containerName?: string, containment?: IWindowsMxcPolicyContainment) {
105 return this._sandboxHelper.buildWindowsMxcSandboxPayload(commandLine, policy, workingDirectory, containerName, containment);
106 }
108 > getSandboxSetting<T>(settingId: string): T | undefined {
109 // The agent host stores sandbox settings nested under a single
110 // top-level `sandbox` object with prefix-free sub-keys (e.g.
120 return sandbox?.[innerKey] as T | undefined;
121 }
123 >
124 > /**
125 > * Construct a per-session {@link TerminalSandboxEngine} for the agent host.
126 > * The returned engine is registered with the caller's instantiation service
127 > * but the caller is responsible for disposing it (typically by registering it
128 > * alongside the per-session {@link ShellManager}).
129 > */
130 > export function createAgentHostSandboxEngine(
131 instantiationService: IInstantiationService,
132 environmentService: IEnvironmentService,