Skip to content

Commit ec15a07

Browse files
committed
fix: preserve tool alias name in API history for consistent conversation context
When tool aliases are used (e.g., 'edit_file' -> 'search_and_replace'), the alias name is now preserved and written to API history instead of the canonical name. This prevents confusion in multi-turn conversations where the model sees a different tool name in history than what it was told the tool was named. Changes: - NativeToolCallParser.ts already stores originalName when alias differs from resolved - Task.ts now uses originalName (alias) when available for API history
1 parent 0c2090c commit ec15a07

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

src/core/assistant-message/NativeToolCallParser.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,16 @@ export class NativeToolCallParser {
249249

250250
// Resolve tool alias to canonical name
251251
const resolvedName = resolveToolAlias(toolCall.name) as ToolName
252+
// Preserve original name if it differs from resolved (i.e., it was an alias)
253+
const originalName = toolCall.name !== resolvedName ? toolCall.name : undefined
252254

253255
// Create partial ToolUse with extracted values
254256
return this.createPartialToolUse(
255257
toolCall.id,
256258
resolvedName,
257259
partialArgs || {},
258260
true, // partial
261+
originalName,
259262
)
260263
} catch {
261264
// Even partial-json-parser can fail on severely malformed JSON
@@ -331,12 +334,14 @@ export class NativeToolCallParser {
331334
/**
332335
* Create a partial ToolUse from currently parsed arguments.
333336
* Used during streaming to show progress.
337+
* @param originalName - The original tool name as called by the model (if different from canonical name)
334338
*/
335339
private static createPartialToolUse(
336340
id: string,
337341
name: ToolName,
338342
partialArgs: Record<string, any>,
339343
partial: boolean,
344+
originalName?: string,
340345
): ToolUse | null {
341346
// Build legacy params for display
342347
// NOTE: For streaming partial updates, we MUST populate params even for complex types
@@ -530,13 +535,20 @@ export class NativeToolCallParser {
530535
break
531536
}
532537

533-
return {
538+
const result: ToolUse = {
534539
type: "tool_use" as const,
535540
name,
536541
params,
537542
partial,
538543
nativeArgs,
539544
}
545+
546+
// Preserve original name for API history when an alias was used
547+
if (originalName) {
548+
;(result as any).originalName = originalName
549+
}
550+
551+
return result
540552
}
541553

542554
/**
@@ -798,6 +810,11 @@ export class NativeToolCallParser {
798810
nativeArgs,
799811
}
800812

813+
// Preserve original name for API history when an alias was used
814+
if (toolCall.name !== resolvedName) {
815+
;(result as any).originalName = toolCall.name
816+
}
817+
801818
return result
802819
} catch (error) {
803820
console.error(

src/core/task/Task.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3106,10 +3106,16 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
31063106
// nativeArgs is already in the correct API format for all tools
31073107
const input = toolUse.nativeArgs || toolUse.params
31083108

3109+
// Use originalName (alias) if present for API history consistency.
3110+
// When tool aliases are used (e.g., "edit_file" -> "search_and_replace"),
3111+
// we want the alias name in the conversation history to match what the model
3112+
// was told the tool was named, preventing confusion in multi-turn conversations.
3113+
const toolNameForHistory = (toolUse as any).originalName || toolUse.name
3114+
31093115
assistantContent.push({
31103116
type: "tool_use" as const,
31113117
id: toolCallId,
3112-
name: toolUse.name,
3118+
name: toolNameForHistory,
31133119
input,
31143120
})
31153121
}

0 commit comments

Comments
 (0)