diff --git a/CHANGELOG.md b/CHANGELOG.md index 48b3b140b77..8e2bf8ddca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai - Limit hook CLI tool authority [AI]. (#81065) Thanks @pgondhi987. - Require admin scope for node device token management [AI]. (#81067) Thanks @pgondhi987. - Restrict chat sender allowlist matching [AI]. (#80898) Thanks @pgondhi987. +- Update: suppress the false newer-config warning during restart health probing after an update handoff, while keeping future-version mutation guards intact. (#78652) - Sessions: redact persisted tool result detail metadata before writing transcripts so diagnostic secrets do not survive tool output redaction. (#80444) Thanks @nimbleenigma. - Codex runtime: allow the official installed `@openclaw/codex` package to use its private task-runtime SDK helper, fixing `MODULE_NOT_FOUND` during migrated OpenAI/Codex beta runs. - Codex migration: make Enter activate the highlighted checkbox row before continuing, so `Skip for now` and bulk-selection rows work even when planned items start preselected. diff --git a/src/cli/daemon-cli/restart-health.test.ts b/src/cli/daemon-cli/restart-health.test.ts index e6f4ba745c0..4a838c52898 100644 --- a/src/cli/daemon-cli/restart-health.test.ts +++ b/src/cli/daemon-cli/restart-health.test.ts @@ -8,6 +8,7 @@ const classifyPortListener = vi.hoisted(() => vi.fn<(_listener: unknown, _port: number) => PortListenerKind>(() => "gateway"), ); const probeGateway = vi.hoisted(() => vi.fn()); +const createConfigIO = vi.hoisted(() => vi.fn()); const readBestEffortConfig = vi.hoisted(() => vi.fn(async () => ({}))); const resolveGatewayProbeAuthSafeWithSecretInputs = vi.hoisted(() => vi.fn<(_opts: unknown) => Promise<{ auth: { token?: string; password?: string } }>>(async () => ({ @@ -26,9 +27,7 @@ vi.mock("../../gateway/probe.js", () => ({ })); vi.mock("../../config/io.js", () => ({ - createConfigIO: () => ({ - readBestEffortConfig: () => readBestEffortConfig(), - }), + createConfigIO: (opts: unknown) => createConfigIO(opts), })); vi.mock("../../gateway/probe-auth.js", () => ({ @@ -139,6 +138,10 @@ describe("inspectGatewayRestart", () => { inspectPortUsage.mockReset(); readBestEffortConfig.mockReset(); readBestEffortConfig.mockResolvedValue({}); + createConfigIO.mockReset(); + createConfigIO.mockReturnValue({ + readBestEffortConfig: () => readBestEffortConfig(), + }); resolveGatewayProbeAuthSafeWithSecretInputs.mockReset(); resolveGatewayProbeAuthSafeWithSecretInputs.mockResolvedValue({ auth: {} }); inspectPortUsage.mockResolvedValue({ @@ -442,6 +445,13 @@ describe("inspectGatewayRestart", () => { expect(authResolveInput.cfg?.gateway?.auth?.mode).toBe("token"); expect(authResolveInput.cfg?.gateway?.auth?.token).toBe("probe-token"); expect(authResolveInput.mode).toBe("local"); + expect(createConfigIO).toHaveBeenCalledWith( + expect.objectContaining({ + env: serviceEnv, + pluginValidation: "skip", + suppressFutureVersionWarning: true, + }), + ); const probeInput = firstCallArg(probeGateway) as { auth?: { token?: string; password?: string }; env?: NodeJS.ProcessEnv; @@ -637,6 +647,8 @@ describe("inspectGatewayRestart", () => { }); it("annotates stopped-free early exits with the actual elapsed time", async () => { + Object.defineProperty(process, "platform", { value: "linux", configurable: true }); + const snapshot = await waitForStoppedFreeGatewayRestart(); expect(snapshot.healthy).toBe(false); diff --git a/src/cli/daemon-cli/restart-health.ts b/src/cli/daemon-cli/restart-health.ts index e35236d5092..acb572f5c74 100644 --- a/src/cli/daemon-cli/restart-health.ts +++ b/src/cli/daemon-cli/restart-health.ts @@ -274,6 +274,7 @@ async function resolveGatewayRestartProbeAuth( const cfg = await createConfigIO({ env: mergedEnv, pluginValidation: "skip", + suppressFutureVersionWarning: true, }) .readBestEffortConfig() .catch((): OpenClawConfig => ({})); diff --git a/src/config/io.ts b/src/config/io.ts index 0185a5c5751..25761a0b7ad 100644 --- a/src/config/io.ts +++ b/src/config/io.ts @@ -878,6 +878,7 @@ export type ConfigIoDeps = { configPath?: string; logger?: Pick; measure?: ConfigSnapshotReadMeasure; + suppressFutureVersionWarning?: boolean; }; function warnOnConfigMiskeys(raw: unknown, logger: Pick): void { @@ -936,6 +937,7 @@ function normalizeDeps(overrides: ConfigIoDeps = {}): Required { configPath: overrides.configPath ?? "", logger: overrides.logger ?? console, measure: overrides.measure ?? (async (_name, run) => await run()), + suppressFutureVersionWarning: overrides.suppressFutureVersionWarning ?? false, }; } @@ -1609,7 +1611,9 @@ export function createConfigIO( .join("\n"); deps.logger.warn(`Config warnings:\n${details}`); } - warnIfConfigFromFuture(validated.config, deps.logger); + if (!deps.suppressFutureVersionWarning) { + warnIfConfigFromFuture(validated.config, deps.logger); + } const cfg = retainRuntimeOnlyShippedPluginInstallConfigRecords( materializeRuntimeConfig(validated.config, "load", { manifestRegistry: pluginMetadataSnapshot?.manifestRegistry, @@ -1816,7 +1820,9 @@ export function createConfigIO( }); } - warnIfConfigFromFuture(validated.config, deps.logger); + if (!deps.suppressFutureVersionWarning) { + warnIfConfigFromFuture(validated.config, deps.logger); + } const snapshotConfig = await deps.measure("config.snapshot.read.materialize", () => retainRuntimeOnlyShippedPluginInstallConfigRecords( materializeRuntimeConfig(validated.config, "snapshot", {