diff --git a/extensions/copilot/src/extension/agents/vscode-node/githubOrgCustomAgentProvider.ts b/extensions/copilot/src/extension/agents/vscode-node/githubOrgCustomAgentProvider.ts index 92853913bbc..9e56489d09d 100644 --- a/extensions/copilot/src/extension/agents/vscode-node/githubOrgCustomAgentProvider.ts +++ b/extensions/copilot/src/extension/agents/vscode-node/githubOrgCustomAgentProvider.ts @@ -136,8 +136,11 @@ export class GitHubOrgCustomAgentProvider extends Disposable implements vscode.C if (agent.model) { frontmatterObj.model = agent.model; } - if (agent.infer) { - frontmatterObj.infer = agent.infer; + if (agent.disable_model_invocation !== undefined) { + frontmatterObj['disable-model-invocation'] = agent.disable_model_invocation; + } + if (agent.user_invocable !== undefined) { + frontmatterObj['user-invocable'] = agent.user_invocable; } const frontmatter = YAML.stringify(frontmatterObj, { diff --git a/extensions/copilot/src/extension/agents/vscode-node/test/githubOrgCustomAgentProvider.spec.ts b/extensions/copilot/src/extension/agents/vscode-node/test/githubOrgCustomAgentProvider.spec.ts index a12f56cf45f..39deeb5455f 100644 --- a/extensions/copilot/src/extension/agents/vscode-node/test/githubOrgCustomAgentProvider.spec.ts +++ b/extensions/copilot/src/extension/agents/vscode-node/test/githubOrgCustomAgentProvider.spec.ts @@ -229,7 +229,7 @@ Test prompt content`; ...mockAgent, prompt: 'Detailed prompt content', model: 'gpt-4', - infer: true, + disable_model_invocation: true, }; mockOctoKitService.setAgentDetails('full_agent', mockDetails); @@ -248,7 +248,7 @@ tools: argument-hint: Provide context target: vscode model: gpt-4 -infer: true +disable-model-invocation: true --- Detailed prompt content `; @@ -256,6 +256,86 @@ Detailed prompt content assert.equal(content, expectedContent); }); + test('generates markdown with user-invocable property', async () => { + const provider = createProvider(); + + const mockAgent: CustomAgentListItem = { + name: 'invocable_agent', + repo_owner_id: 1, + repo_owner: 'testorg', + repo_id: 1, + repo_name: 'testrepo', + display_name: 'Invocable Agent', + description: 'An agent with user-invocable set', + tools: [], + version: 'v1', + }; + mockOctoKitService.setCustomAgents([mockAgent]); + + const mockDetails: CustomAgentDetails = { + ...mockAgent, + prompt: 'Invocable prompt content', + user_invocable: true, + }; + mockOctoKitService.setAgentDetails('invocable_agent', mockDetails); + + await provider.provideCustomAgents({}, {} as any); + await waitForPolling(); + + const content = await resourcesService.readCacheFile(PromptsType.agent, 'testorg', 'invocable_agent.agent.md'); + + const expectedContent = `--- +name: Invocable Agent +description: An agent with user-invocable set +user-invocable: true +--- +Invocable prompt content +`; + + assert.equal(content, expectedContent); + }); + + test('generates markdown with false values for disable-model-invocation and user-invocable', async () => { + const provider = createProvider(); + + const mockAgent: CustomAgentListItem = { + name: 'false_flags_agent', + repo_owner_id: 1, + repo_owner: 'testorg', + repo_id: 1, + repo_name: 'testrepo', + display_name: 'False Flags Agent', + description: 'Agent with false boolean flags', + tools: [], + version: 'v1', + }; + mockOctoKitService.setCustomAgents([mockAgent]); + + const mockDetails: CustomAgentDetails = { + ...mockAgent, + prompt: 'False flags prompt', + disable_model_invocation: false, + user_invocable: false, + }; + mockOctoKitService.setAgentDetails('false_flags_agent', mockDetails); + + await provider.provideCustomAgents({}, {} as any); + await waitForPolling(); + + const content = await resourcesService.readCacheFile(PromptsType.agent, 'testorg', 'false_flags_agent.agent.md'); + + const expectedContent = `--- +name: False Flags Agent +description: Agent with false boolean flags +disable-model-invocation: false +user-invocable: false +--- +False flags prompt +`; + + assert.equal(content, expectedContent); + }); + test('preserves agent name in filename', async () => { // Note: The provider does NOT sanitize filenames - it uses the agent name directly. // This test documents the actual behavior. @@ -640,7 +720,7 @@ Agent 1 prompt`; assert.ok(!content.includes('argument-hint:')); assert.ok(!content.includes('target:')); assert.ok(!content.includes('model:')); - assert.ok(!content.includes('infer:')); + assert.ok(!content.includes('disable-model-invocation:')); }); test('excludes tools field when array contains only wildcard', async () => { diff --git a/extensions/copilot/src/platform/github/common/githubService.ts b/extensions/copilot/src/platform/github/common/githubService.ts index 3f05d80a443..888f89bbc47 100644 --- a/extensions/copilot/src/platform/github/common/githubService.ts +++ b/extensions/copilot/src/platform/github/common/githubService.ts @@ -148,7 +148,8 @@ export interface CustomAgentListItem { target?: string; config_error?: string; model?: string; - infer?: boolean; + disable_model_invocation?: boolean; + user_invocable?: boolean; 'mcp-servers'?: { [serverName: string]: { type: string;