diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 3382266df..b9ab4a034 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -44,7 +44,11 @@ import { } from "../skills.js"; import { filterBootstrapFilesForSession, loadWorkspaceBootstrapFiles } from "../workspace.js"; import { buildEmbeddedExtensionPaths } from "./extensions.js"; -import { logToolSchemasForGoogle, sanitizeSessionHistory } from "./google.js"; +import { + logToolSchemasForGoogle, + sanitizeSessionHistory, + sanitizeToolsForGoogle, +} from "./google.js"; import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "./history.js"; import { resolveGlobalLane, resolveSessionLane } from "./lanes.js"; import { log } from "./logger.js"; @@ -183,7 +187,7 @@ export async function compactEmbeddedPiSession(params: { warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`), }); const runAbortController = new AbortController(); - const tools = createClawdbotCodingTools({ + const toolsRaw = createClawdbotCodingTools({ exec: { ...resolveExecToolDefaults(params.config), elevated: params.bashElevated, @@ -200,6 +204,7 @@ export async function compactEmbeddedPiSession(params: { modelId, modelAuthMode: resolveModelAuthMode(model.provider, params.config), }); + const tools = sanitizeToolsForGoogle({ tools: toolsRaw, provider }); logToolSchemasForGoogle({ tools, provider }); const machineName = await getMachineDisplayName(); const runtimeChannel = normalizeMessageChannel( diff --git a/src/agents/pi-embedded-runner/google.test.ts b/src/agents/pi-embedded-runner/google.test.ts new file mode 100644 index 000000000..ed872ce57 --- /dev/null +++ b/src/agents/pi-embedded-runner/google.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; + +import type { AgentTool } from "@mariozechner/pi-agent-core"; +import { sanitizeToolsForGoogle } from "./google.js"; + +describe("sanitizeToolsForGoogle", () => { + it("strips unsupported schema keywords for Google providers", () => { + const tool = { + name: "test", + description: "test", + parameters: { + type: "object", + additionalProperties: false, + properties: { + foo: { + type: "string", + format: "uuid", + }, + }, + }, + execute: async () => ({ ok: true, content: [] }), + } as unknown as AgentTool; + + const [sanitized] = sanitizeToolsForGoogle({ + tools: [tool], + provider: "google-gemini-cli", + }); + + const params = sanitized.parameters as { + additionalProperties?: unknown; + properties?: Record; + }; + + expect(params.additionalProperties).toBeUndefined(); + expect(params.properties?.foo?.format).toBeUndefined(); + }); +}); diff --git a/src/agents/pi-embedded-runner/google.ts b/src/agents/pi-embedded-runner/google.ts index 3ed7589de..d8e170a08 100644 --- a/src/agents/pi-embedded-runner/google.ts +++ b/src/agents/pi-embedded-runner/google.ts @@ -1,4 +1,5 @@ import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core"; +import type { TSchema } from "@sinclair/typebox"; import type { SessionManager } from "@mariozechner/pi-coding-agent"; import { registerUnhandledRejectionHandler } from "../../infra/unhandled-rejections.js"; @@ -14,6 +15,7 @@ import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js"; import { log } from "./logger.js"; import { describeUnknownError } from "./utils.js"; import { isAntigravityClaude } from "../pi-embedded-helpers/google.js"; +import { cleanToolSchemaForGemini } from "../pi-tools.schema.js"; const GOOGLE_TURN_ORDERING_CUSTOM_TYPE = "google-turn-ordering-bootstrap"; const GOOGLE_SCHEMA_UNSUPPORTED_KEYWORDS = new Set([ @@ -80,17 +82,36 @@ function findUnsupportedSchemaKeywords(schema: unknown, path: string): string[] return violations; } +export function sanitizeToolsForGoogle( + params: { + tools: AgentTool[]; + provider: string; + }, +): AgentTool[] { + if (params.provider !== "google-antigravity" && params.provider !== "google-gemini-cli") { + return params.tools; + } + return params.tools.map((tool) => { + if (!tool.parameters || typeof tool.parameters !== "object") return tool; + return { + ...tool, + parameters: cleanToolSchemaForGemini(tool.parameters as Record) as TSchemaType, + }; + }); +} + export function logToolSchemasForGoogle(params: { tools: AgentTool[]; provider: string }) { if (params.provider !== "google-antigravity" && params.provider !== "google-gemini-cli") { return; } const toolNames = params.tools.map((tool, index) => `${index}:${tool.name}`); + const tools = sanitizeToolsForGoogle(params); log.info("google tool schema snapshot", { provider: params.provider, - toolCount: params.tools.length, + toolCount: tools.length, tools: toolNames, }); - for (const [index, tool] of params.tools.entries()) { + for (const [index, tool] of tools.entries()) { const violations = findUnsupportedSchemaKeywords(tool.parameters, `${tool.name}.parameters`); if (violations.length > 0) { log.warn("google tool schema has unsupported keywords", { diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index fa4a1efa2..065132828 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -45,7 +45,11 @@ import { filterBootstrapFilesForSession, loadWorkspaceBootstrapFiles } from "../ import { isAbortError } from "../abort.js"; import { buildEmbeddedExtensionPaths } from "../extensions.js"; import { applyExtraParamsToAgent } from "../extra-params.js"; -import { logToolSchemasForGoogle, sanitizeSessionHistory } from "../google.js"; +import { + logToolSchemasForGoogle, + sanitizeSessionHistory, + sanitizeToolsForGoogle, +} from "../google.js"; import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "../history.js"; import { log } from "../logger.js"; import { buildModelAliasLines } from "../model.js"; @@ -127,7 +131,7 @@ export async function runEmbeddedAttempt( const agentDir = params.agentDir ?? resolveClawdbotAgentDir(); - const tools = createClawdbotCodingTools({ + const toolsRaw = createClawdbotCodingTools({ exec: { ...resolveExecToolDefaults(params.config), elevated: params.bashElevated, @@ -148,6 +152,7 @@ export async function runEmbeddedAttempt( replyToMode: params.replyToMode, hasRepliedRef: params.hasRepliedRef, }); + const tools = sanitizeToolsForGoogle({ tools: toolsRaw, provider: params.provider }); logToolSchemasForGoogle({ tools, provider: params.provider }); const machineName = await getMachineDisplayName();