From 35a3962cf0ac2586c51d510b82fd39679d96135d Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:51:17 -0300 Subject: [PATCH] fix(gemini): preserve `pattern` in antigravity tool schema sanitizer (#4651) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrated into release/v3.8.36 — port (rebuilt from stale base; defining commit cherry-picked clean over release tip, release-green validated) --- open-sse/translator/helpers/geminiHelper.ts | 5 +- ...mini-antigravity-pattern-preserved.test.ts | 54 +++++++++++++++++++ ...tor-gemini-schema-pattern-property.test.ts | 13 ++--- 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 tests/unit/gemini-antigravity-pattern-preserved.test.ts diff --git a/open-sse/translator/helpers/geminiHelper.ts b/open-sse/translator/helpers/geminiHelper.ts index cb7c3d8fe8..d784dbf888 100644 --- a/open-sse/translator/helpers/geminiHelper.ts +++ b/open-sse/translator/helpers/geminiHelper.ts @@ -10,7 +10,10 @@ export const GEMINI_UNSUPPORTED_SCHEMA_KEYS = new Set([ "maxLength", "exclusiveMinimum", "exclusiveMaximum", - "pattern", + // NOTE: `pattern` is intentionally NOT in this set. Antigravity (Gemini-derived + // surface) accepts `pattern` on string constraints, and glob/grep/file-search + // tools depend on it to express their argument regex. Removing it produced + // upstream 400s and wrong-tool semantics (decolua/9router#1368). "minItems", "maxItems", "format", diff --git a/tests/unit/gemini-antigravity-pattern-preserved.test.ts b/tests/unit/gemini-antigravity-pattern-preserved.test.ts new file mode 100644 index 0000000000..541ab428d4 --- /dev/null +++ b/tests/unit/gemini-antigravity-pattern-preserved.test.ts @@ -0,0 +1,54 @@ +// Regression test: `pattern` must be preserved in antigravity tool schemas. +// +// Upstream: decolua/9router @ f6c2f7ca / Fixes decolua/9router#1368. +// +// Tools such as glob/grep/file-search rely on a `pattern` constraint on +// string parameters. Antigravity (Gemini-derived) DOES accept `pattern`, +// so removing it for that surface drops critical tool semantics and +// produces upstream 400s / wrong-tool-call behavior. +// +// Two assertions, mirroring how the field is consumed downstream: +// 1) `pattern` must NOT live in the unsupported keyword set used by the +// antigravity schema cleaner. +// 2) End-to-end, `cleanJSONSchemaForAntigravity` must preserve a `pattern` +// constraint placed on a string property (typical glob/grep tool). + +import test from "node:test"; +import assert from "node:assert/strict"; + +import { + GEMINI_UNSUPPORTED_SCHEMA_KEYS, + cleanJSONSchemaForAntigravity, +} from "../../open-sse/translator/helpers/geminiHelper.ts"; + +test("GEMINI_UNSUPPORTED_SCHEMA_KEYS does not strip `pattern` for antigravity", () => { + assert.equal( + GEMINI_UNSUPPORTED_SCHEMA_KEYS.has("pattern"), + false, + "`pattern` is supported by antigravity and must be preserved on tool schemas" + ); +}); + +test("cleanJSONSchemaForAntigravity preserves `pattern` on glob/grep-style tool schema", () => { + const schema = { + type: "object", + properties: { + glob: { + type: "string", + description: "Glob pattern to match files", + pattern: "^[A-Za-z0-9_\\-/*.]+$", + }, + }, + required: ["glob"], + }; + + const cleaned = cleanJSONSchemaForAntigravity(schema) as { + properties: { glob: { pattern?: string } }; + }; + + assert.equal( + cleaned.properties.glob.pattern, + "^[A-Za-z0-9_\\-/*.]+$", + "`pattern` constraint on a string property must survive antigravity cleaning" + ); +}); diff --git a/tests/unit/translator-gemini-schema-pattern-property.test.ts b/tests/unit/translator-gemini-schema-pattern-property.test.ts index 85be361ce1..b5f9a869ac 100644 --- a/tests/unit/translator-gemini-schema-pattern-property.test.ts +++ b/tests/unit/translator-gemini-schema-pattern-property.test.ts @@ -41,9 +41,10 @@ test("#1368: a property named 'pattern' survives Gemini schema sanitization", () ); }); -test("#1368: a string-level `pattern` CONSTRAINT is still stripped", () => { - // When `pattern` is an actual validation constraint on a string schema node, - // Gemini does not support it, so it must still be removed. +test("#1368: a string-level `pattern` CONSTRAINT is preserved for antigravity", () => { + // Antigravity (Gemini-derived) DOES accept `pattern` on string constraints. + // Stripping it broke glob/grep/file-search tools that express their argument + // regex via `pattern`. The sanitizer must keep it. (Ported from 9router @ f6c2f7ca.) const schema = { type: "object", properties: { @@ -58,8 +59,8 @@ test("#1368: a string-level `pattern` CONSTRAINT is still stripped", () => { assert.ok(cleaned.properties.code, "the `code` property itself survives"); assert.equal( - Object.prototype.hasOwnProperty.call(cleaned.properties.code, "pattern"), - false, - "the string `pattern` constraint must be stripped" + (cleaned.properties.code as { pattern?: string }).pattern, + "^[A-Z]{3}$", + "the string `pattern` constraint must be preserved for antigravity" ); });