fix(agents): harden Cloud Code Assist compatibility
- Expand schema scrubber to strip additional constraint keywords rejected by Cloud Code Assist (examples, minLength, maxLength, minimum, maximum, multipleOf, pattern, format, minItems, maxItems, uniqueItems, minProperties, maxProperties) - Extend tool call ID sanitization to cover toolUse and toolCall block types (previously only functionCall was sanitized) - Update pi-tools test to include 'examples' in unsupported keywords Fixes 400 errors when using google-antigravity/claude-opus-4-5-thinking: - tools.N.custom.input_schema: JSON schema is invalid - messages.N.content.N.tool_use.id: String should match pattern
This commit is contained in:
committed by
Peter Steinberger
parent
ef08c3f038
commit
64babcac7a
@@ -146,18 +146,20 @@ export async function sanitizeSessionMessagesImages(
|
|||||||
// Also sanitize tool call IDs in assistant messages (function call blocks)
|
// Also sanitize tool call IDs in assistant messages (function call blocks)
|
||||||
const sanitizedContent = await Promise.all(
|
const sanitizedContent = await Promise.all(
|
||||||
filteredContent.map(async (block) => {
|
filteredContent.map(async (block) => {
|
||||||
if (
|
if (!block || typeof block !== "object") return block;
|
||||||
block &&
|
|
||||||
typeof block === "object" &&
|
const type = (block as { type?: unknown }).type;
|
||||||
(block as { type?: unknown }).type === "functionCall" &&
|
const id = (block as { id?: unknown }).id;
|
||||||
(block as { id?: unknown }).id
|
if (typeof id !== "string" || !id) return block;
|
||||||
) {
|
|
||||||
const functionBlock = block as { type: string; id: string };
|
// Cloud Code Assist tool blocks require ids matching ^[a-zA-Z0-9_-]+$.
|
||||||
|
if (type === "functionCall" || type === "toolUse" || type === "toolCall") {
|
||||||
return {
|
return {
|
||||||
...functionBlock,
|
...((block as unknown) as Record<string, unknown>),
|
||||||
id: sanitizeToolCallId(functionBlock.id),
|
id: sanitizeToolCallId(id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ describe("createClawdbotCodingTools", () => {
|
|||||||
"$ref",
|
"$ref",
|
||||||
"$defs",
|
"$defs",
|
||||||
"definitions",
|
"definitions",
|
||||||
|
"examples",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const findUnsupportedKeywords = (
|
const findUnsupportedKeywords = (
|
||||||
|
|||||||
@@ -10,6 +10,23 @@ const UNSUPPORTED_SCHEMA_KEYWORDS = new Set([
|
|||||||
"$ref",
|
"$ref",
|
||||||
"$defs",
|
"$defs",
|
||||||
"definitions",
|
"definitions",
|
||||||
|
// Non-standard (OpenAPI) keyword; Claude validators reject it.
|
||||||
|
"examples",
|
||||||
|
|
||||||
|
// Cloud Code Assist appears to validate tool schemas more strictly/quirkily than
|
||||||
|
// draft 2020-12 in practice; these constraints frequently trigger 400s.
|
||||||
|
"minLength",
|
||||||
|
"maxLength",
|
||||||
|
"minimum",
|
||||||
|
"maximum",
|
||||||
|
"multipleOf",
|
||||||
|
"pattern",
|
||||||
|
"format",
|
||||||
|
"minItems",
|
||||||
|
"maxItems",
|
||||||
|
"uniqueItems",
|
||||||
|
"minProperties",
|
||||||
|
"maxProperties",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if an anyOf/oneOf array contains only literal values that can be flattened.
|
// Check if an anyOf/oneOf array contains only literal values that can be flattened.
|
||||||
@@ -134,14 +151,14 @@ function cleanSchemaForGeminiWithDefs(
|
|||||||
const result: Record<string, unknown> = {
|
const result: Record<string, unknown> = {
|
||||||
...(cleaned as Record<string, unknown>),
|
...(cleaned as Record<string, unknown>),
|
||||||
};
|
};
|
||||||
for (const key of ["description", "title", "default", "examples"]) {
|
for (const key of ["description", "title", "default"]) {
|
||||||
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: Record<string, unknown> = {};
|
const result: Record<string, unknown> = {};
|
||||||
for (const key of ["description", "title", "default", "examples"]) {
|
for (const key of ["description", "title", "default"]) {
|
||||||
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -157,7 +174,7 @@ function cleanSchemaForGeminiWithDefs(
|
|||||||
type: flattened.type,
|
type: flattened.type,
|
||||||
enum: flattened.enum,
|
enum: flattened.enum,
|
||||||
};
|
};
|
||||||
for (const key of ["description", "title", "default", "examples"]) {
|
for (const key of ["description", "title", "default"]) {
|
||||||
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -171,7 +188,7 @@ function cleanSchemaForGeminiWithDefs(
|
|||||||
type: flattened.type,
|
type: flattened.type,
|
||||||
enum: flattened.enum,
|
enum: flattened.enum,
|
||||||
};
|
};
|
||||||
for (const key of ["description", "title", "default", "examples"]) {
|
for (const key of ["description", "title", "default"]) {
|
||||||
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
if (key in obj && obj[key] !== undefined) result[key] = obj[key];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Reference in New Issue
Block a user