Skip to content

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

Description

@Hansen1018

Summary

When an agent uses the message tool (action=send) to deliver its main reply in a turn, models sometimes append a short trailing acknowledgement to their final reply — patterns like:

  • 已发
  • 已发 #22141
  • 已发, 不再追加
  • 主回复已发 (#xxx)
  • 核心回答如下
  • 总结如下
  • Sent above
  • Sent #xxx
  • Sent. Replied in thread.
  • Done.
  • OK
  • Okay
  • Roger
  • Got it
  • OK, that's the fix.

These acks carry no information beyond the message-tool send that already went out, but OpenClaw currently delivers them as a second visible message in the channel — a duplicate-reply failure mode.

Repro

  1. Send an inbound message that requires a non-trivial reply.
  2. Configure the agent to deliver the main reply via message tool (action=send).
  3. After the agent delivers, its final reply text contains a short meta-ack (varies by model + locale).
  4. The channel sees two messages: the main reply + the trailing ack.

Reproduced reliably with:

  • Telegram, Discord, Signal (any provider routed through deliverOutboundPayloads).
  • GPT-4o, Claude Sonnet, DeepSeek, MiniMax-M3 (no model-specific pattern — the behaviour is model-agnostic).
  • English and Chinese locales.

Root cause

The existing filterMessagingToolDuplicates only catches full-content duplicates via substring match with a 10-char minimum:

const MIN_DUPLICATE_TEXT_LENGTH = 10;

Short meta-acks (e.g. 已发 #22141, 9 chars) fall under this floor and pass through, ending up as a second visible message. The current dedupe is correct for "is this a rephrasing of something I already sent" but does not handle "is this an agent ack of its own tool call".

Proposed fix

Add a complementary filter filterMessagingToolMetaCommentary that recognises post-tool-send meta commentary by pattern (Chinese + English), with conservative bounds:

  • Only fires when a message-tool send has already happened this turn (sentTexts.length > 0). Agents without message-tool sends are unaffected.
  • Length-capped at 200 chars.
  • Anchored ack patterns + ≤ 20 chars trailing content.
  • Media payloads always preserved.

This preserves the existing dedup logic (which is correct for content duplicates) and adds a separate axis for "agent ack of own tool call" without overlap.

PR: #96680

Impact

Anyone running an agent that uses message tool to deliver replies sees duplicate messages in the channel after each non-trivial reply. The current workaround is per-agent prompting ("respond with only NO_REPLY after message tool sends"), which is fragile and model-dependent. Native dedupe fixes it once for all agents.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions