diff --git a/src/channels/feishu.rs b/src/channels/feishu.rs index 1132f2b..c311ae3 100644 --- a/src/channels/feishu.rs +++ b/src/channels/feishu.rs @@ -2504,6 +2504,15 @@ impl Channel for FeishuChannel { "open_id" }; + // reaction 语义:仅"终态消息"成功发送后才移除 reaction。 + // 终态 = AssistantResponse(最终响应)或 ErrorNotification(agent 异常终止的错误通知)。 + // ToolCall 等中间过程事件不触碰 reaction——让 reaction 真实反映用户是否已收到 + // agent 的最终产出(reaction 持续 = 尚未收到响应或错误;reaction 消失 = 已收到)。 + // 发送失败时保留 reaction 作为异常信号;空内容不视为送达,保留 reaction。 + let is_terminal = matches!( + msg.event_kind, + OutboundEventKind::AssistantResponse | OutboundEventKind::ErrorNotification + ); let remove_reaction = async { self.remove_reaction_from_metadata(&msg.metadata).await; }; @@ -2514,7 +2523,14 @@ impl Channel for FeishuChannel { // Empty content if content.is_empty() { - remove_reaction.await; + // 空最终响应是异常:保留 reaction 让用户察觉,仅记录 warn。 + // 非最终响应(如 ToolCall 空内容)本来就不触碰 reaction。 + if is_terminal { + tracing::warn!( + chat_id = %msg.chat_id, + "Final response has empty content, keeping reaction as anomaly signal" + ); + } return Ok(()); } @@ -2526,7 +2542,10 @@ impl Channel for FeishuChannel { let result = self .dispatch_send(receive_id, receive_id_type, "text", content, reply_to) .await; - remove_reaction.await; + // 仅最终响应且发送成功才移除 reaction + if is_terminal && result.is_ok() { + remove_reaction.await; + } return result; } MsgFormat::Post => { @@ -2535,7 +2554,9 @@ impl Channel for FeishuChannel { let result = self .dispatch_send(receive_id, receive_id_type, "post", &post_body, reply_to) .await; - remove_reaction.await; + if is_terminal && result.is_ok() { + remove_reaction.await; + } return result; } MsgFormat::Interactive => { @@ -2561,11 +2582,17 @@ impl Channel for FeishuChannel { reply_to, ) .await; - remove_reaction.await; + // 回退成功才移除 reaction;回退失败则保留作为异常信号 + if is_terminal && result.is_ok() { + remove_reaction.await; + } return result; } } - remove_reaction.await; + // 所有 chunk 成功:仅最终响应移除 reaction + if is_terminal { + remove_reaction.await; + } return Ok(()); } } @@ -2618,19 +2645,23 @@ impl Channel for FeishuChannel { Ok(()) => sent_media += 1, Err(error) => { tracing::warn!(error = %error, path = %path, media_type = %media_item.media_type, "Failed to send media message to Feishu"); + // 媒体失败:保留 reaction 作为异常信号(不调用 remove_reaction) return Err(error); } } } if msg.content.trim().is_empty() && sent_media == 0 { - remove_reaction.await; + // 无内容无媒体:保留 reaction 作为异常信号 return Err(ChannelError::Other( "No supported media items were sent to Feishu".to_string(), )); } - remove_reaction.await; + // 全部成功:仅最终响应移除 reaction + if is_terminal { + remove_reaction.await; + } Ok(()) } } diff --git a/src/channels/manager.rs b/src/channels/manager.rs index b480b1c..5da4b35 100644 --- a/src/channels/manager.rs +++ b/src/channels/manager.rs @@ -26,7 +26,7 @@ impl ChannelManager { Self { channels: Arc::new(RwLock::new(channels)), - bus: MessageBus::new(100), + bus: MessageBus::new(256), websocket_channel, } }