@@ -25,7 +25,7 @@ import { Context, Duration, Effect, Exit, Fiber, Layer, Option, Schema } from "e
2525import { EffectFlock } from "@opencode-ai/shared/util/effect-flock"
2626import { InstanceRef } from "@/effect/instance-ref"
2727import { zod , ZodOverride } from "@/util/effect-zod"
28- import { NonNegativeInt , PositiveInt , withStatics } from "@/util/schema"
28+ import { NonNegativeInt , PositiveInt , withStatics , type DeepMutable } from "@/util/schema"
2929import { ConfigAgent } from "./agent"
3030import { ConfigCommand } from "./command"
3131import { ConfigFormatter } from "./formatter"
@@ -249,26 +249,9 @@ export const Info = Schema.Struct({
249249 } ) ) ,
250250 )
251251
252- // Schema.Struct produces readonly types by default, but the service code
253- // below mutates Info objects directly (e.g. `config.mode = ...`). Strip the
254- // readonly recursively so callers get the same mutable shape zod inferred.
255- //
256- // `Types.DeepMutable` from effect-smol would be a drop-in, but its fallback
257- // branch `{ -readonly [K in keyof T]: ... }` collapses `unknown` to `{}`
258- // (since `keyof unknown = never`), which widens `Record<string, unknown>`
259- // fields like `ConfigPlugin.Options`. The local version gates on
260- // `extends object` so `unknown` passes through.
261- //
262- // Tuple branch preserves `ConfigPlugin.Spec`'s `readonly [string, Options]`
263- // shape (otherwise the general array branch widens it to an array).
264- type DeepMutable < T > = T extends readonly [ unknown , ...unknown [ ] ]
265- ? { - readonly [ K in keyof T ] : DeepMutable < T [ K ] > }
266- : T extends readonly ( infer U ) [ ]
267- ? DeepMutable < U > [ ]
268- : T extends object
269- ? { - readonly [ K in keyof T ] : DeepMutable < T [ K ] > }
270- : T
271-
252+ // Uses the shared `DeepMutable` from `@/util/schema`. See the definition
253+ // there for why the local variant is needed over `Types.DeepMutable` from
254+ // effect-smol (the upstream version collapses `unknown` to `{}`).
272255export type Info = DeepMutable < Schema . Schema . Type < typeof Info > > & {
273256 // plugin_origins is derived state, not a persisted config field. It keeps each winning plugin spec together
274257 // with the file and scope it came from so later runtime code can make location-sensitive decisions.
0 commit comments