openclaw - ✅(Solved) Fix Heartbeat model leaks into user session status/runtime model [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#74217Fetched 2026-04-30 06:27:12
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
2
Author
Timeline (top)
cross-referenced ×4commented ×1

A heartbeat / async completion run that uses agents.defaults.heartbeat.model appears to persist its runtime model into the normal chat session (sessionEntry.model / modelProvider). After that, /status / session status reports the user-facing WhatsApp direct session as running the heartbeat model, even though normal turns are configured for the default model and fallbacks are empty.

This is confusing and makes it look like the agent unexpectedly routed normal user traffic through the heartbeat model / fallback path.

Root Cause

A heartbeat / async completion run that uses agents.defaults.heartbeat.model appears to persist its runtime model into the normal chat session (sessionEntry.model / modelProvider). After that, /status / session status reports the user-facing WhatsApp direct session as running the heartbeat model, even though normal turns are configured for the default model and fallbacks are empty.

This is confusing and makes it look like the agent unexpectedly routed normal user traffic through the heartbeat model / fallback path.

Fix Action

Fixed

PR fix notes

PR #51706: Agents: show runtime model in session status

Description (problem / solution / changelog)

Summary

  • Problem: session_status showed the configured default model even when the session store already recorded a different runtime model.
  • Why it matters: this made model routing, fallback, auth, and billing debugging misleading because session_status disagreed with openclaw sessions.
  • What changed: session_status now resolves its main model line from the runtime session model when no explicit per-session override is set, and the regression test covers that case.
  • What did NOT change (scope boundary): no model routing behavior changed, no auth flow changed, and no session persistence format changed.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #51526

User-visible / Behavior Changes

session_status now shows the runtime session model on the 🧠 Model: line when the session store has a persisted runtime model and there is no explicit per-session model override.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS 26.4
  • Runtime/container: local zsh shell
  • Model/provider: N/A
  • Integration/channel (if any): agent session_status
  • Relevant config (redacted): default model openai-codex/gpt-5.4, session store runtime model anthropic/claude-opus-4-6

Steps

  1. Create a session entry whose persisted runtime model differs from the configured default model.
  2. Run session_status for that session.
  3. Inspect the 🧠 Model: line.

Expected

  • The 🧠 Model: line matches the runtime model stored on the session entry.

Actual

  • Before: session_status showed the configured default model.
  • After: session_status shows the persisted runtime session model unless an explicit per-session override exists.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: confirmed the tool now feeds the status card with the runtime model identity resolver instead of the default-model-only path, and added a regression test that asserts anthropic/claude-opus-4-6 wins over default openai-codex/gpt-5.4.
  • Edge cases checked: explicit per-session modelOverride still remains higher priority than runtime fallback; usage provider lookup now follows the same selected provider as the card.
  • What you did not verify: in this Codex terminal, pnpm test -- src/agents/openclaw-tools.session-status.test.ts started Vitest but hung before process exit, so I did not get a clean local wrapper completion signal even though the regression test was added.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert commit 48959d6c8
  • Files/config to restore: src/agents/tools/session-status-tool.ts, src/agents/openclaw-tools.session-status.test.ts
  • Known bad symptoms reviewers should watch for: session_status showing the wrong model when runtime model and default model differ.

Risks and Mitigations

  • Risk: sessions with unusual persisted model strings but no provider may rely on provider inference behavior.
    • Mitigation: the change uses the existing shared resolveSessionModelIdentityRef(...) helper that already backs other session model displays.

Changed files

  • src/agents/openclaw-tools.session-status.test.ts (modified, +43/-0)
  • src/agents/tools/session-status-tool.ts (modified, +9/-3)
  • src/auto-reply/status.test.ts (modified, +58/-0)
  • src/auto-reply/status.ts (modified, +19/-3)

Code Example

{
  "agents.defaults.model": {
    "primary": "openai-codex/gpt-5.5",
    "fallbacks": []
  },
  "agents.defaults.heartbeat": {
    "every": "1h",
    "model": "xai/grok-4-1-fast-reasoning"
  }
}

---

Fallbacks (0):
- none

---

08:05Z model-snapshot provider=openai-codex modelApi=openai-codex-responses modelId=gpt-5.5
08:13Z model-snapshot provider=xai          modelApi=openai-responses       modelId=grok-4-1-fast
08:28Z model-snapshot provider=openai-codex modelApi=openai-codex-responses modelId=gpt-5.5

---

{
  "model": "grok-4-1-fast",
  "modelProvider": "xai",
  "modelOverride": null,
  "providerOverride": null,
  "modelOverrideSource": null
}

---

Model: xai/grok-4-1-fast

---

[context-overflow-diag] sessionKey=agent:<agent>:whatsapp:direct:<redacted> provider=openai-codex/gpt-5.5 ...
context overflow detected ... attempting auto-compaction for openai-codex/gpt-5.5
RAW_BUFFERClick to expand / collapse

Summary

A heartbeat / async completion run that uses agents.defaults.heartbeat.model appears to persist its runtime model into the normal chat session (sessionEntry.model / modelProvider). After that, /status / session status reports the user-facing WhatsApp direct session as running the heartbeat model, even though normal turns are configured for the default model and fallbacks are empty.

This is confusing and makes it look like the agent unexpectedly routed normal user traffic through the heartbeat model / fallback path.

Environment

  • OpenClaw: 2026.4.24 (2026.4.26 available, not tested yet)
  • macOS arm64
  • Channel: WhatsApp direct session
  • Runtime: OpenClaw Pi Default

Relevant config

{
  "agents.defaults.model": {
    "primary": "openai-codex/gpt-5.5",
    "fallbacks": []
  },
  "agents.defaults.heartbeat": {
    "every": "1h",
    "model": "xai/grok-4-1-fast-reasoning"
  }
}

openclaw models fallbacks list also returns:

Fallbacks (0):
- none

Observed timeline

In one WhatsApp direct session JSONL, model snapshots show:

08:05Z model-snapshot provider=openai-codex modelApi=openai-codex-responses modelId=gpt-5.5
08:13Z model-snapshot provider=xai          modelApi=openai-responses       modelId=grok-4-1-fast
08:28Z model-snapshot provider=openai-codex modelApi=openai-codex-responses modelId=gpt-5.5

The 08:13Z event was not a normal user-requested model switch. It was adjacent to an async command completion / heartbeat-style internal event whose instruction was effectively to handle internally and reply HEARTBEAT_OK only.

After that, the session store for the WhatsApp direct session contained:

{
  "model": "grok-4-1-fast",
  "modelProvider": "xai",
  "modelOverride": null,
  "providerOverride": null,
  "modelOverrideSource": null
}

/status then reported the direct user session as:

Model: xai/grok-4-1-fast

But gateway logs around later normal turns showed the real normal run path still using the configured default:

[context-overflow-diag] sessionKey=agent:<agent>:whatsapp:direct:<redacted> provider=openai-codex/gpt-5.5 ...
context overflow detected ... attempting auto-compaction for openai-codex/gpt-5.5

Expected behavior

One of these should happen:

  1. Heartbeat / internal async-completion runs should not persist their runtime model into the user-facing session's model / modelProvider; or
  2. Status should clearly distinguish:
    • configured/selected model for normal user turns, and
    • last runtime model used by an internal heartbeat/background event.

In particular, a heartbeat model should not make a direct user session look like it has switched to that model when no session model override exists and normal fallback lists are empty.

Actual behavior

A heartbeat/internal run using agents.defaults.heartbeat.model persisted xai/grok-4-1-fast into the direct session runtime fields. Status then reported that as the session model, causing apparent unexpected routing to Grok despite:

  • default model = openai-codex/gpt-5.5
  • configured fallbacks = []
  • no per-session model override

Notes

There may be a second, smaller naming/normalization oddity: config uses xai/grok-4-1-fast-reasoning, but the model snapshot/session status showed xai/grok-4-1-fast. That might be provider aliasing, but it adds to the confusion.

No private phone numbers, account ids, or user names are included here; session identifiers above are redacted.

extent analysis

TL;DR

The heartbeat model is persisting into the user-facing session, causing incorrect status reports, and a fix may involve modifying the heartbeat run to not update the session's model or enhancing the status report to distinguish between configured and runtime models.

Guidance

  • Review the heartbeat run configuration to ensure it does not update the session's model, potentially by using a separate model for heartbeat runs that does not affect the user-facing session.
  • Consider enhancing the status report to clearly distinguish between the configured model for normal user turns and the last runtime model used by an internal heartbeat/background event.
  • Investigate the naming/normalization oddity where the config uses xai/grok-4-1-fast-reasoning but the model snapshot/session status shows xai/grok-4-1-fast to ensure it does not contribute to the confusion.
  • Verify that the fallback lists are correctly configured and empty, as the issue mentions that normal fallback lists are empty.

Example

No code snippet is provided as the issue does not imply a specific code change, but rather a configuration or design adjustment.

Notes

The provided information suggests a potential issue with how the heartbeat model is handled in the session, but without further details on the implementation, it's difficult to provide a precise fix. The guidance points are intended to help investigate and potentially resolve the issue.

Recommendation

Apply a workaround to modify the heartbeat run configuration or enhance the status report, as upgrading to a fixed version is not explicitly mentioned as an option in the provided issue. This approach allows for a more targeted solution to the specific problem described.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of these should happen:

  1. Heartbeat / internal async-completion runs should not persist their runtime model into the user-facing session's model / modelProvider; or
  2. Status should clearly distinguish:
    • configured/selected model for normal user turns, and
    • last runtime model used by an internal heartbeat/background event.

In particular, a heartbeat model should not make a direct user session look like it has switched to that model when no session model override exists and normal fallback lists are empty.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Heartbeat model leaks into user session status/runtime model [1 pull requests, 1 comments, 2 participants]