fix: add missing arguments/input field to tool calls
When models call tools without parameters, they sometimes omit the
arguments/input field entirely. Anthropic/Cloud Code Assist APIs require
this field to be present (even if empty {}), causing the error:
messages.X.content.Y.tool_use.input: Field required
This corrupts the session history and prevents further messages.
Fix: Add sanitizeToolCallArguments() in images.ts that ensures all
toolCall, toolUse, and functionCall blocks have the required field.
Closes #issue-number
This commit is contained in:
parent
9688454a30
commit
bc96c27942
@ -117,4 +117,48 @@ describe("sanitizeSessionMessagesImages", () => {
|
||||
expect(out[0]?.role).toBe("user");
|
||||
expect(out[1]?.role).toBe("toolResult");
|
||||
});
|
||||
|
||||
it("adds missing arguments field to tool calls", async () => {
|
||||
const input = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "toolCall", id: "call_1", name: "session_status" }, // Missing arguments!
|
||||
],
|
||||
},
|
||||
] satisfies AgentMessage[];
|
||||
|
||||
const out = await sanitizeSessionMessagesImages(input, "test");
|
||||
|
||||
expect(out).toHaveLength(1);
|
||||
const content = (out[0] as { content?: unknown }).content as Array<{
|
||||
type?: string;
|
||||
arguments?: unknown;
|
||||
}>;
|
||||
expect(content).toHaveLength(1);
|
||||
expect(content[0]?.type).toBe("toolCall");
|
||||
expect(content[0]?.arguments).toEqual({});
|
||||
});
|
||||
|
||||
it("adds missing input field to toolUse blocks", async () => {
|
||||
const input = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "toolUse", id: "call_1", name: "session_status" }, // Missing input!
|
||||
],
|
||||
},
|
||||
] satisfies AgentMessage[];
|
||||
|
||||
const out = await sanitizeSessionMessagesImages(input, "test");
|
||||
|
||||
expect(out).toHaveLength(1);
|
||||
const content = (out[0] as { content?: unknown }).content as Array<{
|
||||
type?: string;
|
||||
input?: unknown;
|
||||
}>;
|
||||
expect(content).toHaveLength(1);
|
||||
expect(content[0]?.type).toBe("toolUse");
|
||||
expect(content[0]?.input).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
@ -21,6 +21,25 @@ export function isEmptyAssistantMessageContent(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures tool call blocks have the required arguments/input field.
|
||||
* Some models omit this field when calling tools with no parameters,
|
||||
* but Anthropic/Cloud Code Assist APIs require it (even if empty `{}`).
|
||||
*/
|
||||
function sanitizeToolCallArguments(content: unknown[]): unknown[] {
|
||||
return content.map((block) => {
|
||||
if (!block || typeof block !== "object") return block;
|
||||
const rec = block as { type?: unknown; arguments?: unknown; input?: unknown };
|
||||
if (rec.type === "toolCall" && rec.arguments === undefined) {
|
||||
return { ...rec, arguments: {} };
|
||||
}
|
||||
if ((rec.type === "toolUse" || rec.type === "functionCall") && rec.input === undefined) {
|
||||
return { ...rec, input: {} };
|
||||
}
|
||||
return block;
|
||||
});
|
||||
}
|
||||
|
||||
export async function sanitizeSessionMessagesImages(
|
||||
messages: AgentMessage[],
|
||||
label: string,
|
||||
@ -97,17 +116,19 @@ export async function sanitizeSessionMessagesImages(
|
||||
}
|
||||
const content = assistantMsg.content;
|
||||
if (Array.isArray(content)) {
|
||||
// Ensure all tool calls have required arguments/input field
|
||||
const sanitizedContent = sanitizeToolCallArguments(content);
|
||||
if (!allowNonImageSanitization) {
|
||||
const nextContent = (await sanitizeContentBlocksImages(
|
||||
content as unknown as ContentBlock[],
|
||||
sanitizedContent as unknown as ContentBlock[],
|
||||
label,
|
||||
)) as unknown as typeof assistantMsg.content;
|
||||
out.push({ ...assistantMsg, content: nextContent });
|
||||
continue;
|
||||
}
|
||||
const strippedContent = options?.preserveSignatures
|
||||
? content // Keep signatures for Antigravity Claude
|
||||
: stripThoughtSignatures(content, options?.sanitizeThoughtSignatures); // Strip for Gemini
|
||||
? sanitizedContent // Keep signatures for Antigravity Claude
|
||||
: stripThoughtSignatures(sanitizedContent, options?.sanitizeThoughtSignatures); // Strip for Gemini
|
||||
|
||||
const filteredContent = strippedContent.filter((block) => {
|
||||
if (!block || typeof block !== "object") return true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user