Skip to content

fix(heartbeat): detect local exec completed/failed events in heartbeat prompt#26213

Closed
Sid-Qin wants to merge 1 commit into
openclaw:mainfrom
Sid-Qin:fix/exec-completed-duplicate-reply-25959
Closed

fix(heartbeat): detect local exec completed/failed events in heartbeat prompt#26213
Sid-Qin wants to merge 1 commit into
openclaw:mainfrom
Sid-Qin:fix/exec-completed-duplicate-reply-25959

Conversation

@Sid-Qin

@Sid-Qin Sid-Qin commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: "Exec completed" system messages cause duplicate user message processing — agent generates two identical replies when a background exec finishes
  • Why it matters: Users receive duplicate messages, wasting API tokens and causing confusion
  • What changed: isExecCompletionEvent now matches "exec completed" and "exec failed" (local exec events from bash-tools.exec-runtime) in addition to "exec finished" (node exec events)
  • What did NOT change: Exec event prompt content, heartbeat flow, system event queuing

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

When a background exec process completes, the agent now correctly uses the exec-specific prompt ("relay the command output to the user") instead of the generic heartbeat prompt. This prevents the agent from misinterpreting echoed command output as a new user request.

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

  • Integration/channel: Feishu, Telegram, or any channel
  • Relevant config: Background exec enabled

Steps

  1. Send a message that triggers a background exec
  2. Wait for exec to complete
  3. Observe agent reply

Expected

  • Single reply relaying the exec result

Actual (before fix)

  • Two identical replies — one for the original message, one triggered by the misinterpreted exec completion event

Evidence

  • Failing test/log before + passing after
  • New tests: isExecCompletionEvent detects "Exec completed", "Exec failed", and "Exec finished"
  • Updated test: isCronSystemEvent correctly rejects all three exec event formats
  • All 101 heartbeat tests pass

Human Verification (required)

  • Verified scenarios: local exec completed, local exec failed, node exec finished, cron events not affected
  • Edge cases checked: exec output containing user message content, empty exec output
  • What you did not verify: live exec workflow end-to-end (unit tests with mocked events only)

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert commit 190455c
  • Files/config to restore: src/infra/heartbeat-events-filter.ts
  • Known bad symptoms: Exec completion events being misclassified as cron events (would affect cron prompt selection)

Risks and Mitigations

  • Risk: Strings containing "exec completed" or "exec failed" in non-exec contexts could be misidentified
    • Mitigation: The pattern matches the specific format emitted by maybeNotifyOnExitExec completed/failed (id, exitLabel) — which is unlikely to appear in regular cron or user content

Greptile Summary

This PR fixes a bug where local exec completion events ("Exec completed" and "Exec failed") were misclassified as cron events, causing duplicate agent replies.

The fix updates isExecCompletionEvent in src/infra/heartbeat-events-filter.ts:86-93 to detect all three exec event formats:

  • Exec finished (node exec events from server-node-events.ts)
  • Exec completed (local exec success from bash-tools.exec-runtime.ts:238)
  • Exec failed (local exec failure from bash-tools.exec-runtime.ts:238)

Key changes:

  • Updated isExecCompletionEvent to use case-insensitive matching for all three patterns
  • Added comprehensive tests for all three exec completion formats
  • Updated isCronSystemEvent tests to verify exec events are properly excluded

Impact:
When a background exec completes, the heartbeat system (via heartbeat-runner.ts:579-582) now correctly applies the exec-specific prompt instead of the generic cron prompt, preventing the agent from misinterpreting command output as new user requests.

Confidence Score: 5/5

  • This PR is safe to merge with no identified risks
  • The change is minimal, well-tested, and directly addresses the reported issue. The logic correctly identifies all three exec event formats emitted by the codebase, and the test coverage is comprehensive
  • No files require special attention

Last reviewed commit: 190455c

…t prompt

isExecCompletionEvent only matched "exec finished" (node exec events)
but missed "exec completed" and "exec failed" (local exec events from
bash-tools.exec-runtime).  This caused exec completion heartbeats to
use the generic heartbeat prompt instead of the exec-specific one,
leading the agent to misinterpret echoed command output as a new user
message and generate duplicate replies.

Closes openclaw#25959

Co-authored-by: Cursor <cursoragent@cursor.com>
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: XS stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] "Exec completed" system message causes duplicate user message processing

2 participants