feat(plugins): expose register_slack_action_handler API - #20589
Closed
bcsmith528 wants to merge 2 commits into
Closed
bcsmith528 wants to merge 2 commits into
bcsmith528 wants to merge 2 commits into
Conversation
Plugins that post Block Kit messages with interactive elements (buttons, overflow menus, datepickers, etc.) had no documented way to receive the resulting click events. The plugin API exposed register_tool, register_hook, register_command, register_platform, and register_context_engine, but nothing for slack_bolt action handlers. The only workaround was to monkey-patch SlackAdapter.connect from inside register(), which is fragile and breaks on every Hermes update. This change adds: * PluginContext.register_slack_action_handler(action_id, callback) — validates inputs and queues the handler on the PluginManager. action_id accepts whatever slack_bolt.App.action() accepts (literal string, compiled re.Pattern, or constraint dict). * PluginManager.get_slack_action_handlers() — accessor used by the Slack adapter at connect time. * SlackAdapter.connect — after wiring its built-in approval and slash-confirm buttons, iterates the plugin-registered handlers and registers each via self._app.action(matcher)(callback). Each callback is wrapped defensively so a misbehaving plugin cannot crash slack_bolt's dispatch loop, with a best-effort ack on exception so Slack stops retrying the click. * Defensive fallback when the plugin layer is unhealthy: a RuntimeError from get_plugin_manager() is logged and swallowed rather than blocking the gateway from starting. * Test coverage in tests/gateway/test_slack_plugin_action_handlers.py for input validation, multi-plugin registration, the connect-time wiring, defensive exception handling, and the plugin-loader- failure fallback path. * Documentation in website/docs/guides/build-a-hermes-plugin.md describing the new API alongside the existing register_command / dispatch_tool documentation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion)
The previous implementation captured loop vars via default arguments::
async def _wrapped(ack, body, action, _cb=_cb, _plugin_name=_plugin_name):
slack_bolt's ``kwargs_injection`` introspects each listener's signature
via ``inspect.signature`` and passes ``None`` for any parameter name it
doesn't recognise (see ``slack_bolt/kwargs_injection/async_utils.py``
``build_async_required_kwargs``). That clobbered ``_cb`` to ``None`` at
dispatch time, so the wrapped plugin handler became ``NoneType`` —
``await _cb(...)`` then raised ``'NoneType' object is not callable`` and
no plugin action handler ever fired.
Replace the default-arg trick with a small closure factory so the
wrapper's public signature is exactly ``(ack, body, action)``. Add a
regression test that introspects the wrapped function's signature.
Found via real Slack click on a Block Kit button registered through
``ctx.register_slack_action_handler`` — gateway log showed
``[Slack] Plugin 'None' action handler raised: 'NoneType' object is
not callable`` despite the registration log line confirming the
handler was wired.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kshitijk4poor
added a commit
that referenced
this pull request
Jun 12, 2026
…ion-handlers feat(plugins): expose register_slack_action_handler API (salvage #20589)
AIalliAI
pushed a commit
to AIalliAI/Hermes
that referenced
this pull request
Jun 14, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
T02200059
pushed a commit
to T02200059/hermes-agent
that referenced
this pull request
Jun 18, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
…k-plugin-action-handlers feat(plugins): expose register_slack_action_handler API (salvage NousResearch#20589)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a public way for Hermes plugins to register Slack Block Kit action handlers (button clicks, overflow menus, datepickers, etc.) without monkey-patching
SlackAdapter.connect.New API on
PluginContext:action_idaccepts whateverslack_bolt.App.action()accepts — a literal string, a compiledre.Patternfor matching multiple ids, or a constraint dict like{\"action_id\": \"...\", \"block_id\": \"...\"}.Why
The plugin API already exposes
register_tool,register_hook,register_command,register_platform, andregister_context_engine, but nothing forslack_boltaction handlers. Plugins that post Block Kit messages with interactive elements had no documented hook for the resulting click events.The only previous workaround was for the plugin to monkey-patch
SlackAdapter.connectfrom insideregister()— fragile, fights with the official API, and breaks on every Hermes update. Block Kit interactivity is core to the Slack experience, so plugins should have first-class access to it.Before / after
Before — plugin had to reach into
gateway.platforms.slack, wrapSlackAdapter.connect, and register handlers post-connect onself._app:After:
How it works
PluginContext.register_slack_action_handler(action_id, callback)validates inputs and queues(action_id, callback, plugin_name)onPluginManager._slack_action_handlers.PluginManager.get_slack_action_handlers()exposes the queue to consumers.SlackAdapter.connect, after wiring its built-in approval and slash-confirm buttons, iterates the queue and registers each viaself._app.action(matcher)(callback).ack()is still issued so Slack stops retrying the click.get_plugin_manager()itself raises (e.g. plugin layer unhealthy), the gateway logs and proceeds with built-ins only rather than failing to start.Files changed
hermes_cli/plugins.py— newregister_slack_action_handlermethod onPluginContext, newget_slack_action_handlers()accessor onPluginManager, queue cleared on force-rediscover.gateway/platforms/slack.py—connect()consumes the queue after the existing built-inapp.action()blocks.tests/gateway/test_slack_plugin_action_handlers.py— new file, 13 tests covering input validation, multi-plugin registration, the connect-time wiring, defensive exception handling, and the plugin-loader-failure fallback path.website/docs/guides/build-a-hermes-plugin.md— new "Handle Slack Block Kit button clicks" section alongside the existing slash-command + dispatch_tool docs.How to test
Manual smoke test — wrote a tiny test plugin that calls
ctx.register_slack_action_handler(\"foo\", cb), posted a Block Kit message with that action_id, clicked the button, callback fired with the expected(ack, body, action)and the plugin name appeared in the gateway log under[Slack] Wired N plugin action handler(s).Compatibility
get_slack_action_handlers()returns[]and the newforloop inconnect()is a no-op.Tested on
Checklist
feat/...ruff)build-a-hermes-plugin.md)