From 49fa565ba67a8e7094f07c304c570b8e6ae19722 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Tue, 21 Apr 2026 20:25:53 -0500 Subject: [PATCH] UI: preserve empty raw config resets --- CHANGELOG.md | 1 + ui/src/ui/controllers/config.test.ts | 21 +++++++++++++++++++++ ui/src/ui/controllers/config.ts | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401ac9f76c3..40a86c86ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai - Discord: keep slash command follow-up chunks ephemeral when the command is configured for ephemeral replies, so long `/status` output no longer leaks fallback model or runtime details into the public channel. (#69869) thanks @gumadeiras. - Plugins/discovery: reject package plugin source entries that escape the package directory before explicit runtime entries or inferred built JavaScript peers can be used. (#69868) thanks @gumadeiras. - CLI/channels: resolve channel presence through a shared policy that keeps ambient env vars and stale persisted auth from surfacing disabled bundled plugins in status, doctor, security audit, and cron delivery validation unless the channel or plugin is effectively enabled or explicitly configured. (#69862) Thanks @gumadeiras. +- Control UI/config: preserve intentionally empty raw config snapshots when clearing pending updates so reset restores the original bytes instead of synthesizing JSON for blank config files. (#68178) Thanks @BunsDev. ### Fixes diff --git a/ui/src/ui/controllers/config.test.ts b/ui/src/ui/controllers/config.test.ts index a6b75194dec..f42fc3f2a0c 100644 --- a/ui/src/ui/controllers/config.test.ts +++ b/ui/src/ui/controllers/config.test.ts @@ -185,6 +185,27 @@ describe("resetConfigPendingChanges", () => { expect(state.configForm).toEqual({ gateway: { mode: "local" } }); expect(state.configRaw).toBe('{\n "gateway": { "mode": "local" }\n}\n'); }); + + it("preserves an intentionally empty original raw config", () => { + const state = createState(); + state.configSnapshot = { + config: {}, + valid: true, + issues: [], + raw: "", + }; + state.configFormOriginal = {}; + state.configRawOriginal = ""; + state.configForm = { gateway: { mode: "remote" } }; + state.configRaw = '{\n "gateway": { "mode": "remote" }\n}\n'; + state.configFormDirty = true; + + resetConfigPendingChanges(state); + + expect(state.configFormDirty).toBe(false); + expect(state.configForm).toEqual({}); + expect(state.configRaw).toBe(""); + }); }); describe("agent config helpers", () => { diff --git a/ui/src/ui/controllers/config.ts b/ui/src/ui/controllers/config.ts index f708ef09081..e2e0cac93ce 100644 --- a/ui/src/ui/controllers/config.ts +++ b/ui/src/ui/controllers/config.ts @@ -223,7 +223,7 @@ export function resetConfigPendingChanges(state: ConfigState) { state.configFormOriginal ?? state.configSnapshot?.config ?? {}, ); state.configRaw = - state.configRawOriginal || + state.configRawOriginal ?? serializeConfigForm(state.configFormOriginal ?? state.configSnapshot?.config ?? {}); state.configFormDirty = false; }