From a11c1aa4ffff71c00ed8c20be367c5ecb1059b18 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 18 Nov 2025 17:49:12 +0100 Subject: [PATCH] Release v0.7.17 --- package-lock.json | 34 +++---- packages/agent/package.json | 6 +- packages/ai/package.json | 4 +- packages/ai/src/providers/google.ts | 9 +- .../ai/test/google-thought-signature.test.ts | 95 +++++++++++++++++++ packages/coding-agent/CHANGELOG.md | 3 + packages/coding-agent/package.json | 6 +- packages/pods/package.json | 4 +- packages/proxy/package.json | 2 +- packages/tui/package.json | 2 +- packages/web-ui/package.json | 6 +- 11 files changed, 138 insertions(+), 33 deletions(-) create mode 100644 packages/ai/test/google-thought-signature.test.ts diff --git a/package-lock.json b/package-lock.json index b203572f..a23c4d28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,7 +86,9 @@ } }, "node_modules/@google/genai": { - "version": "1.29.0", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.30.0.tgz", + "integrity": "sha512-3MRcgczBFbUat1wIlZoLJ0vCCfXgm7Qxjh59cZi2X08RgWLtm9hKOspzp7TOg1TV2e26/MLxR2GR5yD5GmBV2w==", "license": "Apache-2.0", "dependencies": { "google-auth-library": "^10.3.0", @@ -3193,11 +3195,11 @@ }, "packages/agent": { "name": "@mariozechner/pi-agent", - "version": "0.7.16", + "version": "0.7.17", "license": "MIT", "dependencies": { - "@mariozechner/pi-ai": "^0.7.15", - "@mariozechner/pi-tui": "^0.7.15" + "@mariozechner/pi-ai": "^0.7.16", + "@mariozechner/pi-tui": "^0.7.16" }, "devDependencies": { "@types/node": "^24.3.0", @@ -3223,11 +3225,11 @@ }, "packages/ai": { "name": "@mariozechner/pi-ai", - "version": "0.7.16", + "version": "0.7.17", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.61.0", - "@google/genai": "^1.17.0", + "@google/genai": "^1.30.0", "@sinclair/typebox": "^0.34.41", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", @@ -3270,11 +3272,11 @@ }, "packages/coding-agent": { "name": "@mariozechner/pi-coding-agent", - "version": "0.7.16", + "version": "0.7.17", "license": "MIT", "dependencies": { - "@mariozechner/pi-agent": "^0.7.15", - "@mariozechner/pi-ai": "^0.7.15", + "@mariozechner/pi-agent": "^0.7.16", + "@mariozechner/pi-ai": "^0.7.16", "chalk": "^5.5.0", "diff": "^8.0.2", "glob": "^11.0.3" @@ -3317,10 +3319,10 @@ }, "packages/pods": { "name": "@mariozechner/pi", - "version": "0.7.16", + "version": "0.7.17", "license": "MIT", "dependencies": { - "@mariozechner/pi-agent": "^0.7.15", + "@mariozechner/pi-agent": "^0.7.16", "chalk": "^5.5.0" }, "bin": { @@ -3343,7 +3345,7 @@ }, "packages/proxy": { "name": "@mariozechner/pi-proxy", - "version": "0.7.16", + "version": "0.7.17", "dependencies": { "@hono/node-server": "^1.14.0", "hono": "^4.6.16" @@ -3359,7 +3361,7 @@ }, "packages/tui": { "name": "@mariozechner/pi-tui", - "version": "0.7.16", + "version": "0.7.17", "license": "MIT", "dependencies": { "@types/mime-types": "^2.1.4", @@ -3398,12 +3400,12 @@ }, "packages/web-ui": { "name": "@mariozechner/pi-web-ui", - "version": "0.7.16", + "version": "0.7.17", "license": "MIT", "dependencies": { "@lmstudio/sdk": "^1.5.0", - "@mariozechner/pi-ai": "^0.7.15", - "@mariozechner/pi-tui": "^0.7.15", + "@mariozechner/pi-ai": "^0.7.16", + "@mariozechner/pi-tui": "^0.7.16", "docx-preview": "^0.3.7", "jszip": "^3.10.1", "lucide": "^0.544.0", diff --git a/packages/agent/package.json b/packages/agent/package.json index 3a1e2fd3..a3cbab51 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-agent", - "version": "0.7.16", + "version": "0.7.17", "description": "General-purpose agent with transport abstraction, state management, and attachment support", "type": "module", "main": "./dist/index.js", @@ -18,8 +18,8 @@ "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { - "@mariozechner/pi-ai": "^0.7.16", - "@mariozechner/pi-tui": "^0.7.16" + "@mariozechner/pi-ai": "^0.7.17", + "@mariozechner/pi-tui": "^0.7.17" }, "keywords": [ "ai", diff --git a/packages/ai/package.json b/packages/ai/package.json index cf232bb1..fa530df2 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-ai", - "version": "0.7.16", + "version": "0.7.17", "description": "Unified LLM API with automatic model discovery and provider configuration", "type": "module", "main": "./dist/index.js", @@ -21,7 +21,7 @@ }, "dependencies": { "@anthropic-ai/sdk": "^0.61.0", - "@google/genai": "^1.17.0", + "@google/genai": "^1.30.0", "@sinclair/typebox": "^0.34.41", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", diff --git a/packages/ai/src/providers/google.ts b/packages/ai/src/providers/google.ts index 9ceaac53..c83cbdbd 100644 --- a/packages/ai/src/providers/google.ts +++ b/packages/ai/src/providers/google.ts @@ -162,6 +162,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai"> = ( id: toolCallId, name: part.functionCall.name || "", arguments: part.functionCall.args as Record, + ...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }), }; // Validate tool arguments if tool definition is available @@ -361,13 +362,17 @@ function convertMessages(model: Model<"google-generative-ai">, context: Context) }; parts.push(thinkingPart); } else if (block.type === "toolCall") { - parts.push({ + const part: Part = { functionCall: { id: block.id, name: block.name, args: block.arguments, }, - }); + }; + if (block.thoughtSignature) { + part.thoughtSignature = block.thoughtSignature; + } + parts.push(part); } } diff --git a/packages/ai/test/google-thought-signature.test.ts b/packages/ai/test/google-thought-signature.test.ts new file mode 100644 index 00000000..6ce02396 --- /dev/null +++ b/packages/ai/test/google-thought-signature.test.ts @@ -0,0 +1,95 @@ +import { type Static, Type } from "@sinclair/typebox"; +import { describe, expect, it } from "vitest"; +import { getModel } from "../src/models.js"; +import { complete } from "../src/stream.js"; +import type { Context, Tool } from "../src/types.js"; + +// Simple read tool +const readSchema = Type.Object({ + path: Type.String({ description: "Path to the file to read" }), +}); + +type ReadParams = Static; + +const readTool: Tool = { + name: "read", + description: "Read contents of a file", + parameters: readSchema, +}; + +describe("Google Thought Signature Tests", () => { + describe.skipIf(!process.env.GEMINI_API_KEY)("Gemini 3 Pro - Text + Tool Call", () => { + const model = getModel("google", "gemini-3-pro-preview"); + + it("should handle text + tool call in same response and preserve thoughtSignature on subsequent requests", async () => { + // Create a prompt that encourages the model to generate text/thoughts AND a tool call + const context: Context = { + systemPrompt: "You are a helpful assistant. Think through your actions before using tools.", + messages: [], + tools: [readTool], + }; + + // Ask something that should trigger both explanation text and a tool call + context.messages.push({ + role: "user", + content: + "I need you to read the file packages/coding-agent/CHANGELOG.md. First explain what you're going to do, then use the read tool.", + timestamp: Date.now(), + }); + + // Get first response - should contain text + tool call + const firstResponse = await complete(model, context); + console.log("First response:", JSON.stringify(firstResponse, null, 2)); + + // Verify it has both text and tool call + const hasText = firstResponse.content.some((b) => b.type === "text"); + const hasToolCall = firstResponse.content.some((b) => b.type === "toolCall"); + + // If model didn't generate both, skip the test (model behavior varies) + if (!hasText || !hasToolCall) { + console.log("Model did not generate text + tool call in same response, skipping test"); + return; + } + + // Check if thoughtSignature was captured + const toolCall = firstResponse.content.find((b) => b.type === "toolCall"); + if (toolCall && toolCall.type === "toolCall") { + console.log("Tool call thoughtSignature:", toolCall.thoughtSignature); + } + + context.messages.push(firstResponse); + + // Provide tool result + const toolCallBlock = firstResponse.content.find((b) => b.type === "toolCall"); + if (!toolCallBlock || toolCallBlock.type !== "toolCall") { + throw new Error("Expected tool call"); + } + + context.messages.push({ + role: "toolResult", + toolCallId: toolCallBlock.id, + toolName: toolCallBlock.name, + content: [{ type: "text", text: "# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- Some fix" }], + isError: false, + timestamp: Date.now(), + }); + + // Send follow-up message - this will convert the assistant message (with text + tool call) + // back to Google's format. If thoughtSignature is missing, Google will error. + context.messages.push({ + role: "user", + content: "Great, now tell me what version is unreleased?", + timestamp: Date.now(), + }); + + // This is where the error would occur if thoughtSignature is not preserved + const secondResponse = await complete(model, context); + console.log("Second response:", JSON.stringify(secondResponse, null, 2)); + + // The request should succeed + expect(secondResponse.stopReason).not.toBe("error"); + expect(secondResponse.errorMessage).toBeUndefined(); + expect(secondResponse.content.length).toBeGreaterThan(0); + }, 30000); + }); +}); diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index ebbebb3d..c170a501 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,8 +2,11 @@ ## [Unreleased] +## [0.7.17] - 2025-11-18 + ### Added +- **New Model**: Added `gemini-3-pro-preview` to Google provider. - **OAuth Authentication**: Added `/login` and `/logout` commands for OAuth-based authentication with Claude Pro/Max subscriptions. Tokens are stored in `~/.pi/agent/oauth.json` with 0600 permissions and automatically refreshed when expired. OAuth tokens take priority over API keys for Anthropic models. ### Fixed diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index cd3139db..bfd7d8d9 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-coding-agent", - "version": "0.7.16", + "version": "0.7.17", "description": "Coding agent CLI with read, bash, edit, write tools and session management", "type": "module", "bin": { @@ -21,8 +21,8 @@ "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { - "@mariozechner/pi-agent": "^0.7.16", - "@mariozechner/pi-ai": "^0.7.16", + "@mariozechner/pi-agent": "^0.7.17", + "@mariozechner/pi-ai": "^0.7.17", "chalk": "^5.5.0", "diff": "^8.0.2", "glob": "^11.0.3" diff --git a/packages/pods/package.json b/packages/pods/package.json index df32f5f1..9a4ccdce 100644 --- a/packages/pods/package.json +++ b/packages/pods/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi", - "version": "0.7.16", + "version": "0.7.17", "description": "CLI tool for managing vLLM deployments on GPU pods", "type": "module", "bin": { @@ -34,7 +34,7 @@ "node": ">=20.0.0" }, "dependencies": { - "@mariozechner/pi-agent": "^0.7.16", + "@mariozechner/pi-agent": "^0.7.17", "chalk": "^5.5.0" }, "devDependencies": {} diff --git a/packages/proxy/package.json b/packages/proxy/package.json index ac8a5ea7..83cca1fb 100644 --- a/packages/proxy/package.json +++ b/packages/proxy/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-proxy", - "version": "0.7.16", + "version": "0.7.17", "type": "module", "description": "CORS and authentication proxy for pi-ai", "main": "dist/index.js", diff --git a/packages/tui/package.json b/packages/tui/package.json index 425cec42..cab23920 100644 --- a/packages/tui/package.json +++ b/packages/tui/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-tui", - "version": "0.7.16", + "version": "0.7.17", "description": "Terminal User Interface library with differential rendering for efficient text-based applications", "type": "module", "main": "dist/index.js", diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index 6e1facf2..771757d9 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-web-ui", - "version": "0.7.16", + "version": "0.7.17", "description": "Reusable web UI components for AI chat interfaces powered by @mariozechner/pi-ai", "type": "module", "main": "dist/index.js", @@ -18,8 +18,8 @@ }, "dependencies": { "@lmstudio/sdk": "^1.5.0", - "@mariozechner/pi-ai": "^0.7.16", - "@mariozechner/pi-tui": "^0.7.16", + "@mariozechner/pi-ai": "^0.7.17", + "@mariozechner/pi-tui": "^0.7.17", "docx-preview": "^0.3.7", "jszip": "^3.10.1", "lucide": "^0.544.0",