Skip to content

feat(slack): opt-in Block Kit rendering for agent messages (salvage #56090) - #56102

Merged
teknium1 merged 3 commits into
mainfrom
hermes/hermes-f384ab62
Jul 1, 2026
Merged

feat(slack): opt-in Block Kit rendering for agent messages (salvage #56090)#56102
teknium1 merged 3 commits into
mainfrom
hermes/hermes-f384ab62

Conversation

@teknium1

@teknium1 teknium1 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Infographic

Slack Block Kit rendering

Summary

Slack agent replies can now render as structured Block Kit — headers, dividers, true nested lists, blockquotes, code, and native table blocks — behind an opt-in flag (platforms.slack.extra.rich_blocks: true, default off). Salvage of #56090 by @benbarclay onto current main. Closes #18918.

Changes

  • plugins/platforms/slack/block_kit.py (new): pure render_blocks(markdown) — headers, dividers, rich_text nested lists, blockquotes, preformatted code, and native table blocks with per-column alignment and inline-formatted rich_text cells. Enforces Slack's 50-block / 3000-char / table (100 rows · 20 cols · 10k chars) limits; over-limit or unparseable tables fall back to aligned monospace. Never raises → None on any unexpected input.
  • plugins/platforms/slack/adapter.py: send() renders blocks on the single-chunk primary message; edit_message() renders blocks only on finalize=True (streaming edits stay plain mrkdwn). A text= fallback is always sent alongside blocks.
  • Docs: website/docs/user-guide/messaging/slack.md + zh-Hans i18n — config example + key-table row.
  • Follow-up commit (ours): corrected the module + _rich_blocks_enabled docstrings, which still described the earlier monospace-only table approach.

Validation

Result
Targeted tests tests/gateway/test_slack_block_kit.py + _adapter.py — 27/27 pass
E2E render native table block, alignment (left→null, right emitted), in-cell bold + link survive
Footprint plugin-only; core untouched; opt-in default-off = zero behavior change

Plugin-only PR (touches plugins/platforms/slack/, tests/gateway/, website/docs/), contributor authorship preserved via rebase-merge.

benbarclay and others added 3 commits June 30, 2026 23:25
Add platforms.slack.extra.rich_blocks (default off). When enabled, the
final agent message is sent as Slack Block Kit blocks — section headers,
dividers, and true nested lists via rich_text — instead of flat mrkdwn.

- New plugins/platforms/slack/block_kit.py: pure markdown->blocks renderer
  (headers, dividers, nested ordered/bullet lists, blockquotes, fenced code;
  pipe-tables as aligned monospace since Block Kit has no robust table block).
  Enforces Slack's 50-block / 3000-char section limits and returns None to
  fall back to plain text on empty/oversized/unexpected input. Never raises.
- adapter.send(): render blocks on the single-chunk primary message; a
  text= fallback is ALWAYS sent alongside (notifications/accessibility).
- adapter.edit_message(): blocks only on finalize=True, so intermediate
  streaming edits stay plain mrkdwn (no per-flush block re-derivation).
- Docs (EN + zh-Hans) + config example. Send-side only: no app reinstall.

Tests: pure-renderer unit suite + adapter integration suite (blocks present
when on, plain text when off, text fallback always set, finalize gating,
multi-chunk fallback). Prove-failed against a stubbed renderer.
Replace the interim monospace table fallback with Slack's native `table`
block (rows of rich_text cells). Addresses the core ask in #18918.

- _table_block(): builds type:"table" with rich_text cells, so inline
  formatting (bold, links, code) renders inside cells.
- Column alignment parsed from the markdown separator row (:---, :-:, --:)
  into column_settings (left = default/null-skip, center/right emitted).
- Escaped pipes (\\|) are not treated as column separators.
- Respects Slack's table limits (100 rows / 20 cols / 10k aggregate chars);
  oversized or unparseable tables gracefully fall back to aligned monospace
  (rich_text_preformatted), so a big table never breaks the message.

Docs (EN + zh-Hans) updated to describe native tables + the fallback.
Tests: native table shape, alignment->column_settings, inline-formatted
cells, oversized/too-wide monospace fallback, escaped-pipe cell. Prove-
failed against a stubbed _table_block (native-table tests fail, fallback
tests stay green). All existing Slack tests still pass.
The renderer now emits native Block Kit table blocks; the module and
_rich_blocks_enabled docstrings still described the earlier monospace-only
approach.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery platform/slack Slack app adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 1, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: salvage of #56090 (@benbarclay's original, still open) onto current main, closes #18918. Part of the Slack Block Kit cluster anchored by earliest-open issue #8552; competing OPEN native-markdown PR #53893 takes a different approach (native Slack markdown block vs opt-in Block Kit + monospace-table fallback). Not a duplicate — maintainer to pick the canonical Slack rich-rendering path from the cluster.

@tw0316

tw0316 commented Jul 1, 2026

Copy link
Copy Markdown

Live validation found one Slack API schema issue in the merged native table path.

With platforms.slack.extra.rich_blocks: true, a normal markdown table with a right-aligned column produces:

"column_settings": [null, {"align": "right"}, null]

Slack rejects this at send time:

invalid_blocks
must provide an object [json-pointer:/blocks/.../column_settings/0]

The table block docs say missing column_settings entries inherit defaults, but provided entries must be objects. So the fix should be to omit trailing/default entries or emit valid objects, not null.

Focused bug issue: #56615
Focused follow-up PR: #56618

@teknium1 the PR keeps this PR’s Block Kit renderer approach intact and only hardens the table schema + no-block fallback path.

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 P3 Low — cosmetic, nice to have platform/slack Slack app adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slack: render Markdown pipe tables as Block Kit tables

4 participants