@@ -88,6 +88,7 @@ import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-
8888import { MultiFileSearchReplaceDiffStrategy } from "../diff/strategies/multi-file-search-replace"
8989import { readApiMessages , saveApiMessages , readTaskMessages , saveTaskMessages , taskMetadata } from "../task-persistence"
9090import { getEnvironmentDetails } from "../environment/getEnvironmentDetails"
91+ import { checkContextWindowExceededError } from "../context/context-management/context-error-handling"
9192import {
9293 type CheckpointDiffOptions ,
9394 type CheckpointRestoreOptions ,
@@ -2230,6 +2231,59 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
22302231 } ) ( )
22312232 }
22322233
2234+ private async handleContextWindowExceededError ( ) : Promise < void > {
2235+ const state = await this . providerRef . deref ( ) ?. getState ( )
2236+ const { profileThresholds = { } } = state ?? { }
2237+
2238+ const { contextTokens } = this . getTokenUsage ( )
2239+ const modelInfo = this . api . getModel ( ) . info
2240+ const maxTokens = getModelMaxOutputTokens ( {
2241+ modelId : this . api . getModel ( ) . id ,
2242+ model : modelInfo ,
2243+ settings : this . apiConfiguration ,
2244+ } )
2245+ const contextWindow = modelInfo . contextWindow
2246+
2247+ // Get the current profile ID the same way as in attemptApiRequest
2248+ const currentProfileId =
2249+ state ?. listApiConfigMeta ?. find ( ( profile : any ) => profile . name === state ?. currentApiConfigName ) ?. id ??
2250+ "default"
2251+
2252+ // Force aggressive truncation by removing 25% of the conversation history
2253+ const truncateResult = await truncateConversationIfNeeded ( {
2254+ messages : this . apiConversationHistory ,
2255+ totalTokens : contextTokens || 0 ,
2256+ maxTokens,
2257+ contextWindow,
2258+ apiHandler : this . api ,
2259+ autoCondenseContext : true ,
2260+ autoCondenseContextPercent : 75 , // Force 25% reduction
2261+ systemPrompt : await this . getSystemPrompt ( ) ,
2262+ taskId : this . taskId ,
2263+ profileThresholds,
2264+ currentProfileId,
2265+ } )
2266+
2267+ if ( truncateResult . messages !== this . apiConversationHistory ) {
2268+ await this . overwriteApiConversationHistory ( truncateResult . messages )
2269+ }
2270+
2271+ if ( truncateResult . summary ) {
2272+ const { summary, cost, prevContextTokens, newContextTokens = 0 } = truncateResult
2273+ const contextCondense : ContextCondense = { summary, cost, newContextTokens, prevContextTokens }
2274+ await this . say (
2275+ "condense_context" ,
2276+ undefined /* text */ ,
2277+ undefined /* images */ ,
2278+ false /* partial */ ,
2279+ undefined /* checkpoint */ ,
2280+ undefined /* progressStatus */ ,
2281+ { isNonInteractive : true } /* options */ ,
2282+ contextCondense ,
2283+ )
2284+ }
2285+ }
2286+
22332287 public async * attemptApiRequest ( retryAttempt : number = 0 ) : ApiStream {
22342288 const state = await this . providerRef . deref ( ) ?. getState ( )
22352289
@@ -2417,6 +2471,16 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
24172471 this . isWaitingForFirstChunk = false
24182472 } catch ( error ) {
24192473 this . isWaitingForFirstChunk = false
2474+ const isContextWindowExceededError = checkContextWindowExceededError ( error )
2475+
2476+ // If it's a context window error and we haven't already retried for this reason
2477+ if ( isContextWindowExceededError && retryAttempt === 0 ) {
2478+ await this . handleContextWindowExceededError ( )
2479+ // Retry the request after handling the context window error
2480+ yield * this . attemptApiRequest ( retryAttempt + 1 )
2481+ return
2482+ }
2483+
24202484 // 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.
24212485 if ( autoApprovalEnabled && alwaysApproveResubmit ) {
24222486 let errorMsg
0 commit comments