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:
Mimi 2026-01-29 00:13:01 +08:00
parent 9688454a30
commit bc96c27942
2 changed files with 68 additions and 3 deletions

View File

@ -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({});
});
});

View File

@ -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;