@@ -14,6 +14,7 @@ import { ClineProvider } from "../../webview/ClineProvider"
1414import { ApiStreamChunk } from "../../../api/transform/stream"
1515import { ContextProxy } from "../../config/ContextProxy"
1616import { processUserContentMentions } from "../../mentions/processUserContentMentions"
17+ import * as slidingWindow from "../../sliding-window"
1718
1819jest . mock ( "execa" , ( ) => ( {
1920 execa : jest . fn ( ) ,
@@ -533,6 +534,82 @@ describe("Cline", () => {
533534 } )
534535 } )
535536
537+ it ( "should use maxContextWindow when provider is gemini and maxContextWindow is set" , async ( ) => {
538+ // Arrange: set apiProvider to gemini and maxContextWindow
539+ const geminiConfig = {
540+ ...mockApiConfig ,
541+ apiProvider : "gemini" as const ,
542+ maxContextWindow : 42 ,
543+ }
544+ const created = Task . create ( {
545+ provider : mockProvider ,
546+ apiConfiguration : geminiConfig ,
547+ task : "test gemini context window" ,
548+ } ) as unknown as [ any , Promise < void > ]
549+ const cline = created [ 0 ] as any
550+ const task = created [ 1 ] as Promise < void >
551+
552+ // Stub model info to have a different default contextWindow
553+ ; ( cline . api as any ) . getModel = jest . fn ( ) . mockReturnValue ( {
554+ id : "gemini-model" ,
555+ info : {
556+ contextWindow : 100 ,
557+ supportsReasoningBudget : true ,
558+ maxTokens : 1000 ,
559+ supportsComputerUse : false ,
560+ supportsPromptCache : false ,
561+ inputPrice : 0 ,
562+ outputPrice : 0 ,
563+ } ,
564+ } )
565+
566+ // Stub required methods to let attemptApiRequest proceed
567+ ; ( cline as any ) . getSystemPrompt = jest . fn ( ) . mockResolvedValue ( "" )
568+ ; ( cline as any ) . getTokenUsage = jest . fn ( ) . mockReturnValue ( {
569+ contextTokens : 1 ,
570+ totalTokensIn : 0 ,
571+ totalTokensOut : 0 ,
572+ totalCost : 0 ,
573+ } )
574+
575+ // Stub createMessage to avoid real API calls
576+ jest . spyOn ( cline . api as any , "createMessage" ) . mockReturnValue ( ( async function * ( ) { } ) ( ) )
577+
578+ // Spy on truncateConversationIfNeeded to capture its options
579+ const twSpy = jest . spyOn ( slidingWindow , "truncateConversationIfNeeded" ) . mockResolvedValue ( {
580+ messages : [ ] ,
581+ summary : "" ,
582+ cost : 0 ,
583+ prevContextTokens : 0 ,
584+ newContextTokens : 0 ,
585+ error : undefined ,
586+ } )
587+
588+ // Force abort immediately so the stream loop exits
589+ Object . defineProperty ( cline , "abort" , {
590+ get : ( ) => true ,
591+ set : ( ) => { } ,
592+ configurable : true ,
593+ } )
594+
595+ // Act: run through the generator
596+ try {
597+ for await ( const _ of cline . attemptApiRequest ( ) ) {
598+ }
599+ } catch {
600+ /* ignore */
601+ }
602+
603+ // Assert: the contextWindow passed to truncateConversationIfNeeded is the maxContextWindow
604+ expect ( twSpy ) . toHaveBeenCalled ( )
605+ const optionsPassed = twSpy . mock . calls [ 0 ] [ 0 ]
606+ expect ( optionsPassed . contextWindow ) . toBe ( 42 )
607+
608+ // Cleanup
609+ await cline . abortTask ( true )
610+ await task . catch ( ( ) => { } )
611+ } )
612+
536613 it . skip ( "should handle API retry with countdown" , async ( ) => {
537614 const [ cline , task ] = Task . create ( {
538615 provider : mockProvider ,
0 commit comments