fix(ai): update TypeBox nullable array validation (#7243)

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
This commit is contained in:
Petr Houška 2026-07-29 10:31:07 +02:00 • committed by GitHub
parent fb4ecd633e
commit f9476a61e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 34 additions and 21 deletions

12
package-lock.json generated
View file

@ -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"
},

View file

@ -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": [

View file

@ -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",

View file

@ -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"];

View file

@ -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": {

View file

@ -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": {

View file

@ -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"
},