Skip to content

fix(slack): extract rich_text quotes/lists and link unfurl previews - #16213

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-aa28dba4
Apr 26, 2026
Merged

fix(slack): extract rich_text quotes/lists and link unfurl previews#16213
teknium1 merged 2 commits into
mainfrom
hermes/hermes-aa28dba4

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Makes the agent see content Slack was already sending but the adapter was dropping. Two cases:

  1. Forwarded/quoted content: Slack's modern composer embeds quotes, lists, and preformatted code in the blocks array as rich_text_quote / rich_text_list / rich_text_preformatted — NOT in the plain text field. Walking the rich_text tree and rendering as readable markdown (> quoted, • bullet, code fences) surfaces the full message to the agent.

  2. Link unfurl previews: Slack puts Notion/docs/GitHub/etc. preview cards in the attachments array with title, url, body, footer. Reading those and appending a 📎 [title](url)\n body\n _footer_ section per preview lets the agent see the page summaries that users and Slack clients already see inline.

Routing safety

Mention gating and slash-command classification both read original_text (the raw user field), not the augmented text. So a forwarded message that contains <@bot> in a quote can't trick the bot into responding in a channel it shouldn't, and a quoted /deploy doesn't turn a normal message into a command. Regression test for each.

Adjustment from original PR

Dropped _serialize_slack_blocks_for_agent — the original also inlined a redacted JSON dump of non-rich_text blocks (section / accessory / actions / buttons) so the agent could inspect UI-heavy alerts. That added up to 6000 characters of JSON into the prompt context on every qualifying message with no opt-out. The rich_text + attachments coverage here is the clear bug-fix; full block-payload inspection can come back later behind a config opt-in if a concrete use case shows up.

Changes

  • gateway/platforms/slack.py: _extract_text_from_slack_blocks helper + inline call; link-unfurl extraction from attachments array; routing guard (original_text for mention + command detection).
  • gateway/session.py: Slack platform notes updated to accurately describe what gets inlined.
  • Tests: rich_text extraction (quotes, lists, code), attachment unfurl preview, is_msg_unfurl skip, channel routing ignores mentions in quotes, quoted slash-command doesn't reclassify.

Validation

tests/gateway/test_slack.py + test_session.py + test_slack_mention.py + test_slack_approval_buttons.py: 281 passed (3x green).

Credits

Closes #11426

Wang-tianhao and others added 2 commits April 26, 2026 13:02
Slack's modern composer sends messages with a 'blocks' array that
contains rich_text elements. When a user forwards or quotes another
message, the quoted content shows up in the rich_text_quote children
of that array — and is NOT included in the plain 'text' field. The
agent saw only the lossy plain text and was blind to forwarded /
quoted content. Same story for link unfurl previews (Notion, docs,
GitHub, etc.) which Slack puts in the 'attachments' array.

Two fixes in the inbound handler:

1. _extract_text_from_slack_blocks walks rich_text / rich_text_quote /
   rich_text_list / rich_text_preformatted trees and renders readable
   text ('> quoted', '• bullet', code fences), dedupes against the
   plain text field, and appends the extracted content so the agent
   sees everything.

2. Link unfurl / attachment preview extraction reads title, url,
   body, and footer from the 'attachments' array and appends a
   '📎 [title](url)\n   body\n   _footer_' section per preview.
   Skips is_msg_unfurl to avoid echoing our own Slack replies back.

Routing is careful not to trust augmented text: mention gating
(is_mentioned) and slash-command detection both run against the
original 'text' field, so forwarded content containing '<@bot>' or
'/deploy' in a quote can't trick the bot into responding in a
channel it shouldn't or classifying a normal message as a command.

Adjustment from original PR: dropped _serialize_slack_blocks_for_agent,
which inlined a redacted JSON dump of non-rich_text blocks (section,
accessory, actions, etc.) — the agent would see the raw Block Kit
structure for UI-heavy alerts. It added up to 6000 characters to the
prompt context on every qualifying message with no opt-out. The
rich_text extraction and attachment unfurls cover the common bug-fix
case (quoted/forwarded content + link previews) without the prefill
tax. If a user needs block inspection later, it can return as a
config opt-in.

Also updates the Slack platform notes in session.py to accurately
describe what the gateway inlines.
@teknium1
teknium1 merged commit 755a280 into main Apr 26, 2026
10 of 11 checks passed
@teknium1
teknium1 deleted the hermes/hermes-aa28dba4 branch April 26, 2026 20:02
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/slack Slack app adapter labels Apr 26, 2026
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/slack Slack app adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants