fix(agents): stop injecting heartbeat system prompt on non-heartbeat … · openclaw/openclaw@06c058b
stainlu
·
2026-04-25
·
via Recent Commits to openclaw:main
File tree
src/agents/pi-embedded-runner/run
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { describe, expect, it } from "vitest"; |
| 2 | +import { shouldInjectHeartbeatPromptForTrigger } from "./trigger-policy.js"; |
| 3 | + |
| 4 | +describe("shouldInjectHeartbeatPromptForTrigger", () => { |
| 5 | +it("injects the heartbeat prompt on heartbeat-triggered runs", () => { |
| 6 | +expect(shouldInjectHeartbeatPromptForTrigger("heartbeat")).toBe(true); |
| 7 | +}); |
| 8 | + |
| 9 | +// Regression: the heartbeat system prompt instructs the model to reply |
| 10 | +// exactly "HEARTBEAT_OK" when nothing is pending. If that prompt leaks into |
| 11 | +// a user-triggered turn, the model can pattern-match the literal HEARTBEAT_OK |
| 12 | +// token (which the delivery runtime then suppresses, so the user sees |
| 13 | +// silence) or hallucinate a "[object Object]" serialization error as it |
| 14 | +// tries to reconcile the heartbeat instruction with a real user message. |
| 15 | +// See issue #69079 and its parent #50797. |
| 16 | +it.each([ |
| 17 | +["user"] as const, |
| 18 | +["manual"] as const, |
| 19 | +["cron"] as const, |
| 20 | +["memory"] as const, |
| 21 | +["overflow"] as const, |
| 22 | +])("does not inject the heartbeat prompt on %s-triggered runs", (trigger) => { |
| 23 | +expect(shouldInjectHeartbeatPromptForTrigger(trigger)).toBe(false); |
| 24 | +}); |
| 25 | + |
| 26 | +it("does not inject the heartbeat prompt when no trigger is supplied", () => { |
| 27 | +// Defense-in-depth: if a new call site lands without a trigger, it should |
| 28 | +// fall through to the safe default rather than spuriously injecting |
| 29 | +// heartbeat instructions. |
| 30 | +expect(shouldInjectHeartbeatPromptForTrigger(undefined)).toBe(false); |
| 31 | +}); |
| 32 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -4,13 +4,21 @@ type EmbeddedRunTriggerPolicy = {
|
4 | 4 | injectHeartbeatPrompt: boolean; |
5 | 5 | }; |
6 | 6 | |
| 7 | +// The heartbeat system prompt tells the model to reply exactly "HEARTBEAT_OK" |
| 8 | +// when nothing needs attention. It is only meaningful on heartbeat-triggered |
| 9 | +// runs; injecting it on user/manual/memory/overflow runs confuses smaller |
| 10 | +// models into pattern-matching the HEARTBEAT_OK output on real user messages |
| 11 | +// (delivery then suppresses the "reply", so the user sees silence) or into |
| 12 | +// fabricating "[object Object]" serialization errors as they try to reconcile |
| 13 | +// the heartbeat instruction with a non-heartbeat turn. See issue #69079 and |
| 14 | +// its parent #50797. Default off; heartbeat trigger explicitly opts in. |
7 | 15 | const DEFAULT_EMBEDDED_RUN_TRIGGER_POLICY: EmbeddedRunTriggerPolicy = { |
8 | | -injectHeartbeatPrompt: true, |
| 16 | +injectHeartbeatPrompt: false, |
9 | 17 | }; |
10 | 18 | |
11 | 19 | const EMBEDDED_RUN_TRIGGER_POLICY: Partial<Record<EmbeddedRunTrigger, EmbeddedRunTriggerPolicy>> = { |
12 | | -cron: { |
13 | | -injectHeartbeatPrompt: false, |
| 20 | +heartbeat: { |
| 21 | +injectHeartbeatPrompt: true, |
14 | 22 | }, |
15 | 23 | }; |
16 | 24 | |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。