@@ -181,7 +181,6 @@ export class MimoHandler extends OpenAiHandler {
181181 } else if ( block . type === "text" ) {
182182 textBlocks . push ( block . text )
183183 } else if ( block . type === "image" ) {
184- // Convert Anthropic image block to OpenAI image_url format
185184 const src = ( block as any ) . source
186185 if ( src ?. type === "base64" && src ?. media_type ) {
187186 mediaParts . push ( {
@@ -195,10 +194,9 @@ export class MimoHandler extends OpenAiHandler {
195194 } )
196195 }
197196 }
198- // audio/video blocks are not supported in OpenAI chat format — skip silently
199197 }
200198
201- // Add tool results as role:"tool" messages (MiMo supports this)
199+ // Add tool results as role:"tool" messages
202200 for ( const tr of toolResults ) {
203201 let content : string
204202 if ( typeof tr . content === "string" ) {
@@ -216,8 +214,18 @@ export class MimoHandler extends OpenAiHandler {
216214 } )
217215 }
218216
219- // Build user message content — plain string or multimodal array
220- if ( mediaParts . length > 0 ) {
217+ // When both tool_results and text exist in the same user turn (happens
218+ // during resume/delegate flows where <environment_details> is appended),
219+ // fold the text into the last tool message instead of creating a separate
220+ // user message. A fresh role:"user" after tool results drops reasoning
221+ // continuity for thinking models.
222+ if ( toolResults . length > 0 && textBlocks . length > 0 && mediaParts . length === 0 ) {
223+ const lastToolIdx = converted . length - 1
224+ const lastTool = converted [ lastToolIdx ] as any
225+ if ( lastTool ?. role === "tool" ) {
226+ lastTool . content += "\n" + textBlocks . join ( "\n" )
227+ }
228+ } else if ( mediaParts . length > 0 ) {
221229 const content : any [ ] = [ ]
222230 if ( textBlocks . length > 0 ) {
223231 content . push ( { type : "text" , text : textBlocks . join ( "\n" ) } )
0 commit comments