Remove effect-zod bridge (#26956)

This commit is contained in:
Kit Langton
2026-05-11 21:14:55 -04:00
committed by GitHub
parent abb1ee6278
commit e5aa5161f2
21 changed files with 425 additions and 1266 deletions
+63 -9
View File
@@ -15,9 +15,9 @@ import { SkillTool } from "./skill"
import * as Tool from "./tool"
import { Config } from "@/config/config"
import { type ToolContext as PluginToolContext, type ToolDefinition } from "@opencode-ai/plugin"
import type { JSONSchema7, JSONSchema7Definition } from "@ai-sdk/provider"
import { Schema } from "effect"
import z from "zod"
import { ZodOverride } from "@opencode-ai/core/effect-zod"
import { Plugin } from "../plugin"
import { Provider } from "@/provider/provider"
import { ProviderID, type ModelID } from "../provider/schema"
@@ -137,17 +137,19 @@ export const layer: Layer.Layer<
const custom: Tool.Def[] = []
function fromPlugin(id: string, def: ToolDefinition): Tool.Def {
// Plugin tools define their args as a raw Zod shape. Wrap the
// derived Zod object in a `Schema.declare` so it slots into the
// Schema-typed framework, and annotate with `ZodOverride` so the
// walker emits the original Zod object for LLM JSON Schema.
const zodParams = z.object(def.args)
const parameters = Schema.declare<unknown>((u): u is unknown => zodParams.safeParse(u).success).annotate({
[ZodOverride]: zodParams,
})
// Plugin tools still expose Zod args publicly; keep that compatibility
// boxed at the registry boundary and give the LLM the original JSON Schema.
const entries = Object.entries(def.args)
const allZod = entries.every((entry) => isZodType(entry[1]))
const zodParams = allZod ? z.object(def.args) : undefined
const jsonSchema = zodParams ? zodJsonSchema(zodParams) : legacyJsonSchema(entries)
const parameters = zodParams
? Schema.declare<unknown>((u): u is unknown => zodParams.safeParse(u).success)
: Schema.Unknown
return {
id,
parameters,
jsonSchema,
description: def.description,
execute: (args, toolCtx) =>
Effect.gen(function* () {
@@ -323,8 +325,13 @@ export const layer: Layer.Layer<
const output = {
description: tool.description,
parameters: tool.parameters,
jsonSchema: tool.jsonSchema,
}
yield* plugin.trigger("tool.definition", { toolID: tool.id }, output)
const jsonSchema =
output.parameters === tool.parameters || output.jsonSchema !== tool.jsonSchema
? output.jsonSchema
: undefined
return {
id: tool.id,
description: [
@@ -335,6 +342,7 @@ export const layer: Layer.Layer<
.filter(Boolean)
.join("\n"),
parameters: output.parameters,
jsonSchema,
execute: tool.execute,
formatValidationError: tool.formatValidationError,
}
@@ -376,4 +384,50 @@ export const defaultLayer = Layer.suspend(() =>
),
)
function isZodType(value: unknown): value is z.ZodType {
return typeof value === "object" && value !== null && "_zod" in value
}
function isJsonSchemaDefinition(value: unknown): value is JSONSchema7Definition {
return typeof value === "boolean" || (typeof value === "object" && value !== null && !Array.isArray(value))
}
function legacyJsonSchema(entries: [string, unknown][]): JSONSchema7 {
const properties = Object.fromEntries(
entries.filter((entry): entry is [string, JSONSchema7Definition] => isJsonSchemaDefinition(entry[1])),
)
return {
type: "object",
properties,
required: Object.keys(properties),
}
}
function zodJsonSchema(schema: z.ZodType): JSONSchema7 {
const result = normalizeZodJsonSchema(z.toJSONSchema(schema, { io: "input" }))
if (!isJsonSchemaObject(result)) throw new Error("plugin tool Zod schema produced a non-object JSON Schema")
const { $defs, ...rest } = result
return (
$defs && isJsonSchemaObject($defs) ? { ...rest, definitions: $defs as JSONSchema7["definitions"] } : rest
) as JSONSchema7
}
function normalizeZodJsonSchema(value: unknown): unknown {
if (Array.isArray(value)) return value.map((item) => normalizeZodJsonSchema(item))
if (typeof value !== "object" || value === null) return value
return Object.fromEntries(
Object.entries(value)
.filter((entry) =>
(entry[0] === "exclusiveMaximum" || entry[0] === "exclusiveMinimum") && typeof entry[1] === "boolean"
? false
: true,
)
.map(([key, item]) => [key, normalizeZodJsonSchema(item)]),
)
}
function isJsonSchemaObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
}
export * as ToolRegistry from "./registry"