Skip to content

add ICustomizationHarnessService.getCustomAgent#312353

Merged
aeschli merged 3 commits intomainfrom
aeschli/cheerful-alpaca-409
Apr 24, 2026
Merged

add ICustomizationHarnessService.getCustomAgent#312353
aeschli merged 3 commits intomainfrom
aeschli/cheerful-alpaca-409

Conversation

@aeschli
Copy link
Copy Markdown
Contributor

@aeschli aeschli commented Apr 24, 2026

No description provided.

Copilot AI review requested due to automatic review settings April 24, 2026 14:15
@aeschli aeschli enabled auto-merge (squash) April 24, 2026 14:15
@aeschli aeschli self-assigned this Apr 24, 2026
lszomoru
lszomoru previously approved these changes Apr 24, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support in the chat customization harness layer for retrieving custom agents (in addition to slash commands), and threads through source-provenance fields needed to attribute customizations to extensions/plugins.

Changes:

  • Extend ICustomizationHarnessService with getCustomAgents() and onDidChangeCustomAgents, and implement provider-backed + prompts-service fallback resolution.
  • Refactor custom agent construction in PromptsService into a shared helper (CustomAgent.fromParsedPromptFile) and simplify IAgentSource serialization (remove type).
  • Add extensionId / pluginUri provenance fields to customization items and update providers/tests accordingly.
Show a summary per file
File Description
src/vs/workbench/contrib/chat/common/customizationHarnessService.ts Adds getCustomAgents() + onDidChangeCustomAgents and adapts provider items into ICustomAgents.
src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts Refactors agent parsing into CustomAgent.fromParsedPromptFile and adjusts hook parsing flow.
src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts Simplifies IAgentSource by removing the extension type discriminator.
src/vs/workbench/contrib/chat/common/chatModes.ts Updates (de)serialization for the simplified IAgentSource.
src/vs/workbench/contrib/chat/browser/aiCustomization/promptsServiceCustomizationItemProvider.ts Populates extensionId/pluginUri on provided customization items.
src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationItemSource.ts Preserves provenance when expanding hook file items / emitting synthetic items.
src/vs/workbench/api/common/extHostChatAgents2.ts Tightens DTO mapping with satisfies for customization items returned from extensions.
src/vs/workbench/api/common/extHost.protocol.ts Minor formatting change in customization item DTO definition.
src/vs/workbench/api/browser/mainThreadChatAgents2.ts Ensures customization items include the new required provenance fields (set to undefined).
src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostCustomizationHarness.ts Ensures remote agent harness items include provenance fields (set to undefined).
extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/copilotCLICustomizationProvider.ts Forwards extensionId/pluginUri for Copilot CLI skill customizations.
extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotCli.ts Extends CLI agent info model with optional provenance fields.
src/vs/workbench/contrib/chat/test/common/customizationHarnessService.test.ts Adds coverage for onDidChangeCustomAgents forwarding and getCustomAgents() behavior; updates item shapes for new fields.

Copilot's findings

  • Files reviewed: 13/13 changed files
  • Comments generated: 5

Comment on lines 558 to +559
this._providerListeners.push(provider.onDidChange(() => this._onDidChangeSlashCommands.fire({ sessionType: harness.id })));
this._providerListeners.push(provider.onDidChange(() => this._onDidChangeCustomAgents.fire({ sessionType: harness.id })));
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_rebindProviderListeners subscribes to provider.onDidChange twice per harness (once for slash commands and once for custom agents). This duplicates listener registrations/disposals and can add avoidable overhead. Consider using a single onDidChange subscription per provider that fires both _onDidChangeSlashCommands and _onDidChangeCustomAgents for the harness id.

Suggested change
this._providerListeners.push(provider.onDidChange(() => this._onDidChangeSlashCommands.fire({ sessionType: harness.id })));
this._providerListeners.push(provider.onDidChange(() => this._onDidChangeCustomAgents.fire({ sessionType: harness.id })));
this._providerListeners.push(provider.onDidChange(() => {
this._onDidChangeSlashCommands.fire({ sessionType: harness.id });
this._onDidChangeCustomAgents.fire({ sessionType: harness.id });
}));

