Skip to content

feat(mattermost): interactive permission-request buttons (Approve/Deny, slash-confirm, clarify) - #66823

Closed
hermesnzl wants to merge 0 commit into
NousResearch:mainfrom
hermesnzl:feat/mattermost-interactive-permission-buttons
Closed

feat(mattermost): interactive permission-request buttons (Approve/Deny, slash-confirm, clarify)#66823
hermesnzl wants to merge 0 commit into
NousResearch:mainfrom
hermesnzl:feat/mattermost-interactive-permission-buttons

Conversation

@hermesnzl

@hermesnzl hermesnzl commented Jul 18, 2026

Copy link
Copy Markdown

Summary

Mattermost was the only major chat platform without native interactive buttons for the agent's permission/confirmation prompts. This adds them to the Mattermost adapter, mirroring the proven WhatsApp Cloud clickback pattern (gateway/platforms/whatsapp_cloud.py).

  • send_exec_approval() - Approve / Deny buttons for permission requests (e.g. terminal command approval), resolved via tools.approval.
  • send_slash_confirm() - Approve Once / Always Approve / Cancel buttons, resolved via tools.slash_confirm.
  • send_clarify() - multiple-choice buttons, resolved via tools.clarify_gateway.

Threading requirement

Prompts AND their click outcomes are posted in the SAME thread as the originating message (using root_id from the message metadata), so a permission request never spawns a stray top-level post.

Mechanism

Each button carries an action_id encoding the pending request (appr::approve|deny, sc:<once|always|cancel>:, cl::<idx|other>) and an integration.url pointing at a small loopback aiohttp callback server started in connect() (default http://127.0.0.1:8731/mattermost/actions). The handler parses the click, resolves the pending request, and posts the outcome in the same thread. Fails soft if the port can't bind.

Test plan

  • 19 unit checks pass (method binding, action_id encoding, router prefix dispatch, stale-click rejection, helper truncation/eviction).
  • Verified the adapter imports and all interactive methods bind under the real Hermes environment.

Generated with Hermes Agent (https://hermes-agent.nousresearch.com).

@alt-glitch alt-glitch added type/feature New feature or request comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jul 18, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Competing Mattermost interactive-action work: #26537 uses an earlier standalone callback server, while #29373 uses WebhookAdapter reuse and covers clarify. This PR's threaded clarify scope is related rather than duplicate; a maintainer should select the implementation.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding native Mattermost controls. The capability is still absent on current main, but this revision has blockers.

Problems

  • plugins/platforms/mattermost/adapter.py:1471-1478 does not accept allow_permanent or smart_denied; current gateway/run.py:19479-19487 always passes both. That makes exec approvals raise TypeError and fall back to text.
  • The new listener defaults to 0.0.0.0 at adapter.py:140-146, but callback user_id is only read (adapter.py:1580), not authorized before resolve_gateway_approval() (adapter.py:1657). MATTERMOST_ALLOWED_USERS already exists in config (hermes_cli/config.py:4256-4261).
  • The clarify Other branch uses undefined chat_id at adapter.py:1632; regular clicks resolve the index rather than the selected string at adapter.py:1634-1635, contrary to the adapter contract in gateway/platforms/base.py:3167-3174.
  • The PR contains no tests or Mattermost callback setup/security documentation.

Suggested changes

  • Align the approval method with the current gateway kwargs and cover that integration path.
  • Authorize callbacks before consuming pending state; use a safe listener/configuration model.
  • Fix clarify routing and add adapter tests for callback authorization, stale/double clicks, threading, and all prompt types.

Automated hermes-sweeper review.

Comment thread plugins/platforms/mattermost/adapter.py Outdated
session_key: str,
description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None,
) -> "SendResult":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current gateway/run.py:19479-19487 passes allow_permanent and smart_denied to this method. Add those keyword parameters (and honor them in the buttons), otherwise this override raises TypeError and the gateway silently falls back to the text approval path.

Comment thread plugins/platforms/mattermost/adapter.py Outdated
).strip()
channel_id = str(ctx.get("channel_id") or body.get("channel_id") or "")
root_id = str(ctx.get("root_id") or body.get("root_id") or "")
user_id = str(body.get("user_id") or "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user_id is parsed but never authorized before _route_action_click() resolves approvals. Because this PR defaults the listener to 0.0.0.0, validate it against the existing MATTERMOST_ALLOWED_USERS policy before consuming any pending action.

Comment thread plugins/platforms/mattermost/adapter.py Outdated
except Exception: # noqa: BLE001
self._clarify_state[clarify_id] = session_key
return False
await self._post_outcome(chat_id, "✏️ Type your answer:", root_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chat_id is undefined in _route_action_click; this raises after mark_awaiting_text() succeeds. Use the channel_id parameter and preserve/recover pending state if posting the outcome fails.

Comment thread plugins/platforms/mattermost/adapter.py Outdated
return False
await self._post_outcome(chat_id, "✏️ Type your answer:", root_id)
return True
response_text = str(choice)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The action encodes a zero-based index, but resolve_gateway_clarify() must receive the chosen response string. Retain the choices with pending state or encode the selected value so the agent receives the actual option rather than "0", "1", or "2".

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data 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 18, 2026
@hermesnzl hermesnzl closed this Jul 19, 2026
@hermesnzl
hermesnzl force-pushed the feat/mattermost-interactive-permission-buttons branch from ad9f5cb to f9eca7e Compare July 19, 2026 08:52
@hermesnzl
hermesnzl deleted the feat/mattermost-interactive-permission-buttons branch July 20, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants