




















@@ -239,6 +239,12 @@ export type ConfigWriteOptions = {
239239 * Omitted means the observer should use its normal reload plan.
240240 */
241241afterWrite?: ConfigWriteAfterWrite;
242+/**
243+ * Legacy root keys to preserve on disk while excluding them from write validation.
244+ * This is for doctor repair of historical config metadata that should not become
245+ * part of the public schema contract again.
246+ */
247+preservedLegacyRootKeys?: readonly string[];
242248/**
243249 * Skip plugin-aware validation before writing. Use only for safe partial
244250 * migrations (e.g. legacy key removal) where the base schema is valid but
@@ -1229,7 +1235,10 @@ async function collectInvalidConfigLegacyIssues(
12291235}
1230123612311237export function createConfigIO(
1232-overrides: ConfigIoDeps & { pluginValidation?: "full" | "skip" } = {},
1238+overrides: ConfigIoDeps & {
1239+pluginValidation?: "full" | "skip";
1240+preservedLegacyRootKeys?: readonly string[];
1241+} = {},
12331242) {
12341243const deps = normalizeDeps(overrides);
12351244const configPath = resolveConfigPathForDeps(deps);
@@ -1580,6 +1589,7 @@ export function createConfigIO(
15801589pluginValidation: overrides.pluginValidation,
15811590loadPluginMetadataSnapshot: loadValidationPluginMetadataSnapshot,
15821591sourceRaw: snapshotParsed,
1592+preservedLegacyRootKeys: overrides.preservedLegacyRootKeys,
15831593});
15841594if (!validated.ok) {
15851595observeLoadConfigSnapshot({
@@ -1799,6 +1809,7 @@ export function createConfigIO(
17991809pluginValidation: overrides.pluginValidation,
18001810loadPluginMetadataSnapshot: loadValidationPluginMetadataSnapshot,
18011811sourceRaw: effectiveParsed,
1812+preservedLegacyRootKeys: overrides.preservedLegacyRootKeys,
18021813}),
18031814);
18041815if (!validated.ok) {
@@ -2043,6 +2054,7 @@ export function createConfigIO(
20432054const validated = validateConfigObjectRawWithPlugins(persistCandidate, {
20442055env: deps.env,
20452056pluginValidation: options.skipPluginValidation ? "skip" : "full",
2057+preservedLegacyRootKeys: options.preservedLegacyRootKeys,
20462058});
20472059if (!validated.ok) {
20482060const issue = validated.issues[0];
@@ -2373,10 +2385,14 @@ export async function readSourceConfigBestEffort(): Promise<OpenClawConfig> {
23732385export async function readConfigFileSnapshot(options?: {
23742386measure?: ConfigSnapshotReadMeasure;
23752387skipPluginValidation?: boolean;
2388+preservedLegacyRootKeys?: readonly string[];
23762389}): Promise<ConfigFileSnapshot> {
23772390return await createConfigIO({
23782391 ...(options?.measure ? { measure: options.measure } : {}),
23792392 ...(options?.skipPluginValidation ? { pluginValidation: "skip" } : {}),
2393+ ...(options?.preservedLegacyRootKeys
2394+ ? { preservedLegacyRootKeys: options.preservedLegacyRootKeys }
2395+ : {}),
23802396}).readConfigFileSnapshot();
23812397}
23822398@@ -2427,7 +2443,12 @@ export async function writeConfigFile(
24272443cfg: OpenClawConfig,
24282444options: ConfigWriteOptions = {},
24292445): Promise<ConfigWriteResult> {
2430-const io = createConfigIO(options.skipPluginValidation ? { pluginValidation: "skip" } : {});
2446+const io = createConfigIO({
2447+ ...(options.skipPluginValidation ? { pluginValidation: "skip" as const } : {}),
2448+ ...(options.preservedLegacyRootKeys
2449+ ? { preservedLegacyRootKeys: options.preservedLegacyRootKeys }
2450+ : {}),
2451+});
24312452assertConfigWriteAllowedInCurrentMode({ configPath: io.configPath });
24322453let nextCfg = cfg;
24332454const runtimeConfigSnapshot = getRuntimeConfigSnapshotState();
@@ -2456,6 +2477,7 @@ export async function writeConfigFile(
24562477skipRuntimeSnapshotRefresh: options.skipRuntimeSnapshotRefresh,
24572478skipOutputLogs: options.skipOutputLogs,
24582479skipPluginValidation: options.skipPluginValidation,
2480+preservedLegacyRootKeys: options.preservedLegacyRootKeys,
24592481});
24602482if (
24612483options.skipRuntimeSnapshotRefresh &&
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。