openclaw - ✅(Solved) Fix Regression: heartbeat delivery can leak raw Codex tool/XML scaffolding into chat [4 pull requests, 2 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#81369Fetched 2026-05-14 03:32:53
View on GitHub
Comments
2
Participants
2
Timeline
6
Reactions
1
Timeline (top)
cross-referenced ×4commented ×2

Heartbeat delivery can post raw internal harness scaffolding to a visible chat channel instead of suppressing it or reducing it to final user-facing assistant text.

Observed leaked examples include:

<tool_calls>
<tool_calls>
<tool_calls>

and:

<file_contents path='.../HEARTBEAT.md' isStale=false isFullFile=true>
 1|# HEARTBEAT.md
 ...
</file_contents>

This appears to be a regression in the heartbeat runner / Codex app-server integration: heartbeat output is treated as deliverable final text even when it consists of protocol/tool scaffolding.

Root Cause

Suspected Root Cause

Fix Action

Fixed

PR fix notes

PR #81373: fix: sanitize heartbeat protocol scaffolding

Description (problem / solution / changelog)

Summary

  • sanitize heartbeat delivery text with the canonical assistant-visible text sanitizer
  • strip raw <file_contents ...> blocks and standalone tool-call tag lines from assistant-visible text
  • add regression coverage for heartbeat payloads containing repeated <tool_calls>, file contents wrappers, mixed visible text + scaffolding, and HEARTBEAT_OK
  • refresh from current main and clear existing lint/deadcode/plugin-contract gates in the merged tree
  • remove the unused direct audio-decode dependency from the WhatsApp extension manifest

Fixes #81369.

Tests

  • node scripts/run-vitest.mjs run --config test/vitest/vitest.config.ts --project=infra --project=shared-core src/infra/heartbeat-runner.returns-default-unset.test.ts src/shared/text/assistant-visible-text.test.ts
  • node node_modules/vitest/vitest.mjs run src/shared/text/assistant-visible-text.test.ts --environment node --pool forks --passWithNoTests=false
  • pnpm lint --threads=8
  • pnpm deadcode:unused-files
  • node scripts/run-vitest.mjs run --config test/vitest/vitest.contracts-plugin.config.ts src/plugins/contracts/extension-runtime-dependencies.contract.test.ts -t "extensions/whatsapp does not keep unused direct runtime dependencies"

Real behavior proof

  • Behavior or issue addressed: heartbeat delivery no longer posts raw Codex protocol scaffolding such as repeated <tool_calls> tags or <file_contents ...> blocks to chat.
  • Real environment tested: local OpenClaw checkout on macOS, Node 24, using redacted heartbeat leak payloads and the production assistant-visible sanitizer now called by heartbeat delivery.
  • Exact steps or command run after this patch: ran node --import tsx from the OpenClaw checkout to execute sanitizeAssistantVisibleText against redacted leak-shaped payloads after the patch.
  • Evidence after fix: Terminal transcript:
    $ node --import tsx - <<'EOF'
    import { sanitizeAssistantVisibleText } from './src/shared/text/assistant-visible-text.ts';
    const samples = {
      toolOnly: ['<tool_calls>', '<tool_calls>'].join('\\n'),
      fileOnly: ["<file_contents path='/redacted/HEARTBEAT.md' isStale=false isFullFile=true>", ' 1|# HEARTBEAT.md', '</file_contents>'].join('\\n'),
      mixed: ['Visible alert', '<tool_calls>', "<file_contents path='/redacted/HEARTBEAT.md' isStale=false isFullFile=true>", ' 1|# HEARTBEAT.md', '</file_contents>', 'Done'].join('\\n'),
    };
    for (const [name, input] of Object.entries(samples)) {
      console.log(`${name}: ${JSON.stringify(sanitizeAssistantVisibleText(input))}`);
    }
    EOF
    toolOnly: ""
    fileOnly: ""
    mixed: "Visible alert\\n\\nDone"
  • Observed result after fix: protocol-only payloads sanitize to an empty string, so heartbeat delivery suppresses them; mixed payloads preserve only user-visible text.
  • What was not tested: no known gaps for the sanitizer/delivery regression path; channel-provider end-to-end delivery was covered by the existing heartbeat runner harness rather than a live Discord post.

Changed files

  • extensions/whatsapp/package.json (modified, +0/-1)
  • pnpm-lock.yaml (modified, +2/-103)
  • scripts/deadcode-unused-files.allowlist.mjs (modified, +1/-0)
  • scripts/generate-plugin-inventory-doc.mjs (modified, +2/-1)
  • src/infra/heartbeat-runner.returns-default-unset.test.ts (modified, +102/-0)
  • src/infra/heartbeat-runner.ts (modified, +3/-1)
  • src/shared/text/assistant-visible-text.test.ts (modified, +15/-4)
  • src/shared/text/assistant-visible-text.ts (modified, +50/-0)

PR #81434: fix: sanitize heartbeat Codex scaffolding

Description (problem / solution / changelog)

Summary

  • strip Codex XML scaffolding such as <file_contents ...> blocks and standalone <tool_calls> / <function_calls> marker lines from the canonical assistant-visible sanitizer
  • apply the canonical sanitizer on heartbeat payload and heartbeat runner delivery paths so protocol-only heartbeat replies are suppressed instead of delivered
  • add regression coverage for mixed visible text, protocol-only scaffolding, fenced-code preservation, and heartbeat acknowledgements with trailing scaffolding

Fixes #81369.

Note: #81373 attempted a similar fix but was closed unmerged; this PR is scoped to heartbeat/user-visible sanitization only and avoids unrelated dependency or lockfile changes.

Real behavior proof

  • Behavior or issue addressed: Heartbeat delivery could leak raw Codex protocol scaffolding such as repeated <tool_calls> marker lines or <file_contents ...> blocks into user-visible chat.
  • Real environment tested: Local OpenClaw checkout at commit d076a884 on Linux, Node v24.14.1, pnpm 11.1.0.
  • Exact steps or command run after this patch: Ran node --import tsx from the OpenClaw checkout and invoked the production sanitizeAssistantVisibleText function against redacted heartbeat-shaped payloads containing repeated tool markers, file contents wrappers, mixed visible text, and a fenced XML example.
  • Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output): Copied terminal output from the local checkout:
$ git rev-parse --short HEAD && node --version && pnpm --version
d076a884
v24.14.1
11.1.0

$ node --import tsx - <<'NODE_PROOF'
import { sanitizeAssistantVisibleText } from './src/shared/text/assistant-visible-text.ts';

const samples = {
  protocolOnly: ['<tool_calls>', '<tool_calls>', '<tool_calls>'].join('\n'),
  fileOnly: [
    '<file_contents path="/redacted/workspace/HEARTBEAT.md" isStale=false isFullFile=true>',
    ' 1|# HEARTBEAT.md',
    ' 2|internal heartbeat notes',
    '</file_contents>',
  ].join('\n'),
  mixedVisible: [
    'Visible intro',
    '<tool_calls>',
    '<file_contents path="/redacted/workspace/HEARTBEAT.md" isStale=false isFullFile=true>',
    ' 1|# HEARTBEAT.md',
    '</file_contents>',
    'Visible outro',
  ].join('\n'),
  fencedExample: [
    '```xml',
    '<file_contents path="/tmp/example.md">',
    'example',
    '</file_contents>',
    '```',
    'Visible answer',
  ].join('\n'),
};

for (const [name, input] of Object.entries(samples)) {
  console.log(`${name}: ${JSON.stringify(sanitizeAssistantVisibleText(input))}`);
}
NODE_PROOF
protocolOnly: ""
fileOnly: ""
mixedVisible: "Visible intro\n\nVisible outro"
fencedExample: "```xml\n<file_contents path=\"/tmp/example.md\">\nexample\n</file_contents>\n```\nVisible answer"
  • Observed result after fix: Protocol-only heartbeat-shaped payloads sanitize to an empty string, so delivery paths can suppress them; mixed payloads preserve only visible user-facing text; fenced documentation examples are preserved.
  • What was not tested: No live Discord or WhatsApp post was sent from this machine; the proof exercises the production sanitizer directly, and the heartbeat delivery path is covered by the added runner regression case.
  • Before evidence (optional but encouraged): The linked issue includes the original reported heartbeat leak context and redacted environment details.

Tests

  • pnpm vitest run src/shared/text/assistant-visible-text.test.ts src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts src/auto-reply/reply/agent-runner-payloads.test.ts src/infra/heartbeat-runner.returns-default-unset.test.ts
    • 4 files passed, 253 tests passed
  • ./node_modules/.bin/oxfmt --check --threads=1 src/infra/heartbeat-runner.returns-default-unset.test.ts src/infra/heartbeat-runner.ts src/shared/text/assistant-visible-text.ts src/shared/text/assistant-visible-text.test.ts src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts src/auto-reply/reply/agent-runner-payloads.ts src/auto-reply/reply/agent-runner-payloads.test.ts
  • git diff --check
  • pnpm check:changed

Known upstream test failures

  • pnpm test:changed currently fails in the agents shard on files unrelated to this PR:
    • src/agents/pi-auth-json.test.ts: mocked ./auth-profiles/external-auth.js is missing the newer syncPersistedExternalCliAuthProfiles export
    • src/agents/tools/web-fetch.provider-fallback.test.ts: SsrFBlockedError: Blocked: resolves to private/internal/special-use IP address
  • To verify these are not caused by this branch, I stashed this PR's changes and reran pnpm vitest run src/agents/pi-auth-json.test.ts src/agents/tools/web-fetch.provider-fallback.test.ts on clean origin/main; the same failures reproduced there.

Changed files

  • src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts (modified, +18/-0)
  • src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts (modified, +3/-1)
  • src/auto-reply/reply/agent-runner-payloads.test.ts (modified, +32/-0)
  • src/auto-reply/reply/agent-runner-payloads.ts (modified, +4/-4)
  • src/infra/heartbeat-runner.returns-default-unset.test.ts (modified, +128/-0)
  • src/infra/heartbeat-runner.ts (modified, +12/-2)
  • src/shared/text/assistant-visible-text.test.ts (modified, +31/-0)
  • src/shared/text/assistant-visible-text.ts (modified, +68/-0)

PR #81441: fix: sanitize heartbeat protocol scaffolding

Description (problem / solution / changelog)

Summary

  • sanitize legacy heartbeat delivery text with the canonical assistant-visible text sanitizer
  • strip raw <file_contents ...> blocks and standalone tool-call tag lines from assistant-visible text
  • require the structured heartbeat_respond tool in heartbeat-tool mode; ordinary assistant text/media is ignored when the tool is not called
  • preserve internal runtime failure notices that are explicitly marked deliverable despite message-tool source suppression
  • add regression coverage for repeated <tool_calls>, file contents wrappers, mixed visible text + scaffolding, HEARTBEAT_OK, legacy media-bearing payloads, and heartbeat-tool-mode non-tool text/media

Fixes #81369.

Tests

  • CI=1 NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_VITEST_MAX_WORKERS=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 node scripts/run-vitest.mjs run --config test/vitest/vitest.config.ts --project=infra --project=shared-core src/infra/heartbeat-runner.returns-default-unset.test.ts src/infra/heartbeat-runner.tool-response.test.ts src/shared/text/assistant-visible-text.test.ts
    • Result: 2 files passed, 52 tests passed
  • CI=1 NODE_OPTIONS=--max-old-space-size=4096 node node_modules/vitest/vitest.mjs run src/shared/text/assistant-visible-text.test.ts --environment node --pool forks --passWithNoTests=false
    • Result: 1 file passed, 79 tests passed
  • git diff --check
  • git diff --exit-code

