← Back to PRs

#21615: fix(tui): preserve main session model during heartbeat model override

by lailoo open 2026-02-20 04:54 View on GitHub →
size: S experienced-contributor
## Summary - **Bug**: TUI status bar shows the heartbeat model instead of the main agent model after a heartbeat run - **Root cause**: `persistSessionUsageUpdate()` in `session-usage.ts` unconditionally writes `modelUsed`/`providerUsed` to the session entry, so a heartbeat with a temporary model override overwrites the main session's model fields - **Fix**: Skip persisting model/provider when the run is a heartbeat with a `heartbeatModelOverride` Fixes #21524 ## Problem When a heartbeat is configured with a different model (e.g. `heartbeat.model: "openrouter/google/gemini-2.0-flash-001"`), the heartbeat runs in the main session and calls `persistRunSessionUsage()` with the heartbeat model. This overwrites `entry.model` and `entry.modelProvider` on the session entry. The TUI status bar reads these fields via `resolveSessionModelRef()` and displays the heartbeat model instead of the main agent's configured model. The heartbeat runner already restores `updatedAt` after a heartbeat run (`restoreHeartbeatUpdatedAt`), but the model/provider fields were not similarly protected. **Before fix:** ``` Main agent model: anthropic/claude-sonnet-4.6 Heartbeat model: openrouter/gemini-2.0-flash-001 TUI status bar shows: openrouter/gemini-2.0-flash-001 (wrong) ``` ## Changes - `src/auto-reply/reply/agent-runner.ts` — When `isHeartbeat && opts.heartbeatModelOverride`, pass `undefined` for `modelUsed`/`providerUsed` to `persistRunSessionUsage()` so the session entry retains the main agent's model - `src/auto-reply/reply/session-usage.heartbeat-model-isolation.test.ts` — Regression test covering: heartbeat omits model (preserved), normal run updates model, heartbeat without override preserves model **After fix:** ``` Main agent model: anthropic/claude-sonnet-4.6 Heartbeat model: openrouter/gemini-2.0-flash-001 TUI status bar shows: anthropic/claude-sonnet-4.6 (correct) ``` ## Test plan - [x] New test: 3 cases covering heartbeat model isolation - [x] All 56 existing heartbeat tests pass - [x] All 38 session-utils tests pass - [x] Lint passes (0 warnings, 0 errors) - [x] Format check passes on changed files ## Effect on User Experience **Before:** After a heartbeat runs with a different model, the TUI status bar shows the heartbeat model (e.g. gemini-flash) instead of the main agent model (e.g. claude-sonnet). Users think their main session is misconfigured. **After:** The TUI status bar always shows the main agent's model, regardless of which model the heartbeat used. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes a bug where the TUI status bar incorrectly displayed the heartbeat model instead of the main session model after a heartbeat ran with a temporary model override. **Changes:** - Modified `agent-runner.ts` to conditionally pass `undefined` for `modelUsed`/`providerUsed` when `isHeartbeat && opts?.heartbeatModelOverride` is true, preventing the heartbeat model from overwriting the session entry - Added comprehensive regression test covering three scenarios: heartbeat with override (preserves model), normal run (updates model), and heartbeat without override (preserves model) - Updated CHANGELOG.md with user-facing fix description The fix leverages existing fallback logic in `persistSessionUsageUpdate()` that uses `?? entry.model` and `?? entry.modelProvider`, ensuring the session retains its original model configuration when undefined values are passed. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk - The fix is narrowly scoped to a specific bug with clear root cause analysis. The implementation correctly uses existing fallback logic, adds comprehensive test coverage for all scenarios, and follows established patterns in the codebase (similar to `restoreHeartbeatUpdatedAt`). The change only affects heartbeat runs with model overrides and preserves all other behavior. - No files require special attention <sub>Last reviewed commit: 8fb533d</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs