summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-17 16:59:24 -0400
committerGitHub <[email protected]>2026-04-17 20:59:24 +0000
commitce69bd97b90200a4d9794bd0409d3b4bb7996e54 (patch)
treefadda94e2c389174891056e3eda6919fe05448b1
parent999d8651aab7f9531539f686f685375fcbb19437 (diff)
downloadopencode-ce69bd97b90200a4d9794bd0409d3b4bb7996e54.tar.gz
opencode-ce69bd97b90200a4d9794bd0409d3b4bb7996e54.zip
refactor(config): migrate model-id and command to Effect Schema (#23175)
-rw-r--r--packages/opencode/src/config/agent.ts2
-rw-r--r--packages/opencode/src/config/command.ts22
-rw-r--r--packages/opencode/src/config/config.ts8
-rw-r--r--packages/opencode/src/config/model-id.ts13
4 files changed, 30 insertions, 15 deletions
diff --git a/packages/opencode/src/config/agent.ts b/packages/opencode/src/config/agent.ts
index f754f009d..9053b19fc 100644
--- a/packages/opencode/src/config/agent.ts
+++ b/packages/opencode/src/config/agent.ts
@@ -15,7 +15,7 @@ const log = Log.create({ service: "config" })
export const Info = z
.object({
- model: ConfigModelID.optional(),
+ model: ConfigModelID.zod.optional(),
variant: z
.string()
.optional()
diff --git a/packages/opencode/src/config/command.ts b/packages/opencode/src/config/command.ts
index 979925056..3e0adccc3 100644
--- a/packages/opencode/src/config/command.ts
+++ b/packages/opencode/src/config/command.ts
@@ -1,10 +1,12 @@
export * as ConfigCommand from "./command"
import { Log } from "../util"
-import z from "zod"
+import { Schema } from "effect"
import { NamedError } from "@opencode-ai/shared/util/error"
import { Glob } from "@opencode-ai/shared/util/glob"
import { Bus } from "@/bus"
+import { zod } from "@/util/effect-zod"
+import { withStatics } from "@/util/schema"
import { configEntryNameFromPath } from "./entry-name"
import { InvalidError } from "./error"
import * as ConfigMarkdown from "./markdown"
@@ -12,15 +14,15 @@ import { ConfigModelID } from "./model-id"
const log = Log.create({ service: "config" })
-export const Info = z.object({
- template: z.string(),
- description: z.string().optional(),
- agent: z.string().optional(),
- model: ConfigModelID.optional(),
- subtask: z.boolean().optional(),
-})
+export const Info = Schema.Struct({
+ template: Schema.String,
+ description: Schema.optional(Schema.String),
+ agent: Schema.optional(Schema.String),
+ model: Schema.optional(ConfigModelID),
+ subtask: Schema.optional(Schema.Boolean),
+}).pipe(withStatics((s) => ({ zod: zod(s) })))
-export type Info = z.infer<typeof Info>
+export type Info = Schema.Schema.Type<typeof Info>
export async function load(dir: string) {
const result: Record<string, Info> = {}
@@ -49,7 +51,7 @@ export async function load(dir: string) {
...md.data,
template: md.content.trim(),
}
- const parsed = Info.safeParse(config)
+ const parsed = Info.zod.safeParse(config)
if (parsed.success) {
result[config.name] = parsed.data
continue
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 261c5acab..684fdf63a 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -97,7 +97,7 @@ export const Info = z
logLevel: Log.Level.optional().describe("Log level"),
server: Server.optional().describe("Server configuration for opencode serve and web commands"),
command: z
- .record(z.string(), ConfigCommand.Info)
+ .record(z.string(), ConfigCommand.Info.zod)
.optional()
.describe("Command configuration, see https://opencode.ai/docs/commands"),
skills: ConfigSkills.Info.zod.optional().describe("Additional skill folder paths"),
@@ -135,8 +135,10 @@ export const Info = z
.array(z.string())
.optional()
.describe("When set, ONLY these providers will be enabled. All other providers will be ignored"),
- model: ConfigModelID.describe("Model to use in the format of provider/model, eg anthropic/claude-2").optional(),
- small_model: ConfigModelID.describe(
+ model: ConfigModelID.zod
+ .describe("Model to use in the format of provider/model, eg anthropic/claude-2")
+ .optional(),
+ small_model: ConfigModelID.zod.describe(
"Small model to use for tasks like title generation in the format of provider/model",
).optional(),
default_agent: z
diff --git a/packages/opencode/src/config/model-id.ts b/packages/opencode/src/config/model-id.ts
index 909e9aa92..3ad9e035c 100644
--- a/packages/opencode/src/config/model-id.ts
+++ b/packages/opencode/src/config/model-id.ts
@@ -1,3 +1,14 @@
+import { Schema } from "effect"
import z from "zod"
+import { zod, ZodOverride } from "@/util/effect-zod"
+import { withStatics } from "@/util/schema"
-export const ConfigModelID = z.string().meta({ $ref: "https://models.dev/model-schema.json#/$defs/Model" })
+// The original Zod schema carried an external $ref pointing at the models.dev
+// JSON schema. That external reference is not a named SDK component — it is a
+// literal pointer to an outside schema — so the walker cannot re-derive it
+// from AST metadata. Preserve the exact original Zod via ZodOverride.
+export const ConfigModelID = Schema.String.annotate({
+ [ZodOverride]: z.string().meta({ $ref: "https://models.dev/model-schema.json#/$defs/Model" }),
+}).pipe(withStatics((s) => ({ zod: zod(s) })))
+
+export type ConfigModelID = Schema.Schema.Type<typeof ConfigModelID>