← Back to PRs

#23521: heartbeat: prevent model-override from bleeding into main session state

by harshang03 open 2026-02-22 11:26 View on GitHub →
size: S
## 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