Skip to content

Commit d70afef

Browse files
fix: plugin skill files not accessible when connected to remote
When connected to an SSH/WSL/dev container remote, local file:// URIs for agent plugin skill files were being passed as native filesystem paths to the remote extension host, which can't read them. Fix getFilePath() to emit vscode-local:// URIs for local file:// paths when isRemote is true — the remote extension host can resolve these via the built-in local file bridge. Also fix _listExtensionPromptFiles to include plugin-storage files so they pass the trust check without triggering a confirmation dialog on every read. Fixes #305168
1 parent 2e96f10 commit d70afef

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

src/vs/workbench/contrib/chat/common/promptSyntax/chatPromptFilesContribution.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,16 @@ CommandsRegistry.registerCommand('_listExtensionPromptFiles', async (accessor):
175175
promptsService.listPromptFiles(PromptsType.hook, CancellationToken.None),
176176
]);
177177

178-
// Combine all files and collect extension-contributed ones
178+
// Combine all files and collect extension- and plugin-contributed ones.
179+
// Plugin files are included so the copilot extension can trust them and
180+
// serve them to the LLM without a confirmation dialog when connected to a
181+
// remote (where they are emitted as vscode-local:// URIs).
179182
const result: IExtensionPromptFileResult[] = [];
180183
for (const file of [...agents, ...instructions, ...prompts, ...skills, ...hooks]) {
181184
if (file.storage === PromptsStorage.extension) {
182185
result.push({ uri: file.uri.toJSON(), type: file.type, extensionId: file.extension.identifier.value });
186+
} else if (file.storage === PromptsStorage.plugin) {
187+
result.push({ uri: file.uri.toJSON(), type: file.type, extensionId: file.pluginUri.toString() });
183188
}
184189
}
185190

src/vs/workbench/contrib/chat/common/promptSyntax/computeAutomaticInstructions.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,8 @@ export class ComputeAutomaticInstructions {
345345

346346
const remoteEnv = await this._remoteAgentService.getEnvironment();
347347
const remoteOS = remoteEnv?.os;
348-
const filePath = (uri: URI) => getFilePath(uri, remoteOS);
348+
const isRemote = this._remoteAgentService.getConnection() !== null;
349+
const filePath = (uri: URI) => getFilePath(uri, remoteOS, isRemote);
349350

350351
const entries: string[] = [];
351352
if (readTool) {
@@ -579,7 +580,14 @@ export class ComputeAutomaticInstructions {
579580
}
580581

581582

582-
export function getFilePath(uri: URI, remoteOS: OperatingSystem | undefined): string {
583+
export function getFilePath(uri: URI, remoteOS: OperatingSystem | undefined, isRemote = false): string {
584+
// When connected to a remote, local file:// URIs must be represented as
585+
// vscode-local:// so the remote extension host can read them via the local
586+
// file bridge. This works for WSL, SSH, and dev containers without any
587+
// cache migration.
588+
if (isRemote && uri.scheme === Schemas.file) {
589+
return uri.with({ scheme: 'vscode-local' }).toString();
590+
}
583591
if (uri.scheme === Schemas.file || uri.scheme === Schemas.vscodeRemote) {
584592
const fsPath = uri.fsPath;
585593
// uri.fsPath uses the local OS's path separators, but the path

src/vs/workbench/contrib/chat/test/common/promptSyntax/computeAutomaticInstructions.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ suite('ComputeAutomaticInstructions', () => {
185185

186186
instaService.stub(IRemoteAgentService, {
187187
getEnvironment: () => Promise.resolve(null),
188+
getConnection: () => null,
188189
});
189190

190191
instaService.stub(IContextKeyService, new MockContextKeyService());
@@ -2700,4 +2701,28 @@ suite('getFilePath', () => {
27002701
const result = getFilePath(uri, undefined);
27012702
assert.strictEqual(result, uri.fsPath);
27022703
});
2704+
2705+
test('should return vscode-local:// URI string for file:// URIs when connected to a remote', () => {
2706+
const uri = URI.file('/C:/Users/user/AppData/Roaming/agent-plugins/my-skill/SKILL.md');
2707+
const result = getFilePath(uri, OperatingSystem.Linux, /* isRemote */ true);
2708+
assert.strictEqual(result, uri.with({ scheme: 'vscode-local' }).toString());
2709+
});
2710+
2711+
test('should return vscode-local:// URI string for file:// URIs when connected to a Windows remote', () => {
2712+
const uri = URI.file('/C:/Users/user/AppData/Roaming/agent-plugins/my-skill/SKILL.md');
2713+
const result = getFilePath(uri, OperatingSystem.Windows, /* isRemote */ true);
2714+
assert.strictEqual(result, uri.with({ scheme: 'vscode-local' }).toString());
2715+
});
2716+
2717+
test('should not convert file:// URIs to vscode-local:// when not connected to a remote', () => {
2718+
const uri = URI.file('/home/user/.copilot/agent-plugins/my-skill/SKILL.md');
2719+
const result = getFilePath(uri, undefined, /* isRemote */ false);
2720+
assert.strictEqual(result, uri.fsPath);
2721+
});
2722+
2723+
test('should not convert vscode-remote:// URIs when connected to a remote', () => {
2724+
const uri = URI.from({ scheme: Schemas.vscodeRemote, authority: 'wsl+ubuntu', path: '/home/user/project/file.ts' });
2725+
const result = getFilePath(uri, OperatingSystem.Linux, /* isRemote */ true);
2726+
assert.strictEqual(result, uri.fsPath);
2727+
});
27032728
});

0 commit comments

Comments
 (0)