@@ -210,40 +210,46 @@ function normalizeTools(tools) {
210210 . filter ( ( t ) => t && t . function ?. name ) ;
211211}
212212
213- function historicalToolNames ( messages ) {
214- const names = new Set ( ) ;
213+ export function filterUnknownToolHistory ( messages = [ ] , tools = [ ] ) {
214+ const normalizedTools = normalizeTools ( tools ) ;
215+ const definedNames = new Set ( normalizedTools . map ( ( tool ) => tool . function . name ) ) ;
216+ const droppedToolCallIds = new Set ( ) ;
217+ const droppedToolNames = new Set ( ) ;
218+ const filtered = [ ] ;
219+
215220 for ( const message of Array . isArray ( messages ) ? messages : [ ] ) {
216- if ( message ?. role !== "assistant" || ! Array . isArray ( message . tool_calls ) ) continue ;
217- for ( const toolCall of message . tool_calls ) {
218- const fn = toolCall ?. function && typeof toolCall . function === "object" ? toolCall . function : { } ;
219- const name = String ( fn . name || toolCall ?. name || "" ) . trim ( ) ;
220- if ( name ) names . add ( name ) ;
221+ if ( message ?. role === "assistant" && Array . isArray ( message . tool_calls ) ) {
222+ const retainedCalls = [ ] ;
223+ for ( const toolCall of message . tool_calls ) {
224+ const fn = toolCall ?. function && typeof toolCall . function === "object" ? toolCall . function : { } ;
225+ const name = String ( fn . name || toolCall ?. name || "" ) . trim ( ) ;
226+ if ( name && definedNames . has ( name ) ) {
227+ retainedCalls . push ( toolCall ) ;
228+ continue ;
229+ }
230+ if ( typeof toolCall ?. id === "string" && toolCall . id . trim ( ) ) {
231+ droppedToolCallIds . add ( toolCall . id . trim ( ) ) ;
232+ }
233+ if ( name ) droppedToolNames . add ( name ) ;
234+ }
235+ if ( ! retainedCalls . length ) {
236+ if ( contentToString ( message . content ) ) filtered . push ( { ...message , tool_calls : undefined } ) ;
237+ continue ;
238+ }
239+ filtered . push ( { ...message , tool_calls : retainedCalls } ) ;
240+ continue ;
241+ }
242+ if ( message ?. role === "tool" && typeof message . tool_call_id === "string" && droppedToolCallIds . has ( message . tool_call_id . trim ( ) ) ) {
243+ continue ;
221244 }
245+ filtered . push ( message ) ;
222246 }
223- return names ;
224- }
225247
226- export function addHistoricalToolDefinitions ( tools = [ ] , messages = [ ] ) {
227- const normalized = normalizeTools ( tools ) ;
228- const defined = new Set ( normalized . map ( ( tool ) => tool . function . name ) ) ;
229- const added = [ ] ;
230- for ( const name of historicalToolNames ( messages ) ) {
231- if ( defined . has ( name ) ) continue ;
232- normalized . push ( {
233- type : "function" ,
234- function : {
235- name,
236- description : "Compatibility definition for a tool call preserved in conversation history." ,
237- parameters : {
238- type : "object" ,
239- additionalProperties : true ,
240- } ,
241- } ,
242- } ) ;
243- defined . add ( name ) ;
244- added . push ( name ) ;
245- }
246- return { tools : normalized , added } ;
248+ return {
249+ messages : filtered ,
250+ droppedToolCallIds : [ ...droppedToolCallIds ] ,
251+ droppedToolNames : [ ...droppedToolNames ] ,
252+ } ;
247253}
248254
249255function normalizeToolCall ( toolCall , messageIndex , callIndex , usedIds , sourceIds ) {
@@ -442,8 +448,8 @@ export function buildPlainChatBody({
442448 const sessionId = crypto . randomUUID ( ) ;
443449 const mapped = mapModel ( model ) ;
444450
445- const openaiMessages = normalizeMessagesForUpstream ( messages || [ ] ) ;
446- const compatibleTools = addHistoricalToolDefinitions ( tools , messages || [ ] ) ;
451+ const filteredHistory = filterUnknownToolHistory ( messages || [ ] , tools ) ;
452+ const openaiMessages = normalizeMessagesForUpstream ( filteredHistory . messages ) ;
447453
448454 // Chat-API mode:
449455 // - Do NOT inherit capture-template Qoder agent system/tools (they explode multiturn).
@@ -535,7 +541,7 @@ export function buildPlainChatBody({
535541 ? [ ...systemMessages , ...( nonSystem . length ? nonSystem : [ { role : "user" , content : "ping" } ] ) ]
536542 : [ { role : "user" , content : "ping" } ] ,
537543 // Pass through caller tools (OpenAI function tools). Do NOT inherit capture-template tools.
538- tools : compatibleTools . tools ,
544+ tools : normalizeTools ( tools ) ,
539545 parameters,
540546 business : {
541547 product : "cli" ,
0 commit comments