fix(feishu): message handling improvements for cross-type edit, chunk resilience, and post fallback - #18371
Open
highland0971 wants to merge 4 commits into
Open
Conversation
Fixes Feishu bot replies creating new topics instead of staying inside
the existing topic thread in topic-mode groups (话题模式群).
Root causes and fixes:
A. Inbound: root_id → thread_id fallback
- _process_inbound_message now uses root_id as fallback when
thread_id is absent (common in topic-mode groups)
- Also adds root_id to reply_to_message_id fallback chain
B. Outbound: create path uses thread_id delivery
- _send_raw_message now distinguishes om_ (message ID) vs omt_
(topic ID) prefixes for thread_id
- om_ IDs: use as reply_to via reply API (most reliable)
- omt_ IDs: use receive_id_type='thread_id' via create API
- Falls back to chat_id when no thread_id present
C. Gateway: propagate reply_to to all mid-turn messages
- GatewayStreamConsumer gains initial_reply_to_id parameter
- First stream send, fallback final, and commentary all use it
- run.py passes event_message_id for Feishu topic threads
- send_progress_messages and status callbacks include reply_to
D. Defense: reply failure fallback stays in thread
- _feishu_send_with_retry: when reply fails (230011/231003),
if thread_id is om_ ID, retry reply to it instead of create
- Prevents spawning new topics when original message is withdrawn
Combines insights from 10 community PRs (NousResearch#9760, NousResearch#9986, NousResearch#10929, NousResearch#16018,
NousResearch#16131, NousResearch#16442, NousResearch#16603, NousResearch#16620, NousResearch#17877, NousResearch#17895) that each addressed
only part of the problem.
…ic thread reply fix
Four fixes for Feishu message delivery issues: 1. **Cross-type edit preservation**: Track msg_type per message_id in _sent_msg_types cache so edit_message can preserve the original type (post→text edits are rejected by Feishu API). 2. **Fence boundary handling**: _build_markdown_post_rows now correctly handles chunk indicators like (1/3) appended to closing fence lines by truncate_message. Strip indicator from fence line and emit as standalone prose row after code block. 3. **Chunk-level resilience**: send() now uses per-chunk try-except so a single chunk failure does not discard remaining chunks. Each chunk is sent independently; any success returns partial success. 4. **Lightweight fallback formatting**: New _strip_unsafe_markdown() removes only Feishu-unsafe patterns while preserving readability (lists, indentation) instead of full markdown stripping. Telegram send() also updated with chunk-level resilience using the same per-chunk error handling pattern. Tests: 189 feishu + 309 telegram tests passing.
19 tasks
This was referenced May 16, 2026
Closed
teknium1
reviewed
Jul 12, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for identifying several real Feishu delivery edge cases. Current main still has the reported behaviors, but the patch needs focused salvage into the migrated adapter.
Problems
gateway/platforms/feishu.py:1804-1805returns_finalize_send_result(last_response)after any earlier success. If a later chunk fails, this returns failure despite the newany_successbranch.gateway/platforms/feishu.py:1851-1854falls back from a rejectedpostupdate totext; that reintroduces the post→text edit that the PR correctly identifies as rejected by Feishu.- Current main moved the active adapter to
plugins/platforms/feishu/adapter.pyin5600105478ffde29d7566b45421b100eaa29c4ef, so the legacy path in this PR is no longer the live implementation.
Suggested changes
- Preserve a successful chunk response separately, or return a clear partial-delivery result.
- Make the edit fallback retain the original message type, or deliberately delete and resend.
- Port the focused changes to
plugins/platforms/feishu/adapter.pyand add regression coverage intests/gateway/test_feishu.py.
Automated hermes-sweeper review.
| # should not discard the rest of the message. | ||
|
|
||
| if any_success: | ||
| return self._finalize_send_result(last_response, "send failed") |
Contributor
There was a problem hiding this comment.
any_success can be true while last_response is a failed final chunk, so this returns failure after a partial delivery. Preserve a successful response separately or return an explicit partial-delivery result.
| logger.warning("[Feishu] Invalid post update payload rejected by API; falling back to lightweight text") | ||
| fallback_text = _strip_unsafe_markdown(content) | ||
| fallback_body = self._build_update_message_body( | ||
| msg_type="text", |
Contributor
There was a problem hiding this comment.
This fallback changes a rejected post update to text, which is the cross-type edit this patch is intended to avoid. Keep the original type here or use a deliberate delete-and-resend fallback.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes three Feishu-specific message handling issues:
Problem 1: Cross-type edit content loss
Problem 2: Fenced code block truncation with chunk indicators
(1/3)appended to closing fence lines break fence detectionProblem 3: Chunk send failure losing subsequent chunks
Problem 4: Post payload rejection losing formatting
_strip_markdown_to_plain_textremoves code block content entirely_strip_unsafe_markdown()function preserves code as indented text, keeping readabilityChanges
gateway/platforms/feishu.py:_sent_msg_typesdict to track message types for edit preservation_strip_unsafe_markdown()for lightweight fallback formatting_CHUNK_INDICATOR_REto handle truncate_message indicatorssend()with per-chunk continue error handlingedit_message()to preserve original msg_type_build_markdown_post_rows()to strip indicators from fence linesTesting
All fixes verified with E2E tests: