Add chat mode 'source' and sort built-in custom modes into the builtin section#272197
Add chat mode 'source' and sort built-in custom modes into the builtin section#272197roblourens merged 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
Pull Request Overview
Adds provenance tracking for custom chat modes (source of mode: extension, local, or user) and updates UI logic to surface built-in extension-provided custom modes alongside core built-ins.
- Introduces IChatModeSource, integrates source propagation through promptsService -> chatModes, serialization, and tests.
- Updates mode picker to regroup custom modes (extension-provided vs user) and adjusts ordering logic; adds readonly support to groupBy.
- Extends tests to assert new source field.
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| promptsService.test.ts | Updates expected custom mode objects with source field; import reordering. |
| chatModeService.test.ts | Adds source to mock custom modes and asserts it in service behavior. |
| promptValidator.test.ts | Adds source to constructed CustomChatMode and necessary imports. |
| promptsServiceImpl.ts | Populates source for discovered custom modes using new promptPathToChatModeSource helper. |
| promptsService.ts | Defines IChatModeSource, conversion helper, and adds source to ICustomChatMode. |
| chatModes.ts | Propagates, observes, serializes/deserializes source in custom chat mode lifecycle. |
| modePickerActionItem.ts | Regroups modes (built-in vs custom) using source metadata; adds grouping and ordering logic. |
| collections.ts | Makes groupBy accept readonly arrays for broader applicability. |
| const customModes = groupBy( | ||
| modes.custom, | ||
| mode => mode.source?.storage === PromptsStorage.extension && mode.source.extensionId.value === productService.defaultChatAgent?.chatExtensionId ? | ||
| 'builtin' : 'custom'); | ||
|
|
||
| const customBuiltinModeActions = customModes.builtin.map(mode => { | ||
| const action = makeActionFromCustomMode(mode, currentMode); | ||
| action.category = builtInCategory; | ||
| return action; | ||
| }); | ||
|
|
||
| const orderedModes = coalesce([ | ||
| agentMode && makeAction(agentMode, currentMode), | ||
| ...customBuiltinModeActions, | ||
| ...otherBuiltinModes.map(mode => mode && makeAction(mode, currentMode)), | ||
| ...customModes.custom.map(mode => makeActionFromCustomMode(mode, currentMode)) | ||
| ]); |
There was a problem hiding this comment.
modes.custom was previously optional (earlier code guarded with if (modes.custom)); calling groupBy with undefined will throw, and subsequently accessing customModes.builtin/custom without existence checks will also throw when those groups are absent. Use a fallback empty array for modes.custom and guard missing groups to avoid runtime errors when there are no custom modes or no modes in a subgroup. For example: const customModes = groupBy(modes.custom ?? [], ...); const builtinGroup = customModes.builtin ?? []; const customGroup = customModes.custom ?? []; and then map over those safe arrays.
|
@roblourens Looks good! |
cc @aeschli lmk if you're ok with how the source works here or if you have something else in mind