fix(config): preserve permission order with Effect decode (#24308)

This commit is contained in:
Kit Langton
2026-04-25 13:30:12 -04:00
committed by GitHub
parent 62651c7114
commit a9740b9133
5 changed files with 146 additions and 90 deletions
+3 -34
View File
@@ -1,7 +1,6 @@
export * as ConfigPermission from "./permission"
import { Schema, SchemaGetter } from "effect"
import z from "zod"
import { ZodOverride, zod } from "@/util/effect-zod"
import { zod } from "@/util/effect-zod"
import { withStatics } from "@/util/schema"
export const Action = Schema.Literals(["ask", "allow", "deny"])
@@ -20,8 +19,8 @@ export const Rule = Schema.Union([Action, Object])
export type Rule = Schema.Schema.Type<typeof Rule>
// Known permission keys get explicit types in the Effect schema for generated
// docs/types. Runtime config parsing uses `InfoZod` below so user key order is
// preserved for permission precedence.
// docs/types. Runtime config parsing uses Effect's `propertyOrder: "original"`
// parse option so user key order is preserved for permission precedence.
const InputObject = Schema.StructWithRest(
Schema.Struct({
read: Schema.optional(Rule),
@@ -53,35 +52,6 @@ const InputSchema = Schema.Union([Action, InputObject])
const normalizeInput = (input: Schema.Schema.Type<typeof InputSchema>): Schema.Schema.Type<typeof InputObject> =>
typeof input === "string" ? { "*": input } : input
const InfoZod = z
.union([
zod(Action),
z.intersection(
z.record(z.string(), zod(Rule)),
z
.object({
read: zod(Rule).optional(),
edit: zod(Rule).optional(),
glob: zod(Rule).optional(),
grep: zod(Rule).optional(),
list: zod(Rule).optional(),
bash: zod(Rule).optional(),
task: zod(Rule).optional(),
external_directory: zod(Rule).optional(),
todowrite: zod(Action).optional(),
question: zod(Action).optional(),
webfetch: zod(Action).optional(),
websearch: zod(Action).optional(),
codesearch: zod(Action).optional(),
lsp: zod(Rule).optional(),
doom_loop: zod(Action).optional(),
skill: zod(Rule).optional(),
})
.catchall(zod(Rule)),
),
])
.transform(normalizeInput)
export const Info = InputSchema.pipe(
Schema.decodeTo(InputObject, {
decode: SchemaGetter.transform(normalizeInput),
@@ -92,7 +62,6 @@ export const Info = InputSchema.pipe(
}),
)
.annotate({ identifier: "PermissionConfig" })
.annotate({ [ZodOverride]: InfoZod })
.pipe(
// Walker already emits the decodeTo transform into the derived zod (see
// `encoded()` in effect-zod.ts), so just expose that directly.