fix(update): suppress internal handoff version warnings

This commit is contained in:
Vincent Koc
2026-05-24 21:42:39 +02:00
parent 0acc3e3216
commit 6cc8244333
5 changed files with 65 additions and 7 deletions

View File

@@ -104,6 +104,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Gateway/update: avoid fetching unrelated tags during dev-channel git updates so moved release tags do not block branch-based updates. (#84737) Thanks @rubencu.
- CLI/update: suppress the expected future-config warning while an old update parent hands off to the freshly installed post-core process.
- MiniMax: store OAuth token expiry as an absolute millisecond timestamp so OAuth profiles no longer appear expired on every request. (#83480) Thanks @NianJiuZst.
- Agents/Anthropic: strip missing or blank thinking signatures for signed-thinking providers even when recovery supplies a narrow replay policy without signature preservation. Fixes #84430. (#84448) Thanks @NianJiuZst.
- Agents/channels: send a visible notice when an aborted main session cannot be resumed after restart, including Telegram group targets. (#85805) Thanks @pfrederiksen.

View File

@@ -896,6 +896,10 @@ describe("update-cli", () => {
expect(call?.[2]?.env?.OPENCLAW_UPDATE_POST_CORE).toBe("1");
expect(call?.[2]?.env?.OPENCLAW_UPDATE_POST_CORE_CHANNEL).toBe("dev");
expect(call?.[2]?.env?.OPENCLAW_COMPATIBILITY_HOST_VERSION).toBe("1.0.0");
expect(vi.mocked(readConfigFileSnapshot).mock.calls[1]?.[0]).toEqual({
skipPluginValidation: true,
suppressFutureVersionWarning: true,
});
expect(updateNpmInstalledPlugins).not.toHaveBeenCalled();
expect(runDaemonInstall).not.toHaveBeenCalled();
expect(runDaemonRestart).not.toHaveBeenCalled();
@@ -1179,7 +1183,11 @@ describe("update-cli", () => {
expect(
vi
.mocked(readConfigFileSnapshot)
.mock.calls.some(([options]) => options?.skipPluginValidation === true),
.mock.calls.some(
([options]) =>
options?.skipPluginValidation === true &&
options.suppressFutureVersionWarning === true,
),
).toBe(true);
expect(defaultRuntime.exit).toHaveBeenCalledWith(0);
expect(syncPluginsForUpdateChannel).toHaveBeenCalledTimes(1);

View File

@@ -2950,7 +2950,10 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
process.env.OPENCLAW_COMPATIBILITY_HOST_VERSION = (await readPackageVersion(root)) ?? VERSION;
let postCoreConfigSnapshot = await readConfigFileSnapshot({ skipPluginValidation: true });
let postCoreConfigSnapshot = await readConfigFileSnapshot({
skipPluginValidation: true,
suppressFutureVersionWarning: true,
});
const preUpdateSourceConfig = await readPostCorePreUpdateSourceConfig({
sourceConfigPath: process.env[POST_CORE_UPDATE_SOURCE_CONFIG_PATH_ENV],
currentSnapshot: postCoreConfigSnapshot,
@@ -3416,7 +3419,10 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
let postUpdateConfigSnapshot =
result.status === "ok" && !opts.dryRun
? await readConfigFileSnapshot({ skipPluginValidation: true })
? await readConfigFileSnapshot({
skipPluginValidation: true,
suppressFutureVersionWarning: shouldResumePostCoreInFreshProcess,
})
: configSnapshot;
if (!shouldResumePostCoreInFreshProcess) {
postUpdateConfigSnapshot = await persistRequestedUpdateChannel({

View File

@@ -150,6 +150,38 @@ describe("config io paths", () => {
});
});
it("does not warn about newer config during internal update handoff reads", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".openclaw", "openclaw.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
JSON.stringify(
{
meta: { lastTouchedVersion: "9999.1.1" },
gateway: { mode: "local" },
},
null,
2,
),
);
const logger = {
error: vi.fn(),
warn: vi.fn(),
};
const io = createConfigIO({
configPath,
env: { OPENCLAW_UPDATE_POST_CORE: "1" } as NodeJS.ProcessEnv,
homedir: () => home,
logger,
});
io.loadConfig();
expect(logger.warn).not.toHaveBeenCalled();
});
});
it("normalizes safe-bin config entries at config load time", () => {
const cfg = {
tools: {

View File

@@ -7,6 +7,7 @@ import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent
import { ensureOwnerDisplaySecret } from "../agents/owner-display.js";
import { isVerbose } from "../global-state.js";
import { loadDotEnv } from "../infra/dotenv.js";
import { isTruthyEnvValue } from "../infra/env.js";
import { formatErrorMessage } from "../infra/errors.js";
import { resolveRequiredHomeDir } from "../infra/home-dir.js";
import { replaceFileAtomic, replaceFileAtomicSync } from "../infra/replace-file.js";
@@ -956,6 +957,7 @@ export type ConfigSnapshotReadOptions = {
observe?: boolean;
skipPluginValidation?: boolean;
preservedLegacyRootKeys?: readonly string[];
suppressFutureVersionWarning?: boolean;
};
function warnOnConfigMiskeys(raw: unknown, logger: Pick<typeof console, "warn">): void {
@@ -997,6 +999,13 @@ function warnIfConfigFromFuture(cfg: OpenClawConfig, logger: Pick<typeof console
}
}
function shouldSuppressFutureVersionWarningForEnv(env: NodeJS.ProcessEnv): boolean {
return (
isTruthyEnvValue(env.OPENCLAW_UPDATE_IN_PROGRESS) ||
isTruthyEnvValue(env.OPENCLAW_UPDATE_POST_CORE)
);
}
function resolveConfigPathForDeps(deps: Required<ConfigIoDeps>): string {
if (deps.configPath) {
return deps.configPath;
@@ -1005,16 +1014,17 @@ function resolveConfigPathForDeps(deps: Required<ConfigIoDeps>): string {
}
function normalizeDeps(overrides: ConfigIoDeps = {}): Required<ConfigIoDeps> {
const env = overrides.env ?? process.env;
return {
fs: overrides.fs ?? fs,
json5: overrides.json5 ?? JSON5,
env: overrides.env ?? process.env,
homedir:
overrides.homedir ?? (() => resolveRequiredHomeDir(overrides.env ?? process.env, os.homedir)),
env,
homedir: overrides.homedir ?? (() => resolveRequiredHomeDir(env, os.homedir)),
configPath: overrides.configPath ?? "",
logger: overrides.logger ?? console,
measure: overrides.measure ?? (async (_name, run) => await run()),
suppressFutureVersionWarning: overrides.suppressFutureVersionWarning ?? false,
suppressFutureVersionWarning:
overrides.suppressFutureVersionWarning ?? shouldSuppressFutureVersionWarningForEnv(env),
observe: overrides.observe ?? true,
};
}
@@ -2544,6 +2554,7 @@ export async function readConfigFileSnapshot(
...(options.measure ? { measure: options.measure } : {}),
...(options.observe === false ? { observe: false } : {}),
...(options.skipPluginValidation ? { pluginValidation: "skip" } : {}),
...(options.suppressFutureVersionWarning ? { suppressFutureVersionWarning: true } : {}),
...(options.preservedLegacyRootKeys
? { preservedLegacyRootKeys: options.preservedLegacyRootKeys }
: {}),