/** * Chi Commons configuration. * * The zod schema follows the chi-base settings-editor constraint: top-level * `z.enum` or `z.string` fields with defaults. Booleans are modeled as * "on"/"off" enums and numbers as strings; `parseCommonsConfig` converts the * stored shape into the typed `CommonsConfig` the module consumes (per * MVP_PLAN.md "Chi Base config consumed"). */ import { z } from "zod"; export const commonsConfigSchema = z.object({ enabled: z.enum(["on", "off"]).default("on"), autoReduce: z.enum(["on", "off"]).default("off"), reduceCadenceRounds: z.string().default("4"), generatedTitles: z.enum(["on", "off"]).default("on"), maxInsertTokens: z.string().default("2000"), requireRawInsertConfirmation: z.enum(["on", "off"]).default("on"), /** Comma-separated model preference for reduction; first available wins. */ reduceModels: z.string().default("claude-sonnet-5,gpt-5.6-terra"), }); export type StoredCommonsConfig = z.infer; /** Typed runtime config consumed by chi-commons. */ export interface CommonsConfig { enabled: boolean; autoReduce: boolean; reduceCadenceRounds: number; generatedTitles: boolean; maxInsertTokens: number; requireRawInsertConfirmation: boolean; reduceModels: string[]; } export const DEFAULT_COMMONS_CONFIG: CommonsConfig = { enabled: true, autoReduce: false, reduceCadenceRounds: 4, generatedTitles: true, maxInsertTokens: 2000, requireRawInsertConfirmation: true, reduceModels: ["claude-sonnet-5", "gpt-5.6-terra"], }; function toPositiveInt(value: string, fallback: number): number { const parsed = Number.parseInt(value, 10); if (!Number.isFinite(parsed) || parsed <= 0) return fallback; return parsed; } /** Convert the stored (string/enum) shape into the typed runtime config. */ export function parseCommonsConfig( stored: Partial | undefined, ): CommonsConfig { const parsed = commonsConfigSchema.parse(stored ?? {}); return { enabled: parsed.enabled === "on", autoReduce: parsed.autoReduce === "on", reduceCadenceRounds: toPositiveInt( parsed.reduceCadenceRounds, DEFAULT_COMMONS_CONFIG.reduceCadenceRounds, ), generatedTitles: parsed.generatedTitles === "on", maxInsertTokens: toPositiveInt( parsed.maxInsertTokens, DEFAULT_COMMONS_CONFIG.maxInsertTokens, ), requireRawInsertConfirmation: parsed.requireRawInsertConfirmation === "on", reduceModels: parsed.reduceModels.split(",").map((m) => m.trim()).filter(Boolean), }; }