Skip to content

fix(reply): suppress trailing acks after message-tool sends#97513

Closed
mushuiyu886 wants to merge 2 commits into
openclaw:mainfrom
mushuiyu886:feat/issue-96681
Closed

fix(reply): suppress trailing acks after message-tool sends#97513
mushuiyu886 wants to merge 2 commits into
openclaw:mainfrom
mushuiyu886:feat/issue-96681

Conversation

@mushuiyu886

@mushuiyu886 mushuiyu886 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Fixes #96681

Summary

  • Suppresses simple and compound post-message-tool meta acknowledgements such as 已发 #22141, Sent above, 核心回答如下, OK, 已发, 不再追加总结, Sent. Replied in thread., and OK, that's the fix. from final reply payloads after a same-route message-tool send.
  • Keeps the existing long-text duplicate detector unchanged, including the 10-character floor and substring semantics.
  • Preserves media payloads, route scoping, thread/account matching, provider configuration, and substantive short replies such as OK, here is the actual answer..

What Problem This Solves

When an agent sends the main response via the message tool, some models still append a short final acknowledgement. OpenClaw's delivery path treated that acknowledgement as a second source-channel reply, so users could see the main message followed by a duplicate-looking acknowledgement message.

  • Behavior or issue addressed: A same-route message-tool send followed by text-only final payloads that are only simple or compound meta acknowledgements now resolves to no extra final delivery payload. Before this patch, the resolver returned those ack payloads as visible second replies because they were neither long enough for substring duplicate matching nor actual content duplicates of the sent body.
  • Why it matters / User impact: Users no longer receive a second visible message that only acknowledges a message-tool send that already happened. This reduces duplicate-looking replies without suppressing distinct content replies, routed replies, or media replies.

User Impact

Users receive the intended message-tool response without a second acknowledgement-only bubble. Distinct short replies, replies for another route, and media payloads remain deliverable.

Origin / follow-up

N/A — independent root-cause fix linked directly to #96681.

Root Cause

  • Root cause: The source-of-truth final payload path is resolveFollowupDeliveryPayloads -> filterMessagingToolDuplicates -> isMessagingToolDuplicate. That duplicate helper intentionally ignores normalized text shorter than MIN_DUPLICATE_TEXT_LENGTH = 10 and only checks overlap with sentTexts. Simple and compound post-tool-send acknowledgement text is usually below that floor or semantically unrelated to the long sent body, so the resolver returned it as a second payload.
  • Why this is root-cause fix: The fix stays at the same source-of-truth resolver boundary where route-scoped message-tool sends are already filtered. It adds a conservative, anchored meta-ack invariant to the existing text-only duplicate filter instead of lowering the duplicate length floor or adding a downstream delivery workaround. That means the path that creates final source-channel payloads stops producing the extra ack payload in the first place.
  • Fix classification: Root cause fix.
  • Maintainer-ready confidence: High for the production resolver/runtime behavior covered by the regression and runtime log artifact. Full maintainer-side visible channel proof can still provide additional confidence, but the local artifact below exercises the resolver boundary that decides whether the trailing ack payload is delivered.

Competition / linked PR analysis

Real behavior proof

  • Behavior or issue addressed: After a same-route message tool send, acknowledgement-only final payloads no longer become a second source-channel reply.
  • Canonical reachability path: user input from an inbound channel → channel ingestion and route normalization → agent runtime turn → message tool send → MessagingToolSend route/text metadata → resolveMessagingToolPayloadDeduperesolveFollowupDeliveryPayloads or normal reply-payload construction → filterMessagingToolDuplicates → outbound source-channel delivery effect.
  • Boundary crossed: Local runtime at the production payload-resolver boundary outside the test runner. Native Telegram Desktop transport was not crossed and remains explicitly listed below as not tested.
  • Shared helper / provider constraint check: The classifier reuses the existing normalizeTextForComparison helper; no provider-owned contract or numeric constraint is involved.
  • Real environment tested: Linux worktree at /media/vdb/code/ai/aispace/openclaw-worktrees/pr-97513, exercising the production source-channel reply payload resolver and existing dedupe helper through the project's auto-reply reply Vitest config plus a standalone Node runtime proof outside the test runner. The runtime artifacts are embedded below with route details redacted where relevant.
  • Exact steps or command run after this patch:
