Skip to content

Commit a04f628

Browse files
authored
Missing prompt contribution should not break chat (#281451)
1 parent 541da45 commit a04f628

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { IModelService } from '../../../../../../editor/common/services/model.js
1616
import { localize } from '../../../../../../nls.js';
1717
import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js';
1818
import { IExtensionDescription } from '../../../../../../platform/extensions/common/extensions.js';
19-
import { IFileService } from '../../../../../../platform/files/common/files.js';
19+
import { FileOperationError, FileOperationResult, IFileService } from '../../../../../../platform/files/common/files.js';
2020
import { IExtensionService } from '../../../../../services/extensions/common/extensions.js';
2121
import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js';
2222
import { ILabelService } from '../../../../../../platform/label/common/label.js';
@@ -387,7 +387,7 @@ export class PromptsService extends Disposable implements IPromptsService {
387387
let agentFiles = await this.listPromptFiles(PromptsType.agent, token);
388388
const disabledAgents = this.getDisabledPromptFiles(PromptsType.agent);
389389
agentFiles = agentFiles.filter(promptPath => !disabledAgents.has(promptPath.uri));
390-
const customAgents = await Promise.all(
390+
const customAgentsResults = await Promise.allSettled(
391391
agentFiles.map(async (promptPath): Promise<ICustomAgent> => {
392392
const uri = promptPath.uri;
393393
const ast = await this.parseNew(uri, token);
@@ -432,6 +432,23 @@ export class PromptsService extends Disposable implements IPromptsService {
432432
return { uri, name, description, model, tools, handOffs, argumentHint, target, infer, agentInstructions, source };
433433
})
434434
);
435+
436+
const customAgents: ICustomAgent[] = [];
437+
for (let i = 0; i < customAgentsResults.length; i++) {
438+
const result = customAgentsResults[i];
439+
if (result.status === 'fulfilled') {
440+
customAgents.push(result.value);
441+
} else {
442+
const uri = agentFiles[i].uri;
443+
const error = result.reason;
444+
if (error instanceof FileOperationError && error.fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
445+
this.logger.warn(`[computeCustomAgents] Skipping agent file that does not exist: ${uri}`, error.message);
446+
} else {
447+
this.logger.error(`[computeCustomAgents] Failed to parse agent file: ${uri}`, error);
448+
}
449+
}
450+
}
451+
435452
return customAgents;
436453
}
437454

src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,57 @@ suite('PromptsService', () => {
12021202

12031203
registered.dispose();
12041204
});
1205+
1206+
test('Contributed agent file that does not exist should not crash', async () => {
1207+
const nonExistentUri = URI.parse('file://extensions/my-extension/nonexistent.agent.md');
1208+
const existingUri = URI.parse('file://extensions/my-extension/existing.agent.md');
1209+
const extension = {
1210+
identifier: { value: 'test.my-extension' }
1211+
} as unknown as IExtensionDescription;
1212+
1213+
// Only create the existing file
1214+
await mockFiles(fileService, [
1215+
{
1216+
path: existingUri.path,
1217+
contents: [
1218+
'---',
1219+
'name: \'Existing Agent\'',
1220+
'description: \'An agent that exists\'',
1221+
'---',
1222+
'I am an existing agent.',
1223+
]
1224+
}
1225+
]);
1226+
1227+
// Register both agents (one exists, one doesn't)
1228+
const registered1 = service.registerContributedFile(
1229+
PromptsType.agent,
1230+
'NonExistent Agent',
1231+
'An agent that does not exist',
1232+
nonExistentUri,
1233+
extension
1234+
);
1235+
1236+
const registered2 = service.registerContributedFile(
1237+
PromptsType.agent,
1238+
'Existing Agent',
1239+
'An agent that exists',
1240+
existingUri,
1241+
extension
1242+
);
1243+
1244+
// Verify that getCustomAgents doesn't crash and returns only the valid agent
1245+
const agents = await service.getCustomAgents(CancellationToken.None);
1246+
1247+
// Should only get the existing agent, not the non-existent one
1248+
assert.strictEqual(agents.length, 1, 'Should only return the agent that exists');
1249+
assert.strictEqual(agents[0].name, 'Existing Agent');
1250+
assert.strictEqual(agents[0].description, 'An agent that exists');
1251+
assert.strictEqual(agents[0].uri.toString(), existingUri.toString());
1252+
1253+
registered1.dispose();
1254+
registered2.dispose();
1255+
});
12051256
});
12061257

12071258
suite('findClaudeSkills', () => {

0 commit comments

Comments
 (0)