#23521: heartbeat: prevent model-override from bleeding into main session state
size: S
Cluster:
Heartbeat Model Override Fixes
## Summary
Fixes #22133 — when `heartbeat.model` is configured with a local Ollama (or any non-default) model, the heartbeat run shares the main session key but uses a different provider/model with a different context window. After the heartbeat completed, `persistRunSessionUsage` was unconditionally writing the heartbeat model's `model`, `modelProvider`, and `contextTokens` back to the session entry, which caused:
- The main session's "current model" display to switch to the heartbeat model (e.g. `ollama/qwen3:8b` instead of the configured main model).
- The context-window utilisation counter to reset to the heartbeat model's context size (e.g. 16 k instead of the main model's larger window), causing compaction to trigger far too early and disrupting session continuity.
## Changes
**`src/auto-reply/reply/session-usage.ts`**
Added `skipModelAndContextUpdate?: boolean` param to `persistSessionUsageUpdate`. When true, `model`, `modelProvider`, `contextTokens`, `totalTokens`, and `totalTokensFresh` are omitted from the session-entry patch — only billing counters (`inputTokens`/`outputTokens`) are written.
**`src/auto-reply/reply/session-run-accounting.ts`**
Threads `skipModelAndContextUpdate` through `persistRunSessionUsage` to `persistSessionUsageUpdate`.
**`src/auto-reply/reply/agent-runner.ts`**
Passes `skipModelAndContextUpdate: isHeartbeat && Boolean(opts?.heartbeatModelOverride)` to `persistRunSessionUsage` so the guard activates precisely when a heartbeat explicitly resolved a different model from `heartbeat.model` config.
**`src/infra/heartbeat-runner.ts`**
1. Emits a `debug` log when a model-override heartbeat run begins, so operators can confirm the correct model is being used.
2. Snapshots the session's `model`, `modelProvider`, and `contextTokens` before calling `getReplyFromConfig`.
3. Adds `restoreHeartbeatModelState` as a belt-and-suspenders defensive restore: after `getReplyFromConfig` returns, if the session entry now carries the heartbeat model name, it is reverted to the pre-heartbeat values. This secondary guard ensures correctness even if the primary `skipModelAndContextUpdate` path is ever bypassed by future code changes.
Fixes https://github.com/openclaw/openclaw/issues/22133
Most Similar PRs
#22277: fix: prevent heartbeat model override from bleeding into main session
by zhangjunmengyang · 2026-02-21
83.8%
#9429: fix: skip session model override for heartbeat runs
by dbottme · 2026-02-05
80.3%
#21615: fix(tui): preserve main session model during heartbeat model override
by lailoo · 2026-02-20
77.5%
#19772: Honor configured heartbeat model overrides in all heartbeat runners
by harshang03 · 2026-02-18
74.2%
#12837: fix(heartbeat): suppress HEARTBEAT_OK token delivery
by JBrady · 2026-02-09
69.3%
#15575: fix(heartbeat): suppress prefixed HEARTBEAT_OK ack replies (#15505)
by TsekaLuk · 2026-02-13
68.7%
#17414: fix(sessions): refresh contextTokens when model override changes
by michaelbship · 2026-02-15
67.9%
#9721: fix: heartbeat model override not working for per-agent config (#9556)
by divol89 · 2026-02-05
67.5%
#14241: fix(heartbeat): propagate originating session key for exec event qu...
by aldoeliacim · 2026-02-11
66.6%
#21232: fix(heartbeat): preserve real delivery target in session context
by sleitor · 2026-02-19
66.5%