@@ -2922,88 +2922,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
29222922 }
29232923 }
29242924
2925- // Properly type cleaned conversation history to include either standard Anthropic messages
2926- // or provider-specific reasoning items (for encrypted continuity).
2927- type ReasoningItemForRequest = {
2928- type : "reasoning"
2929- encrypted_content : string
2930- id ?: string
2931- summary ?: any [ ]
2932- }
2933- type CleanConversationMessage = Anthropic . Messages . MessageParam | ReasoningItemForRequest
2934-
29352925 const messagesSinceLastSummary = getMessagesSinceLastSummary ( this . apiConversationHistory )
29362926 const messagesWithoutImages = maybeRemoveImageBlocks ( messagesSinceLastSummary , this . api )
2937- const cleanConversationHistory : CleanConversationMessage [ ] = [ ]
2938-
2939- for ( const msg of messagesWithoutImages as ApiMessage [ ] ) {
2940- // Legacy path: standalone reasoning items stored as separate messages
2941- if ( msg . type === "reasoning" && msg . encrypted_content ) {
2942- cleanConversationHistory . push ( {
2943- type : "reasoning" ,
2944- summary : msg . summary ,
2945- encrypted_content : msg . encrypted_content ! ,
2946- ...( msg . id ? { id : msg . id } : { } ) ,
2947- } )
2948- continue
2949- }
2950-
2951- // Preferred path: assistant message with embedded reasoning as first content block
2952- if ( msg . role === "assistant" ) {
2953- const rawContent = msg . content
2954-
2955- const contentArray : Anthropic . Messages . ContentBlockParam [ ] = Array . isArray ( rawContent )
2956- ? ( rawContent as Anthropic . Messages . ContentBlockParam [ ] )
2957- : rawContent !== undefined
2958- ? ( [
2959- { type : "text" , text : rawContent } satisfies Anthropic . Messages . TextBlockParam ,
2960- ] as Anthropic . Messages . ContentBlockParam [ ] )
2961- : [ ]
2962-
2963- const [ first , ...rest ] = contentArray
2964-
2965- const hasEmbeddedReasoning =
2966- first && ( first as any ) . type === "reasoning" && typeof ( first as any ) . encrypted_content === "string"
2967-
2968- if ( hasEmbeddedReasoning ) {
2969- const reasoningBlock = first as any
2970-
2971- // Emit a separate reasoning item for the provider
2972- cleanConversationHistory . push ( {
2973- type : "reasoning" ,
2974- summary : reasoningBlock . summary ?? [ ] ,
2975- encrypted_content : reasoningBlock . encrypted_content ,
2976- ...( reasoningBlock . id ? { id : reasoningBlock . id } : { } ) ,
2977- } )
2978-
2979- // Build assistant message without the embedded reasoning block
2980- let assistantContent : Anthropic . Messages . MessageParam [ "content" ]
2981-
2982- if ( rest . length === 0 ) {
2983- assistantContent = ""
2984- } else if ( rest . length === 1 && rest [ 0 ] . type === "text" ) {
2985- assistantContent = ( rest [ 0 ] as Anthropic . Messages . TextBlockParam ) . text
2986- } else {
2987- assistantContent = rest
2988- }
2989-
2990- cleanConversationHistory . push ( {
2991- role : "assistant" ,
2992- content : assistantContent ,
2993- } satisfies Anthropic . Messages . MessageParam )
2994-
2995- continue
2996- }
2997- }
2998-
2999- // Default path for regular messages (no embedded reasoning)
3000- if ( msg . role ) {
3001- cleanConversationHistory . push ( {
3002- role : msg . role ,
3003- content : msg . content as Anthropic . Messages . ContentBlockParam [ ] | string ,
3004- } )
3005- }
3006- }
2927+ const cleanConversationHistory = this . buildCleanConversationHistory ( messagesWithoutImages as ApiMessage [ ] )
30072928
30082929 // Check auto-approval limits
30092930 const approvalResult = await this . autoApprovalHandler . checkAutoApprovalLimits (
@@ -3235,6 +3156,91 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32353156 return checkpointSave ( this , force , suppressMessage )
32363157 }
32373158
3159+ private buildCleanConversationHistory (
3160+ messages : ApiMessage [ ] ,
3161+ ) : Array <
3162+ Anthropic . Messages . MessageParam | { type : "reasoning" ; encrypted_content : string ; id ?: string ; summary ?: any [ ] }
3163+ > {
3164+ type ReasoningItemForRequest = {
3165+ type : "reasoning"
3166+ encrypted_content : string
3167+ id ?: string
3168+ summary ?: any [ ]
3169+ }
3170+
3171+ const cleanConversationHistory : ( Anthropic . Messages . MessageParam | ReasoningItemForRequest ) [ ] = [ ]
3172+
3173+ for ( const msg of messages ) {
3174+ // Legacy path: standalone reasoning items stored as separate messages
3175+ if ( msg . type === "reasoning" && msg . encrypted_content ) {
3176+ cleanConversationHistory . push ( {
3177+ type : "reasoning" ,
3178+ summary : msg . summary ,
3179+ encrypted_content : msg . encrypted_content ! ,
3180+ ...( msg . id ? { id : msg . id } : { } ) ,
3181+ } )
3182+ continue
3183+ }
3184+
3185+ // Preferred path: assistant message with embedded reasoning as first content block
3186+ if ( msg . role === "assistant" ) {
3187+ const rawContent = msg . content
3188+
3189+ const contentArray : Anthropic . Messages . ContentBlockParam [ ] = Array . isArray ( rawContent )
3190+ ? ( rawContent as Anthropic . Messages . ContentBlockParam [ ] )
3191+ : rawContent !== undefined
3192+ ? ( [
3193+ { type : "text" , text : rawContent } satisfies Anthropic . Messages . TextBlockParam ,
3194+ ] as Anthropic . Messages . ContentBlockParam [ ] )
3195+ : [ ]
3196+
3197+ const [ first , ...rest ] = contentArray
3198+
3199+ const hasEmbeddedReasoning =
3200+ first && ( first as any ) . type === "reasoning" && typeof ( first as any ) . encrypted_content === "string"
3201+
3202+ if ( hasEmbeddedReasoning ) {
3203+ const reasoningBlock = first as any
3204+
3205+ // Emit a separate reasoning item for the provider
3206+ cleanConversationHistory . push ( {
3207+ type : "reasoning" ,
3208+ summary : reasoningBlock . summary ?? [ ] ,
3209+ encrypted_content : reasoningBlock . encrypted_content ,
3210+ ...( reasoningBlock . id ? { id : reasoningBlock . id } : { } ) ,
3211+ } )
3212+
3213+ // Build assistant message without the embedded reasoning block
3214+ let assistantContent : Anthropic . Messages . MessageParam [ "content" ]
3215+
3216+ if ( rest . length === 0 ) {
3217+ assistantContent = ""
3218+ } else if ( rest . length === 1 && rest [ 0 ] . type === "text" ) {
3219+ assistantContent = ( rest [ 0 ] as Anthropic . Messages . TextBlockParam ) . text
3220+ } else {
3221+ assistantContent = rest
3222+ }
3223+
3224+ cleanConversationHistory . push ( {
3225+ role : "assistant" ,
3226+ content : assistantContent ,
3227+ } satisfies Anthropic . Messages . MessageParam )
3228+
3229+ continue
3230+ }
3231+ }
3232+
3233+ // Default path for regular messages (no embedded reasoning)
3234+ if ( msg . role ) {
3235+ cleanConversationHistory . push ( {
3236+ role : msg . role ,
3237+ content : msg . content as Anthropic . Messages . ContentBlockParam [ ] | string ,
3238+ } )
3239+ }
3240+ }
3241+
3242+ return cleanConversationHistory
3243+ }
32383244 public async checkpointRestore ( options : CheckpointRestoreOptions ) {
32393245 return checkpointRestore ( this , options )
32403246 }
0 commit comments