@@ -1795,6 +1795,145 @@ describe("Cline", () => {
17951795 // Verify cancelCurrentRequest was called
17961796 expect ( cancelSpy ) . toHaveBeenCalled ( )
17971797 } )
1798+ describe ( "abortSignal" , ( ) => {
1799+ it ( "should pass AbortController signal to createMessage metadata" , async ( ) => {
1800+ const task = new Task ( {
1801+ provider : mockProvider ,
1802+ apiConfiguration : mockApiConfig ,
1803+ task : "test task" ,
1804+ startTask : false ,
1805+ } )
1806+
1807+ // Mock required methods for attemptApiRequest to work without hanging
1808+ vi . spyOn ( task as any , "getSystemPrompt" ) . mockResolvedValue ( "mock system prompt" )
1809+
1810+ vi . spyOn ( task . api , "getModel" ) . mockReturnValue ( {
1811+ id : mockApiConfig . apiModelId ! ,
1812+ info : {
1813+ supportsImages : false ,
1814+ supportsPromptCache : true ,
1815+ contextWindow : 200000 ,
1816+ maxTokens : 4096 ,
1817+ inputPrice : 0.3 ,
1818+ outputPrice : 1.5 ,
1819+ } as ModelInfo ,
1820+ } )
1821+
1822+ const providerState = await mockProvider . getState ( )
1823+ vi . spyOn ( mockProvider , "getState" ) . mockResolvedValue ( {
1824+ ...providerState ,
1825+ apiConfiguration : mockApiConfig ,
1826+ autoApprovalEnabled : true ,
1827+ requestDelaySeconds : 0 ,
1828+ } )
1829+
1830+ // Mock the API stream response
1831+ const mockStream = {
1832+ async * [ Symbol . asyncIterator ] ( ) {
1833+ yield { type : "text" , text : "response" }
1834+ } ,
1835+ async next ( ) {
1836+ return { done : true , value : { type : "text" , text : "response" } }
1837+ } ,
1838+ async return ( ) {
1839+ return { done : true , value : undefined }
1840+ } ,
1841+ async throw ( e : any ) {
1842+ throw e
1843+ } ,
1844+ [ Symbol . asyncDispose ] : async ( ) => { } ,
1845+ } as AsyncGenerator < ApiStreamChunk >
1846+
1847+ const createMessageSpy = vi . spyOn ( task . api , "createMessage" ) . mockReturnValue ( mockStream )
1848+
1849+ task . apiConversationHistory = [
1850+ {
1851+ role : "user" as const ,
1852+ content : [ { type : "text" as const , text : "test message" } ] ,
1853+ ts : Date . now ( ) ,
1854+ } ,
1855+ ] as any
1856+
1857+ const iterator = task . attemptApiRequest ( 0 )
1858+ await iterator . next ( )
1859+
1860+ // Verify createMessage was called with metadata containing abortSignal
1861+ expect ( createMessageSpy ) . toHaveBeenCalled ( )
1862+ const [ , , metadata ] = createMessageSpy . mock . calls [ 0 ] !
1863+
1864+ expect ( metadata ) . toBeDefined ( )
1865+ expect ( metadata ! . abortSignal ) . toBeInstanceOf ( AbortSignal )
1866+ } )
1867+
1868+ it ( "should use the same AbortController signal as currentRequestAbortController" , async ( ) => {
1869+ const task = new Task ( {
1870+ provider : mockProvider ,
1871+ apiConfiguration : mockApiConfig ,
1872+ task : "test task" ,
1873+ startTask : false ,
1874+ } )
1875+
1876+ // Mock required methods for attemptApiRequest to work without hanging
1877+ vi . spyOn ( task as any , "getSystemPrompt" ) . mockResolvedValue ( "mock system prompt" )
1878+
1879+ vi . spyOn ( task . api , "getModel" ) . mockReturnValue ( {
1880+ id : mockApiConfig . apiModelId ! ,
1881+ info : {
1882+ supportsImages : false ,
1883+ supportsPromptCache : true ,
1884+ contextWindow : 200000 ,
1885+ maxTokens : 4096 ,
1886+ inputPrice : 0.3 ,
1887+ outputPrice : 1.5 ,
1888+ } as ModelInfo ,
1889+ } )
1890+
1891+ const providerState = await mockProvider . getState ( )
1892+ vi . spyOn ( mockProvider , "getState" ) . mockResolvedValue ( {
1893+ ...providerState ,
1894+ apiConfiguration : mockApiConfig ,
1895+ autoApprovalEnabled : true ,
1896+ requestDelaySeconds : 0 ,
1897+ } )
1898+
1899+ // Mock the API stream response
1900+ const mockStream = {
1901+ async * [ Symbol . asyncIterator ] ( ) {
1902+ yield { type : "text" , text : "response" }
1903+ } ,
1904+ async next ( ) {
1905+ return { done : true , value : { type : "text" , text : "response" } }
1906+ } ,
1907+ async return ( ) {
1908+ return { done : true , value : undefined }
1909+ } ,
1910+ async throw ( e : any ) {
1911+ throw e
1912+ } ,
1913+ [ Symbol . asyncDispose ] : async ( ) => { } ,
1914+ } as AsyncGenerator < ApiStreamChunk >
1915+
1916+ const createMessageSpy = vi . spyOn ( task . api , "createMessage" ) . mockReturnValue ( mockStream )
1917+
1918+ task . apiConversationHistory = [
1919+ {
1920+ role : "user" as const ,
1921+ content : [ { type : "text" as const , text : "test message" } ] ,
1922+ ts : Date . now ( ) ,
1923+ } ,
1924+ ] as any
1925+
1926+ const iterator = task . attemptApiRequest ( 0 )
1927+ await iterator . next ( )
1928+
1929+ // Get the signal from metadata
1930+ const [ , , metadata ] = createMessageSpy . mock . calls [ 0 ] !
1931+ const metadataSignal = metadata ! . abortSignal
1932+
1933+ // The signal in metadata should be the same as the one from currentRequestAbortController
1934+ expect ( metadataSignal ) . toBe ( task . currentRequestAbortController ! . signal )
1935+ } )
1936+ } )
17981937 } )
17991938 } )
18001939
0 commit comments