Copilot uses AI. Check for mistakes.
Comment on lines +681 to +696
const result: ICustomAgent[] = [];
for (const item of items) {
if ((item.enabled !== false) && item.type === PromptsType.agent) {
const promptFile = await this.promptsService.parseNew(item.uri, token);
const extra = {
name: item.name,
description: item.description,
sessionTypes: [sessionType],
hooks: undefined,
source: getSource(item),
type: PromptsType.agent,
};
result.push(CustomAgent.fromParsedPromptFile(promptFile, extra));
}
}
return result;
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCustomAgents parses provider items sequentially with await inside a loop. If a harness provides many agent items, this will unnecessarily serialize I/O and slow down the UI. Consider resolving agents in parallel (e.g. Promise.all) while still honoring cancellation, and preserving the original item ordering if needed.

Suggested change
const result: ICustomAgent[] = [];
for (const item of items) {
if ((item.enabled !== false) && item.type === PromptsType.agent) {
const promptFile = await this.promptsService.parseNew(item.uri, token);
const extra = {
name: item.name,
description: item.description,
sessionTypes: [sessionType],
hooks: undefined,
source: getSource(item),
type: PromptsType.agent,
};
result.push(CustomAgent.fromParsedPromptFile(promptFile, extra));
}
}
return result;
const agentItems = items.filter(item => (item.enabled !== false) && item.type === PromptsType.agent);
return Promise.all(agentItems.map(async item => {
const promptFile = await this.promptsService.parseNew(item.uri, token);
const extra = {
name: item.name,
description: item.description,
sessionTypes: [sessionType],
hooks: undefined,
source: getSource(item),
type: PromptsType.agent,
};
return CustomAgent.fromParsedPromptFile(promptFile, extra);
}));

Copilot uses AI. Check for mistakes.
Comment on lines +684 to +693
const promptFile = await this.promptsService.parseNew(item.uri, token);
const extra = {
name: item.name,
description: item.description,
sessionTypes: [sessionType],
hooks: undefined,
source: getSource(item),
type: PromptsType.agent,
};
result.push(CustomAgent.fromParsedPromptFile(promptFile, extra));
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCustomAgents will reject the entire call if parseNew throws for any single provider item URI (missing file, parse error, etc.). Since these URIs come from an external provider, one bad entry could break the whole agents list. Consider handling errors per item (skip and optionally surface a status) so other valid agents still load.

Suggested change
const promptFile = await this.promptsService.parseNew(item.uri, token);
const extra = {
name: item.name,
description: item.description,
sessionTypes: [sessionType],
hooks: undefined,
source: getSource(item),
type: PromptsType.agent,
};
result.push(CustomAgent.fromParsedPromptFile(promptFile, extra));
try {
const promptFile = await this.promptsService.parseNew(item.uri, token);
const extra = {
name: item.name,
description: item.description,
sessionTypes: [sessionType],
hooks: undefined,
source: getSource(item),
type: PromptsType.agent,
};
result.push(CustomAgent.fromParsedPromptFile(promptFile, extra));
} catch {
continue;
}

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +78
const listener = store.add(service.onDidChangeCustomAgents(e => firedSessionType = e.sessionType));
store.add(listener);
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listener disposable is already added to store via const listener = store.add(...); adding it again with store.add(listener) is redundant and can lead to double-dispose. Remove the second store.add(listener).

Copilot uses AI. Check for mistakes.
Comment on lines +803 to +811
const extra = {
sessionTypes: promptPath.sessionTypes,
hooks,
name: promptPath.name,
description: promptPath.description,
source: IAgentSource.fromPromptPath(promptPath)
};
const agent = CustomAgent.fromParsedPromptFile(ast, extra);
return { status: 'loaded', promptPath: this.withPromptPathMetadata(promptPath, agent.name, agent.description), agent };
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computeAgentDiscoveryInfo no longer forwards the contributed when clause from promptPath.when into the resolved ICustomAgent. As a result, extension-contributed agents will lose their when gating (and may appear/enabled when they shouldn’t). Pass the original promptPath.when through (e.g. via extra.when) so CustomAgent.fromParsedPromptFile can deserialize it and preserve the condition.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <copilot@github.com>
@aeschli aeschli merged commit c5b38e3 into main Apr 24, 2026
26 checks passed
@aeschli aeschli deleted the aeschli/cheerful-alpaca-409 branch April 24, 2026 14:57
@vs-code-engineering vs-code-engineering Bot added this to the 1.118.0 milestone Apr 24, 2026
@aeschli aeschli changed the title add ICustomizationHarnessService.getCustomAgentsorigin/aeschli add ICustomizationHarnessService.getCustomAgent Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants