Skip to content

fix(slack): use Block Kit markdown block type instead of legacy mrkdwn - #8554

Closed
shivasymbl wants to merge 1 commit into
NousResearch:mainfrom
shivasymbl:fix/slack-markdown-block-type-8552
Closed

fix(slack): use Block Kit markdown block type instead of legacy mrkdwn#8554
shivasymbl wants to merge 1 commit into
NousResearch:mainfrom
shivasymbl:fix/slack-markdown-block-type-8552

Conversation

@shivasymbl

Copy link
Copy Markdown
Contributor

Summary

  • Use Block Kit markdown block type for Slack message rendering instead of legacy mrkdwn
  • Keep format_message() output only as text fallback for notifications/search
  • Fix edit_message() to include blocks in chat_update (streaming responses were bypassing the fix)
  • Remove format_message() pre-conversion in send_message_tool.py for Slack

Problem

format_message() converts standard markdown to Slack's legacy mrkdwn format before sending:

  • **bold***bold*
  • [text](url)<url|text>
  • | table | rows | → stripped/broken
  • Headers → *bold*

The legacy mrkdwn format does not support tables. The markdown block type in Block Kit natively renders standard markdown including tables.

The critical issue was that edit_message() (used by the stream consumer to update responses in-place) was sending only text with no blocks, so even after fixing send(), the final streamed response reverted to legacy rendering.

Changes

File Change
gateway/platforms/slack.py send() Raw content → blocks, format_message()text fallback only
gateway/platforms/slack.py edit_message() Added blocks=[{type: "markdown", text: content}] to chat_update
tools/send_message_tool.py _send_to_platform() Removed format_message() call for Slack
tools/send_message_tool.py _send_slack() mrkdwn: Trueblocks: [{type: "markdown", text: message}]

Test plan

  • Tables render as native Slack table blocks
  • Bold, italic, links, code blocks render correctly
  • Thread replies work with thread_ts in blocks payload
  • Streaming responses render correctly via edit_message()
  • Notifications and search still work via text fallback

Fixes #8552

🤖 Generated with Claude Code

The Slack platform adapter was sending messages using the legacy mrkdwn
format which does not support tables. format_message() was converting
standard markdown (bold, links, headers) to mrkdwn before sending,
which corrupted the content when used with Block Kit markdown blocks.

Changes:
- send(): Use blocks=[{type: "markdown", text: raw_content}] for
  rendering, keep format_message() output only as text fallback for
  notifications and search
- edit_message(): Add blocks parameter to chat_update so streaming
  response edits also use the markdown block type
- send_message_tool.py: Remove format_message() pre-conversion for
  Slack, use markdown block in _send_slack() payload

The markdown block type natively renders standard markdown including
tables, bold, italic, code blocks, and links. Slack auto-translates
it to rich_text + table blocks on the client side.

Fixes NousResearch#8552

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@24601

24601 commented Apr 18, 2026

Copy link
Copy Markdown

This is a great quality of life improvement, nice addition! Cherry picking this into my agent until it is merged, great job @shivasymbl

@kunlabs

kunlabs commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

The max message of a markdown block is 12,000
We need to update the MAX_MESSAGE_LENGTH to be lower than this.

https://docs.slack.dev/reference/block-kit/blocks/markdown-block

But, I don't think truncating a markdown message via truncate_message is a good idea. as it may break the markdown rendering.
imagine the LLM is sending a large table in markdown and you truncate it into multi parts and send each by each.

I think the best way is to remove truncate_message also, just tell the LLM that the message limit is 12,000 and let it break itself, also tell it to send a csv file instead of large table. this way is simpler and safer.

@24601

24601 commented Apr 19, 2026

Copy link
Copy Markdown

The max message of a markdown block is 12,000 We need to update the MAX_MESSAGE_LENGTH to be lower than this.

docs.slack.dev/reference/block-kit/blocks/markdown-block

But, I don't think truncating a markdown message via truncate_message is a good idea. as it may break the markdown rendering. imagine the LLM is sending a large table in markdown and you truncate it into multi parts and send each by each.

