Skip to content

feat(heartbeat): add skipIfRunActive option + cron directive tag sanitization#29659

Closed
Imccccc wants to merge 6 commits intoopenclaw:mainfrom
Imccccc:feat/heartbeat-skip-if-run-active
Closed

feat(heartbeat): add skipIfRunActive option + cron directive tag sanitization#29659
Imccccc wants to merge 6 commits intoopenclaw:mainfrom
Imccccc:feat/heartbeat-skip-if-run-active

Conversation

@Imccccc
Copy link
Copy Markdown
Contributor

@Imccccc Imccccc commented Feb 28, 2026

Summary

Implements feature request #29537: Add skipIfRunActive option to heartbeat configuration.

Changes

1. Heartbeat skipIfRunActive Feature

  • Config Type (src/config/types.agent-defaults.ts):

    • Added skipIfRunActive?: boolean to heartbeat config
  • Zod Schema (src/config/zod-schema.agent-runtime.ts):

    • Added skipIfRunActive to HeartbeatSchema for runtime validation
  • Heartbeat Runner (src/infra/heartbeat-runner.ts):

    • Import and use isEmbeddedPiRunActive() to check if an agent run is in progress
    • Skip heartbeat tick if skipIfRunActive: true and a run is active
    • Returns { status: "skipped", reason: "run-active" } when skipped
  • Tests (src/infra/heartbeat-runner.returns-default-unset.test.ts):

    • Added 4 unit tests covering all scenarios

2. Cron Directive Tag Sanitization

  • Cron Helpers (src/cron/isolated-agent/helpers.ts):
    • Added directive tag stripping in pickLastDeliverablePayload()
    • Prevents [[reply_to_current]] and [[audio_as_voice]] tags from leaking to users via direct cron/heartbeat delivery paths

Usage

{
  "agents": {
    "defaults": {
      "heartbeat": {
        "every": "30m",
        "skipIfRunActive": true
      }
    }
  }
}

When enabled, the heartbeat will not fire while the agent is actively executing (e.g., during long coding sessions, research chains, or multi-tool workflows).

Closes #29537

Tyler added 2 commits February 28, 2026 15:44
- Add skipIfRunActive config to heartbeat settings
- Skip heartbeat tick if an embedded Pi run is currently active
- Add unit tests for the new functionality
@openclaw-barnacle openclaw-barnacle bot added commands Command implementations size: M labels Feb 28, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2cf1810617

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

*
* Default: false.
*/
skipIfRunActive?: boolean;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add skipIfRunActive to heartbeat config schema

skipIfRunActive is introduced in the heartbeat config type and read by runHeartbeatOnce, but the runtime validator is still strict and does not allow this key (HeartbeatSchema in src/config/zod-schema.agent-runtime.ts only lists existing fields and uses .strict()). In practice, adding agents.defaults.heartbeat.skipIfRunActive to a real config will fail validation as an unrecognized property, so the new option cannot actually be configured by users.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 28, 2026

Greptile Summary

This PR adds a skipIfRunActive option to heartbeat configuration and includes a separate bug fix for status command context limits.

Main feature (skipIfRunActive):

  • Adds optional config to skip heartbeat ticks when an embedded Pi run is active
  • Implementation checks isEmbeddedPiRunActive(sessionId) before firing heartbeat
  • Returns { status: "skipped", reason: "run-active" } when skipped
  • Prevents heartbeat interruptions during active agent sessions

Secondary fix (context limit display):

  • Fixes status command showing wrong context tokens when runtime model differs from config model
  • Previously fell back to config model for context calculation, now correctly uses session's runtime model

Test coverage:

  • 4 comprehensive tests for skipIfRunActive feature covering all scenarios
  • 1 test validating the context limit fix

Both changes are well-implemented with appropriate tests.

Confidence Score: 5/5

  • This PR is safe to merge with no issues found
  • Implementation is straightforward and correct with comprehensive test coverage. Both the heartbeat feature and status fix are well-tested. No bugs, security issues, or logical errors detected. Code follows repository patterns and TypeScript best practices.
  • No files require special attention

