Skip to content

Agent-authored Slack Block Kit components in respond_to_user - #23

Open
josephschorr wants to merge 1 commit into
mainfrom
worktree-slack-blockkit-components
Open

josephschorr wants to merge 1 commit into
mainfrom
worktree-slack-blockkit-components

Conversation

@josephschorr

Copy link
Copy Markdown
Member

What

Lets an agent compose presentational Slack Block Kit layout in its replies — not just a markdown string. The agent passes a blocks array on respond_to_user; the Slack kind renders it as the message body, with the existing text field as the notification preview and degrade fallback.

How

Channel-neutral seam, Slack-native content — no kind == "slack" branch leaks into the agent tool:

  • "components" capability on channelkinds.Kind (Slack opts in). Flows to ChannelBinding.Capabilities → respond_to_user, exactly like "markdown".
  • Two optional interfaces on the kind (mirroring TextFormatter): ComponentFormatter (model-facing authoring instructions, injected into the blocks schema description) and ComponentValidator (ValidateComponents + ComponentsPlainText). The runner already blank-imports the Slack kind, so both are reachable in-process.
  • Validate before publish. respond_to_user calls the kind's validator synchronously; a bad payload returns a precise, model-facing IsError (no envelope published) instead of a silent post-time degrade to plain text.
  • Transport. New OutboundUserMessagePayload.Components json.RawMessage; the Slack sender posts it via MsgOptionBlocks with text as fallback, keeping the existing blocks→plaintext degrade net.
  • Egress safety. The info-leakage gate measures block text too (via ComponentsPlainText), so content can't bypass the gate by riding in blocks.

Supported blocks (read-only / presentation-only)

header, section (no accessory), divider, context, rich_text, markdown, table, data_visualization (line/bar/area/pie charts), card (no actions), container, plan, task_card.

Rejected with precise, correctable errors: interactive blocks/elements (actions, input, context_actions, data_table, carousel, section accessories, card actions), alert (modals-only), images/icons (deferred — see Scope), and @channel/@here/@everyone broadcasts. Chart and container payload shapes are validated to Slack's actual requirements — verified against the live API — so the agent gets a clean error rather than a degrade (container needs a title/rich_text_title; charts need a title plus per-type segments or series + axis_config.categories).

slack-go upgrade

Bumps github.com/slack-go/slack v0.23.1 → v0.30.1 for typed support of the newer blocks (data_visualization, table, card, container, markdown, plan, task_card, …). The only breaking change was GetUserInfoContext becoming variadic; the Slack wrapper interfaces and test fakes are adapted to match.

Scope

Slice 1 is presentational only. Interactivity (buttons/menus routing clicks back into the session) is out of scope. Images are deferred to Slice 2 (artifact-backed — resolve a session-owned artifact handle rather than an arbitrary URL).

Testing

  • Table-driven validator tests: one row per accept/reject rule (each block type, each interactive/image/broadcast rejection, structural limits, chart/container shape requirements).
  • respond_to_user tests against the real Slack validator: schema exposes blocks only under the capability; validation failure → IsError, no publish; success carries Components; a leakage-gate test proves block-only content is still measured.
  • Slack sender render + degrade tests.
  • Exercised end-to-end on a live Slack workspace (charts, tables, cards, containers, rich_text, markdown all render; rejections surface clean errors).

Follow-ups (tracked, not in this PR)

  • Surface an upstream Slack invalid_blocks back to the agent as a system note for any future/unknown block that passes our validator but Slack still rejects (channelsd → session feedback), instead of only degrading.
  • The channelsd silence-watchdog counts outbound-message silence, not internal tool-call progress, so a long internal run can read as a false "stall."

Let the agent compose presentational Block Kit layout in Slack replies via a
new `blocks` field on respond_to_user, gated by a "components" channel
capability. A channel kind opts in by implementing the new ComponentFormatter
and ComponentValidator interfaces (channel-neutral seam, Slack-native content):
respond_to_user injects the kind authoring instructions into the blocks schema,
validates synchronously in the runner before publish — returning a precise,
model-facing error instead of a post-time degrade — and carries the validated
blocks on OutboundUserMessagePayload.Components. The Slack sender renders them
as the message body with the text field as the notification/degrade fallback,
and the info-leakage egress gate measures block text via ComponentsPlainText.

Supported read-only blocks: header, section (no accessory), divider, context,
rich_text, markdown, table, data_visualization, card (no actions), container,
plan, task_card. Interactive blocks/elements, images (deferred), and
@channel/@here/@everyone broadcasts are rejected with precise errors. Chart and
container payload shapes are validated to Slack requirements verified against
the live API (container needs a title; charts need per-type segments/series +
axis_config).

Upgrade slack-go v0.23.1 -> v0.30.1 for typed support of the newer blocks,
adapting the variadic GetUserInfoContext signature change.
@vercel

vercel Bot commented Oct 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
openagentprimitives Ready Ready Preview Oct 9, 2026 11:15pm UTC

Request Review

This branch was successfully deployed

1 active deployment
Preview — 055a9de8 Deployed Oct 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant