From fe9a18e4ea7feac3fe21b59951281cc3404cfdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Hou=C5=A1ka?= Date: Tue, 28 Jul 2026 22:31:01 +0200 Subject: [PATCH] fix(ai): update TypeBox nullable array validation Bump TypeBox to 1.3.7 so compiled validators guard array-specific keywords when null is accepted, and cover tool-argument validation with a regression test. Breaking for extensions: TypeBox 1.3 removes deprecated APIs including Type.Base, Type.Awaited, Type.Promise, Type.AsyncIterator, Type.Iterator, Type.Options, and Value.Mutate. Extensions using these through Pi's bundled TypeBox aliases must migrate to supported TypeBox APIs. Fixes #7003 --- package-lock.json | 12 ++++++------ packages/agent/package.json | 2 +- packages/ai/package.json | 2 +- packages/ai/test/validation.test.ts | 13 +++++++++++++ .../coding-agent/install-lock/package-lock.json | 12 ++++++------ packages/coding-agent/npm-shrinkwrap.json | 12 ++++++------ packages/coding-agent/package.json | 2 +- 7 files changed, 34 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2360d1c06e9..6f7d1358334 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4971,9 +4971,9 @@ "license": "Unlicense" }, "node_modules/typebox": { - "version": "1.1.38", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", - "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz", + "integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==", "license": "MIT" }, "node_modules/typescript": { @@ -5359,7 +5359,7 @@ "@earendil-works/pi-ai": "^0.82.1", "diff": "8.0.4", "ignore": "7.0.5", - "typebox": "1.1.38", + "typebox": "1.3.7", "yaml": "2.9.0" }, "devDependencies": { @@ -5459,7 +5459,7 @@ "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", - "typebox": "1.1.38" + "typebox": "1.3.7" }, "bin": { "pi-ai": "dist/cli.js" @@ -5510,7 +5510,7 @@ "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", - "typebox": "1.1.38", + "typebox": "1.3.7", "undici": "8.5.0", "yaml": "2.9.0" }, diff --git a/packages/agent/package.json b/packages/agent/package.json index b80ed795727..92d0949aae3 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -32,7 +32,7 @@ "@earendil-works/pi-ai": "^0.82.1", "diff": "8.0.4", "ignore": "7.0.5", - "typebox": "1.1.38", + "typebox": "1.3.7", "yaml": "2.9.0" }, "keywords": [ diff --git a/packages/ai/package.json b/packages/ai/package.json index e3272d0717e..c9c02236a8f 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -70,7 +70,7 @@ "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", - "typebox": "1.1.38" + "typebox": "1.3.7" }, "keywords": [ "ai", diff --git a/packages/ai/test/validation.test.ts b/packages/ai/test/validation.test.ts index dcbe9ebe9e6..25a8fe04f8c 100644 --- a/packages/ai/test/validation.test.ts +++ b/packages/ai/test/validation.test.ts @@ -1,4 +1,5 @@ import { Type } from "typebox"; +import { Compile } from "typebox/compile"; import { describe, expect, it } from "vitest"; import type { Tool, ToolCall } from "../src/types.ts"; import { validateToolArguments } from "../src/utils/validation.ts"; @@ -97,6 +98,18 @@ describe("validateToolArguments", () => { } }); + it("accepts null for nullable array schemas with items", () => { + const { tool, toolCall } = createToolCallWithPlainSchema( + { type: ["array", "null"], items: { type: "string" } } as Tool["parameters"], + null, + ); + // The CSP test above selects TypeBox's process-wide interpreted fallback, so exercise the generated validator explicitly. + const generatedCheck = new Function(Compile(tool.parameters).Code())() as (value: unknown) => boolean; + + expect(generatedCheck(toolCall.arguments)).toBe(true); + expect(validateToolArguments(tool, toolCall)).toEqual({ value: null }); + }); + it("rejects invalid coercions for serialized plain JSON schemas", () => { const failingCases: Array<{ schema: Tool["parameters"]; diff --git a/packages/coding-agent/install-lock/package-lock.json b/packages/coding-agent/install-lock/package-lock.json index bb8066052d5..5439cfd93ed 100644 --- a/packages/coding-agent/install-lock/package-lock.json +++ b/packages/coding-agent/install-lock/package-lock.json @@ -457,7 +457,7 @@ "@earendil-works/pi-ai": "^0.82.1", "diff": "8.0.4", "ignore": "7.0.5", - "typebox": "1.1.38", + "typebox": "1.3.7", "yaml": "2.9.0" }, "engines": { @@ -479,7 +479,7 @@ "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", - "typebox": "1.1.38" + "typebox": "1.3.7" }, "bin": { "pi-ai": "dist/cli.js" @@ -508,7 +508,7 @@ "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", - "typebox": "1.1.38", + "typebox": "1.3.7", "undici": "8.5.0", "yaml": "2.9.0" }, @@ -1718,9 +1718,9 @@ "license": "0BSD" }, "node_modules/typebox": { - "version": "1.1.38", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", - "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz", + "integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==", "license": "MIT" }, "node_modules/undici": { diff --git a/packages/coding-agent/npm-shrinkwrap.json b/packages/coding-agent/npm-shrinkwrap.json index 17b92c98daa..87820419571 100644 --- a/packages/coding-agent/npm-shrinkwrap.json +++ b/packages/coding-agent/npm-shrinkwrap.json @@ -24,7 +24,7 @@ "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", - "typebox": "1.1.38", + "typebox": "1.3.7", "undici": "8.5.0", "yaml": "2.9.0" }, @@ -481,7 +481,7 @@ "@earendil-works/pi-ai": "^0.82.1", "diff": "8.0.4", "ignore": "7.0.5", - "typebox": "1.1.38", + "typebox": "1.3.7", "yaml": "2.9.0" }, "engines": { @@ -503,7 +503,7 @@ "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", - "typebox": "1.1.38" + "typebox": "1.3.7" }, "bin": { "pi-ai": "dist/cli.js" @@ -1708,9 +1708,9 @@ "license": "0BSD" }, "node_modules/typebox": { - "version": "1.1.38", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", - "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz", + "integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==", "license": "MIT" }, "node_modules/undici": { diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index 3ba3d1f9582..805d29956fd 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -54,7 +54,7 @@ "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", - "typebox": "1.1.38", + "typebox": "1.3.7", "undici": "8.5.0", "yaml": "2.9.0" },