AI Customizations – Design Document
This document describes the AI customization experience: a management editor and tree view that surface customization items (agents, skills, instructions, prompts, hooks, MCP servers) across workspace, user, and extension storage.
Architecture
File Structure
The management editor lives in vs/workbench (shared between core VS Code and sessions):
The tree view and overview live in vs/sessions (agent sessions window only):
Sessions-specific overrides:
IAICustomizationWorkspaceService
The IAICustomizationWorkspaceService interface controls per-window behavior:
| Property / Method | Core VS Code | Agent Sessions Window |
|---|---|---|
managementSections | All sections except Models | All sections except Models |
getStorageSourceFilter(type) | Delegates to ICustomizationHarnessService | Delegates to ICustomizationHarnessService |
isSessionsWindow | false | true |
activeProjectRoot | First workspace folder | Active session worktree |
welcomePageFeatures | Shows getting-started banner + per-card AI actions | Shows getting-started banner, hides per-card AI actions |
ICustomizationHarnessService
A harness represents the AI execution environment that consumes customizations. Storage answers "where did this come from?"; harness answers "who consumes it?".
The service is defined in common/customizationHarnessService.ts which also provides:
CustomizationHarnessServiceBase— reusable base class handling active-harness state, the observable list, andgetStorageSourceFilterdispatch.ISectionOverride— per-section UI customization:commandId(command invocation),rootFile+label(root-file creation),typeLabel(custom type name),fileExtension(override default),rootFileShortcuts(dropdown shortcuts).Factory functions —
createVSCodeHarnessDescriptor,createCliHarnessDescriptor,createClaudeHarnessDescriptor. The VS Code harness receives[PromptsStorage.extension, BUILTIN_STORAGE]as extras; CLI and Claude in core receive[](no extension source). Sessions CLI receives[BUILTIN_STORAGE].Well-known root helpers —
getCliUserRoots(userHome)andgetClaudeUserRoots(userHome)centralize the~/.copilot,~/.claude,~/.agentspath knowledge.Filter helpers —
matchesWorkspaceSubpath()for segment-safe subpath matching;matchesInstructionFileFilter()for filename/path-prefix pattern matching.
Available harnesses:
| Harness | Label | Description |
|---|---|---|
vscode | Local | Shows all storage sources (default in core) |
cli | Copilot CLI | Restricts user roots to ~/.copilot, ~/.claude, ~/.agents |
claude | Claude | Restricts user roots to ~/.claude; hides Prompts + Plugins sections |
In core VS Code, all three harnesses are registered but CLI and Claude only appear when their respective agents are registered (requiredAgentId checked via IChatAgentService). VS Code is the default. In sessions, harnesses are accepted for any session type that has a registered content provider (checked via IChatSessionsService.getContentProviderSchemes()). AHP remote servers register directly via registerExternalHarness.
Remote agent hosts can also register external harnesses dynamically. Each remote agent harness may contribute:
an
itemProviderthat surfaces plugins already configured on the remote host (or synced into the active remote session),a
disableProviderthat lets users opt out individual files/plugins from auto-sync, andpluginActionsthat replace the default local install/create buttons in the Plugins section toolbar with environment-specific commands such as "Add Remote Plugin".
The Plugins section renders remote harness itemProvider entries with type: 'plugin' directly. This is separate from the prompt-file pipeline used for Agents, Skills, Instructions, Prompts, and Hooks.
IHarnessDescriptor
Key properties on the harness descriptor:
| Property | Purpose |
|---|---|
itemProvider | ICustomizationItemProvider supplying items; when absent, falls back to PromptsServiceCustomizationItemProvider |
disableProvider | ICustomizationDisableProvider enabling opt-out of individual items from auto-sync |
hiddenSections | Sidebar sections to hide (e.g. Claude: [Prompts, Plugins]) |
workspaceSubpaths | Restrict file creation/display to directories (e.g. Claude: ['.claude']) |
hideGenerateButton | Replace "Generate X" sparkle button with "New X" |
sectionOverrides | Per-section ISectionOverride map for button behavior |
requiredAgentId | Agent ID that must be registered for harness to appear |
instructionFileFilter | Filename/path patterns to filter instruction items |
IStorageSourceFilter
A unified per-type filter controlling which storage sources and user file roots are visible. Replaces the old visibleStorageSources, getVisibleStorageSources(type), and excludedUserFileRoots.
The shared applyStorageSourceFilter() helper applies this filter to any {uri, storage} array.
Sessions filter behavior (CLI harness):
| Type | sources | includedUserFileRoots |
|---|---|---|
| Hooks | [local, plugin] | N/A |
| Prompts | [local, user, plugin, builtin] | undefined (all roots) |
| Agents, Skills, Instructions | [local, user, plugin, builtin] | [~/.copilot, ~/.claude, ~/.agents] |
Core VS Code filter behavior:
Local harness: all types use [local, user, extension, plugin, builtin] with no user root filter. Items from the default chat extension (productService.defaultChatAgent.chatExtensionId) are grouped under "Built-in" via groupKey override in the list widget.
CLI harness (core):
| Type | sources | includedUserFileRoots |
|---|---|---|
| Hooks | [local, plugin] | N/A |
| Prompts | [local, user, plugin] | undefined (all roots) |
| Agents, Skills, Instructions | [local, user, plugin] | [~/.copilot, ~/.claude, ~/.agents] |
Claude harness (core):
| Type | sources | includedUserFileRoots |
|---|---|---|
| Hooks | [local, plugin] | N/A |
| Prompts | [local, user, plugin] | undefined (all roots) |
| Agents, Skills, Instructions | [local, user, plugin] | [~/.claude] |
Claude additionally applies:
hiddenSections: [Prompts, Plugins]instructionFileFilter: ['CLAUDE.md', 'CLAUDE.local.md', '.claude/rules/', 'copilot-instructions.md']workspaceSubpaths: ['.claude'](instruction files matchinginstructionFileFilterare exempt)sectionOverrides: Hooks →copilot.claude.hookscommand; Instructions → "Add CLAUDE.md" primary, "Rule" type label,.mdfile extension
Built-in Extension Grouping (Core VS Code)
In core VS Code, customization items contributed by the default chat extension (productService.defaultChatAgent.chatExtensionId, typically GitHub.copilot-chat) are grouped under the "Built-in" header in the management editor list widget, separate from third-party "Extensions".
PromptsServiceCustomizationItemProvider handles this via applyBuiltinGroupKeys(): it builds a URI→extension-ID lookup from prompt file metadata, then sets groupKey: BUILTIN_STORAGE on items whose extension matches the chat extension ID (checked via the shared isChatExtensionItem() utility). The underlying storage remains PromptsStorage.extension — the grouping is a groupKey override that keeps applyStorageSourceFilter working while visually distinguishing chat-extension items from third-party extension items.
BUILTIN_STORAGE is defined in aiCustomizationWorkspaceService.ts (common layer) and re-exported by both aiCustomizationManagement.ts (browser) and builtinPromptsStorage.ts (sessions) for backward compatibility.
Management Editor Item Pipeline
All customization sources — IPromptsService, extension-contributed providers, and AHP remote servers — produce items conforming to the same ICustomizationItem contract (defined in customizationHarnessService.ts). This contract carries uri, type, name, description, optional storage, groupKey, badge, and status fields.
Key files:
aiCustomizationItemSource.ts— The browser-side pipeline:IAICustomizationListItem(view model),IAICustomizationItemSource(data contract),AICustomizationItemNormalizer(mapsICustomizationItem→ view model, inferring storage/grouping from URIs when the provider doesn't supply them),ProviderCustomizationItemSource(orchestrates provider + sync + normalizer), and shared utilities (expandHookFileItems,getFriendlyName,isChatExtensionItem).promptsServiceCustomizationItemProvider.ts— AdaptsIPromptsServicetoICustomizationItemProvider. Reads agents, skills, instructions, hooks, and prompts from the core service, expands instruction categories and hook entries, applies harness-specific filters (storage sources, workspace subpaths, instruction file patterns), and returnsICustomizationItem[]withstorageset from the authoritative promptsService metadata. Used as the default item provider for harnesses that don't supply their own.customizationHarnessService.ts(common layer) — DefinesICustomizationItem,ICustomizationItemProvider,ICustomizationDisableProvider, andIHarnessDescriptor. A harness descriptor optionally carries anitemProvider; when absent, the widget falls back toPromptsServiceCustomizationItemProvider.
Structured Detail Preview
For markdown-backed customizations (.agent.md, SKILL.md, .instructions.md, .prompt.md), the management editor opens a structured preview by default instead of showing the raw file immediately.
The preview parses the file with
PromptFileParserHeader metadata is rendered as labeled rows
Each row includes an inline help affordance whose hover text comes from
getAttributeDefinition(...)The markdown body is rendered via
IMarkdownRendererServiceA header button switches between the structured preview and the raw editor/viewer
Hooks and other non-markdown detail views continue to open directly in their existing raw/detail experiences.
AgenticPromptsService (Sessions)
Sessions overrides PromptsService via AgenticPromptsService (in promptsService.ts):
Discovery:
AgenticPromptFilesLocatorscopes workspace folders to the active session's worktreeBuilt-in skills: Discovers bundled
SKILL.mdfiles fromvs/sessions/skills/{name}/and surfaces them withPromptsStorage.builtinstorage typeUser override: Built-in skills are omitted when a user or workspace skill with the same name exists
Creation targets:
getSourceFolders()override replaces VS Code profile user roots with~/.copilot/{subfolder}for CLI compatibilityHook folders: Falls back to
.github/hooksin the active worktree
Built-in Skills
All built-in customizations bundled with the Sessions app are skills, living in src/vs/sessions/skills/{name}/SKILL.md. They are:
Discovered at runtime via
FileAccess.asFileUri('vs/sessions/skills')Tagged with
PromptsStorage.builtinstorage typeShown in a "Built-in" group in the AI Customization tree view and management editor
Filtered out when a user/workspace skill shares the same name (override behavior)
Skills with UI integrations (e.g.
act-on-feedback,generate-run-commands) display a "UI Integration" badge in the management editor
UI Integration Badges
Skills that are directly invoked by UI elements (toolbar buttons, menu items) are annotated with a "UI Integration" badge in the management editor. The mapping is provided by IAICustomizationWorkspaceService.getSkillUIIntegrations(), which the Sessions implementation populates with the relevant skill names and tooltip descriptions. The badge appears on both the built-in skill and any user/workspace override, ensuring users understand that overriding the skill affects a UI surface.
Count Consistency
Counts shown in the sidebar (per-link badges and the header total in AICustomizationShortcutsWidget) are driven by the same IAICustomizationItemsModel singleton (workbench/contrib/chat/browser/aiCustomization/aiCustomizationItemsModel.ts) that feeds the customizations editor's list widget. The model owns the per-active-harness ProviderCustomizationItemSource cache and exposes per-section IObservable<readonly IAICustomizationListItem[]>; sidebar consumers read .length from those observables. There is exactly one discovery path, so editor and sidebar counts cannot diverge. McpServers and Plugins use their own service observables (IMcpService.servers, IAgentPluginService.plugins) directly.
Sidebar Overview Entrypoint
The Agents sidebar AICustomizationShortcutsWidget exposes a home action in the Customizations header. The header label remains the collapse toggle, while the separate icon-only action opens the AI Customization management editor and calls showWelcomePage() so users can return to the overview/welcome page from any customization section. The action lives in the header, rather than inside the collapsible list content, so it remains visible and is not clipped by the sidebar's scrollable layout.
Item Badges
IAICustomizationListItem.badge is an optional string that renders as a small inline tag next to the item name (same visual style as the MCP "Bridged" badge). For context instructions, this badge shows the raw applyTo pattern (e.g. a glob like **/*.ts), while the tooltip (badgeTooltip) explains the behavior. For skills with UI integrations, the badge reads "UI Integration" with a tooltip describing which UI surface invokes the skill. The badge text is also included in search filtering.
Embedded Detail Editors
The management editor opens inline detail panes for prompt files, MCP servers, and plugins. Prompt-file details use the standard text editor pane. MCP and plugin details render dedicated compact widgets — EmbeddedMcpServerDetail and EmbeddedAgentPluginDetail — purpose-built for the narrow split-pane host. They show the icon, name, scope/source, and description. Do not embed the full extension-editor panes inside the split-pane host: they assume a wide page-level layout and don't shrink cleanly.
The MCP detail fixture in src/vs/workbench/test/browser/componentFixtures/sessions/aiCustomizationManagementEditor.fixture.ts must open a real server row (not a group header) and use a local server with concrete config so the compact widget's scope/description rendering is covered by screenshots.
Debug Panel
Toggle via Command Palette: "Toggle Customizations Debug Panel". Shows a diagnostic view of the item pipeline:
Provider data — items returned by the active
ICustomizationItemProviderAfter filtering — what was removed by storage source and workspace subpath filters
Widget state — allItems vs displayEntries with group counts
Source/resolved folders — creation targets and discovery order
Key Services
Prompt discovery:
IPromptsService— parsing, lifecycle, storage enumerationMCP servers:
IMcpService— server list, tool accessActive worktree:
IActiveSessionService— source of truth for workspace scoping (sessions only)File operations:
IFileService,ITextModelService— file and model plumbing
Browser compatibility is required — no Node.js APIs.
Feature Gating
All commands and UI respect ChatContextKeys.enabled.
Commands
| Command ID | Purpose |
|---|---|
aiCustomization.openManagementEditor | Opens the management editor, optionally accepting an AICustomizationManagementSection to deep-link |
aiCustomization.openMarketplace | Opens the management editor with marketplace browse mode active. Accepts an optional section (mcpServers or plugins); defaults to mcpServers |
Settings
User-facing settings use the chat.customizations. namespace:
| Setting | Default | Description |
|---|---|---|
chat.customizations.harnessSelector.enabled | true | Show the harness selector dropdown in the sidebar |