Skip to content

Agent posts an extra reply in the source chat after a tool already delivered the visible output #170147

Open

Description

@obviyus

Summary

After an agent turn delivers its visible output through a tool (for example a message-tool send to another chat), the source chat can still get an extra, unwanted reply. Three forms were observed:

  1. The canned fallback "The tool run finished, but no final summary was produced. I did not repeat any completed actions." (src/agents/embedded-agent-runner/run/settled-turn-finalization.ts).
  2. With Anthropic models, a model reply to the injected runtime context: "I've received the runtime context. What would you like me to do?"
  3. A final that starts with NO_REPLY but has trailing text (for example NO_REPLY\nREADY) shows the trailing text.

Steps to reproduce

  1. In Telegram topic A, ask the agent to send a message to topic B with the message tool, and to reply NO_REPLY in A.
  2. B receives the message. A also gets one of the extra replies above.

Observed on the Telegram Test Server with Anthropic Haiku 4.5 and 5.5 (forms 1 and 2), and on Discord with OpenAI (form 3). The same fallback was seen on current main with Anthropic, so it is not caused by the transcript-owner work in #169438.

Expected behavior

When the model explicitly chooses silence after a confirmed visible send, an external source chat gets no extra message. Existing protections stay: an empty final after tool work still gets the fallback or recovery; mixed NO_REPLY + text is still delivered; WebChat, Control UI and subagent sessions still must return a result.

Causes found

Design notes for a fix

A prototype (closed draft #170129, carrier split #170140) showed these constraints:

  • Honor only a token-only NO_REPLY after a confirmed last-batch send, and only in external message-channel source sessions.
  • Do not treat an empty final as intentional silence.
  • Do not suppress mixed NO_REPLY + text.
  • Merge the Anthropic runtime-context carrier into the tool-result user turn without changing the cached prompt prefix.

Impact

A confusing extra message in the source chat after a successful cross-chat send. Also seen live on a production install with the canned fallback.

Activity

  1. self-assigned this
    on Oct 12, 2026
  2. added
    P2Normal backlog priority with limited blast radius.
    clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.
    impact:ux-frictionUser-facing flow adds avoidable confusion or support burden without fully blocking progress.
    issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.
    on Oct 12, 2026
  3. clawsweeper commented on Oct 12, 2026

    @clawsweeper
    Contributor

    Codex review: this still needs some work.

    Summary
    Keep this issue open: current main retains the reported completion conflict, and the collaborator author explicitly deferred repair while preserving this issue as its tracker.

    Reproducibility: Current source establishes the required-reply recovery path and separate Anthropic runtime-context carrier; Telegram and Discord observations are reporter evidence, with no independent live reproduction performed in this review.

    Root-cause cluster
    Relationship: canonical
    Canonical: #170147
    Summary: The author consolidated both closed repair prototypes into this issue; destination transcript recording remains separate work.

    Members:

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

    Next step
    Retain this issue under the author's explicit repair deferral; future work needs a failing cross-chat regression covering both terminal finalization and outer reply completion.

    Review details

    Best possible solution:

    When repair resumes, carry confirmed last-batch delivery and explicit silence through both completion layers while correcting Anthropic carrier placement.

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

    Current source establishes the required-reply recovery path and separate Anthropic runtime-context carrier; Telegram and Discord observations are reporter evidence, with no independent live reproduction performed in this review.

    Is this the best way to solve the issue?

    The report's narrowed constraints are appropriate, but the closed prototype is incomplete: the repair must cover outer completion too, preserve empty-final recovery, and continue delivering mixed NO_REPLY plus substantive text.

    AGENTS.md: found and applied where relevant.

    Remaining risk / open question:

    • Broad suppression could hide genuinely missing answers, failed or partial sends, or work performed after delivery.
    • Anthropic carrier changes must preserve runtime facts and the reusable prompt-cache prefix.

    Codex review notes: model internal, reasoning medium; reviewed against 51211ac26ca0.

    Label changes

    Label changes:

    • add P2: The requested delivery succeeds, but the source conversation receives a confusing additional response.
    • add impact:ux-friction: An unwanted fallback or runtime-context acknowledgment adds noise after successful tool delivery.
    • add issue-rating: 🦞 diamond lobster: Current issue advisory state selects this label.
    • add clawsweeper:source-repro: Current issue advisory state selects this label.

    Label justifications:

    • P2: The requested delivery succeeds, but the source conversation receives a confusing additional response.
    • impact:ux-friction: An unwanted fallback or runtime-context acknowledgment adds noise after successful tool delivery.
    Evidence reviewed

    What I checked:

    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; those execution lanes are retired.
    • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

    Reviewed October 12, 2026, 3:20 PM ET / 19:20 UTC.

  4. pelleeee commented on Oct 12, 2026

    @pelleeee

    Another variant of this, on WhatsApp direct chats with 2026.10.1 (code unchanged in 2026.10.2): a message react on the current inbound message, followed by a token-only NO_REPLY.

    Setup

    • WhatsApp channel, direct chat (agent:<id>:whatsapp:direct:<peer>), so resolveSourceReplyExpectation → "required" (silentReply only applies to groups).
    • The agent is told to acknowledge messages like "ok, thanks" with an emoji reaction instead of a text reply, then answer NO_REPLY.
    • Local model through an OpenAI-compatible provider; tool search with the compact surface is active, so the model calls message through the tool_call wrapper.

    What happens

    1. The model calls message with {"action":"react","emoji":"🌸"}, without messageId (so it targets the current message) and without final.
    2. The reaction is delivered (Sent reaction "🌸" -> message …, tool result {"ok":true,"added":"🌸"}).
    3. The model's final text is exactly NO_REPLY.
    4. settled post-tool turn lacked a final answer … running isolated finalization. Both finalization attempts return NO_REPLY again, then using terminal fallback reply.
    5. The user gets the reaction and "The tool run finished, but no final summary was produced. I did not repeat any completed actions."

    Why
    resolveDeliveredCurrentSourceReply only counts a reaction as a source reply when sourceReplyFinal === true. In the message tool, requestedSourceReplyFinal is undefined unless the model passes final explicitly. So the delivery state stays "missing", and the explicit NO_REPLY is ignored because the expectation is "required".

    The final schema description says to set it to true only when the reaction is the complete response. That is our case, but in practice smaller models don't set an optional boolean reliably. Here the model never mentioned it in its reasoning, even with an explicit instruction in AGENTS.md, probably because it copied earlier reaction calls from the transcript. It also sees the tool only through the compact tool_call surface.

    Suggestion (fits the fix constraints in this issue)
    If the last tool batch contains a confirmed, non-removing react on the current source message, and the assistant's final text is a token-only NO_REPLY, treat the turn as answered or silent:

    • no isolated finalization,
    • no terminal fallback.

    A reaction plus an explicit silence token is unambiguous user-visible closure. Turns where the model reacts and then writes text are unaffected.

    Local workaround (for reference only, not proposed as-is because it changes the default meaning of final for everyone): in the message tool,

    const requestedSourceReplyFinal = typeof params.final === "boolean" ? params.final : action === "react" ? true : void 0;

    After that the reaction counts as the delivered source reply, the turn ends right after the tool call, and no fallback text is sent. Verified in production on WhatsApp DMs.

    Side note (see also #144118): the fallback string is hard-coded English and is delivered to end users in non-English chats. An option to suppress it for channel sessions would help a lot.

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

Metadata

Metadata

Assignees

Labels

P2Normal backlog priority with limited blast radius.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:ux-frictionUser-facing flow adds avoidable confusion or support burden without fully blocking progress.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