node scripts/run-vitest.mjs run --config test/vitest/vitest.auto-reply-reply.config.ts src/auto-reply/reply/followup-delivery.test.ts
node scripts/run-vitest.mjs run --config test/vitest/vitest.auto-reply-reply.config.ts src/auto-reply/reply/followup-delivery.test.ts src/auto-reply/reply/reply-payloads.test.ts
node scripts/run-vitest.mjs run --config test/vitest/vitest.auto-reply-reply.config.ts src/auto-reply/reply/followup-delivery.test.ts --reporter verbose
node --import tsx "$EVIDENCE_DIR/runtime-resolver-proof-20260629.mjs"
git diff --check
git diff --check origin/main...HEAD
node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.src.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-src.tsbuildinfo
  • Evidence after fix:
RED before initial implementation:
FAIL src/auto-reply/reply/followup-delivery.test.ts > resolveFollowupDeliveryPayloads > drops short trailing meta acknowledgements after a same-route message tool send
expected [ { text: '已发 #22141' }, { text: 'Sent above' }, { text: '核心回答如下' }, { text: 'OK' } ] to strictly equal []

RED before compound-ack update:
FAIL src/auto-reply/reply/followup-delivery.test.ts > resolveFollowupDeliveryPayloads > drops compound meta acknowledgements without dropping substantive short replies
expected only [{ text: "OK, here is the actual answer." }], but received compound ack payloads plus the substantive reply.

Fresh GREEN focused resolver tests after syncing current `main`:
Test Files  1 passed (1)
Tests  25 passed (25)

Fresh related reply payload tests after syncing current `main`:
Test Files  2 passed (2)
Tests  50 passed (50)

Verbose runtime/log artifact after fix:
✓ resolveFollowupDeliveryPayloads > drops short trailing meta acknowledgements after a same-route message tool send
✓ resolveFollowupDeliveryPayloads > drops compound meta acknowledgements without dropping substantive short replies
✓ resolveFollowupDeliveryPayloads > dedupes duplicate replies when a messaging tool already sent to the same provider and target
✓ resolveFollowupDeliveryPayloads > delivers distinct replies when a messaging tool already sent to the same provider and target

Fresh standalone runtime resolver proof outside the test runner after syncing current `main`:
HEAD: 6483c4911ead83d9c8a8391ac22e24eb53d1edd1
Boundary: production resolveFollowupDeliveryPayloads with same-route message-tool delivery metadata.
- same-route short final acknowledgement payloads -> []
- compound acknowledgements plus substantive short content -> [{ text: "OK, here is the actual answer." }]
- no prior message-tool sent text -> [{ text: "OK" }]
- different route -> [{ text: "Sent above" }]

Mantis Telegram Desktop dispatch attempt:
workflow_dispatch for PR #97513 returned HTTP 403: Must have admin rights to Repository.

Whitespace check:
git diff --check
passed with no output

Source test typecheck:
node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.src.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-src.tsbuildinfo
passed with no output
  • Observed result after fix: The simple-ack regression that previously returned four short ack payloads now returns []; the compound-ack regression drops 已发, 不再追加总结, Sent. Replied in thread., and OK, that's the fix. while preserving OK, here is the actual answer. as a substantive short reply. The standalone runtime proof outside the test runner also shows ack-shaped text remains deliverable when there is no prior same-route message-tool sent text or when the message-tool send is for a different route. Existing route/media/text dedupe tests still pass.
  • What was not tested: Full native Telegram Desktop/channel recording was not completed locally. A workflow_dispatch attempt for the Mantis Telegram Desktop proof on PR fix(reply): suppress trailing acks after message-tool sends #97513 failed with HTTP 403: Must have admin rights to Repository, so an authorized maintainer can still add stronger visible-channel proof by dispatching the workflow or posting @openclaw-mantis telegram desktop proof: verify a message-tool main reply followed by a short final ack shows one Telegram message and no trailing ack bubble.

