|
3 | 3 | * Licensed under the MIT License. See License.txt in the project root for license information. |
4 | 4 | *--------------------------------------------------------------------------------------------*/ |
5 | 5 |
|
6 | | -import { Attachment, SessionOptions } from '@github/copilot/sdk'; |
| 6 | +import { Attachment, SessionOptions, SweCustomAgent } from '@github/copilot/sdk'; |
7 | 7 | import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; |
8 | 8 | import * as vscode from 'vscode'; |
9 | 9 | import { Uri } from 'vscode'; |
@@ -1861,4 +1861,112 @@ describe('CopilotCLIChatSessionParticipant.handleRequest', () => { |
1861 | 1861 | ); |
1862 | 1862 | }); |
1863 | 1863 | }); |
| 1864 | + |
| 1865 | + describe('agent tool references via modeInstructions2', () => { |
| 1866 | + class MockCopilotCLIAgentsWithCustomAgent extends NullCopilotCLIAgents { |
| 1867 | + constructor(private readonly agentTools: string[] | null) { |
| 1868 | + super(); |
| 1869 | + } |
| 1870 | + override resolveAgent(agentId: string): Promise<SweCustomAgent | undefined> { |
| 1871 | + if (agentId === 'custom-agent') { |
| 1872 | + return Promise.resolve({ |
| 1873 | + name: 'custom-agent', |
| 1874 | + displayName: 'Custom Agent', |
| 1875 | + description: 'A test agent', |
| 1876 | + tools: this.agentTools, |
| 1877 | + prompt: async () => 'System prompt', |
| 1878 | + disableModelInvocation: false, |
| 1879 | + }); |
| 1880 | + } |
| 1881 | + return Promise.resolve(undefined); |
| 1882 | + } |
| 1883 | + } |
| 1884 | + |
| 1885 | + function makeParticipantWithAgents(agents: MockCopilotCLIAgentsWithCustomAgent): CopilotCLIChatSessionParticipant { |
| 1886 | + const nullDelegationService = new class extends mock<IChatDelegationSummaryService>() { |
| 1887 | + override async summarize(_context: vscode.ChatContext, _token: vscode.CancellationToken): Promise<string | undefined> { |
| 1888 | + return undefined; |
| 1889 | + } |
| 1890 | + }(); |
| 1891 | + return new CopilotCLIChatSessionParticipant( |
| 1892 | + contentProvider, |
| 1893 | + promptResolver, |
| 1894 | + itemProvider, |
| 1895 | + cloudProvider, |
| 1896 | + repositoryTracker, |
| 1897 | + git, |
| 1898 | + models as unknown as ICopilotCLIModels, |
| 1899 | + agents, |
| 1900 | + sessionService, |
| 1901 | + worktree, |
| 1902 | + worktreeCheckpointService, |
| 1903 | + workspaceFolderService, |
| 1904 | + telemetry, |
| 1905 | + logService, |
| 1906 | + new PromptsServiceImpl(new NullWorkspaceService()), |
| 1907 | + nullDelegationService, |
| 1908 | + folderRepositoryManager, |
| 1909 | + configurationService, |
| 1910 | + sdk, |
| 1911 | + new MockChatSessionMetadataStore(), |
| 1912 | + customSessionTitleService, |
| 1913 | + new (mock<IOctoKitService>())(), |
| 1914 | + ); |
| 1915 | + } |
| 1916 | + |
| 1917 | + it('preserves agent tools when modeInstructions2 has no tool references', async () => { |
| 1918 | + const agentParticipant = makeParticipantWithAgents(new MockCopilotCLIAgentsWithCustomAgent(['original-tool'])); |
| 1919 | + const createSessionSpy = vi.spyOn(sessionService, 'createSession'); |
| 1920 | + |
| 1921 | + const request = new TestChatRequest('Do something'); |
| 1922 | + (request as any).modeInstructions2 = { name: 'custom-agent', content: 'agent content' }; |
| 1923 | + const context = createChatContext('temp-new', true); |
| 1924 | + const stream = new MockChatResponseStream(); |
| 1925 | + const token = disposables.add(new CancellationTokenSource()).token; |
| 1926 | + |
| 1927 | + await agentParticipant.createHandler()(request, context, stream, token); |
| 1928 | + |
| 1929 | + expect(createSessionSpy).toHaveBeenCalled(); |
| 1930 | + const { agent } = createSessionSpy.mock.calls[0][0]; |
| 1931 | + expect(agent?.tools).toEqual(['original-tool']); |
| 1932 | + }); |
| 1933 | + |
| 1934 | + it('overrides agent tools when modeInstructions2 provides tool references', async () => { |
| 1935 | + const agentParticipant = makeParticipantWithAgents(new MockCopilotCLIAgentsWithCustomAgent(['original-tool'])); |
| 1936 | + const createSessionSpy = vi.spyOn(sessionService, 'createSession'); |
| 1937 | + |
| 1938 | + const request = new TestChatRequest('Do something'); |
| 1939 | + (request as any).modeInstructions2 = { |
| 1940 | + name: 'custom-agent', |
| 1941 | + content: 'agent content', |
| 1942 | + toolReferences: [{ name: 'override-tool-1' }, { name: 'override-tool-2' }], |
| 1943 | + }; |
| 1944 | + const context = createChatContext('temp-new', true); |
| 1945 | + const stream = new MockChatResponseStream(); |
| 1946 | + const token = disposables.add(new CancellationTokenSource()).token; |
| 1947 | + |
| 1948 | + await agentParticipant.createHandler()(request, context, stream, token); |
| 1949 | + |
| 1950 | + expect(createSessionSpy).toHaveBeenCalled(); |
| 1951 | + const { agent } = createSessionSpy.mock.calls[0][0]; |
| 1952 | + expect(agent?.tools).toEqual(['override-tool-1', 'override-tool-2']); |
| 1953 | + }); |
| 1954 | + |
| 1955 | + it('preserves null tools when modeInstructions2 has no tool references', async () => { |
| 1956 | + const agentParticipant = makeParticipantWithAgents(new MockCopilotCLIAgentsWithCustomAgent(null)); |
| 1957 | + const createSessionSpy = vi.spyOn(sessionService, 'createSession'); |
| 1958 | + |
| 1959 | + const request = new TestChatRequest('Do something'); |
| 1960 | + (request as any).modeInstructions2 = { name: 'custom-agent', content: 'agent content' }; |
| 1961 | + const context = createChatContext('temp-new', true); |
| 1962 | + const stream = new MockChatResponseStream(); |
| 1963 | + const token = disposables.add(new CancellationTokenSource()).token; |
| 1964 | + |
| 1965 | + await agentParticipant.createHandler()(request, context, stream, token); |
| 1966 | + |
| 1967 | + expect(createSessionSpy).toHaveBeenCalled(); |
| 1968 | + const { agent } = createSessionSpy.mock.calls[0][0]; |
| 1969 | + expect(agent?.tools).toBeNull(); |
| 1970 | + }); |
| 1971 | + }); |
1864 | 1972 | }); |
0 commit comments