openclaw - ✅(Solved) Fix Heartbeat schedules by time of day (daypart / variable intervals) [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#58581Fetched 2026-04-08 02:00:40
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Timeline (top)
commented ×1cross-referenced ×1referenced ×1

OpenClaw heartbeat currently uses a single fixed heartbeat.every interval. That works, but it is a blunt instrument.

It would be useful to support different heartbeat intervals at different times of day, so users can run heartbeats more frequently during active hours and less frequently during evenings / overnight.

Root Cause

OpenClaw heartbeat currently uses a single fixed heartbeat.every interval. That works, but it is a blunt instrument.

It would be useful to support different heartbeat intervals at different times of day, so users can run heartbeats more frequently during active hours and less frequently during evenings / overnight.

Fix Action

Fixed

PR fix notes

PR #58683: feat(heartbeat): add time-of-day schedule for variable intervals

Description (problem / solution / changelog)

Summary

  • Problem: Heartbeat uses a single fixed interval (heartbeat.every), forcing a tradeoff between responsiveness (low intervals, high token cost) and economy (high intervals, unresponsive during active work).
  • Why it matters: Users want different heartbeat frequencies at different times of day to align automation costs with actual operational rhythms.
  • What changed: Added heartbeat.schedule[] config array where each entry maps a time-of-day window to an interval. First matching window wins; falls back to every when no window matches. Reuses existing timezone infrastructure from activeHours.
  • What did NOT change (scope boundary): Existing configs without schedule behave identically. activeHours gate, in-flight request deferral, manual wake bypass all preserved. No breaking changes.

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 #58581
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

N/A - new feature.

Regression Test Plan (if applicable)

N/A - new feature with new test coverage:

  • src/infra/heartbeat-schedule.test.ts (15 tests): window matching, overnight wrap, first-match-wins, timezone, boundary resolution
  • src/config/zod-schema.agent-defaults.test.ts (6 tests): schema validation for valid/invalid schedule entries
  • src/infra/heartbeat-runner.scheduler.test.ts (1 test): schedule-based interval in the runner loop

User-visible / Behavior Changes

New optional config field heartbeat.schedule:

{
  heartbeat: {
    every: "30m",  // fallback
    schedule: [
      { start: "08:00", end: "18:00", every: "15m" },  // work hours
      { start: "18:00", end: "23:00", every: "30m" },  // evening
      { start: "23:00", end: "08:00", every: "2h" },   // overnight
    ],
  }
}

Diagram (if applicable)

Before:
[heartbeat tick] -> fixed intervalMs -> [next tick]

After:
[heartbeat tick] -> resolveScheduleIntervalMs(now) -> [dynamic intervalMs] -> [next tick]
                    |                                                          |
                    +-- also wakes at window boundaries for prompt transitions -+

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: macOS (Darwin 25.3.0)
  • Runtime: Node 23.3.0
  • Model/provider: N/A (config/scheduling change)

Steps

  1. Add schedule array to heartbeat config
  2. Set system time inside a schedule window
  3. Observe heartbeat fires at the window's interval, not the base every

Expected

  • Heartbeat uses 15m interval during 08:00-18:00 window
  • Heartbeat uses 2h interval during 23:00-08:00 window
  • Falls back to 30m when no window matches

Actual

  • Confirmed via unit tests (29 pass) and scheduler integration test (9 pass)

Evidence

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

Test results: 38 tests pass across 4 test files. pnpm build and pnpm check pass clean.

Human Verification (required)

  • Verified scenarios: schedule window matching, overnight wrap, first-match-wins, timezone resolution, boundary wakeup, fallback to every, Zod schema validation
  • Edge cases checked: empty schedule array, invalid times, invalid durations, zero-width windows, overlapping entries
  • What you did not verify: live gateway deployment with real heartbeat runs

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
  • Config/env changes? Yes (new optional heartbeat.schedule field)
  • Migration needed? No

Risks and Mitigations

  • Risk: Window boundary timer could fire unnecessarily when no schedule is configured
    • Mitigation: resolveNextWindowBoundaryMs returns null when no schedule exists, so the boundary check is skipped entirely

AI Disclosure

  • This PR was AI-assisted (Claude Code / Opus 4.6)
  • Fully tested (38 tests, build, lint all pass)
  • Author understands what the code does

Changed files

  • docs/gateway/heartbeat.md (modified, +35/-0)
  • src/config/schema.help.ts (modified, +10/-0)
  • src/config/schema.labels.ts (modified, +5/-0)
  • src/config/types.agent-defaults.ts (modified, +11/-0)
  • src/config/zod-schema.agent-defaults.test.ts (modified, +59/-0)
  • src/config/zod-schema.agent-runtime.ts (modified, +70/-0)
  • src/infra/heartbeat-active-hours.ts (modified, +100/-0)
  • src/infra/heartbeat-runner.scheduler.test.ts (modified, +32/-0)
  • src/infra/heartbeat-runner.ts (modified, +35/-6)
  • src/infra/heartbeat-schedule.test.ts (added, +233/-0)
  • src/infra/heartbeat-summary.ts (modified, +17/-0)

Code Example

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m", // fallback
        schedule: [
          { start: "08:00", end: "18:00", every: "15m" },
          { start: "18:00", end: "23:00", every: "30m" },
          { start: "23:00", end: "08:00", every: "2h" }
        ],
        timezone: "America/Los_Angeles"
      }
    }
  }
}

