From b8d4888f3bbae76741bbf9eebe84e4c874ef4987 Mon Sep 17 00:00:00 2001 From: xiwan <5155280@qq.com> Date: Tue, 3 Mar 2026 15:28:57 +0800 Subject: [PATCH] fix(config): detect top-level heartbeat as invalid config path (#30894) Top-level `heartbeat` is not a valid config path but was silently ignored, causing users to believe heartbeat.model was configured when it was not. Add a legacy config rule so startup validation surfaces a clear migration hint pointing to agents.defaults.heartbeat. Fixes #30894 --- ...cy-config-detection.accepts-imessage-dmpolicy.test.ts | 9 +++++++++ src/config/legacy.rules.ts | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/src/config/config.legacy-config-detection.accepts-imessage-dmpolicy.test.ts b/src/config/config.legacy-config-detection.accepts-imessage-dmpolicy.test.ts index 87df6130336..89632bbc543 100644 --- a/src/config/config.legacy-config-detection.accepts-imessage-dmpolicy.test.ts +++ b/src/config/config.legacy-config-detection.accepts-imessage-dmpolicy.test.ts @@ -274,6 +274,15 @@ describe("legacy config detection", () => { }, ); }); + it("flags top-level heartbeat as legacy in snapshot", async () => { + await withSnapshotForConfig( + { heartbeat: { model: "anthropic/claude-3-5-haiku-20241022", every: "30m" } }, + async (ctx) => { + expect(ctx.snapshot.valid).toBe(false); + expect(ctx.snapshot.legacyIssues.some((issue) => issue.path === "heartbeat")).toBe(true); + }, + ); + }); it("flags legacy provider sections in snapshot", async () => { await withSnapshotForConfig({ whatsapp: { allowFrom: ["+1555"] } }, async (ctx) => { expect(ctx.snapshot.valid).toBe(false); diff --git a/src/config/legacy.rules.ts b/src/config/legacy.rules.ts index 9f4ef6098be..f61ae6ccdbb 100644 --- a/src/config/legacy.rules.ts +++ b/src/config/legacy.rules.ts @@ -204,4 +204,9 @@ export const LEGACY_CONFIG_RULES: LegacyConfigRule[] = [ match: (value) => isLegacyGatewayBindHostAlias(value), requireSourceLiteral: true, }, + { + path: ["heartbeat"], + message: + "top-level heartbeat is not a valid config path; use agents.defaults.heartbeat instead.", + }, ];