Files
vscode/extensions/copilot/src/extension/tools/node/createDirectoryTool.tsx
T
Alex Dima 27ecfb15cf Thread session workingDirectory through tools, prompts, and confirmations
In the agents window, each chat session has its own working directory
that may differ from the current workspace folders (which change when
switching between sessions). This caused tools to search the wrong
folder, show spurious 'Allow reading external files?' prompts, and
render incorrect workspace_info in the system prompt.

Core plumbing:
- Add workingDirectory to IToolInvocationContext, IToolInvocationPreparationContext,
  ILanguageModelToolConfirmationRef, and IChatAgentRequest
- Enrich tool invocation context from model.workingDirectory in invokeTool()
- Include workingDirectory in toolInvocationToken built in extHostTypeConverters
- Pass workingDirectory through LanguageModelToolInvocationOptions and
  LanguageModelToolInvocationPrepareOptions (proposed API)
- Revive workingDirectory URI in extHostLanguageModelTools

Tool fixes (when workingDirectory is set, use it exclusively):
- chatExternalPathConfirmation: auto-approve paths within workingDirectory
- isFileExternalAndNeedsConfirmation / isDirExternalAndNeedsConfirmation /
  assertFileOkForTool: treat workingDirectory as workspace-internal
- createEditConfirmation: use workingDirectory for edit trust checks
- All edit tools (create_file, replace_string, multi_replace, apply_patch,
  insert_edit, edit_notebook, create_directory): pass workingDirectory
- resolveToolUri: resolve relative paths against workingDirectory
- inputGlobToPattern: scope unscoped globs to workingDirectory
- file_search / grep_search: scope searches to workingDirectory
- semantic_search: prefer workingDirectory for cwd
- run_in_terminal: prefer workingDirectory for terminal cwd
- fetchPageTool: check workingDirectory for file URI trust
- readFileTool / listDirTool / viewImageTool: pass workingDirectory

Prompt fixes:
- WorkspaceFoldersHint: show workingDirectory instead of workspace folders
- AgentMultirootWorkspaceStructure: generate file tree from workingDirectory
2026-05-08 16:15:11 +02:00

72 lines
3.0 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as l10n from '@vscode/l10n';
import type * as vscode from 'vscode';
import { IFileSystemService } from '../../../platform/filesystem/common/fileSystemService';
import { IPromptPathRepresentationService } from '../../../platform/prompts/common/promptPathRepresentationService';
import { createFencedCodeBlock } from '../../../util/common/markdown';
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
import { LanguageModelTextPart, LanguageModelToolResult, MarkdownString } from '../../../vscodeTypes';
import { ToolName } from '../common/toolNames';
import { ICopilotTool, ToolRegistry } from '../common/toolsRegistry';
import { formatUriForFileWidget } from '../common/toolUtils';
import { createEditConfirmation } from './editFileToolUtils';
import { resolveToolInputPath } from './toolUtils';
export interface ICreateDirectoryParams {
dirPath: string;
}
export class CreateDirectoryTool implements ICopilotTool<ICreateDirectoryParams> {
public static toolName = ToolName.CreateDirectory;
constructor(
@IPromptPathRepresentationService private readonly promptPathRepresentationService: IPromptPathRepresentationService,
@IFileSystemService private readonly fileSystemService: IFileSystemService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
) { }
async invoke(options: vscode.LanguageModelToolInvocationOptions<ICreateDirectoryParams>, token: vscode.CancellationToken) {
const uri = this.promptPathRepresentationService.resolveFilePath(options.input.dirPath);
if (!uri) {
throw new Error(`Invalid directory path`);
}
await this.fileSystemService.createDirectory(uri);
return new LanguageModelToolResult([
new LanguageModelTextPart(
`Created directory at ${this.promptPathRepresentationService.getFilePath(uri)}`,
)
]);
}
async prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<ICreateDirectoryParams>, token: vscode.CancellationToken): Promise<vscode.PreparedToolInvocation> {
const uri = resolveToolInputPath(options.input.dirPath, this.promptPathRepresentationService);
const confirmation = await this.instantiationService.invokeFunction(
createEditConfirmation,
[uri],
undefined,
async () => {
return 'Creating the directory:\n\n' + createFencedCodeBlock('plaintext', uri.fsPath);
},
options.forceConfirmationReason,
undefined,
options.workingDirectory,
);
return {
...confirmation,
presentation: undefined,
invocationMessage: new MarkdownString(l10n.t`Creating ${formatUriForFileWidget(uri)}`),
pastTenseMessage: new MarkdownString(l10n.t`Created ${formatUriForFileWidget(uri)}`)
};
}
}
ToolRegistry.registerTool(CreateDirectoryTool);