fix(antigravity): update user-agent and fix schema validation
- Update hardcoded User-Agent `antigravity/1.11.5` to `1.15.8` in `@mariozechner/pi-ai` via pnpm patch to resolve "version not supported" error. - Add `sanitizeToolUseInput` helper to ensure `tool_use` payloads include the required `input` field. - Update `src/infra` usage fetcher User-Agent to match supported version.
This commit is contained in:
parent
6af205a13a
commit
f2958be6e0
@ -250,6 +250,9 @@
|
||||
"@sinclair/typebox": "0.34.47",
|
||||
"hono": "4.11.4",
|
||||
"tar": "7.5.4"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@mariozechner/pi-ai": "patches/@mariozechner__pi-ai.patch"
|
||||
}
|
||||
},
|
||||
"vitest": {
|
||||
|
||||
13
patches/@mariozechner__pi-ai.patch
Normal file
13
patches/@mariozechner__pi-ai.patch
Normal file
@ -0,0 +1,13 @@
|
||||
diff --git a/dist/providers/google-gemini-cli.js b/dist/providers/google-gemini-cli.js
|
||||
index 3741764906f41e87eda9259f567b1d5332551a63..41fc6a92fc4c1f6f503b92320248391013badfb0 100644
|
||||
--- a/dist/providers/google-gemini-cli.js
|
||||
+++ b/dist/providers/google-gemini-cli.js
|
||||
@@ -22,7 +22,7 @@ const GEMINI_CLI_HEADERS = {
|
||||
};
|
||||
// Headers for Antigravity (sandbox endpoint) - requires specific User-Agent
|
||||
const ANTIGRAVITY_HEADERS = {
|
||||
- "User-Agent": "antigravity/1.11.5 darwin/arm64",
|
||||
+ "User-Agent": "antigravity/1.15.8 darwin/arm64",
|
||||
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
||||
"Client-Metadata": JSON.stringify({
|
||||
ideType: "IDE_UNSPECIFIED",
|
||||
13
pnpm-lock.yaml
generated
13
pnpm-lock.yaml
generated
@ -9,6 +9,11 @@ overrides:
|
||||
hono: 4.11.4
|
||||
tar: 7.5.4
|
||||
|
||||
patchedDependencies:
|
||||
'@mariozechner/pi-ai':
|
||||
hash: a959dedd4f17a3a05dc9bfe16a6ad07d57d53abd992ee4526e451a80c4909c36
|
||||
path: patches/@mariozechner__pi-ai.patch
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@ -45,7 +50,7 @@ importers:
|
||||
version: 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai':
|
||||
specifier: 0.49.3
|
||||
version: 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
version: 0.49.3(patch_hash=a959dedd4f17a3a05dc9bfe16a6ad07d57d53abd992ee4526e451a80c4909c36)(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-coding-agent':
|
||||
specifier: 0.49.3
|
||||
version: 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
@ -6996,7 +7001,7 @@ snapshots:
|
||||
|
||||
'@mariozechner/pi-agent-core@0.49.3(ws@8.19.0)(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai': 0.49.3(patch_hash=a959dedd4f17a3a05dc9bfe16a6ad07d57d53abd992ee4526e451a80c4909c36)(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-tui': 0.49.3
|
||||
transitivePeerDependencies:
|
||||
- '@modelcontextprotocol/sdk'
|
||||
@ -7007,7 +7012,7 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@mariozechner/pi-ai@0.49.3(ws@8.19.0)(zod@4.3.6)':
|
||||
'@mariozechner/pi-ai@0.49.3(patch_hash=a959dedd4f17a3a05dc9bfe16a6ad07d57d53abd992ee4526e451a80c4909c36)(ws@8.19.0)(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@anthropic-ai/sdk': 0.71.2(zod@4.3.6)
|
||||
'@aws-sdk/client-bedrock-runtime': 3.972.0
|
||||
@ -7034,7 +7039,7 @@ snapshots:
|
||||
'@mariozechner/clipboard': 0.3.0
|
||||
'@mariozechner/jiti': 2.6.5
|
||||
'@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai': 0.49.3(patch_hash=a959dedd4f17a3a05dc9bfe16a6ad07d57d53abd992ee4526e451a80c4909c36)(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-tui': 0.49.3
|
||||
'@silvia-odwyer/photon-node': 0.3.4
|
||||
chalk: 5.6.2
|
||||
|
||||
@ -32,7 +32,11 @@ export {
|
||||
parseImageDimensionError,
|
||||
parseImageSizeError,
|
||||
} from "./pi-embedded-helpers/errors.js";
|
||||
export { isGoogleModelApi, sanitizeGoogleTurnOrdering } from "./pi-embedded-helpers/google.js";
|
||||
export {
|
||||
isGoogleModelApi,
|
||||
sanitizeGoogleTurnOrdering,
|
||||
sanitizeToolUseInput,
|
||||
} from "./pi-embedded-helpers/google.js";
|
||||
|
||||
export { downgradeOpenAIReasoningBlocks } from "./pi-embedded-helpers/openai.js";
|
||||
export {
|
||||
|
||||
88
src/agents/pi-embedded-helpers/google-tool-input.test.ts
Normal file
88
src/agents/pi-embedded-helpers/google-tool-input.test.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import { sanitizeToolUseInput } from "./google.js";
|
||||
|
||||
describe("sanitizeToolUseInput", () => {
|
||||
it("should add empty input to toolUse blocks missing it", () => {
|
||||
const messages: AgentMessage[] = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "toolUse",
|
||||
id: "tool-1",
|
||||
name: "readFile",
|
||||
// missing input
|
||||
} as any,
|
||||
{
|
||||
type: "text",
|
||||
text: "Searching...",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const sanitized = sanitizeToolUseInput(messages);
|
||||
const content = sanitized[0].content;
|
||||
const toolUse = (Array.isArray(content) ? content[0] : null) as any;
|
||||
|
||||
expect(toolUse).toBeDefined();
|
||||
expect(toolUse.input).toEqual({});
|
||||
});
|
||||
|
||||
it("should preserve existing input", () => {
|
||||
const messages: AgentMessage[] = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "toolUse",
|
||||
id: "tool-2",
|
||||
name: "writeFile",
|
||||
input: { path: "foo.txt" },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const sanitized = sanitizeToolUseInput(messages);
|
||||
const content = sanitized[0].content;
|
||||
const toolUse = (Array.isArray(content) ? content[0] : null) as any;
|
||||
|
||||
expect(toolUse.input).toEqual({ path: "foo.txt" });
|
||||
});
|
||||
|
||||
it("should handle non-array content gracefully", () => {
|
||||
const messages: AgentMessage[] = [
|
||||
{
|
||||
role: "user",
|
||||
content: "Hello",
|
||||
},
|
||||
];
|
||||
const sanitized = sanitizeToolUseInput(messages);
|
||||
expect(sanitized).toEqual(messages);
|
||||
});
|
||||
|
||||
it("should recurse through all messages", () => {
|
||||
const messages: AgentMessage[] = [
|
||||
{ role: "user", content: "Hi" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "toolUse", id: "1", name: "a" } as any, // fix me
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "toolUse", id: "2", name: "b", input: { foo: 1 } }, // leave me
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const sanitized = sanitizeToolUseInput(messages);
|
||||
|
||||
expect((sanitized[1].content as any[])[0].input).toEqual({});
|
||||
expect((sanitized[2].content as any[])[0].input).toEqual({ foo: 1 });
|
||||
});
|
||||
});
|
||||
@ -18,3 +18,25 @@ export function isAntigravityClaude(params: {
|
||||
}
|
||||
|
||||
export { sanitizeGoogleTurnOrdering };
|
||||
|
||||
export function sanitizeToolUseInput(messages: any[]): any[] {
|
||||
return messages.map((msg) => {
|
||||
if (!msg || typeof msg !== "object") return msg;
|
||||
if (msg.role !== "assistant" && msg.role !== "toolUse") return msg;
|
||||
if (!Array.isArray(msg.content)) return msg;
|
||||
|
||||
return {
|
||||
...msg,
|
||||
content: msg.content.map((block: any) => {
|
||||
if (!block || typeof block !== "object") return block;
|
||||
if (block.type === "toolUse" || block.type === "toolCall") {
|
||||
// If input is missing, add empty object
|
||||
if (!("input" in block) || block.input === undefined) {
|
||||
return { ...block, input: {} };
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
isGoogleModelApi,
|
||||
sanitizeGoogleTurnOrdering,
|
||||
sanitizeSessionMessagesImages,
|
||||
sanitizeToolUseInput,
|
||||
} from "../pi-embedded-helpers.js";
|
||||
import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
|
||||
import { log } from "./logger.js";
|
||||
@ -336,6 +337,9 @@ export async function sanitizeSessionHistory(params: {
|
||||
? sanitizeToolUseResultPairing(sanitizedThinking)
|
||||
: sanitizedThinking;
|
||||
|
||||
// Ensure toolUse blocks have input field (fixes schema validation "Field required")
|
||||
const sanitizedInputs = sanitizeToolUseInput(repairedTools);
|
||||
|
||||
const isOpenAIResponsesApi =
|
||||
params.modelApi === "openai-responses" || params.modelApi === "openai-codex-responses";
|
||||
const hasSnapshot = Boolean(params.provider || params.modelApi || params.modelId);
|
||||
@ -350,8 +354,8 @@ export async function sanitizeSessionHistory(params: {
|
||||
: false;
|
||||
const sanitizedOpenAI =
|
||||
isOpenAIResponsesApi && modelChanged
|
||||
? downgradeOpenAIReasoningBlocks(repairedTools)
|
||||
: repairedTools;
|
||||
? downgradeOpenAIReasoningBlocks(sanitizedInputs)
|
||||
: sanitizedInputs;
|
||||
|
||||
if (hasSnapshot && (!priorSnapshot || modelChanged)) {
|
||||
appendModelSnapshot(params.sessionManager, {
|
||||
|
||||
@ -174,7 +174,7 @@ export async function fetchAntigravityUsage(
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "antigravity",
|
||||
"User-Agent": "antigravity/1.15.8",
|
||||
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
||||
};
|
||||
|
||||
|
||||
70
verify_logic.mjs
Normal file
70
verify_logic.mjs
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
// Logic from src/agents/pi-embedded-helpers/google.ts
|
||||
function sanitizeToolUseInput(messages) {
|
||||
return messages.map((msg) => {
|
||||
if (!msg || typeof msg !== "object") return msg;
|
||||
if (msg.role !== "assistant" && msg.role !== "toolUse") return msg;
|
||||
if (!Array.isArray(msg.content)) return msg;
|
||||
|
||||
return {
|
||||
...msg,
|
||||
content: msg.content.map((block) => {
|
||||
if (!block || typeof block !== "object") return block;
|
||||
if (block.type === "toolUse" || block.type === "toolCall") {
|
||||
// If input is missing, add empty object
|
||||
if (!("input" in block) || block.input === undefined) {
|
||||
return { ...block, input: {} };
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Test cases
|
||||
function runTests() {
|
||||
console.log("Running tests...");
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, message) {
|
||||
if (condition) {
|
||||
console.log(`✅ PASS: ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(`❌ FAIL: ${message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Test 1: Add empty input
|
||||
const msgs1 = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "toolUse", id: "1", name: "f" }],
|
||||
},
|
||||
];
|
||||
const res1 = sanitizeToolUseInput(msgs1);
|
||||
assert(res1[0].content[0].input && Object.keys(res1[0].content[0].input).length === 0, "Should add empty input");
|
||||
|
||||
// Test 2: Preserve existing input
|
||||
const msgs2 = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "toolUse", id: "2", name: "f", input: { a: 1 } }],
|
||||
},
|
||||
];
|
||||
const res2 = sanitizeToolUseInput(msgs2);
|
||||
assert(res2[0].content[0].input.a === 1, "Should preserve existing input");
|
||||
|
||||
// Test 3: Ignore other roles
|
||||
const msgs3 = [{ role: "user", content: "hi" }];
|
||||
const res3 = sanitizeToolUseInput(msgs3);
|
||||
assert(res3[0] === msgs3[0], "Should ignore user messages");
|
||||
|
||||
console.log(`\nResults: ${passed} passed, ${failed} failed.`);
|
||||
if (failed > 0) process.exit(1);
|
||||
}
|
||||
|
||||
runTests();
|
||||
Loading…
Reference in New Issue
Block a user