I think the best way is to remove truncate_message also, just tell the LLM that the message limit is 12,000 and let it break itself, also tell it to send a csv file instead of large table. this way is simpler and safer.

I've been using this as a cherry pick on top of my agent and you are definitely right, I have been able to prompt and give it memories such it respects that limit, but that's not a great use of precious memory/context, and the (somewhat) more deterministic fix (at least one that doesn't use memory slots) is def the right one. @shivasymbl - happy to make the change if you want or help you test

@shivasymbl

shivasymbl commented Apr 20, 2026 via email

Copy link
Copy Markdown
Contributor Author

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the clear rendering diagnosis. The underlying default mrkdwn path still exists, but this patch needs substantial salvage on current main.

Problems

  • The patched adapter and standalone sender have moved: active gateway/streaming delivery is now in plugins/platforms/slack/adapter.py:1376-1410 and :1487-1501; cron/standalone delivery is _standalone_send at :4297-4350.
  • A direct port cannot use the current 39,000-character Slack platform limit (plugins/platforms/slack/adapter.py:4568-4570) for a markdown block. Slack documents a 12,000-character cumulative limit for markdown blocks.
  • Main already has a deliberate default-off rich_blocks renderer with native tables and final-edit support (plugins/platforms/slack/adapter.py:1873-1900). The PR needs to reconcile with that policy rather than replace the deleted legacy paths.

Suggested changes

  • Rework the proposal in the plugin adapter, preserve or explicitly change the documented rich_blocks policy, enforce markdown-block limits, and test gateway send, final edit, and standalone delivery.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages 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 12, 2026
@alt-glitch alt-glitch added comp/tools Tool registry, model_tools, toolsets needs-decision Awaiting maintainer decision before any implementation labels Jul 18, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Re-triage: the patch edits retired gateway/platforms/slack.py; current main uses plugins/platforms/slack/adapter.py. The native-Markdown approach remains related to #8552/#53893, but this branch needs a rebase plus a decision on the 12k Block Kit markdown limit and safe splitting before review.

@alt-glitch alt-glitch added comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have and removed comp/gateway Gateway runner, session dispatch, delivery labels Jul 18, 2026
@alt-glitch alt-glitch added comp/gateway Gateway runner, session dispatch, delivery and removed P2 Medium — degraded but workaround exists comp/plugins Plugin system and bundled plugins sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 18, 2026
teknium1 pushed a commit that referenced this pull request Jul 23, 2026
…arkdown

Slack's Block Kit `markdown` block accepts standard markdown (tables,
headers, task lists, fenced code with syntax highlighting, links) and
lets Slack translate it natively — eliminating the lossy markdown→mrkdwn
conversion for the rendered layout.  Enable via
platforms.slack.extra.markdown_blocks.

Safety rails added on top of the original design:

* opt-in (default off) — Slack documents the block for 'apps that use
  platform AI features' and does not guarantee availability across all
  app types / surfaces, so unconditional adoption is not safe yet
* the mrkdwn-converted text field is ALWAYS kept as the
  notification/search/accessibility fallback
* content over Slack's 12k cumulative markdown-block cap declines to the
  rich_blocks renderer / plain text path
* the existing block-rejection retry (invalid_blocks / msg_too_long /
  too_many_blocks) re-sends the plain mrkdwn payload, so an unsupported
  surface degrades gracefully instead of dropping the message
* when both modes are enabled, markdown_blocks is preferred over the
  local rich_blocks renderer; rich_blocks remains the fallback

Adapted from #8554 by @shivasymbl — the original patched the deleted
gateway/platforms/slack.py and switched unconditionally; reimplemented
against the plugin adapter's _maybe_blocks/sanitize_blocks pipeline.

Fixes #8552.
teknium1 pushed a commit that referenced this pull request Jul 23, 2026
…arkdown

Slack's Block Kit `markdown` block accepts standard markdown (tables,
headers, task lists, fenced code with syntax highlighting, links) and
lets Slack translate it natively — eliminating the lossy markdown→mrkdwn
conversion for the rendered layout.  Enable via
platforms.slack.extra.markdown_blocks.

Safety rails added on top of the original design:

