@@ -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"
@@ -265,26 +265,9 @@ export const Info = Schema.Struct({
265265 } ) ) ,
266266 )
267267
268- // Schema.Struct produces readonly types by default, but the service code
269- // below mutates Info objects directly (e.g. `config.mode = ...`). Strip the
270- // readonly recursively so callers get the same mutable shape zod inferred.
271- //
272- // `Types.DeepMutable` from effect-smol would be a drop-in, but its fallback
273- // branch `{ -readonly [K in keyof T]: ... }` collapses `unknown` to `{}`
274- // (since `keyof unknown = never`), which widens `Record<string, unknown>`
275- // fields like `ConfigPlugin.Options`. The local version gates on
276- // `extends object` so `unknown` passes through.
277- //
278- // Tuple branch preserves `ConfigPlugin.Spec`'s `readonly [string, Options]`
279- // shape (otherwise the general array branch widens it to an array).
280- type DeepMutable < T > = T extends readonly [ unknown , ...unknown [ ] ]
281- ? { - readonly [ K in keyof T ] : DeepMutable < T [ K ] > }
282- : T extends readonly ( infer U ) [ ]
283- ? DeepMutable < U > [ ]
284- : T extends object
285- ? { - readonly [ K in keyof T ] : DeepMutable < T [ K ] > }
286- : T
287-
268+ // Uses the shared `DeepMutable` from `@/util/schema`. See the definition
269+ // there for why the local variant is needed over `Types.DeepMutable` from
270+ // effect-smol (the upstream version collapses `unknown` to `{}`).
288271export type Info = DeepMutable < Schema . Schema . Type < typeof Info > > & {
289272 // plugin_origins is derived state, not a persisted config field. It keeps each winning plugin spec together
290273 // with the file and scope it came from so later runtime code can make location-sensitive decisions.
0 commit comments