Skip to content

message_tool_only addressed turns carry no per-turn delivery contract; visible replies silently swallowed #99371

Description

@obviyus

What happened

On a production deployment tracking latest main (Telegram group, claude-cli backend, message_tool_only source-reply delivery), the agent stopped delivering visible replies for directly addressed turns. The model composes the answer but returns it as plain final text instead of calling message(action=send), so source-reply/private-final keeps it private. Over ~90 minutes of live traffic, ~25 consecutive visible turns produced zero delivered replies (one intermittent success), each logging:

[source-reply/private-final] agent produced a long private final reply without calling the configured delivery tool (message_tool_only); response kept private and not delivered to the source channel
[turn/kernel] visible channel turn dispatched with no queued reply payloads: channel=telegram messageId=…

Tool availability is not the problem: the same deployment successfully called mcp__openclaw__message on other turns, and the MCP config is unchanged.

Root cause

Captured raw claude-cli session for an addressed turn (was_mentioned: true, inbound_event_kind: user_request) shows the turn input contains no delivery instruction at all — no statement that final text stays private, no pointer to message(action=send).

  • 63cbb35 (fix(auto-reply): single canonical group history and deduped turn metadata #99256) removed the per-turn MESSAGE_TOOL_DELIVERY_HINT block from buildInboundUserContextPrefix as duplication, leaving the delivery contract stated only once, as a single line inside the appended system prompt (src/agents/system-prompt.ts ## Messaging).
  • e081028 generalized the group message-tool etiquette line from Discord-only to all channels, so Telegram group turns now also read "message-tool-only delivery does not require visible output … prefer a reaction when available or no channel message", which reads as license to answer privately.
  • Room-event turns still carry an explicit per-turn delivery directive (buildRoomEventContext in src/auto-reply/reply/prompt-prelude.ts) and behave correctly. Addressed turns carry none — the asymmetry is the bug: the per-turn hint was load-bearing for CLI backends, where the OpenClaw system prompt is appended after the harness's own large system prompt.

Expected

Every message_tool_only turn states its delivery contract exactly once per turn, in one owner (the envelope builder), for both room events and addressed turns — without reintroducing the pre-#99256 3-5x duplication, and without leaking the directive into persisted transcript rows (strip-inbound-meta sentinel symmetry).

Evidence

  • Raw CLI turn input for an addressed mention with zero delivery directive; model replied with plain text; reply swallowed.
  • Same deployment, minutes earlier/later: room-event turns correctly silent; one intermittent addressed turn did call mcp__openclaw__message and delivered (confirming tools/wiring are healthy).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.bugSomething isn't workingclawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.maintainerMaintainer-authored PR

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions