mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-11 03:13:24 +00:00
fix(channels): deliver NO_REPLY placeholder cleanup signal to channels (#1586)
The agent loop signals silent replies (NO_REPLY, cancelled runs, suppressed errors) by publishing an outbound message with empty content plus the inbound routing metadata (placeholder_key / local_key). Slack, Telegram and Discord each implement an empty-content branch in Send() that deletes their streamed 'Thinking...' placeholder — the partial draft left visible in the thread when delivery is suppressed. deliverOutbound skipped every text-only empty outbound, so that cleanup signal never reached channel.Send and the stray partial draft stayed published (issue #1475). Pass the signal through when placeholder routing metadata is present; keep skipping bare empty messages so channels without an empty-content branch never render empty bubbles. Fixes #1475
This commit is contained in:
1 parent
e1dca7be6b
commit
4e99816a87
2 files changed
+62
-1
No files matched your search
@@ -148,7 +148,16 @@ func (m *Manager) deliverOutbound(ctx context.Context, msg bus.OutboundMessage)
|
||||
msg.Media = kept
|
||||
|
||||
// If only media was in this message and every file is gone, skip entirely.
|
||||
if len(msg.Media) == 0 && msg.Content == "" {
|
||||
// Exception: empty content carrying placeholder routing metadata is the
|
||||
// agent loop's NO_REPLY / silent-reply cleanup signal — channels such as
|
||||
// Slack, Telegram and Discord implement an empty-content branch in Send()
|
||||
// that deletes their streamed "Thinking..." placeholder. Dropping the
|
||||
// message here leaves the partial streamed draft visible in the thread
|
||||
// (issue #1475). Messages without that metadata are not cleanup signals
|
||||
// and stay skipped so channels without an empty-content branch never
|
||||
// render empty bubbles.
|
||||
if len(msg.Media) == 0 && msg.Content == "" &&
|
||||
msg.Metadata["placeholder_key"] == "" && msg.Metadata["local_key"] == "" {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -83,3 +83,55 @@ func TestHandleSendFailure_NonForwardTextOnlyDropped(t *testing.T) {
|
||||
t.Fatalf("expected no notice sent for non-forward text-only failure, got: %+v", ch.lastMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #1475: the agent loop signals NO_REPLY / silent replies by publishing
|
||||
// an outbound message with empty content plus the inbound routing metadata
|
||||
// (placeholder_key / local_key). Slack, Telegram and Discord implement an
|
||||
// empty-content branch in Send() that deletes the streamed "Thinking..."
|
||||
// placeholder — the stray partial draft left behind when delivery is
|
||||
// suppressed. deliverOutbound must not drop these cleanup signals before
|
||||
// they reach the channel.
|
||||
func TestDeliverOutbound_EmptyContentWithPlaceholderMetaReachesSend(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr := NewManager(bus.New())
|
||||
ch := newMockChannel("slack-main", TypeSlack)
|
||||
mgr.channels["slack-main"] = ch
|
||||
|
||||
msg := bus.OutboundMessage{
|
||||
Channel: "slack-main",
|
||||
ChatID: "C012345",
|
||||
Content: "",
|
||||
Metadata: map[string]string{
|
||||
"placeholder_key": "C012345:thread:1727500000.000100",
|
||||
"local_key": "C012345:thread:1727500000.000100",
|
||||
},
|
||||
}
|
||||
|
||||
mgr.deliverOutbound(context.Background(), msg)
|
||||
|
||||
if ch.lastMsg.Content != "" || ch.lastMsg.ChatID != "C012345" {
|
||||
t.Fatal("empty-content cleanup signal with placeholder metadata was dropped before reaching channel.Send — stray partial draft is never deleted (issue #1475)")
|
||||
}
|
||||
}
|
||||
|
||||
// The media-gone skip must keep working for empty messages that carry no
|
||||
// placeholder routing metadata: those are NOT cleanup signals, and delivering
|
||||
// them would make channels without an empty-content branch render empty bubbles.
|
||||
func TestDeliverOutbound_EmptyContentWithoutMetaStillSkipped(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr := NewManager(bus.New())
|
||||
ch := newMockChannel("feishu-main", TypeFeishu)
|
||||
mgr.channels["feishu-main"] = ch
|
||||
|
||||
mgr.deliverOutbound(context.Background(), bus.OutboundMessage{
|
||||
Channel: "feishu-main",
|
||||
ChatID: "oc_1",
|
||||
Content: "",
|
||||
})
|
||||
|
||||
if ch.lastMsg.ChatID != "" {
|
||||
t.Fatalf("empty content without routing metadata should be skipped, got: %+v", ch.lastMsg)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user