diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index f3c5d5992..6400420c6 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixed +- Fixed Cloud Code Assist (Gemini / Antigravity) rejecting tool schemas with `Invalid JSON payload received. Unknown name "propertyNames"` (HTTP 400) when a tool exposed a property literally named `properties` (e.g. the Resend MCP `create_contact` tool). The schema normalizer's `insideProperties` flag was re-asserted when descending into such a property's value schema, so Google-unsupported keywords (`propertyNames`, `additionalProperties`, …) nested inside it were never stripped. The flag is now only set when entering a real `properties` map from a schema node, not from within another `properties` map. - Fixed local/self-hosted providers leaking machine-specific endpoints into the bundled `models.json`. A `generate-models` run on a machine with a LiteLLM proxy baked 1202 `litellm` models pinned to `http://localhost:4000/v1` into the committed catalog. `litellm` (and `lm-studio`) now join `ollama`/`vllm` in the generator's discovery-only exclusion set, so local providers are never fetched during generation nor written to `models.json` — they are discovered dynamically at runtime instead. LiteLLM model discovery now enriches metadata against models.dev (the same reference source the other gateway providers use) rather than a bundled reference map. Added a regression test pinning the invariant (no local provider blocks, no loopback/private-network `baseUrl`s in the bundled catalog). ## [15.8.2] - 2026-06-03 diff --git a/packages/ai/src/utils/schema/normalize.ts b/packages/ai/src/utils/schema/normalize.ts index 90311e8ac..1b21afd67 100644 --- a/packages/ai/src/utils/schema/normalize.ts +++ b/packages/ai/src/utils/schema/normalize.ts @@ -280,7 +280,7 @@ function normalizeSchemaNode(value: unknown, options: NormalizeSchemaWalkOptions if (options.stripNullableKeyword && key === "nullable") continue; result[key] = normalizeSchemaNode(entry, { ...options, - insideProperties: key === "properties", + insideProperties: !options.insideProperties && key === "properties", }); } applyDescriptionSpill(result, spill, options); @@ -302,7 +302,7 @@ function normalizeSchemaNode(value: unknown, options: NormalizeSchemaWalkOptions } result[key] = normalizeSchemaNode(entry, { ...options, - insideProperties: key === "properties", + insideProperties: !options.insideProperties && key === "properties", }); } diff --git a/packages/ai/test/google-tool-schema.test.ts b/packages/ai/test/google-tool-schema.test.ts index 54cb3709f..69c425893 100644 --- a/packages/ai/test/google-tool-schema.test.ts +++ b/packages/ai/test/google-tool-schema.test.ts @@ -70,6 +70,37 @@ describe("Cloud Code Assist Claude tool schema conversion", () => { }); }); + it("strips schema keywords inside a property literally named properties", () => { + // Regression: the Resend MCP `create_contact` tool exposes a property + // literally named `properties`. The walker must not treat that property's + // value schema as a properties map — otherwise nested `propertyNames` / + // `additionalProperties` keywords leak to the CCA wire and get rejected + // with `Unknown name "propertyNames"` (HTTP 400). + const schema = { + type: "object", + properties: { + properties: { + description: "Custom property key-value pairs", + type: "object", + propertyNames: { type: "string" }, + additionalProperties: { type: "string" }, + properties: {}, + }, + }, + } as unknown; + + expect(normalizeSchemaForCCA(schema)).toEqual({ + type: "object", + properties: { + properties: { + description: "Custom property key-value pairs", + type: "object", + properties: {}, + }, + }, + }); + }); + it("uses sanitized parameters for claude models with deterministic output", () => { const parameters = { type: "object",