@@ -115,7 +115,7 @@ export class Agent<T extends PromptBasedToolName> {
115
115
}
116
116
117
117
injectImagesToLastMessage ( messages : CoreMessage [ ] , images : Base64ImageData [ ] ) {
118
- const lastMessage = messages [ messages . length - 1 ]
118
+ const lastMessage = structuredClone ( messages [ messages . length - 1 ] )
119
119
if ( lastMessage && lastMessage . role === 'user' ) {
120
120
if ( typeof lastMessage . content === 'string' ) {
121
121
lastMessage . content = [
@@ -126,8 +126,9 @@ export class Agent<T extends PromptBasedToolName> {
126
126
else {
127
127
lastMessage . content . push ( ...images . map ( ( img ) => ( { type : 'image' as const , image : img . data , mimeType : img . type } ) ) )
128
128
}
129
+ return [ ...messages . slice ( 0 , - 1 ) , lastMessage ]
129
130
}
130
- return messages
131
+ return [ ... messages ]
131
132
}
132
133
133
134
overrideSystemPrompt ( messages : CoreMessage [ ] , systemPrompt ?: string ) {
@@ -254,14 +255,18 @@ export class Agent<T extends PromptBasedToolName> {
254
255
}
255
256
256
257
// iteration starts from 1
257
- buildExtendedUserMessage ( iteration : number , originalUserMessage : string , toolResults ?: string ) {
258
+ buildExtendedUserMessage ( iteration : number , originalUserMessage : string , toolResults ?: ( AgentToolExecuteResultToolResult & { toolName : T } ) [ ] ) {
258
259
if ( iteration === 1 ) return `${ originalUserMessage } \n\n${ AGENT_INITIAL_GUIDANCE . build ( ) } `
259
- const textBuilder = new TextBuilder ( `${ originalUserMessage } ` )
260
- if ( toolResults ) {
261
- textBuilder . insertContent ( toolResults )
260
+ if ( toolResults ?. length ) {
261
+ const toolResultPart = this . toolResultsToPrompt ( toolResults )
262
+ const hasViewImageTool = toolResults . some ( ( t ) => t . toolName === 'view_image' )
263
+ // compatible with gemma3 model, which will loop infinitely if the view_image tool result has original user message
264
+ const textBuilder = new TextBuilder ( ! hasViewImageTool ? originalUserMessage : '' )
265
+ textBuilder . insertContent ( toolResultPart )
262
266
textBuilder . insertContent ( AGENT_TOOL_CALL_RESULT_GUIDANCE )
267
+ return textBuilder . build ( ) . trim ( )
263
268
}
264
- return textBuilder . build ( )
269
+ return new TextBuilder ( originalUserMessage ) . build ( ) . trim ( )
265
270
}
266
271
267
272
async run ( rawBaseMessages : CoreMessage [ ] ) {
@@ -340,14 +345,12 @@ export class Agent<T extends PromptBasedToolName> {
340
345
taskMessageModifier = this . makeTaskMessageGroupProxy ( abortController . signal )
341
346
}
342
347
this . log . debug ( 'Executing tool calls' , currentLoopToolCalls )
343
- const toolResults = await this . executeToolCalls ( currentLoopToolCalls , taskScopeToolCalls , loopImages , taskMessageModifier , eventBus )
344
- this . log . debug ( 'Tool calls executed' , currentLoopToolCalls , toolResults )
345
- if ( toolResults . length === 0 ) {
346
- const errorResult = TagBuilder . fromStructured ( 'error' , { message : `Tool not found, available tools are: ${ Object . keys ( this . tools ) . join ( ', ' ) } ` } )
347
- loopMessages . push ( { role : 'user' , content : renderPrompt `${ errorResult } ` } )
348
- }
349
- else if ( toolResults . some ( ( result ) => result . type === 'hand-off' ) ) {
350
- const handoffResult = toolResults . find ( ( result ) => result . type === 'hand-off' )
348
+ const toolExecuteResults = await this . executeToolCalls ( currentLoopToolCalls , taskScopeToolCalls , loopImages , taskMessageModifier , eventBus )
349
+ this . log . debug ( 'Tool calls executed' , currentLoopToolCalls , toolExecuteResults )
350
+ const toolResults = toolExecuteResults . filter ( ( r ) => r . type === 'tool-result' )
351
+ const handOffResults = toolExecuteResults . filter ( ( r ) => r . type === 'hand-off' )
352
+ if ( handOffResults . length > 0 ) {
353
+ const handoffResult = handOffResults [ 0 ]
351
354
if ( handoffResult ) {
352
355
// This feature is in beta, not used yet
353
356
this . log . debug ( 'Hand-off detected' , handoffResult )
@@ -359,9 +362,12 @@ export class Agent<T extends PromptBasedToolName> {
359
362
if ( lastMsg ?. content ) loopMessages . push ( lastMsg )
360
363
}
361
364
}
365
+ else if ( toolResults . length ) {
366
+ loopMessages . push ( { role : 'user' , content : this . buildExtendedUserMessage ( iteration + 1 , originalUserMessageText , toolResults ) } )
367
+ }
362
368
else {
363
- const toolResultPart = this . toolResultsToPrompt ( toolResults . filter ( ( t ) => t . type === 'tool-result' ) )
364
- loopMessages . push ( { role : 'user' , content : this . buildExtendedUserMessage ( iteration + 1 , originalUserMessageText , toolResultPart ) } )
369
+ const errorResult = TagBuilder . fromStructured ( 'error' , { message : `Tool not found, available tools are: ${ Object . keys ( this . tools ) . join ( ', ' ) } ` } )
370
+ loopMessages . push ( { role : 'user' , content : renderPrompt ` ${ errorResult } ` } )
365
371
}
366
372
}
367
373
}
0 commit comments