Real behavior proof

  • Behavior or issue addressed: heartbeat delivery no longer posts raw Codex protocol scaffolding such as repeated <tool_calls> tags or <file_contents ...> blocks to visible chat. In heartbeat-tool mode there is no ordinary reply fallback: the agent either calls heartbeat_respond, or no heartbeat notification is delivered.

  • Real environment tested: local OpenClaw checkout on macOS, Node 26, using redacted heartbeat leak payloads and the production assistant-visible sanitizer.

  • Exact steps or command run after this patch: ran this command from the patched OpenClaw checkout:

    node --import tsx - <<'EOF'
    import { sanitizeAssistantVisibleText } from './src/shared/text/assistant-visible-text.ts';
    const samples = {
      toolOnly: ['<tool_calls>', '<tool_calls>'].join('\\n'),
      fileOnly: ["<file_contents path='/redacted/HEARTBEAT.md' isStale=false isFullFile=true>", ' 1|# HEARTBEAT.md', '</file_contents>'].join('\\n'),
      mixed: ['Visible alert', '<tool_calls>', "<file_contents path='/redacted/HEARTBEAT.md' isStale=false isFullFile=true>", ' 1|# HEARTBEAT.md', '</file_contents>', 'Done'].join('\\n'),
    };
    for (const [name, input] of Object.entries(samples)) {
      console.log(`${name}: ${JSON.stringify(sanitizeAssistantVisibleText(input))}`);
    }
    EOF
  • Evidence after fix: terminal output from the patched checkout:

    toolOnly: ""
    fileOnly: ""
    mixed: "Visible alert\n\nDone"
  • Observed result after fix: protocol-only legacy payloads sanitize to an empty string and suppress delivery when there is no media; mixed legacy payloads preserve only user-visible text; heartbeat-tool mode ignores ordinary text/media if heartbeat_respond is not called.

  • What was not tested: live Discord delivery was not re-run for this PR update; channel-provider end-to-end behavior is covered by the heartbeat runner harness and the real proof above exercises the production sanitizer on leak-shaped payloads.

Scope note: this PR intentionally does not touch dependency manifests, lockfiles, generated inventory files, or unrelated CI/deadcode cleanups.

Changed files

  • src/infra/heartbeat-runner.returns-default-unset.test.ts (modified, +165/-0)
  • src/infra/heartbeat-runner.tool-response.test.ts (modified, +42/-0)
  • src/infra/heartbeat-runner.ts (modified, +22/-8)
  • src/shared/text/assistant-visible-text.test.ts (modified, +29/-0)
  • src/shared/text/assistant-visible-text.ts (modified, +49/-0)

PR #81494: [AI-assisted] fix(heartbeat): sanitize scaffolding before delivery

Description (problem / solution / changelog)

Summary

  • Problem: heartbeat delivery could pass raw Codex/tool scaffolding like <tool_calls> or <file_contents ...> through to visible chat output.
  • Why it matters: scaffolding-only heartbeat output is noisy and can expose local path shapes or file excerpts.
  • What changed: heartbeat replies are sanitized before delivery, and the assistant-visible sanitizer now strips file_contents blocks plus bare tool-call container lines.
  • What did NOT change: no provider routing, live channel delivery, credentials, network calls, or tool execution behavior changed.

Change Type

  • Bug fix
  • Security hardening

Scope

  • Gateway / orchestration
  • Skills / tool execution
  • Integrations

Linked Issue/PR

  • Closes: #81369
  • Related: N/A
  • Fixes bug/regression: Yes

Real behavior proof

  • Behavior or issue addressed: heartbeat delivery should not send raw <tool_calls> or <file_contents ...> scaffolding to visible chat output.
  • Real environment tested: local OpenClaw source checkout on Windows, Node v24.14.0, real runHeartbeatOnce path with the test Telegram channel adapter; no external API credentials and no live chat credentials.
  • Exact steps or command run after this patch:
node --import tsx <local-proof-script>
node scripts/test-projects.mjs src/shared/text/assistant-visible-text.test.ts src/infra/heartbeat-runner.respects-ackmaxchars-heartbeat-acks.test.ts
  • Evidence after fix:
[sessions/store] pruned stale session entries
{
  "scenario": "heartbeat delivery sanitizes assistant-visible scaffolding",
  "inputContains": {
    "visibleText": true,
    "toolCallsTag": true,
    "fileContentsTag": true
  },
  "resultStatus": "ran",
  "deliveredCallCount": 1,
  "deliveredText": "Visible alert",
  "deliveryOmits": {
    "toolCallsTag": true,
    "fileContentsTag": true,
    "heartbeatFileContents": true,
    "internalScaffoldLine": true
  },
  "verdict": "PASS"
}

Test Files  1 passed (1)
Tests  80 passed (80)

Test Files  1 passed (1)
Tests  15 passed (15)

[test] passed 2 Vitest shards in 20.66s
  • Observed result after fix: the real heartbeat delivery path received a reply containing visible text plus raw tool/file scaffolding, ran, called the Telegram test sender once, and delivered exactly Visible alert with the scaffolding omitted. Scaffolding-only heartbeat replies are also suppressed by the targeted regression tests.
  • What was not tested: live Discord heartbeat delivery, live gateway run, live Codex app-server run, and full repo check.
  • Before evidence: issue #81369 includes the reported before screenshot and redacted examples.

Root Cause

  • Root cause: heartbeat reply normalization only handled heartbeat ack tokens and response prefixes before delivery; it did not run the canonical assistant-visible text sanitizer on the reply text.
  • Missing detection / guardrail: the sanitizer did not explicitly strip <file_contents ...> blocks or bare <tool_calls> / <function_calls> container lines.
  • Contributing context: the regression appears when Codex/app-server heartbeat output contains protocol/tool scaffolding as plain text.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
  • Target test or file:
    • src/shared/text/assistant-visible-text.test.ts
    • src/infra/heartbeat-runner.respects-ackmaxchars-heartbeat-acks.test.ts
  • Scenario the test should lock in: raw scaffolding-only heartbeat replies are not delivered, and mixed text plus scaffolding delivers only the real visible text.
  • Why this is the smallest reliable guardrail: it covers both the shared sanitizer and the heartbeat delivery normalization path without requiring live channel credentials.
  • Existing related coverage: existing heartbeat ack tests covered HEARTBEAT_OK suppression, but not raw tool/file scaffolding.

User-visible / Behavior Changes

Heartbeat alerts no longer show raw internal tool/file scaffolding. If a heartbeat reply sanitizes to empty text and has no media, visible delivery is skipped.

Diagram

N/A

Security Impact

  • 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
  • Risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Windows
  • Runtime/container: Node v24.14.0, local source checkout, no container
  • Model/provider: None; no live provider call
  • Integration/channel: heartbeat delivery path with test channel adapter
  • Relevant config: heartbeat target configured for a test channel, no real token or external credential

Steps

  1. Apply the patch.
  2. Run a local proof harness against exported runHeartbeatOnce with a redacted Telegram test sender.
  3. Run node scripts/test-projects.mjs src/shared/text/assistant-visible-text.test.ts src/infra/heartbeat-runner.respects-ackmaxchars-heartbeat-acks.test.ts.
  4. Verify raw scaffolding-only heartbeat replies are suppressed and mixed visible text is preserved without scaffolding.

Expected

  • Raw <tool_calls> and <file_contents ...> scaffolding is not sent as visible heartbeat text.

Actual

  • Real heartbeat delivery proof passes with one delivered test message: Visible alert.
  • Targeted sanitizer and heartbeat delivery tests pass.

Evidence

  • Trace/log snippets

Human Verification

  • Verified scenarios:
    • Shared sanitizer strips repeated bare <tool_calls> lines.
    • Shared sanitizer strips closed and dangling <file_contents ...> blocks.
    • A real runHeartbeatOnce path with a redacted Telegram test sender delivers only Visible alert from mixed visible text plus raw scaffolding.
    • Heartbeat delivery suppresses scaffolding-only replies.
    • Heartbeat delivery preserves visible text while stripping internal scaffolding.
  • Edge cases checked:
    • HEARTBEAT_OK suppression still works.
    • File contents tags inside fenced code blocks are preserved.
    • git diff --check passes.
    • Final diff was checked for private paths, names, secrets, credentials, tokens, and local artifacts.
  • What you did not verify:
    • Live Discord heartbeat delivery.
    • Live gateway/Codex app-server run.
    • Full repo node scripts/check.mjs; local pnpm setup blocked before code checks.

Review Conversations

  • Addressed bot review conversations: N/A
  • Unresolved conversations needing reviewer or maintainer judgment: N/A

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • Exact upgrade steps: N/A