Last reviewed commit: 463b2e0

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 463b2e0c7e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +628 to +629
if (heartbeat?.skipIfRunActive && sessionId && isEmbeddedPiRunActive(sessionId)) {
return { status: "skipped", reason: "run-active" };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry wake-triggered heartbeats after run-active skips

This new early return for skipIfRunActive runs before preflight inspects wake reasons, so wake-triggered heartbeats (including exec/cron events) can now be skipped while a run is active and then silently dropped until the next interval. In src/infra/heartbeat-wake.ts:156-164, retries are only scheduled for requests-in-flight, not run-active, so users with longer heartbeat intervals can see delayed or missed timely event summaries despite a wake already being queued.

Useful? React with 👍 / 👎.

thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Feb 28, 2026
…cation, hook bridge

Comprehensive cron subsystem reliability overhaul addressing 9 open issues:

**Configurable retry policy (openclaw#29527, openclaw#29546)**
- Extract backoff logic into `retry-policy.ts` with configurable `cron.retryBackoff`
  schedule and `cron.stuckRunTimeoutMs` config fields
- Classify execution errors as transient (retry with backoff) or terminal
  (disable job immediately) to prevent futile retry storms
- Add Zod validation for new config fields

**Fix delivery status reporting (openclaw#29660)**
- `resolveDeliveryStatus` now accepts `deliveryAttempted` and returns
  "not-delivered" when delivery was attempted but not confirmed
- Add explicit `delivered: false` to all error paths in delivery-dispatch.ts
- Pass `deliveryAttempted` through the full result chain

**Configurable response prefix (openclaw#29687)**
- Add `responsePrefix` to CronServiceDeps (defaults to "Cron")
- Use dep-injected prefix for main session summary messages

**Strip directive tags from cron output (openclaw#29646)**
- Strip `[[reply_to_current]]`, `[[reply_to:<id>]]`, `[[audio_as_voice]]`
  from isolated cron run output before delivery

**Startup schedule preservation (openclaw#29690)**
- Use maintenance-only recompute in `start()` to avoid advancing past-due
  `nextRunAtMs` values for jobs that `runMissedJobs` intentionally skipped
- Clear `nextRunAtMs` for interrupted jobs so they get fresh schedule computation

**Internal hook bridge (openclaw#29682)**
- Add "cron" to `InternalHookEventType` with `CronExecutionHookEvent` type
- Bridge `emitJobFinished` to `triggerInternalHook` so extensions can react
  to cron job completions (alerting, chaining, etc.)
- Add `isCronExecutionEvent` type guard

**Per-job skip-if-active flag (openclaw#29659)**
- Add `skipIfRunActive?: boolean` to CronJob type for semantic overlap prevention

Tests: 290 cron tests + 21 new retry-policy tests pass.

Fixes: openclaw#29527, openclaw#29546, openclaw#29601, openclaw#29646, openclaw#29659, openclaw#29660, openclaw#29682, openclaw#29687, openclaw#29690
Tyler added 2 commits February 28, 2026 22:13
Prevent [[reply_to_current]] and [[audio_as_voice]] tags from leaking
to users via direct cron/heartbeat delivery paths. The strip pass already
handles summary/outputText/synthesizedText, but direct delivery uses
pickLastDeliverablePayload which was not sanitized.

Fixes PR review comment.
@Imccccc Imccccc changed the title feat(heartbeat): add skipIfRunActive option (#29537) feat(heartbeat): add skipIfRunActive + strip directive tags Feb 28, 2026
@Imccccc Imccccc changed the title feat(heartbeat): add skipIfRunActive + strip directive tags feat(heartbeat): add skipIfRunActive option + cron directive tag sanitization Feb 28, 2026
@Takhoffman
Copy link
Copy Markdown
Contributor

Thanks for the contribution.

Closing this PR because it bundles multiple unrelated behavior changes in one patch:

  • new heartbeat scheduling option (skipIfRunActive),
  • directive-tag sanitization in cron delivery,
  • unrelated status summary model fallback change.

This scope is too broad/risky for safe landing. Please split into small focused PRs with isolated regression coverage.

@Takhoffman Takhoffman closed this Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: heartbeat.skipIfRunActive — skip heartbeat when an agent run is in progress

2 participants