Upgrade to Zod v4 (#2605)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Dax
2025-09-15 03:12:07 -04:00
committed by GitHub
parent 89d820b1c4
commit c1b4e1f19d
75 changed files with 1106 additions and 1304 deletions

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { exec } from "child_process"
import { Tool } from "./tool"

View File

@@ -3,7 +3,7 @@
// https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/utils/editCorrector.ts
// https://github.com/cline/cline/blob/main/evals/diff-edits/diff-apply/diff-06-26-25.ts
import { z } from "zod"
import z from "zod/v4"
import * as path from "path"
import { Tool } from "./tool"
import { LSP } from "../lsp"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import path from "path"
import { Tool } from "./tool"
import DESCRIPTION from "./glob.txt"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
import { Ripgrep } from "../file/ripgrep"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
export const InvalidTool = Tool.define("invalid", {

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
import * as path from "path"
import DESCRIPTION from "./ls.txt"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
import path from "path"
import { LSP } from "../lsp"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
import path from "path"
import { LSP } from "../lsp"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
import { EditTool } from "./edit"
import DESCRIPTION from "./multiedit.txt"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import * as path from "path"
import * as fs from "fs/promises"
import { Tool } from "./tool"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import * as fs from "fs"
import * as path from "path"
import { Tool } from "./tool"

View File

@@ -1,4 +1,4 @@
import z from "zod"
import z from "zod/v4"
import { BashTool } from "./bash"
import { EditTool } from "./edit"
import { GlobTool } from "./glob"
@@ -124,35 +124,13 @@ export namespace ToolRegistry {
return allTools().map((t) => t.id)
}
export async function tools(providerID: string, _modelID: string) {
export async function tools(_providerID: string, _modelID: string) {
const result = await Promise.all(
allTools().map(async (t) => ({
id: t.id,
...(await t.init()),
})),
)
if (providerID === "openai") {
return result.map((t) => ({
...t,
parameters: optionalToNullable(t.parameters as unknown as z.ZodTypeAny),
}))
}
if (providerID === "azure") {
return result.map((t) => ({
...t,
parameters: optionalToNullable(t.parameters as unknown as z.ZodTypeAny),
}))
}
if (providerID === "google") {
return result.map((t) => ({
...t,
parameters: sanitizeGeminiParameters(t.parameters as unknown as z.ZodTypeAny),
}))
}
return result
}
@@ -178,93 +156,4 @@ export namespace ToolRegistry {
return result
}
function sanitizeGeminiParameters(schema: z.ZodTypeAny, visited = new Set()): z.ZodTypeAny {
if (!schema || visited.has(schema)) {
return schema
}
visited.add(schema)
if (schema instanceof z.ZodDefault) {
const innerSchema = schema.removeDefault()
// Handle Gemini's incompatibility with `default` on `anyOf` (unions).
if (innerSchema instanceof z.ZodUnion) {
// The schema was `z.union(...).default(...)`, which is not allowed.
// We strip the default and return the sanitized union.
return sanitizeGeminiParameters(innerSchema, visited)
}
// Otherwise, the default is on a regular type, which is allowed.
// We recurse on the inner type and then re-apply the default.
return sanitizeGeminiParameters(innerSchema, visited).default(schema._def.defaultValue())
}
if (schema instanceof z.ZodOptional) {
return z.optional(sanitizeGeminiParameters(schema.unwrap(), visited))
}
if (schema instanceof z.ZodObject) {
const newShape: Record<string, z.ZodTypeAny> = {}
for (const [key, value] of Object.entries(schema.shape)) {
newShape[key] = sanitizeGeminiParameters(value as z.ZodTypeAny, visited)
}
return z.object(newShape)
}
if (schema instanceof z.ZodArray) {
return z.array(sanitizeGeminiParameters(schema.element, visited))
}
if (schema instanceof z.ZodUnion) {
// This schema corresponds to `anyOf` in JSON Schema.
// We recursively sanitize each option in the union.
const sanitizedOptions = schema.options.map((option: z.ZodTypeAny) => sanitizeGeminiParameters(option, visited))
return z.union(sanitizedOptions as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]])
}
if (schema instanceof z.ZodString) {
const newSchema = z.string({ description: schema.description })
const safeChecks = ["min", "max", "length", "regex", "startsWith", "endsWith", "includes", "trim"]
// rome-ignore lint/suspicious/noExplicitAny: <explanation>
;(newSchema._def as any).checks = (schema._def as z.ZodStringDef).checks.filter((check) =>
safeChecks.includes(check.kind),
)
return newSchema
}
return schema
}
function optionalToNullable(schema: z.ZodTypeAny): z.ZodTypeAny {
if (schema instanceof z.ZodObject) {
const shape = schema.shape
const newShape: Record<string, z.ZodTypeAny> = {}
for (const [key, value] of Object.entries(shape)) {
const zodValue = value as z.ZodTypeAny
if (zodValue instanceof z.ZodOptional) {
newShape[key] = zodValue.unwrap().nullable()
} else {
newShape[key] = optionalToNullable(zodValue)
}
}
return z.object(newShape)
}
if (schema instanceof z.ZodArray) {
return z.array(optionalToNullable(schema.element))
}
if (schema instanceof z.ZodUnion) {
return z.union(
schema.options.map((option: z.ZodTypeAny) => optionalToNullable(option)) as [
z.ZodTypeAny,
z.ZodTypeAny,
...z.ZodTypeAny[],
],
)
}
return schema
}
}

View File

@@ -1,6 +1,6 @@
import { Tool } from "./tool"
import DESCRIPTION from "./task.txt"
import { z } from "zod"
import z from "zod/v4"
import { Session } from "../session"
import { Bus } from "../bus"
import { MessageV2 } from "../session/message-v2"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
import DESCRIPTION_WRITE from "./todowrite.txt"
import { Instance } from "../project/instance"

View File

@@ -1,4 +1,4 @@
import type { StandardSchemaV1 } from "@standard-schema/spec"
import z from "zod/v4"
export namespace Tool {
interface Metadata {
@@ -13,13 +13,13 @@ export namespace Tool {
extra?: { [key: string]: any }
metadata(input: { title?: string; metadata?: M }): void
}
export interface Info<Parameters extends StandardSchemaV1 = StandardSchemaV1, M extends Metadata = Metadata> {
export interface Info<Parameters extends z.ZodType = z.ZodType, M extends Metadata = Metadata> {
id: string
init: () => Promise<{
description: string
parameters: Parameters
execute(
args: StandardSchemaV1.InferOutput<Parameters>,
args: z.infer<Parameters>,
ctx: Context,
): Promise<{
title: string
@@ -29,7 +29,7 @@ export namespace Tool {
}>
}
export function define<Parameters extends StandardSchemaV1, Result extends Metadata>(
export function define<Parameters extends z.ZodType, Result extends Metadata>(
id: string,
init: Info<Parameters, Result>["init"] | Awaited<ReturnType<Info<Parameters, Result>["init"]>>,
): Info<Parameters, Result> {

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import { Tool } from "./tool"
import TurndownService from "turndown"
import DESCRIPTION from "./webfetch.txt"

View File

@@ -1,4 +1,4 @@
import { z } from "zod"
import z from "zod/v4"
import * as path from "path"
import { Tool } from "./tool"
import { LSP } from "../lsp"