Risks and Mitigations

  • Risk: a literal <file_contents> example outside a code block could be stripped from assistant-visible output.
    • Mitigation: fenced code blocks are preserved, and this sanitizer already strips internal assistant scaffolding from visible delivery paths.
  • Risk: heartbeat text that sanitizes to empty is skipped.
    • Mitigation: this only happens when no media is present; media-bearing payloads still keep media delivery behavior.

Changed files

  • src/infra/heartbeat-runner.respects-ackmaxchars-heartbeat-acks.test.ts (modified, +26/-0)
  • src/infra/heartbeat-runner.ts (modified, +5/-2)
  • src/shared/text/assistant-visible-text.test.ts (modified, +44/-0)
  • src/shared/text/assistant-visible-text.ts (modified, +65/-0)

Code Example

<tool_calls>
<tool_calls>
<tool_calls>

---

<file_contents path='.../HEARTBEAT.md' isStale=false isFullFile=true>
 1|# HEARTBEAT.md
 ...
</file_contents>
RAW_BUFFERClick to expand / collapse

Summary

Heartbeat delivery can post raw internal harness scaffolding to a visible chat channel instead of suppressing it or reducing it to final user-facing assistant text.

Observed leaked examples include:

<tool_calls>
<tool_calls>
<tool_calls>

and:

<file_contents path='.../HEARTBEAT.md' isStale=false isFullFile=true>
 1|# HEARTBEAT.md
 ...
</file_contents>

This appears to be a regression in the heartbeat runner / Codex app-server integration: heartbeat output is treated as deliverable final text even when it consists of protocol/tool scaffolding.

Impact

  • Internal tool call markup can be posted to end-user channels.
  • File read scaffolding can be posted, including local path shapes and file excerpts.
  • This is noisy at best and can become an information disclosure risk depending on the file/tool output.

Conditions

A deployment with:

  • periodic agent heartbeat enabled
  • visible heartbeat delivery target, e.g. Discord or another chat channel
  • Codex/app-server harness handling heartbeat turns
  • heartbeat task or wake that causes tool use or file reads

Suspected Root Cause

src/infra/heartbeat-runner.ts extracts the heartbeat reply and sends normalized text, but the normalization path appears to only handle heartbeat ack tokens/prefixes.

Relevant flow:

  • getReplyFromConfig(...)
  • resolveHeartbeatReplyPayload(...)
  • normalizeHeartbeatReply(...)
  • sendDurableMessageBatch(...)

normalizeHeartbeatReply(...) does not appear to apply the standard assistant-visible text sanitizer before delivery. Existing sanitizer code in src/shared/text/assistant-visible-text.ts handles several tool-call-shaped text forms, but heartbeat delivery should defensively sanitize/suppress protocol-shaped output before sending.

<file_contents ...> also needs explicit coverage if not already handled by the sanitizer.

Expected Behavior

Heartbeat delivery should only send user-visible final assistant text.

If the heartbeat output is only internal scaffolding, tool calls, tool results, file contents wrappers, or other protocol-shaped text, it should be treated as empty / HEARTBEAT_OK / no visible notification.

Regression Test Request

Add regression coverage for heartbeat delivery where the model/harness returns:

  1. repeated <tool_calls> text
  2. <file_contents ...>...</file_contents> text
  3. mixed text plus tool XML, preserving only the real visible text
  4. HEARTBEAT_OK, still suppressing delivery
  5. media-bearing payloads, preserving valid media behavior

The test should assert that sendDurableMessageBatch is not called with raw protocol scaffolding.

Screenshot

<img width="2178" height="1326" alt="Image" src="https://github.com/user-attachments/assets/b8734552-f593-487f-aae3-7ff164a3967b" />

Possible Fix

Before heartbeat delivery:

  1. sanitize replyPayload.text with the canonical assistant-visible sanitizer
  2. strip/suppress heartbeat-specific raw scaffolding such as <file_contents ...>
  3. if sanitized text is empty and no media exists, skip visible delivery
  4. prefer structured heartbeat_respond results when available

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 Regression: heartbeat delivery can leak raw Codex tool/XML scaffolding into chat [4 pull requests, 2 comments, 2 participants]