diff --git a/apps/rowboat/app/lib/agents.ts b/apps/rowboat/app/lib/agents.ts index a8f617e10..b6b215fd7 100644 --- a/apps/rowboat/app/lib/agents.ts +++ b/apps/rowboat/app/lib/agents.ts @@ -1156,6 +1156,10 @@ export async function* streamResponse( const usageTracker = new UsageTracker(); const turnMsgs: z.infer[] = [...messages]; + // Add deduplication tracking for tool calls + const processedToolCallIds = new Set(); + const processedToolResponseIds = new Set(); + logger.log('🎬 STARTING AGENT TURN'); // stack-based agent execution loop @@ -1208,6 +1212,15 @@ export async function* streamResponse( // handle tool call invocation // except for transfer_to_* tool calls if (output.type === 'function_call' && !output.name.startsWith('transfer_to')) { + // Check if this tool call has already been processed + if (processedToolCallIds.has(output.callId)) { + eventLogger.log(`⚠️ SKIPPING: tool call ${output.callId} (${output.name}) already processed`); + continue; + } + + // Mark this tool call as processed + processedToolCallIds.add(output.callId); + const m: z.infer = { role: 'assistant', content: null, @@ -1309,6 +1322,16 @@ export async function* streamResponse( event.item.rawItem.type === 'function_call_result' && event.item.rawItem.status === 'completed' && event.item.rawItem.output.type === 'text') { + + // Check if this tool response has already been processed + if (processedToolResponseIds.has(event.item.rawItem.callId)) { + eventLogger.log(`⚠️ SKIPPING: tool response ${event.item.rawItem.callId} (${event.item.rawItem.name}) already processed`); + continue; + } + + // Mark this tool response as processed + processedToolResponseIds.add(event.item.rawItem.callId); + const m: z.infer = { role: 'tool', content: event.item.rawItem.output.text,