@@ -1812,7 +1812,7 @@ describe("Cline", () => {
18121812
18131813 expect ( summarizeConversation ) . toHaveBeenCalled ( )
18141814 const [ options ] = vi . mocked ( summarizeConversation ) . mock . calls . at ( - 1 ) !
1815- expect ( options . metadata ?. abortSignal ) . toBeInstanceOf ( AbortSignal )
1815+ expect ( options . metadata ?. abortSignal ) . toBe ( task . currentRequestAbortController ! . signal )
18161816 } )
18171817
18181818 it ( "should omit abortSignal from condenseContext metadata when no current request exists" , async ( ) => {
@@ -1899,7 +1899,7 @@ describe("Cline", () => {
18991899 const [ , , metadata ] = createMessageSpy . mock . calls [ 0 ] !
19001900
19011901 expect ( metadata ) . toBeDefined ( )
1902- expect ( metadata ! . abortSignal ) . toBeInstanceOf ( AbortSignal )
1902+ expect ( metadata ! . abortSignal ) . toBe ( task . currentRequestAbortController ! . signal )
19031903 } )
19041904
19051905 it ( "should invoke abort on currentRequestAbortController during first-chunk wait" , async ( ) => {
@@ -2136,7 +2136,7 @@ describe("Cline", () => {
21362136 const [ , , metadata ] = createMessageSpy . mock . calls [ 0 ] !
21372137 expect ( metadata ) . toBeDefined ( )
21382138 expect ( "abortSignal" in metadata ! ) . toBe ( true )
2139- expect ( metadata ! . abortSignal ) . toBeInstanceOf ( AbortSignal )
2139+ expect ( metadata ! . abortSignal ) . toBe ( task . currentRequestAbortController ! . signal )
21402140 } )
21412141
21422142 it ( "should keep createMessage abortSignal metadata unaborted before cancellation" , async ( ) => {
@@ -2197,11 +2197,100 @@ describe("Cline", () => {
21972197 await iterator . next ( )
21982198
21992199 const [ , , metadata ] = createMessageSpy . mock . calls [ 0 ] !
2200- expect ( metadata ?. abortSignal ) . toBeInstanceOf ( AbortSignal )
2200+ expect ( metadata ?. abortSignal ) . toBe ( task . currentRequestAbortController ! . signal )
22012201 expect ( metadata ?. abortSignal ?. aborted ) . toBe ( false )
22022202 } )
22032203 } )
22042204
2205+ it ( "should create a fresh AbortController for each sequential request" , async ( ) => {
2206+ const task = new Task ( {
2207+ provider : mockProvider ,
2208+ apiConfiguration : mockApiConfig ,
2209+ task : "test task" ,
2210+ startTask : false ,
2211+ } )
2212+
2213+ vi . spyOn ( task as any , "getSystemPrompt" ) . mockResolvedValue ( "mock system prompt" )
2214+ vi . spyOn ( task . api , "getModel" ) . mockReturnValue ( {
2215+ id : mockApiConfig . apiModelId ! ,
2216+ info : {
2217+ supportsImages : false ,
2218+ supportsPromptCache : true ,
2219+ contextWindow : 200000 ,
2220+ maxTokens : 4096 ,
2221+ inputPrice : 0.3 ,
2222+ outputPrice : 1.5 ,
2223+ } as ModelInfo ,
2224+ } )
2225+
2226+ const providerState = await mockProvider . getState ( )
2227+ vi . spyOn ( mockProvider , "getState" ) . mockResolvedValue ( {
2228+ ...providerState ,
2229+ apiConfiguration : mockApiConfig ,
2230+ autoApprovalEnabled : true ,
2231+ requestDelaySeconds : 0 ,
2232+ } )
2233+
2234+ let callCount = 0
2235+ const mockStreamFactory = ( ) => {
2236+ return {
2237+ async * [ Symbol . asyncIterator ] ( ) {
2238+ yield { type : "text" , text : `response ${ callCount } ` }
2239+ } ,
2240+ async next ( ) {
2241+ callCount ++
2242+ return { done : true , value : { type : "text" , text : `response ${ callCount - 1 } ` } }
2243+ } ,
2244+ async return ( ) {
2245+ return { done : true , value : undefined }
2246+ } ,
2247+ async throw ( e : any ) {
2248+ throw e
2249+ } ,
2250+ [ Symbol . asyncDispose ] : async ( ) => { } ,
2251+ } as AsyncGenerator < ApiStreamChunk >
2252+ }
2253+
2254+ const createMessageSpy = vi
2255+ . spyOn ( task . api , "createMessage" )
2256+ . mockImplementation ( ( ) => mockStreamFactory ( ) )
2257+
2258+ task . apiConversationHistory = [
2259+ {
2260+ role : "user" as const ,
2261+ content : [ { type : "text" as const , text : "test message" } ] ,
2262+ ts : Date . now ( ) ,
2263+ } ,
2264+ ] as any
2265+
2266+ // First request
2267+ const iterator1 = task . attemptApiRequest ( 0 )
2268+ await iterator1 . next ( )
2269+
2270+ expect ( createMessageSpy ) . toHaveBeenCalledTimes ( 1 )
2271+ const [ , , metadata1 ] = createMessageSpy . mock . calls [ 0 ] !
2272+ const signal1 = metadata1 ! . abortSignal
2273+ expect ( signal1 ) . toBeDefined ( )
2274+ expect ( signal1 ! . aborted ) . toBe ( false )
2275+
2276+ // Simulate request completion and cancellation to clear the controller
2277+ task . cancelCurrentRequest ( )
2278+
2279+ // Second request should create a fresh AbortController with a new signal
2280+ callCount = 0
2281+ const iterator2 = task . attemptApiRequest ( 0 )
2282+ await iterator2 . next ( )
2283+
2284+ expect ( createMessageSpy ) . toHaveBeenCalledTimes ( 2 )
2285+ const [ , , metadata2 ] = createMessageSpy . mock . calls [ 1 ] !
2286+ const signal2 = metadata2 ! . abortSignal
2287+
2288+ // Signals should be different instances (fresh controller per request)
2289+ expect ( signal2 ) . not . toBe ( signal1 )
2290+ expect ( signal2 ) . toBe ( task . currentRequestAbortController ! . signal )
2291+ expect ( signal2 ! . aborted ) . toBe ( false )
2292+ } )
2293+
22052294 it ( "should propagate AbortController signal through attemptApiRequest context-window retry path" , async ( ) => {
22062295 const task = new Task ( {
22072296 provider : mockProvider ,
0 commit comments