Merge 9391d10434 into 09be5d45d5
This commit is contained in:
commit
d97c7ec1b1
@ -250,6 +250,9 @@
|
|||||||
"@sinclair/typebox": "0.34.47",
|
"@sinclair/typebox": "0.34.47",
|
||||||
"hono": "4.11.4",
|
"hono": "4.11.4",
|
||||||
"tar": "7.5.4"
|
"tar": "7.5.4"
|
||||||
|
},
|
||||||
|
"patchedDependencies": {
|
||||||
|
"@mariozechner/pi-ai": "patches/@mariozechner__pi-ai.patch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vitest": {
|
"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
|
hono: 4.11.4
|
||||||
tar: 7.5.4
|
tar: 7.5.4
|
||||||
|
|
||||||
|
patchedDependencies:
|
||||||
|
'@mariozechner/pi-ai':
|
||||||
|
hash: a959dedd4f17a3a05dc9bfe16a6ad07d57d53abd992ee4526e451a80c4909c36
|
||||||
|
path: patches/@mariozechner__pi-ai.patch
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
@ -45,7 +50,7 @@ importers:
|
|||||||
version: 0.49.3(ws@8.19.0)(zod@4.3.6)
|
version: 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||||
'@mariozechner/pi-ai':
|
'@mariozechner/pi-ai':
|
||||||
specifier: 0.49.3
|
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':
|
'@mariozechner/pi-coding-agent':
|
||||||
specifier: 0.49.3
|
specifier: 0.49.3
|
||||||
version: 0.49.3(ws@8.19.0)(zod@4.3.6)
|
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)':
|
'@mariozechner/pi-agent-core@0.49.3(ws@8.19.0)(zod@4.3.6)':
|
||||||
dependencies:
|
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
|
'@mariozechner/pi-tui': 0.49.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@modelcontextprotocol/sdk'
|
- '@modelcontextprotocol/sdk'
|
||||||
@ -7007,7 +7012,7 @@ snapshots:
|
|||||||
- ws
|
- ws
|
||||||
- zod
|
- 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:
|
dependencies:
|
||||||
'@anthropic-ai/sdk': 0.71.2(zod@4.3.6)
|
'@anthropic-ai/sdk': 0.71.2(zod@4.3.6)
|
||||||
'@aws-sdk/client-bedrock-runtime': 3.972.0
|
'@aws-sdk/client-bedrock-runtime': 3.972.0
|
||||||
@ -7034,7 +7039,7 @@ snapshots:
|
|||||||
'@mariozechner/clipboard': 0.3.0
|
'@mariozechner/clipboard': 0.3.0
|
||||||
'@mariozechner/jiti': 2.6.5
|
'@mariozechner/jiti': 2.6.5
|
||||||
'@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
'@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
|
'@mariozechner/pi-tui': 0.49.3
|
||||||
'@silvia-odwyer/photon-node': 0.3.4
|
'@silvia-odwyer/photon-node': 0.3.4
|
||||||
chalk: 5.6.2
|
chalk: 5.6.2
|
||||||
|
|||||||
@ -32,7 +32,11 @@ export {
|
|||||||
parseImageDimensionError,
|
parseImageDimensionError,
|
||||||
parseImageSizeError,
|
parseImageSizeError,
|
||||||
} from "./pi-embedded-helpers/errors.js";
|
} 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 { downgradeOpenAIReasoningBlocks } from "./pi-embedded-helpers/openai.js";
|
||||||
export {
|
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 { 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;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -224,12 +224,14 @@ describe("sanitizeSessionHistory (google thinking)", () => {
|
|||||||
{
|
{
|
||||||
type: "toolCall",
|
type: "toolCall",
|
||||||
id: "call_1",
|
id: "call_1",
|
||||||
|
input: {},
|
||||||
name: "read",
|
name: "read",
|
||||||
arguments: { path: "/tmp/foo" },
|
arguments: { path: "/tmp/foo" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "toolCall",
|
type: "toolCall",
|
||||||
id: "call_2",
|
id: "call_2",
|
||||||
|
input: {},
|
||||||
name: "read",
|
name: "read",
|
||||||
arguments: { path: "/tmp/bar" },
|
arguments: { path: "/tmp/bar" },
|
||||||
thoughtSignature: "c2ln",
|
thoughtSignature: "c2ln",
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
isGoogleModelApi,
|
isGoogleModelApi,
|
||||||
sanitizeGoogleTurnOrdering,
|
sanitizeGoogleTurnOrdering,
|
||||||
sanitizeSessionMessagesImages,
|
sanitizeSessionMessagesImages,
|
||||||
|
sanitizeToolUseInput,
|
||||||
} from "../pi-embedded-helpers.js";
|
} from "../pi-embedded-helpers.js";
|
||||||
import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
|
import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
|
||||||
import { log } from "./logger.js";
|
import { log } from "./logger.js";
|
||||||
@ -336,6 +337,9 @@ export async function sanitizeSessionHistory(params: {
|
|||||||
? sanitizeToolUseResultPairing(sanitizedThinking)
|
? sanitizeToolUseResultPairing(sanitizedThinking)
|
||||||
: sanitizedThinking;
|
: sanitizedThinking;
|
||||||
|
|
||||||
|
// Ensure toolUse blocks have input field (fixes schema validation "Field required")
|
||||||
|
const sanitizedInputs = sanitizeToolUseInput(repairedTools);
|
||||||
|
|
||||||
const isOpenAIResponsesApi =
|
const isOpenAIResponsesApi =
|
||||||
params.modelApi === "openai-responses" || params.modelApi === "openai-codex-responses";
|
params.modelApi === "openai-responses" || params.modelApi === "openai-codex-responses";
|
||||||
const hasSnapshot = Boolean(params.provider || params.modelApi || params.modelId);
|
const hasSnapshot = Boolean(params.provider || params.modelApi || params.modelId);
|
||||||
@ -350,8 +354,8 @@ export async function sanitizeSessionHistory(params: {
|
|||||||
: false;
|
: false;
|
||||||
const sanitizedOpenAI =
|
const sanitizedOpenAI =
|
||||||
isOpenAIResponsesApi && modelChanged
|
isOpenAIResponsesApi && modelChanged
|
||||||
? downgradeOpenAIReasoningBlocks(repairedTools)
|
? downgradeOpenAIReasoningBlocks(sanitizedInputs)
|
||||||
: repairedTools;
|
: sanitizedInputs;
|
||||||
|
|
||||||
if (hasSnapshot && (!priorSnapshot || modelChanged)) {
|
if (hasSnapshot && (!priorSnapshot || modelChanged)) {
|
||||||
appendModelSnapshot(params.sessionManager, {
|
appendModelSnapshot(params.sessionManager, {
|
||||||
|
|||||||
@ -174,7 +174,7 @@ export async function fetchAntigravityUsage(
|
|||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"User-Agent": "antigravity",
|
"User-Agent": "antigravity/1.15.8",
|
||||||
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
"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