* opt-in (default off) — Slack documents the block for 'apps that use
  platform AI features' and does not guarantee availability across all
  app types / surfaces, so unconditional adoption is not safe yet
* the mrkdwn-converted text field is ALWAYS kept as the
  notification/search/accessibility fallback
* content over Slack's 12k cumulative markdown-block cap declines to the
  rich_blocks renderer / plain text path
* the existing block-rejection retry (invalid_blocks / msg_too_long /
  too_many_blocks) re-sends the plain mrkdwn payload, so an unsupported
  surface degrades gracefully instead of dropping the message
* when both modes are enabled, markdown_blocks is preferred over the
  local rich_blocks renderer; rich_blocks remains the fallback

Adapted from #8554 by @shivasymbl — the original patched the deleted
gateway/platforms/slack.py and switched unconditionally; reimplemented
against the plugin adapter's _maybe_blocks/sanitize_blocks pipeline.

Fixes #8552.
teknium1 pushed a commit that referenced this pull request Jul 23, 2026
…arkdown

Slack's Block Kit `markdown` block accepts standard markdown (tables,
headers, task lists, fenced code with syntax highlighting, links) and
lets Slack translate it natively — eliminating the lossy markdown→mrkdwn
conversion for the rendered layout.  Enable via
platforms.slack.extra.markdown_blocks.

Safety rails added on top of the original design:

* opt-in (default off) — Slack documents the block for 'apps that use
  platform AI features' and does not guarantee availability across all
  app types / surfaces, so unconditional adoption is not safe yet
* the mrkdwn-converted text field is ALWAYS kept as the
  notification/search/accessibility fallback
* content over Slack's 12k cumulative markdown-block cap declines to the
  rich_blocks renderer / plain text path
* the existing block-rejection retry (invalid_blocks / msg_too_long /
  too_many_blocks) re-sends the plain mrkdwn payload, so an unsupported
  surface degrades gracefully instead of dropping the message
* when both modes are enabled, markdown_blocks is preferred over the
  local rich_blocks renderer; rich_blocks remains the fallback

Adapted from #8554 by @shivasymbl — the original patched the deleted
gateway/platforms/slack.py and switched unconditionally; reimplemented
against the plugin adapter's _maybe_blocks/sanitize_blocks pipeline.

Fixes #8552.
@teknium1

Copy link
Copy Markdown
Contributor

Merged via #70191 — your commit was cherry-picked/reapplied onto current main with your authorship preserved in git history: your markdown-block adoption landed as the opt-in markdown_blocks mode with your authorship — mrkdwn kept as default/fallback per plan-tier caveats.

Thanks for the contribution!

@teknium1 teknium1 closed this Jul 23, 2026
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…arkdown

Slack's Block Kit `markdown` block accepts standard markdown (tables,
headers, task lists, fenced code with syntax highlighting, links) and
lets Slack translate it natively — eliminating the lossy markdown→mrkdwn
conversion for the rendered layout.  Enable via
platforms.slack.extra.markdown_blocks.

Safety rails added on top of the original design:

* opt-in (default off) — Slack documents the block for 'apps that use
  platform AI features' and does not guarantee availability across all
  app types / surfaces, so unconditional adoption is not safe yet
* the mrkdwn-converted text field is ALWAYS kept as the
  notification/search/accessibility fallback
* content over Slack's 12k cumulative markdown-block cap declines to the
  rich_blocks renderer / plain text path
* the existing block-rejection retry (invalid_blocks / msg_too_long /
  too_many_blocks) re-sends the plain mrkdwn payload, so an unsupported
  surface degrades gracefully instead of dropping the message
* when both modes are enabled, markdown_blocks is preferred over the
  local rich_blocks renderer; rich_blocks remains the fallback

Adapted from NousResearch#8554 by @shivasymbl — the original patched the deleted
gateway/platforms/slack.py and switched unconditionally; reimplemented
against the plugin adapter's _maybe_blocks/sanitize_blocks pipeline.

Fixes NousResearch#8552.
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 comp/tools Tool registry, model_tools, toolsets needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have platform/slack Slack app adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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.

Slack platform: use Block Kit markdown block type instead of legacy mrkdwn

5 participants