Skip to content

fix(auto-reply): suppress JSON-wrapped NO_REPLY payloads before channel delivery#56612

Merged
hydro13 merged 1 commit intoopenclaw:mainfrom
hydro13:fix/suppress-json-no-reply-payload
Mar 28, 2026
Merged

fix(auto-reply): suppress JSON-wrapped NO_REPLY payloads before channel delivery#56612
hydro13 merged 1 commit intoopenclaw:mainfrom
hydro13:fix/suppress-json-no-reply-payload

Conversation

@hydro13
Copy link
Copy Markdown
Member

@hydro13 hydro13 commented Mar 28, 2026

Summary

  • Add shared isSilentReplyPayloadText() detector that catches both bare NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes
  • Apply at three call sites: reply directive parser, reply normalizer, and embedded agent payload builder
  • Control payload is stripped before any channel sees it — fixes the leak for Telegram and all other channels
  • Preserves media when text is only a silent control envelope

Root Cause

parseReplyDirectives() in src/auto-reply/reply/reply-directives.ts only treated exact NO_REPLY as silent. When models returned the control payload as raw JSON ({"action":"NO_REPLY"}), it was treated as normal text and delivered to users.

This is a shared auto-reply layer issue, not Telegram-specific.

Change Type

  • Bug fix

Testing

136 tests pass across 4 test files. New regression tests for:

  • JSON envelope suppression in reply directives
  • Pretty-printed JSON envelope in delivery pipeline
  • Media preservation when text is a silent envelope

Fixes #37727

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S maintainer Maintainer-authored PR labels Mar 28, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 28, 2026

Greptile Summary

This PR fixes a bug where models returning {"action":"NO_REPLY"} JSON-wrapped control payloads — instead of the bare NO_REPLY token — would have that text delivered to end users. The fix introduces a new isSilentReplyEnvelopeText detector (and a isSilentReplyPayloadText combiner) in src/auto-reply/tokens.ts, then applies it at all three call sites in the pipeline (directive parser, reply normalizer, embedded-runner payload builder).

Key observations:

  • The core logic in isSilentReplyEnvelopeText is correct: the fast-path guard (startsWith("{"), endsWith("}"), includes(token)) avoids unnecessary JSON.parse calls, and the final parsed.action.trim() === token uses strict equality — so {"action":"NO_REPLY_MORE"} or {"action":"NOT_NO_REPLY"} are not falsely caught.
  • normalize-reply.ts correctly retains the original isSilentReplyText import for the trailing-strip code-path (the mixed-content case, e.g. "text NO_REPLY"), while switching the exact-match check to the new isSilentReplyPayloadText.
  • Media preservation when the text is a silent JSON envelope is correctly handled in normalizeReplyPayload (text is set to "" and the payload is returned), and covered by the new regression test.
  • Test coverage spans all three pipelines and also validates pretty-printed JSON envelopes ({\n "action": "NO_REPLY"\n}).
  • No regressions introduced — the existing isSilentReplyText usages that still need the bare-token behavior are unchanged.

Confidence Score: 5/5

Safe to merge — targeted bug fix with no regressions and solid regression test coverage across all affected pipelines.

All changes are narrow and correct: the new detector is well-guarded, the three call-site updates are consistent, and the previously missing JSON-envelope case is now blocked before channel delivery. No P0 or P1 issues found.

No files require special attention.

Important Files Changed

Filename Overview
src/auto-reply/tokens.ts Adds isSilentReplyEnvelopeText to detect JSON-wrapped {"action":"NO_REPLY"} payloads, and isSilentReplyPayloadText composing both bare-token and envelope detectors. Implementation is correct.
src/auto-reply/reply/reply-directives.ts Swaps isSilentReplyText for isSilentReplyPayloadText so the JSON envelope form is treated as silent before the directive parse result propagates.
src/auto-reply/reply/normalize-reply.ts Uses isSilentReplyPayloadText for the exact-match check while correctly retaining isSilentReplyText for the trailing-strip code-path handling mixed-content messages.
src/agents/pi-embedded-runner/run/payloads.ts Updates the outbound filter to use isSilentReplyPayloadText, aligning the embedded-runner path with the normalizer and directive-parser changes.
src/auto-reply/reply/reply-utils.test.ts Adds regression tests for JSON envelope suppression and media preservation when text is a silent JSON envelope.
src/agents/pi-embedded-runner/run/payloads.test.ts Adds a test confirming JSON NO_REPLY payloads are suppressed in the embedded-runner pipeline.
src/infra/outbound/payloads.test.ts Covers both compact and pretty-printed JSON NO_REPLY envelopes in the delivery-pipeline filter test.

Reviews (1): Last reviewed commit: "fix(auto-reply): suppress JSON-wrapped N..." | Re-trigger Greptile

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: 59c7c9486e

ℹ️ 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 thread src/auto-reply/tokens.ts Outdated
…el delivery

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes openclaw#37727
@hydro13 hydro13 force-pushed the fix/suppress-json-no-reply-payload branch from 59c7c94 to b8c4a63 Compare March 28, 2026 21:00
@hydro13 hydro13 merged commit 17479ce into openclaw:main Mar 28, 2026
34 of 35 checks passed
hydro13 added a commit that referenced this pull request Mar 28, 2026
alexcode-cc pushed a commit to alexcode-cc/clawdbot that referenced this pull request Mar 30, 2026
…el delivery (openclaw#56612)

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes openclaw#37727
alexjiang1 pushed a commit to alexjiang1/openclaw that referenced this pull request Mar 31, 2026
…el delivery (openclaw#56612)

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes openclaw#37727
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
…el delivery (openclaw#56612)

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes openclaw#37727
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…el delivery (openclaw#56612)

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes openclaw#37727
Tardisyuan pushed a commit to Tardisyuan/openclaw that referenced this pull request Apr 30, 2026
…el delivery (openclaw#56612)

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes openclaw#37727
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…el delivery (openclaw#56612)

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes openclaw#37727
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…el delivery (openclaw#56612)

Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

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

Labels

agents Agent runtime and tooling maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Telegram: occasional raw {"action":"NO_REPLY"} payload is delivered to end user instead of being suppressed

1 participant