Skip to content

Heartbeat replies show literal {model} instead of interpolated model name in responsePrefix #43064

Description

@yweiii

Description

When responsePrefix uses template variables like {model}, heartbeat replies output the literal placeholder text (e.g. {model}: The heartbeat just came in...) instead of the interpolated model name (e.g. gemini-2.5-flash: The heartbeat just came in...).

Normal user-initiated replies correctly interpolate {model}, {provider}, {thinkingLevel}, etc. via resolveResponsePrefixTemplate(). The heartbeat code path bypasses this interpolation.

Steps to reproduce

  1. Set responsePrefix: "{model}: " in a channel config (e.g. Telegram)
  2. Wait for a heartbeat alert reply (not HEARTBEAT_OK, an actual alert)
  3. Observe the reply starts with {model}: literally

Root cause

In src/infra/heartbeat-runner.ts, runHeartbeatOnce():

  1. Resolves responsePrefix from config as the raw template string ("{model}: ")
  2. Calls getReplyFromConfig() without passing onModelSelected — so no model context is captured
  3. Passes the raw un-interpolated template to normalizeHeartbeatReply() which prepends it literally

The normal reply path (src/channels/reply-prefix.ts) creates a prefixContext via createReplyPrefixContext(), wires up onModelSelected to capture model/provider info, and later calls resolveResponsePrefixTemplate() to interpolate. The heartbeat path skips all of this.

Expected behavior

Heartbeat replies should interpolate responsePrefix template variables the same way normal replies do.

Environment

  • OpenClaw v2026.2.17
  • Channel: Telegram (but affects all channels with template responsePrefix)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions