@@ -86,6 +86,7 @@ import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-
8686import { MultiFileSearchReplaceDiffStrategy } from "../diff/strategies/multi-file-search-replace"
8787import { readApiMessages , saveApiMessages , readTaskMessages , saveTaskMessages , taskMetadata } from "../task-persistence"
8888import { getEnvironmentDetails } from "../environment/getEnvironmentDetails"
89+ import { checkContextWindowExceededError } from "../context/context-management/context-error-handling"
8990import {
9091 type CheckpointDiffOptions ,
9192 type CheckpointRestoreOptions ,
@@ -2121,6 +2122,59 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
21212122 } ) ( )
21222123 }
21232124
2125+ private async handleContextWindowExceededError ( ) : Promise < void > {
2126+ const state = await this . providerRef . deref ( ) ?. getState ( )
2127+ const { profileThresholds = { } } = state ?? { }
2128+
2129+ const { contextTokens } = this . getTokenUsage ( )
2130+ const modelInfo = this . api . getModel ( ) . info
2131+ const maxTokens = getModelMaxOutputTokens ( {
2132+ modelId : this . api . getModel ( ) . id ,
2133+ model : modelInfo ,
2134+ settings : this . apiConfiguration ,
2135+ } )
2136+ const contextWindow = modelInfo . contextWindow
2137+
2138+ // Get the current profile ID the same way as in attemptApiRequest
2139+ const currentProfileId =
2140+ state ?. listApiConfigMeta ?. find ( ( profile : any ) => profile . name === state ?. currentApiConfigName ) ?. id ??
2141+ "default"
2142+
2143+ // Force aggressive truncation by removing 25% of the conversation history
2144+ const truncateResult = await truncateConversationIfNeeded ( {
2145+ messages : this . apiConversationHistory ,
2146+ totalTokens : contextTokens || 0 ,
2147+ maxTokens,
2148+ contextWindow,
2149+ apiHandler : this . api ,
2150+ autoCondenseContext : true ,
2151+ autoCondenseContextPercent : 75 , // Force 25% reduction
2152+ systemPrompt : await this . getSystemPrompt ( ) ,
2153+ taskId : this . taskId ,
2154+ profileThresholds,
2155+ currentProfileId,
2156+ } )
2157+
2158+ if ( truncateResult . messages !== this . apiConversationHistory ) {
2159+ await this . overwriteApiConversationHistory ( truncateResult . messages )
2160+ }
2161+
2162+ if ( truncateResult . summary ) {
2163+ const { summary, cost, prevContextTokens, newContextTokens = 0 } = truncateResult
2164+ const contextCondense : ContextCondense = { summary, cost, newContextTokens, prevContextTokens }
2165+ await this . say (
2166+ "condense_context" ,
2167+ undefined /* text */ ,
2168+ undefined /* images */ ,
2169+ false /* partial */ ,
2170+ undefined /* checkpoint */ ,
2171+ undefined /* progressStatus */ ,
2172+ { isNonInteractive : true } /* options */ ,
2173+ contextCondense ,
2174+ )
2175+ }
2176+ }
2177+
21242178 public async * attemptApiRequest ( retryAttempt : number = 0 ) : ApiStream {
21252179 const state = await this . providerRef . deref ( ) ?. getState ( )
21262180
@@ -2308,6 +2362,16 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
23082362 this . isWaitingForFirstChunk = false
23092363 } catch ( error ) {
23102364 this . isWaitingForFirstChunk = false
2365+ const isContextWindowExceededError = checkContextWindowExceededError ( error )
2366+
2367+ // If it's a context window error and we haven't already retried for this reason
2368+ if ( isContextWindowExceededError && retryAttempt === 0 ) {
2369+ await this . handleContextWindowExceededError ( )
2370+ // Retry the request after handling the context window error
2371+ yield * this . attemptApiRequest ( retryAttempt + 1 )
2372+ return
2373+ }
2374+
23112375 // note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely.
23122376 if ( autoApprovalEnabled && alwaysApproveResubmit ) {
23132377 let errorMsg
0 commit comments