

























@@ -3,10 +3,15 @@ import chokidar from "chokidar";
33import { bumpSkillsSnapshotVersion } from "../agents/skills/refresh-state.js";
44import type { ConfigWriteNotification } from "../config/io.js";
55import { formatConfigIssueLines } from "../config/issue-format.js";
6-import { shouldAttemptLastKnownGoodRecovery } from "../config/recovery-policy.js";
6+import { materializeRuntimeConfig } from "../config/materialize.js";
7+import {
8+isPluginLocalInvalidConfigSnapshot,
9+shouldAttemptLastKnownGoodRecovery,
10+} from "../config/recovery-policy.js";
711import { resolveConfigWriteFollowUp } from "../config/runtime-snapshot.js";
812import type { GatewayReloadMode } from "../config/types.gateway.js";
913import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.openclaw.js";
14+import { validateConfigObjectWithPlugins } from "../config/validation.js";
1015import { isPlainObject } from "../utils.js";
1116import {
1217buildGatewayReloadPlan,
@@ -71,6 +76,39 @@ function isNoopReloadPlan(plan: GatewayReloadPlan): boolean {
7176);
7277}
737879+function resolvePluginLocalInvalidReloadSnapshot(params: {
80+snapshot: ConfigFileSnapshot;
81+log: {
82+warn: (msg: string) => void;
83+};
84+}): ConfigFileSnapshot | null {
85+if (!isPluginLocalInvalidConfigSnapshot(params.snapshot)) {
86+return null;
87+}
88+const validated = validateConfigObjectWithPlugins(params.snapshot.sourceConfig, {
89+pluginValidation: "skip",
90+});
91+if (!validated.ok) {
92+return null;
93+}
94+const runtimeConfig = materializeRuntimeConfig(validated.config, "load");
95+for (const issue of params.snapshot.issues) {
96+params.log.warn(
97+`config reload skipped plugin config validation issue at ${issue.path}: ${issue.message}. Run "openclaw doctor --fix" to quarantine the plugin config.`,
98+);
99+}
100+return {
101+ ...params.snapshot,
102+sourceConfig: params.snapshot.sourceConfig,
103+resolved: params.snapshot.resolved,
104+valid: true,
105+ runtimeConfig,
106+config: runtimeConfig,
107+issues: [],
108+warnings: [...params.snapshot.warnings, ...params.snapshot.issues, ...validated.warnings],
109+};
110+}
111+74112export function diffConfigPaths(prev: unknown, next: unknown, prefix = ""): string[] {
75113if (prev === next) {
76114return [];
@@ -389,16 +427,28 @@ export function startGatewayConfigReloader(opts: {
389427if (handleMissingSnapshot(snapshot)) {
390428return;
391429}
430+let degradedPluginSnapshot = false;
392431if (!snapshot.valid) {
393432const recoveredSnapshot = await recoverAndReadSnapshot(snapshot, "invalid-config");
394433if (!recoveredSnapshot) {
395-handleInvalidSnapshot(snapshot);
396-return;
434+const pluginLocalSnapshot = resolvePluginLocalInvalidReloadSnapshot({
435+ snapshot,
436+log: opts.log,
437+});
438+if (!pluginLocalSnapshot) {
439+handleInvalidSnapshot(snapshot);
440+return;
441+}
442+snapshot = pluginLocalSnapshot;
443+degradedPluginSnapshot = true;
444+} else {
445+snapshot = recoveredSnapshot;
397446}
398-snapshot = recoveredSnapshot;
399447}
400448await applySnapshot(snapshot.config, snapshot.sourceConfig);
401-await promoteAcceptedSnapshot(snapshot, "valid-config");
449+if (!degradedPluginSnapshot) {
450+await promoteAcceptedSnapshot(snapshot, "valid-config");
451+}
402452} catch (err) {
403453opts.log.error(`config reload failed: ${String(err)}`);
404454} finally {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。