Skip to content

fix(feishu): pass replied-to message attachments through to the agent - #57168

Open
ll823-png wants to merge 1 commit into
NousResearch:mainfrom
ll823-png:fix/feishu-reply-media-passthrough
Open

fix(feishu): pass replied-to message attachments through to the agent#57168
ll823-png wants to merge 1 commit into
NousResearch:mainfrom
ll823-png:fix/feishu-reply-media-passthrough

Conversation

@ll823-png

Copy link
Copy Markdown

Problem

On Feishu/Lark, replying to a file or image message and @-mentioning the bot
hands the agent only a text placeholder like [Replying to: "[Attachment: x.pdf]"].
The parent message's file_key/image_key are parsed during normalization and
then discarded, so the agent has no way to reach the actual bytes. Users must
re-send the attachment directly in a DM for the bot to see it.

Fix

  • _fetch_message_normalized: fetch + cache the referenced message's
    normalized payload (keeps image_keys/media_refs); _fetch_message_text
    now derives its text from it (behavior unchanged).
  • _fetch_reply_media: download the replied-to message's attachments via the
    existing message-resource pipeline, cached per parent message_id.
  • _process_inbound_message: append reply media to the event; for non-image
    attachments, prepend a note with the cached file path (the downstream
    document note only fires for DOCUMENT-typed events, not TEXT replies).
  • The empty-text guard now runs after reply-media passthrough, so a bare
    "@bot" reply to an attachment survives instead of being dropped.
  • Text batching merges media_urls/media_types (deduped) instead of
    silently dropping them.

Note: downloading attachments of non-mention group messages requires the app
scope im:message.group_msg (read all group messages). Without it the fetch
fails gracefully (warning log, no media attached) — same as before this patch.

Tests

Two new cases in tests/gateway/test_feishu.py:

  • reply attachments are passed through to the dispatched event (media +
    path note in text);
  • a bare "@bot" reply to an attachment message is not dropped by the
    empty-text guard.

Verified end-to-end on a live Feishu tenant: "reply to PDF + @bot" and
"reply to screenshot + @bot" both deliver the attachment to the agent.

Replying to a file/image message and @-mentioning the bot previously
handed the agent only a text placeholder like [Attachment: x.pdf] -
the parent message's file_key/image_key were parsed and then discarded,
so the agent had no way to reach the actual bytes.

- _fetch_message_normalized: fetch + cache the referenced message's
  normalized payload (keeps image_keys/media_refs); _fetch_message_text
  now derives its text from it.
- _fetch_reply_media: download the replied-to message's attachments via
  the existing message-resource pipeline, cached per parent message_id.
- _process_inbound_message: append reply media to the event and, for
  non-image attachments, prepend a note with the cached file path
  (the downstream document note only fires for DOCUMENT events).
- Empty-text guard now runs after reply-media passthrough so a bare
  "@bot" reply to an attachment survives instead of being dropped.
- Text batching merges media_urls/media_types (deduped) instead of
  silently dropping them.
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery platform/feishu Feishu / Lark adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages P2 Medium — degraded but workaround exists labels Jul 2, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Competing fix for the Feishu reply-attachment-loss family (fixes #54578) — same problem as the open Feishu reply-media PRs #14371 and #18385, but a distinct/more complete implementation, so relating rather than marking a duplicate. This one caches the referenced message's normalized payload, downloads the replied-to attachments via the message-resource pipeline, and keeps a bare @Bot reply to an attachment from being dropped. Maintainer picks the canonical Feishu reply-media PR.

@ll823-png

Copy link
Copy Markdown
Author

Thanks @alt-glitch for triaging. To help compare with #14371 (same root cause, similar core approach): this PR additionally ships two new regression tests (reply-media passthrough; bare "@bot" reply surviving the empty-text guard), plus three hardening fixes found in production — media merge in text batching (media was silently dropped when debounced), per-parent media caching, and graceful degradation when the app lacks im:message.group_msg.

The fix has been verified end-to-end on a live Feishu tenant (PDF and screenshot reply scenarios). Happy to rebase or fold any of this into #14371 instead if the maintainers prefer that direction.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for identifying a real Feishu reply-media gap. Current main still fetches only reply_to_text after the empty-text guard in plugins/platforms/feishu/adapter.py:3246-3263; the existing resource path is available at :3751-3804.

Problems

  • The patch targets removed gateway/platforms/feishu.py; Feishu moved to plugins/platforms/feishu/adapter.py in 5600105478ffde29d7566b45421b100eaa29c4ef.
  • The proposed _message_context_cache and _reply_media_cache are unbounded. Main deliberately caps reply-context caching at plugins/platforms/feishu/adapter.py:4192 following e8cacb57d531137dec3109617e807b30ff5187c9.
  • A reply-specific non-image note would duplicate current generic document context handling in gateway/run.py:10648-10690.

Suggested changes

  • Port the media lookup before the current guard, use bounded cache lifecycle, and rely on the gateway's existing document note.
  • Add a test covering parent fetch/normalization/resource download, not only a mocked _fetch_reply_media.

Automated hermes-sweeper review.

self._sent_message_id_order: List[str] = [] # LRU order for _sent_message_ids_to_chat
self._chat_info_cache: Dict[str, Dict[str, Any]] = {}
self._message_text_cache: Dict[str, Optional[str]] = {}
self._message_context_cache: Dict[str, FeishuNormalizedMessage] = {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new per-parent caches need bounded eviction when ported. Main previously fixed unbounded reply-context caching in e8cacb57d531137dec3109617e807b30ff5187c9; please use a capped LRU policy rather than retaining every referenced message and downloaded attachment for the adapter lifetime.

if reply_media_urls:
media_urls = list(media_urls) + reply_media_urls
media_types = list(media_types) + reply_media_types
text = self._prepend_reply_attachment_notes(text, reply_media_urls, reply_media_types)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On current main, gateway/run.py:10648-10690 already adds a document path note for non-image media even on TEXT events. When porting this logic, pass the media through but avoid adding a second reply-specific note.

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

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/feishu Feishu / Lark adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants