@@ -43,10 +43,15 @@ import {
4343 DidFocusDocumentNotification ,
4444 ForkSessionRequest ,
4545 ForkSessionResponse ,
46+ ListProvidersRequest ,
47+ ListProvidersResponse ,
4648 ListSessionsRequest ,
4749 ListSessionsResponse ,
4850 ResumeSessionRequest ,
4951 ResumeSessionResponse ,
52+ SetProvidersRequest ,
53+ DisableProvidersRequest ,
54+ DisableProvidersResponse ,
5055 CreateElicitationRequest ,
5156 CreateElicitationResponse ,
5257 CompleteElicitationNotification ,
@@ -1698,6 +1703,225 @@ describe("Connection", () => {
16981703 expect ( closeResponse ) . toEqual ( { } ) ;
16991704 } ) ;
17001705
1706+ it ( "handles providers request lifecycle" , async ( ) => {
1707+ let receivedSetRequest : SetProvidersRequest | undefined ;
1708+ let receivedDisableRequest : DisableProvidersRequest | undefined ;
1709+
1710+ class TestClient implements Client {
1711+ async writeTextFile (
1712+ _ : WriteTextFileRequest ,
1713+ ) : Promise < WriteTextFileResponse > {
1714+ return { } ;
1715+ }
1716+ async readTextFile (
1717+ _ : ReadTextFileRequest ,
1718+ ) : Promise < ReadTextFileResponse > {
1719+ return { content : "" } ;
1720+ }
1721+ async requestPermission (
1722+ _ : RequestPermissionRequest ,
1723+ ) : Promise < RequestPermissionResponse > {
1724+ return { outcome : { outcome : "selected" , optionId : "allow" } } ;
1725+ }
1726+ async sessionUpdate ( _ : SessionNotification ) : Promise < void > { }
1727+ }
1728+
1729+ class TestAgent implements Agent {
1730+ async initialize ( _ : InitializeRequest ) : Promise < InitializeResponse > {
1731+ return {
1732+ protocolVersion : 1 ,
1733+ agentCapabilities : { loadSession : false , providers : { } } ,
1734+ authMethods : [ ] ,
1735+ } ;
1736+ }
1737+ async newSession ( _ : NewSessionRequest ) : Promise < NewSessionResponse > {
1738+ return { sessionId : "test-session" } ;
1739+ }
1740+ async authenticate ( _ : AuthenticateRequest ) : Promise < void > { }
1741+ async prompt ( _ : PromptRequest ) : Promise < PromptResponse > {
1742+ return { stopReason : "end_turn" } ;
1743+ }
1744+ async cancel ( _ : CancelNotification ) : Promise < void > { }
1745+
1746+ async unstable_listProviders (
1747+ _ : ListProvidersRequest ,
1748+ ) : Promise < ListProvidersResponse > {
1749+ return {
1750+ providers : [
1751+ {
1752+ id : "main" ,
1753+ supported : [ "anthropic" , "openai" ] ,
1754+ required : true ,
1755+ current : {
1756+ apiType : "anthropic" ,
1757+ baseUrl : "https://api.anthropic.com" ,
1758+ } ,
1759+ } ,
1760+ {
1761+ id : "openai" ,
1762+ supported : [ "openai" ] ,
1763+ required : false ,
1764+ } ,
1765+ {
1766+ id : "azure" ,
1767+ supported : [ "azure" ] ,
1768+ required : false ,
1769+ current : null ,
1770+ } ,
1771+ ] ,
1772+ } ;
1773+ }
1774+
1775+ async unstable_setProvider ( params : SetProvidersRequest ) : Promise < void > {
1776+ receivedSetRequest = params ;
1777+ }
1778+
1779+ async unstable_disableProvider (
1780+ params : DisableProvidersRequest ,
1781+ ) : Promise < DisableProvidersResponse > {
1782+ receivedDisableRequest = params ;
1783+ return { } ;
1784+ }
1785+ }
1786+
1787+ const agentConnection = new ClientSideConnection (
1788+ ( ) => new TestClient ( ) ,
1789+ ndJsonStream ( clientToAgent . writable , agentToClient . readable ) ,
1790+ ) ;
1791+ const clientConnection = new AgentSideConnection (
1792+ ( ) => new TestAgent ( ) ,
1793+ ndJsonStream ( agentToClient . writable , clientToAgent . readable ) ,
1794+ ) ;
1795+
1796+ void clientConnection ;
1797+
1798+ const listResponse = await agentConnection . unstable_listProviders ( { } ) ;
1799+ expect ( listResponse . providers ) . toEqual ( [
1800+ {
1801+ id : "main" ,
1802+ supported : [ "anthropic" , "openai" ] ,
1803+ required : true ,
1804+ current : {
1805+ apiType : "anthropic" ,
1806+ baseUrl : "https://api.anthropic.com" ,
1807+ } ,
1808+ } ,
1809+ {
1810+ id : "openai" ,
1811+ supported : [ "openai" ] ,
1812+ required : false ,
1813+ } ,
1814+ {
1815+ id : "azure" ,
1816+ supported : [ "azure" ] ,
1817+ required : false ,
1818+ current : null ,
1819+ } ,
1820+ ] ) ;
1821+ expect ( "current" in listResponse . providers [ 1 ] ) . toBe ( false ) ;
1822+
1823+ const setResponse = await agentConnection . unstable_setProvider ( {
1824+ id : "main" ,
1825+ apiType : "openai" ,
1826+ baseUrl : "https://llm-gateway.corp.example.com/openai/v1" ,
1827+ headers : {
1828+ Authorization : "Bearer token" ,
1829+ "X-Request-Source" : "test-client" ,
1830+ } ,
1831+ } ) ;
1832+ expect ( setResponse ) . toEqual ( { } ) ;
1833+ expect ( receivedSetRequest ) . toEqual ( {
1834+ id : "main" ,
1835+ apiType : "openai" ,
1836+ baseUrl : "https://llm-gateway.corp.example.com/openai/v1" ,
1837+ headers : {
1838+ Authorization : "Bearer token" ,
1839+ "X-Request-Source" : "test-client" ,
1840+ } ,
1841+ } ) ;
1842+
1843+ const disableResponse = await agentConnection . unstable_disableProvider ( {
1844+ id : "openai" ,
1845+ } ) ;
1846+ expect ( disableResponse ) . toEqual ( { } ) ;
1847+ expect ( receivedDisableRequest ) . toEqual ( { id : "openai" } ) ;
1848+ } ) ;
1849+
1850+ it ( "rejects providers requests when agent does not implement handlers" , async ( ) => {
1851+ class TestClient implements Client {
1852+ async writeTextFile (
1853+ _ : WriteTextFileRequest ,
1854+ ) : Promise < WriteTextFileResponse > {
1855+ return { } ;
1856+ }
1857+ async readTextFile (
1858+ _ : ReadTextFileRequest ,
1859+ ) : Promise < ReadTextFileResponse > {
1860+ return { content : "" } ;
1861+ }
1862+ async requestPermission (
1863+ _ : RequestPermissionRequest ,
1864+ ) : Promise < RequestPermissionResponse > {
1865+ return { outcome : { outcome : "selected" , optionId : "allow" } } ;
1866+ }
1867+ async sessionUpdate ( _ : SessionNotification ) : Promise < void > { }
1868+ }
1869+
1870+ class TestAgent implements Agent {
1871+ async initialize ( _ : InitializeRequest ) : Promise < InitializeResponse > {
1872+ return {
1873+ protocolVersion : 1 ,
1874+ agentCapabilities : { loadSession : false } ,
1875+ authMethods : [ ] ,
1876+ } ;
1877+ }
1878+ async newSession ( _ : NewSessionRequest ) : Promise < NewSessionResponse > {
1879+ return { sessionId : "test-session" } ;
1880+ }
1881+ async authenticate ( _ : AuthenticateRequest ) : Promise < void > { }
1882+ async prompt ( _ : PromptRequest ) : Promise < PromptResponse > {
1883+ return { stopReason : "end_turn" } ;
1884+ }
1885+ async cancel ( _ : CancelNotification ) : Promise < void > { }
1886+ }
1887+
1888+ const agentConnection = new ClientSideConnection (
1889+ ( ) => new TestClient ( ) ,
1890+ ndJsonStream ( clientToAgent . writable , agentToClient . readable ) ,
1891+ ) ;
1892+ const clientConnection = new AgentSideConnection (
1893+ ( ) => new TestAgent ( ) ,
1894+ ndJsonStream ( agentToClient . writable , clientToAgent . readable ) ,
1895+ ) ;
1896+
1897+ void clientConnection ;
1898+
1899+ await expect (
1900+ agentConnection . unstable_listProviders ( { } ) ,
1901+ ) . rejects . toMatchObject ( {
1902+ code : - 32601 ,
1903+ data : { method : "providers/list" } ,
1904+ } ) ;
1905+
1906+ await expect (
1907+ agentConnection . unstable_setProvider ( {
1908+ id : "main" ,
1909+ apiType : "openai" ,
1910+ baseUrl : "https://api.openai.com/v1" ,
1911+ } ) ,
1912+ ) . rejects . toMatchObject ( {
1913+ code : - 32601 ,
1914+ data : { method : "providers/set" } ,
1915+ } ) ;
1916+
1917+ await expect (
1918+ agentConnection . unstable_disableProvider ( { id : "main" } ) ,
1919+ ) . rejects . toMatchObject ( {
1920+ code : - 32601 ,
1921+ data : { method : "providers/disable" } ,
1922+ } ) ;
1923+ } ) ;
1924+
17011925 it ( "handles NES notifications" , async ( ) => {
17021926 const notificationLog : unknown [ ] = [ ] ;
17031927
0 commit comments