Regression Test Plan

  • Target test file: src/auto-reply/reply/followup-delivery.test.ts covers the production resolver path; src/auto-reply/reply/reply-payloads.test.ts was rerun to guard existing text/media/route dedupe helpers.
  • Scenario locked in: A same-route message tool send records route text, then final payloads contain simple and compound acknowledgement text. The expected resolved payload list is empty for ack-only payloads, while OK, here is the actual answer. remains deliverable.
  • Why this is the smallest reliable guardrail: The regression sits at resolveFollowupDeliveryPayloads, the same boundary that applies threading, route matching, media dedupe, and text dedupe before source-channel delivery. It catches the user-visible duplicate without requiring a brittle end-to-end fixture for every provider.

Review findings addressed

  • RF-001 / RF-004: Native Telegram Desktop visible proof remains a maintainer-decision item. The prior workflow_dispatch attempt returned HTTP 403: Must have admin rights to Repository; this PR does not claim that transport-level recording was completed.
  • RF-002: The classifier remains length-capped and whole-text anchored. The regression and standalone production-resolver proof preserve OK, here is the actual answer. as substantive content, and preserve ack-shaped text when there is no prior same-route send or the send targets another route.
  • RF-003: The competing same-root PRs fix(reply): suppress post-tool-send meta commentary as duplicate reply #96680 and fix(reply): filter short trailing acks after message-tool sends #96763 are now closed. Current main still lacks this acknowledgement filter, leaving this PR as the only open candidate linked to Duplicate reply: message-tool sends followed by short trailing acks ("已发 #xxx", "Sent above", "核心回答如下", "OK") #96681.
  • RF-005: ClawSweeper identified no remaining automated code repair. The branch was synchronized with current main, and the focused implementation remains unchanged.
  • RF-006: Fresh focused resolver verification on current head passed: 1 test file, 25 tests.
  • RF-007: Fresh related reply-payload verification on current head passed: 2 test files, 50 tests.
  • RF-008: Fresh source test typecheck passed with no output.
  • RF-009: git diff --check and git diff --check origin/main...HEAD passed with no output.

Merge risk

  • Risk labels considered: merge-risk:message-delivery and session-state-adjacent signals were considered because this patch suppresses final text payloads after message-tool sends and touches embedded-agent helper exports.
  • Risk explanation: The patch quality warning is not a hidden default or fallback behavior change; the added return false is the normal non-match result for an anchored predicate. The public-surface-looking export is a helper barrel export used internally by reply payload dedupe, not a config/schema/protocol/default contract change.
  • Why acceptable: The new predicate only runs after sentTexts.length > 0, inside the existing message-tool text dedupe filter, and media payloads return before the predicate. Legitimate long content, unrelated routes, payloads without prior sent text, and the explicit substantive short-reply guard keep the existing behavior.

Risk / Compatibility

  • Patch quality notes: The diff is limited to the existing dedupe helper and resolver tests. It adds a conservative predicate, wires it into the existing text dedupe helper, and covers both positive and false-positive cases. No unrelated formatting, lockfile, config, provider, or channel plugin changes are included.
  • What did NOT change: The existing MIN_DUPLICATE_TEXT_LENGTH behavior, substring duplicate semantics, route matching, account/thread matching, media duplicate handling, provider configuration, and channel plugin behavior are unchanged.
  • Architecture / source-of-truth check: The canonical source-of-truth for final source-channel payload filtering remains filterMessagingToolDuplicates after resolveMessagingToolPayloadDedupe has chosen same-route sent text evidence. The fix does not add a second downstream delivery filter or alter provider routing contracts.

What was not changed

  • No API, bot setup, provider behavior, or channel plugin behavior was changed.
  • No duplicate-length threshold was lowered.
  • No fallback path was added for unknown messages or unknown routes.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Jun 28, 2026
@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 10, 2026, 9:57 PM ET / July 11, 2026, 01:57 UTC.

Summary
The PR adds a length-capped multilingual acknowledgement classifier to route-scoped message-tool text dedupe, exports the helper, and adds follow-up resolver regression coverage.

PR surface: Source +19, Tests +48. Total +67 across 4 files.

Reproducibility: yes. at source level. Current main's 10-character duplicate floor explains why short acknowledgements survive, and the PR supplies the pre-fix resolver failure plus after-fix production-resolver output.

