From 923b9cb9ee3a314985f2b734a8890285f54c5e5c Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Fri, 16 Jan 2026 22:16:13 +0300 Subject: [PATCH] fix(ai): coerce string numbers in tool argument validation (#786) * fix(ai): coerce string numbers in tool argument validation * fix(ai): clone tool arguments before AJV validation for type coercion --- packages/ai/src/utils/validation.ts | 12 ++++++++---- packages/ai/test/.temp-images/small.png | Bin 321 -> 0 bytes 2 files changed, 8 insertions(+), 4 deletions(-) delete mode 100644 packages/ai/test/.temp-images/small.png diff --git a/packages/ai/src/utils/validation.ts b/packages/ai/src/utils/validation.ts index 233d6893..b5f48faa 100644 --- a/packages/ai/src/utils/validation.ts +++ b/packages/ai/src/utils/validation.ts @@ -19,6 +19,7 @@ if (!isBrowserExtension) { ajv = new Ajv({ allErrors: true, strict: false, + coerceTypes: true, }); addFormats(ajv); } catch (_e) { @@ -46,7 +47,7 @@ export function validateToolCall(tools: Tool[], toolCall: ToolCall): any { * Validates tool call arguments against the tool's TypeBox schema * @param tool The tool definition with TypeBox schema * @param toolCall The tool call from the LLM - * @returns The validated arguments + * @returns The validated (and potentially coerced) arguments * @throws Error with formatted message if validation fails */ export function validateToolArguments(tool: Tool, toolCall: ToolCall): any { @@ -60,9 +61,12 @@ export function validateToolArguments(tool: Tool, toolCall: ToolCall): any { // Compile the schema const validate = ajv.compile(tool.parameters); - // Validate the arguments - if (validate(toolCall.arguments)) { - return toolCall.arguments; + // Clone arguments so AJV can safely mutate for type coercion + const args = structuredClone(toolCall.arguments); + + // Validate the arguments (AJV mutates args in-place for type coercion) + if (validate(args)) { + return args; } // Format validation errors nicely diff --git a/packages/ai/test/.temp-images/small.png b/packages/ai/test/.temp-images/small.png deleted file mode 100644 index 91dd80f12a9fa62c3304a290c829829e6e01768c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 321 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRBMrH;E1}`0UaRvqkg=CK)Uj~LMH3o);76yi2 z3=9knFBlj~4Hy_+B``2p&0t^Zr-eX{3U@!6Xb!C6W$ji#XHo2hhKLZ1Uh^LEVh{fsT1PRu~2_ijBfe!u*46GFl zjGBcz|1dBxsFt`!l%yncndurB>KYn{7?@faSXddDX&V?=85m@p zWOYN)kei>9nO2EggY=ZWAkS#PZ79jiO)V}-%q_sJ$ER_QDgy%pgQu&X%Q~loCIG+k BP_zI5