@@ -24,7 +24,7 @@ import { InstanceState } from "@/effect"
2424import { Context , Duration , Effect , Exit , Fiber , Layer , Option , Schema } from "effect"
2525import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
2626import { InstanceRef } from "@/effect/instance-ref"
27- import { zod , ZodOverride } from "@/util/effect-zod"
27+ import { zod } from "@/util/effect-zod"
2828import { NonNegativeInt , PositiveInt , withStatics , type DeepMutable } from "@/util/schema"
2929import { ConfigAgent } from "./agent"
3030import { ConfigCommand } from "./command"
@@ -81,12 +81,10 @@ export const Server = ConfigServer.Server.zod
8181export const Layout = ConfigLayout . Layout . zod
8282export type Layout = ConfigLayout . Layout
8383
84- // Schemas that still live at the zod layer (have .transform / .preprocess /
85- // .meta not expressible in current Effect Schema) get referenced via a
86- // ZodOverride-annotated Schema.Any. Walker sees the annotation and emits the
87- // exact zod directly, preserving component $refs.
88- const AgentRef = Schema . Any . annotate ( { [ ZodOverride ] : ConfigAgent . Info } )
89- const LogLevelRef = Schema . Any . annotate ( { [ ZodOverride ] : Log . Level } )
84+ const LogLevelRef = Schema . Literals ( [ "DEBUG" , "INFO" , "WARN" , "ERROR" ] ) . annotate ( {
85+ identifier : "LogLevel" ,
86+ description : "Log level" ,
87+ } )
9088
9189// The Effect Schema is the canonical source of truth. The `.zod` compatibility
9290// surface is derived so existing Hono validators keep working without a parallel
@@ -152,27 +150,27 @@ export const Info = Schema.Struct({
152150 mode : Schema . optional (
153151 Schema . StructWithRest (
154152 Schema . Struct ( {
155- build : Schema . optional ( AgentRef ) ,
156- plan : Schema . optional ( AgentRef ) ,
153+ build : Schema . optional ( ConfigAgent . Info ) ,
154+ plan : Schema . optional ( ConfigAgent . Info ) ,
157155 } ) ,
158- [ Schema . Record ( Schema . String , AgentRef ) ] ,
156+ [ Schema . Record ( Schema . String , ConfigAgent . Info ) ] ,
159157 ) ,
160158 ) . annotate ( { description : "@deprecated Use `agent` field instead." } ) ,
161159 agent : Schema . optional (
162160 Schema . StructWithRest (
163161 Schema . Struct ( {
164162 // primary
165- plan : Schema . optional ( AgentRef ) ,
166- build : Schema . optional ( AgentRef ) ,
163+ plan : Schema . optional ( ConfigAgent . Info ) ,
164+ build : Schema . optional ( ConfigAgent . Info ) ,
167165 // subagent
168- general : Schema . optional ( AgentRef ) ,
169- explore : Schema . optional ( AgentRef ) ,
166+ general : Schema . optional ( ConfigAgent . Info ) ,
167+ explore : Schema . optional ( ConfigAgent . Info ) ,
170168 // specialized
171- title : Schema . optional ( AgentRef ) ,
172- summary : Schema . optional ( AgentRef ) ,
173- compaction : Schema . optional ( AgentRef ) ,
169+ title : Schema . optional ( ConfigAgent . Info ) ,
170+ summary : Schema . optional ( ConfigAgent . Info ) ,
171+ compaction : Schema . optional ( ConfigAgent . Info ) ,
174172 } ) ,
175- [ Schema . Record ( Schema . String , AgentRef ) ] ,
173+ [ Schema . Record ( Schema . String , ConfigAgent . Info ) ] ,
176174 ) ,
177175 ) . annotate ( { description : "Agent configuration, see https://opencode.ai/docs/agents" } ) ,
178176 provider : Schema . optional ( Schema . Record ( Schema . String , ConfigProvider . Info ) ) . annotate ( {
@@ -184,7 +182,7 @@ export const Info = Schema.Struct({
184182 Schema . Union ( [
185183 ConfigMCP . Info ,
186184 // Matches the legacy `{ enabled: false }` form used to disable a server.
187- Schema . Any . annotate ( { [ ZodOverride ] : z . object ( { enabled : z . boolean ( ) } ) . strict ( ) } ) ,
185+ Schema . Struct ( { enabled : Schema . Boolean } ) ,
188186 ] ) ,
189187 ) ,
190188 ) . annotate ( { description : "MCP (Model Context Protocol) server configurations" } ) ,
@@ -362,7 +360,7 @@ export const layer = Layer.effect(
362360 ) ,
363361 )
364362 const parsed = ConfigParse . jsonc ( expanded , source )
365- const data = ConfigParse . schema ( Info . zod , normalizeLoadedConfig ( parsed , source ) , source )
363+ const data = ConfigParse . effectSchema ( Info , normalizeLoadedConfig ( parsed , source ) , source )
366364 if ( ! ( "path" in options ) ) return data
367365
368366 yield * Effect . promise ( ( ) => resolveLoadedPlugins ( data , options . path ) )
@@ -754,13 +752,13 @@ export const layer = Layer.effect(
754752
755753 let next : Info
756754 if ( ! file . endsWith ( ".jsonc" ) ) {
757- const existing = ConfigParse . schema ( Info . zod , ConfigParse . jsonc ( before , file ) , file )
755+ const existing = ConfigParse . effectSchema ( Info , ConfigParse . jsonc ( before , file ) , file )
758756 const merged = mergeDeep ( writable ( existing ) , writable ( config ) )
759757 yield * fs . writeFileString ( file , JSON . stringify ( merged , null , 2 ) ) . pipe ( Effect . orDie )
760758 next = merged
761759 } else {
762760 const updated = patchJsonc ( before , writable ( config ) )
763- next = ConfigParse . schema ( Info . zod , ConfigParse . jsonc ( updated , file ) , file )
761+ next = ConfigParse . effectSchema ( Info , ConfigParse . jsonc ( updated , file ) , file )
764762 yield * fs . writeFileString ( file , updated ) . pipe ( Effect . orDie )
765763 }
766764
0 commit comments