Review metrics: none identified.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #96681
Summary: This PR is the only open candidate fix for the canonical post-message-tool duplicate-acknowledgement issue; the alternative implementations are closed unmerged.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Refresh the branch against current main and rerun exact-head checks before merge.

Mantis proof suggestion
A native Telegram recording would directly confirm the visible delivery outcome and complement the already sufficient resolver proof. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram desktop proof: verify a same-route message-tool reply followed by a short final acknowledgement shows one Telegram message and no trailing acknowledgement bubble.

Risk before merge

  • [P1] A false-positive acknowledgement phrase would suppress a legitimate final channel message after a same-route message-tool send.
  • [P1] The PR head is behind current main; although the synthetic merge result is clean, checks and review should be refreshed on a current exact head before landing.

Maintainer options:

  1. Refresh and land (recommended)
    Bring the branch onto current main, rerun exact-head checks, and land the clean route-scoped implementation.
  2. Add visible Telegram proof
    Request a short Telegram Desktop recording before landing if maintainers want transport-level confirmation beyond the sufficient production-resolver output.

Next step before merge

  • [P2] No concrete code repair remains; the active PR needs current-head refresh and normal maintainer landing review rather than a separate repair job.

Security
Cleared: The patch only changes internal TypeScript reply filtering and tests, with no dependency, workflow, secret, permission, install, publish, or supply-chain changes.

Review details

Best possible solution:

Refresh this focused candidate onto current main, retain its sent-text, route, thread/account, and media guards, and land it after exact-head checks; Telegram Desktop proof can add visible confirmation but is not required to repair the implementation.

Do we have a high-confidence way to reproduce the issue?

Yes at source level. Current main's 10-character duplicate floor explains why short acknowledgements survive, and the PR supplies the pre-fix resolver failure plus after-fix production-resolver output.

Is this the best way to solve the issue?

Yes. The PR extends the canonical route-scoped text-dedupe boundary established by the merged predecessor and is narrower and better targeted than the closed competing implementations.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 67e1d4391153.

Label changes

Label justifications:

  • P2: The PR fixes duplicate user-visible channel messages with a limited reply-delivery blast radius.
  • merge-risk: 🚨 message-delivery: The classifier intentionally suppresses final text, so an overly broad phrase match could lose a legitimate channel reply.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The contributor provides after-fix standalone execution of the production resolver outside the test runner, including same-route, no-send, different-route, and substantive-reply controls.
  • proof: sufficient: Contributor real behavior proof is sufficient. The contributor provides after-fix standalone execution of the production resolver outside the test runner, including same-route, no-send, different-route, and substantive-reply controls.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The intended behavior is directly visible as the absence of a second Telegram acknowledgement bubble after the main message.
Evidence reviewed

PR surface:

Source +19, Tests +48. Total +67 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 3 21 2 +19
Tests 1 48 0 +48
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 69 2 +67

What I checked:

Likely related people:

  • steipete: Authored, merged, and landed the predecessor that established route-aware dedupe across the normal and follow-up delivery paths. (role: introduced route-scoped dedupe behavior; confidence: high; commits: 72f6016ce596; files: src/auto-reply/reply/reply-payloads-dedupe.ts, src/auto-reply/reply/followup-delivery.ts, src/auto-reply/reply/agent-runner-payloads.ts)
  • vincentkoc: Authored the payload-dedupe helper split and later messaging-type boundary cleanup in the same reply-delivery modules. (role: adjacent reply-path contributor; confidence: medium; commits: 125e778fe618, 159e6bc09931; files: src/auto-reply/reply/reply-payloads-dedupe.ts, src/auto-reply/reply/followup-delivery.ts, src/auto-reply/reply/agent-runner-payloads.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (1 earlier review cycle)
  • reviewed 2026-06-29T01:31:36.118Z sha e0f6690 :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 28, 2026
@mushuiyu886

mushuiyu886 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 29, 2026
@mushuiyu886 mushuiyu886 changed the title fix #96681: Duplicate reply: message-tool sends followed by short trailing acks ("已发 #xxx", "Sent above", "核心回答如下", "OK") fix(reply): suppress trailing acks after message-tool sends Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate reply: message-tool sends followed by short trailing acks ("已发 #xxx", "Sent above", "核心回答如下", "OK")

1 participant