@mastra/schema-compat 1.3.6-alpha.3 → 1.3.7-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @mastra/schema-compat
2
2
 
3
+ ## 1.3.7-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Optional nested JSON Schema properties with multiple types no longer produce exponentially large OpenAI tool payloads. Payload growth now remains linear as these schemas become more deeply nested. ([#21190](https://github.com/mastra-ai/mastra/pull/21190))
8
+
9
+ ## 1.3.6
10
+
11
+ ### Patch Changes
12
+
13
+ - Fixed OpenAI structured output requests failing when a schema uses `z.record()`. The OpenAI compatibility layer now removes the `propertyNames` keyword, which OpenAI strict mode does not permit. Requests that used to fail with "Invalid schema ... 'propertyNames' is not permitted" are now accepted. ([#20977](https://github.com/mastra-ai/mastra/pull/20977))
14
+
15
+ **Known limitation.** OpenAI strict mode cannot express an open-ended map. A `z.record()` field is still sent as a plain object with no value schema, so the model is not told what keys or values to produce. Use an explicit `z.object({ ... })` shape when you need the model to fill a map. See [#19273](https://github.com/mastra-ai/mastra/issues/19273).
16
+
17
+ - Fix supervisor agent tool schemas for Gemini via OpenRouter. Properties with no Gemini-compatible type — most commonly `z.any()`, which serializes to an empty schema — are now rewritten into a permissive `anyOf` instead of being dropped. This resolves the misleading `required[N]: property is not defined` error when using Gemini models through OpenRouter as a supervisor agent (fixes #17325), while keeping fields the model is expected to fill (such as `resumeData` for tool suspend/resume) present in the tool contract. ([#17386](https://github.com/mastra-ai/mastra/pull/17386))
18
+
19
+ - Fixed Zod v3 schema conversion for CommonJS consumers. ([#21147](https://github.com/mastra-ai/mastra/pull/21147))
20
+
21
+ - Fixed tool execute-time input validation for Zod tools on Anthropic Claude 3.5 Haiku. The compat layer now skips string min/max checks that were removed from the model-facing JSON Schema, while preserving refinements, defaults, and other validation semantics. ([#19701](https://github.com/mastra-ai/mastra/pull/19701))
22
+
3
23
  ## 1.3.6-alpha.3
4
24
 
5
25
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -5189,36 +5189,48 @@ var OpenAISchemaCompatLayer = class extends SchemaCompatLayer {
5189
5189
  if (!schema.required?.includes(key)) {
5190
5190
  schema["x-optional"] = [...schema["x-optional"] || [], key];
5191
5191
  schema.required?.push(key);
5192
- if (prop.type) {
5193
- if (Array.isArray(prop.type)) {
5194
- const types = [...prop.type];
5195
- if (!types.includes("null")) types.push("null");
5196
- const propSchema = { ...prop };
5197
- delete propSchema.anyOf;
5198
- delete propSchema.type;
5199
- delete prop.type;
5200
- prop.anyOf = types.map((type) => type === "null" ? { type: "null" } : {
5201
- ...propSchema,
5202
- type
5203
- });
5204
- } else if (prop.type !== "null") {
5205
- const originalType = prop.type;
5206
- const propSchema = { ...prop };
5207
- delete propSchema.anyOf;
5208
- delete propSchema.type;
5209
- delete prop.type;
5210
- prop.anyOf = [{
5211
- ...propSchema,
5212
- type: originalType
5213
- }, { type: "null" }];
5214
- }
5192
+ if (Array.isArray(prop.type)) {
5193
+ const types = [...prop.type];
5194
+ if (!types.includes("null")) types.push("null");
5195
+ delete prop.anyOf;
5196
+ delete prop.type;
5197
+ if ("const" in prop) {
5198
+ const constValue = prop.const;
5199
+ prop.enum = !prop.enum || prop.enum.some((value) => JSON.stringify(value) === JSON.stringify(constValue)) ? [constValue, null] : [null];
5200
+ delete prop.const;
5201
+ } else if (prop.enum && !prop.enum.includes(null)) prop.enum = [...prop.enum, null];
5202
+ const objectKeywords = [
5203
+ "properties",
5204
+ "required",
5205
+ "additionalProperties",
5206
+ "x-optional"
5207
+ ];
5208
+ const arrayKeywords = ["items"];
5209
+ prop.anyOf = types.map((type) => {
5210
+ if (type === "null") return { type: "null" };
5211
+ const branch = { type };
5212
+ const keywords = type === "object" ? objectKeywords : type === "array" ? arrayKeywords : [];
5213
+ for (const keyword of keywords) if (keyword in prop) branch[keyword] = prop[keyword];
5214
+ return branch;
5215
+ });
5216
+ for (const keyword of [...objectKeywords, ...arrayKeywords]) delete prop[keyword];
5217
+ } else if (prop.type && prop.type !== "null") {
5218
+ const originalType = prop.type;
5219
+ const propSchema = { ...prop };
5220
+ delete propSchema.anyOf;
5221
+ delete propSchema.type;
5222
+ delete prop.type;
5223
+ prop.anyOf = [{
5224
+ ...propSchema,
5225
+ type: originalType
5226
+ }, { type: "null" }];
5215
5227
  }
5216
5228
  }
5217
5229
  }
5218
5230
  }
5219
5231
  }
5220
5232
  #traverse(value, schema) {
5221
- const resolved = this.#resolveAnyOf(schema);
5233
+ const resolved = this.#resolveAnyOf(schema, value);
5222
5234
  if ((isDateFormat(resolved) || resolved["x-date"] === true) && typeof value === "string") return new Date(value);
5223
5235
  if (resolved.type === "array" || Array.isArray(resolved.type) && resolved.type.includes("array")) {
5224
5236
  if (!Array.isArray(value)) return value;
@@ -5234,13 +5246,33 @@ var OpenAISchemaCompatLayer = class extends SchemaCompatLayer {
5234
5246
  return obj;
5235
5247
  }
5236
5248
  /**
5237
- * If schema has anyOf, return the first non-null variant for traversal.
5249
+ * If schema has anyOf, return the variant whose type matches the value's shape
5250
+ * (branches are type-specific), falling back to the first non-null variant.
5238
5251
  * Otherwise return the schema itself.
5239
5252
  */
5240
- #resolveAnyOf(schema) {
5253
+ #resolveAnyOf(schema, value) {
5241
5254
  if (Array.isArray(schema.anyOf)) {
5242
- const nonNull = schema.anyOf.find((s) => s.type !== "null");
5243
- if (nonNull) return nonNull;
5255
+ const nonNullVariants = schema.anyOf.filter((s) => s && s.type !== "null");
5256
+ const valueType = Array.isArray(value) ? "array" : value !== null && typeof value === "object" ? "object" : typeof value === "number" ? Number.isInteger(value) ? "integer" : "number" : typeof value === "string" || typeof value === "boolean" ? typeof value : void 0;
5257
+ if (valueType) {
5258
+ const hasType = (variant, type) => (Array.isArray(variant.type) ? variant.type : [variant.type]).includes(type);
5259
+ const exactMatch = nonNullVariants.find((variant) => hasType(variant, valueType));
5260
+ if (exactMatch) return {
5261
+ ...schema,
5262
+ ...exactMatch
5263
+ };
5264
+ if (valueType === "integer") {
5265
+ const numberMatch = nonNullVariants.find((variant) => hasType(variant, "number"));
5266
+ if (numberMatch) return {
5267
+ ...schema,
5268
+ ...numberMatch
5269
+ };
5270
+ }
5271
+ }
5272
+ if (nonNullVariants[0]) return {
5273
+ ...schema,
5274
+ ...nonNullVariants[0]
5275
+ };
5244
5276
  }
5245
5277
  return schema;
5246
5278
  }