Skip to content

fix(core): suppress empty responses to prevent (Silent) channel messages#221

Open
jcenters wants to merge 1 commit intoTinyAGI:mainfrom
jcenters:fix/suppress-empty-responses
Open

fix(core): suppress empty responses to prevent (Silent) channel messages#221
jcenters wants to merge 1 commit intoTinyAGI:mainfrom
jcenters:fix/suppress-empty-responses

Conversation

@jcenters
Copy link
Copy Markdown

Problem

When an agent returns an empty string (e.g. when intentionally ignoring a read-only crew chat message), streamResponse() was still calling enqueueResponse() with an empty message. Telegram delivers empty messages as (Silent), which shows up as a notification with no content — confusing and noisy for users.

Fix

Added a guard in streamResponse() before enqueueResponse():

if (!responseMessage.trim() && allFiles.length === 0) {
    log('DEBUG', `Empty response suppressed...`);
    return;
}

Empty responses (no text, no files) are now dropped silently at the pipeline level rather than reaching the channel client.

Related

Companion to #220 (chatroom fan-out loop fix). Both issues were discovered together when agents in a crew team began flooding a user's Telegram with status updates and silent messages.

🤖 Generated with Claude Code

When an agent returns an empty string, streamResponse() was still
queuing it as an outgoing message. Telegram delivers empty messages
as "(Silent)", which is noisy and confusing for users.

Added a guard before enqueueResponse() to skip empty messages
(no text and no files). These were most commonly produced when agents
intentionally ignored crew chat messages or other read-only inputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 16, 2026

Greptile Summary

This PR adds a one-guard early-return in streamResponse() to drop empty responses — ones where both the post-hook message text is blank and no files are attached — before they ever reach enqueueResponse(). This cleanly eliminates the "(Silent)" push-notification noise seen in Telegram and WhatsApp when agents intentionally produce no output (e.g., ignoring a read-only crew chat message).

Key changes:

  • Guard is correctly placed after runOutgoingHooks and handleLongResponse, so any hook or transformation that produces an empty result is also covered.
  • A DEBUG-level log records the suppression with channel/sender/agent context for observability.
  • enqueueResponse and emitEvent('response_ready') are both skipped, which is semantically correct — there is nothing to deliver or report.
  • The condition !responseMessage.trim() && allFiles.length === 0 correctly allows file-only messages (empty text but with attachments) to still be delivered.

Confidence Score: 5/5

  • This PR is safe to merge — it is a minimal, targeted guard with no side-effects on normal (non-empty) response paths.
  • The change is 6 lines, placed at the correct point in the pipeline (after all transformations and hook processing), uses a conservative condition that cannot accidentally suppress messages with content or files, and is clearly motivated by an observable production bug. No existing behaviour for non-empty responses is altered.
  • No files require special attention.

Important Files Changed

Filename Overview
packages/core/src/response.ts Adds a guard before enqueueResponse() to early-return when both responseMessage is blank and no files are attached, correctly suppressing noisy "(Silent)" Telegram/WhatsApp notifications.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant streamResponse
    participant runOutgoingHooks
    participant handleLongResponse
    participant enqueueResponse
    participant ChannelClient

    Caller->>streamResponse: response (possibly empty)
    streamResponse->>streamResponse: trim & transform
    streamResponse->>streamResponse: collectFiles
    streamResponse->>runOutgoingHooks: finalResponse
    runOutgoingHooks-->>streamResponse: hookedResponse + metadata
    streamResponse->>handleLongResponse: hookedResponse + outboundFiles
    handleLongResponse-->>streamResponse: responseMessage + allFiles

    alt responseMessage is blank AND allFiles is empty
        streamResponse->>streamResponse: log DEBUG "Empty response suppressed"
        streamResponse-->>Caller: return (early exit, nothing sent)
    else has content or files
        streamResponse->>enqueueResponse: channel, sender, message, files...
        enqueueResponse->>ChannelClient: deliver message
        streamResponse->>streamResponse: emitEvent("response_ready")
    end
Loading

Last reviewed commit: f70f7d4

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant