From 870bfa94ed1dc991bae685ee1c4e528af14d72b0 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 22 Jan 2026 23:51:48 +0000 Subject: [PATCH] fix: skip tool id sanitization for openai responses --- CHANGELOG.md | 1 + ...ed-runner.google-sanitize-thinking.test.ts | 14 ++++++++----- ...ed-runner.sanitize-session-history.test.ts | 21 ++++++++++++++++++- src/agents/pi-embedded-runner/google.ts | 8 +------ 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 897fbd7fb..20aacf256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Docs: https://docs.clawd.bot - Auto-reply: only report a model switch when session state is available. (#1465) Thanks @robbyczgw-cla. - Control UI: resolve local avatar URLs with basePath across injection + identity RPC. (#1457) Thanks @dlauer. - Agents: surface concrete API error details instead of generic AI service errors. +- Agents: avoid sanitizing tool call IDs for OpenAI responses to preserve Pi pairing. - Docs: fix gog auth services example to include docs scope. (#1454) Thanks @zerone0x. - macOS: prefer linked channels in gateway summary to avoid false “not linked” status. diff --git a/src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts b/src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts index 5e58e6d6c..ed4b52940 100644 --- a/src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts +++ b/src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts @@ -320,7 +320,7 @@ describe("sanitizeSessionHistory (google thinking)", () => { expect(assistant.content?.map((block) => block.type)).toEqual(["thinking"]); }); - it("sanitizes tool call ids for OpenAI-compatible APIs", async () => { + it("sanitizes tool call ids for Google APIs", async () => { const sessionManager = SessionManager.inMemory(); const longId = `call_${"a".repeat(60)}`; const input = [ @@ -338,17 +338,21 @@ describe("sanitizeSessionHistory (google thinking)", () => { const out = await sanitizeSessionHistory({ messages: input, - modelApi: "openai-responses", + modelApi: "google-antigravity", sessionManager, - sessionId: "session:openai", + sessionId: "session:google", }); - const assistant = out[0] as Extract; + const assistant = out.find( + (msg) => (msg as { role?: unknown }).role === "assistant", + ) as Extract; const toolCall = assistant.content?.[0] as { id?: string }; expect(toolCall.id).toBeDefined(); expect(toolCall.id?.length).toBeLessThanOrEqual(40); - const toolResult = out[1] as Extract; + const toolResult = out.find( + (msg) => (msg as { role?: unknown }).role === "toolResult", + ) as Extract; expect(toolResult.toolCallId).toBe(toolCall.id); }); }); diff --git a/src/agents/pi-embedded-runner.sanitize-session-history.test.ts b/src/agents/pi-embedded-runner.sanitize-session-history.test.ts index c7a9b3d05..7d812985b 100644 --- a/src/agents/pi-embedded-runner.sanitize-session-history.test.ts +++ b/src/agents/pi-embedded-runner.sanitize-session-history.test.ts @@ -73,7 +73,7 @@ describe("sanitizeSessionHistory", () => { ); }); - it("does not sanitize tool call ids for non-Google, non-OpenAI APIs", async () => { + it("does not sanitize tool call ids for non-Google APIs", async () => { vi.mocked(helpers.isGoogleModelApi).mockReturnValue(false); await sanitizeSessionHistory({ @@ -92,6 +92,25 @@ describe("sanitizeSessionHistory", () => { ); }); + it("does not sanitize tool call ids for openai-responses", async () => { + vi.mocked(helpers.isGoogleModelApi).mockReturnValue(false); + + await sanitizeSessionHistory({ + messages: mockMessages, + modelApi: "openai-responses", + provider: "openai", + sessionManager: mockSessionManager, + sessionId: "test-session", + }); + + expect(helpers.isGoogleModelApi).toHaveBeenCalledWith("openai-responses"); + expect(helpers.sanitizeSessionMessagesImages).toHaveBeenCalledWith( + mockMessages, + "session:history", + expect.objectContaining({ sanitizeToolCallIds: false }), + ); + }); + it("keeps reasoning-only assistant messages for openai-responses", async () => { vi.mocked(helpers.isGoogleModelApi).mockReturnValue(false); diff --git a/src/agents/pi-embedded-runner/google.ts b/src/agents/pi-embedded-runner/google.ts index 0ec334192..41f776568 100644 --- a/src/agents/pi-embedded-runner/google.ts +++ b/src/agents/pi-embedded-runner/google.ts @@ -40,12 +40,6 @@ const GOOGLE_SCHEMA_UNSUPPORTED_KEYWORDS = new Set([ "minProperties", "maxProperties", ]); -const OPENAI_TOOL_CALL_ID_APIS = new Set([ - "openai", - "openai-completions", - "openai-responses", - "openai-codex-responses", -]); const MISTRAL_MODEL_HINTS = [ "mistral", "mixtral", @@ -67,7 +61,7 @@ function isValidAntigravitySignature(value: unknown): value is string { function shouldSanitizeToolCallIds(modelApi?: string | null): boolean { if (!modelApi) return false; - return isGoogleModelApi(modelApi) || OPENAI_TOOL_CALL_ID_APIS.has(modelApi); + return isGoogleModelApi(modelApi); } function isMistralModel(params: { provider?: string | null; modelId?: string | null }): boolean {