---

{
  agents: {
    defaults: {
      heartbeat: {
        profiles: {
          work: { every: "15m" },
          evening: { every: "30m" },
          night: { every: "2h" }
        },
        schedule: [
          { start: "08:00", end: "18:00", profile: "work" },
          { start: "18:00", end: "23:00", profile: "evening" },
          { start: "23:00", end: "08:00", profile: "night" }
        ]
      }
    }
  }
}
RAW_BUFFERClick to expand / collapse

Summary

OpenClaw heartbeat currently uses a single fixed heartbeat.every interval. That works, but it is a blunt instrument.

It would be useful to support different heartbeat intervals at different times of day, so users can run heartbeats more frequently during active hours and less frequently during evenings / overnight.

Problem

A fixed interval forces a tradeoff:

  • Set it low (ex: 5m) and you get good responsiveness, but unnecessary token burn during low-value hours
  • Set it high (ex: 30m or 2h) and you save cost, but lose responsiveness when active work is happening

In practice, users often want something like:

  • work hours: faster checks
  • evenings: slower checks
  • overnight: very infrequent or disabled

Example use cases

Example A — simple dayparts

  • 08:00–18:00 → every 15m
  • 18:00–23:00 → every 30m
  • 23:00–08:00 → every 2h or disabled

Example B — operator mode

  • 07:00–09:00 → every 10m
  • 09:00–17:00 → every 15m
  • 17:00–22:00 → every 30m
  • 22:00–07:00 → disabled except explicit wakeups

Example C — low-cost mode

  • workday → every 30m
  • off hours → every 2h
  • late night → disabled

Proposed config shape

Option 1: schedule blocks

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m", // fallback
        schedule: [
          { start: "08:00", end: "18:00", every: "15m" },
          { start: "18:00", end: "23:00", every: "30m" },
          { start: "23:00", end: "08:00", every: "2h" }
        ],
        timezone: "America/Los_Angeles"
      }
    }
  }
}

Option 2: named profiles

{
  agents: {
    defaults: {
      heartbeat: {
        profiles: {
          work: { every: "15m" },
          evening: { every: "30m" },
          night: { every: "2h" }
        },
        schedule: [
          { start: "08:00", end: "18:00", profile: "work" },
          { start: "18:00", end: "23:00", profile: "evening" },
          { start: "23:00", end: "08:00", profile: "night" }
        ]
      }
    }
  }
}

Suggested MVP

A minimal first version could be:

  • support heartbeat.schedule[]
  • first matching window wins
  • fallback to heartbeat.every if no schedule window matches
  • reuse existing timezone handling
  • preserve current requests-in-flight deferral behavior

Questions / edge cases

  • How should overnight wraparound windows be defined?
  • What happens on schedule boundaries if a run is already in flight?
  • Which interval applies if schedule blocks overlap?
  • Should wakeMode: "now" bypass quiet windows?
  • Should disabled windows still allow urgent system events?
  • How should DST transitions behave?

Why this would help

  • reduces cost during low-value hours
  • improves responsiveness during active work windows
  • better fits human/operator rhythms
  • makes heartbeat more practical for real monitoring/assistant workflows

A fixed interval is fine for simple setups, but daypart scheduling would make heartbeat significantly more flexible.

extent analysis

TL;DR

Implementing a scheduling system for heartbeat intervals, allowing for different intervals at different times of day, can be achieved by introducing a heartbeat.schedule configuration option.

Guidance

  • Introduce a schedule array within the heartbeat configuration to define time-based intervals, allowing for more flexibility in heartbeat frequency.
  • Define a fallback interval using heartbeat.every to ensure heartbeats continue even when no schedule window matches.
  • Consider implementing a "first matching window wins" approach to handle overlapping schedule blocks.
  • Reuse existing timezone handling to ensure schedule windows are correctly interpreted across different regions.

Example

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m", // fallback
        schedule: [
          { start: "08:00", end: "18:00", every: "15m" },
          { start: "18:00", end: "23:00", every: "30m" },
          { start: "23:00", end: "08:00", every: "2h" }
        ],
        timezone: "America/Los_Angeles"
      }
    }
  }
}

Notes

  • The implementation should consider edge cases such as overnight wraparound windows, schedule boundaries, and overlapping schedule blocks.
  • Additional considerations include how to handle DST transitions and whether disabled windows should allow urgent system events.

Recommendation

Apply a workaround by implementing a scheduling system, such as the proposed heartbeat.schedule configuration option, to allow for more flexible heartbeat intervals. This approach will enable users to define custom intervals for different times of day, reducing cost during low-value hours and improving responsiveness during active work windows.

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…

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 schedules by time of day (daypart / variable intervals) [1 pull requests, 1 comments, 2 participants]