fix(approval): honor allow_session across all button adapters

Widen the allow_session tier from Matrix to every adapter the gateway
notifies: Telegram, Discord, Slack, Feishu, and Teams gate their Session
button on it; WhatsApp Cloud and qqbot accept the kwarg (no session tier
in their button sets). Also thread allow_session through the plugin-
escalation gate, the execute_code guard payload, and the plain-text
fallback so every notify path carries the same capability flags.
This commit is contained in:
Teknium 2026-07-21 06:41:36 -07:00
parent a3297bd232
commit 02d8cbadec
7 changed files with 16 additions and 6 deletions

View File

@ -2672,6 +2672,7 @@ class QQAdapter(BasePlatformAdapter):
description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
) -> SendResult:
"""Send a button-based exec-approval prompt for a dangerous command.
@ -2682,6 +2683,7 @@ class QQAdapter(BasePlatformAdapter):
adapter's interaction callback (:meth:`_default_interaction_dispatch`).
"""
del metadata # QQ doesn't have thread_id / DM targeting overrides.
del allow_session # QQ's 3-button keyboard has no session tier (once/always/deny).
if smart_denied:
description += " Owner override applies to this one operation only."

View File

@ -820,6 +820,7 @@ class WhatsAppCloudAdapter(WhatsAppBehaviorMixin, BasePlatformAdapter):
description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
) -> SendResult:
"""Render a dangerous-command approval prompt with native buttons.
@ -832,7 +833,7 @@ class WhatsAppCloudAdapter(WhatsAppBehaviorMixin, BasePlatformAdapter):
if self._http_client is None:
return SendResult(success=False, error="Not connected")
del allow_permanent # This adapter already offers one-shot Approve / Deny only.
del allow_permanent, allow_session # This adapter already offers one-shot Approve / Deny only.
# WhatsApp body caps at 1024 chars; reserve room for the
# framing prose around the command.
cmd = command or ""

View File

@ -6454,6 +6454,7 @@ class DiscordAdapter(BasePlatformAdapter):
description: str = "dangerous command",
metadata: Optional[dict] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
) -> SendResult:
"""
@ -6529,6 +6530,7 @@ class DiscordAdapter(BasePlatformAdapter):
require_admin=require_admin,
admin_user_ids=admin_user_ids,
allow_permanent=allow_permanent,
allow_session=allow_session,
smart_denied=smart_denied,
)
@ -7793,6 +7795,7 @@ def _define_discord_view_classes() -> None:
require_admin: bool = False,
admin_user_ids: Optional[set] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
):
super().__init__(timeout=_read_discord_prompt_timeout())
@ -7807,7 +7810,7 @@ def _define_discord_view_classes() -> None:
str(a).strip() for a in (admin_user_ids or set()) if str(a).strip()
}
self.resolved = False
if smart_denied:
if smart_denied or not allow_session:
self.remove_item(self.allow_session)
self.remove_item(self.allow_always)
elif not allow_permanent:

View File

@ -2004,6 +2004,7 @@ class FeishuAdapter(BasePlatformAdapter):
description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
) -> SendResult:
"""Send an interactive card with approval buttons.
@ -2028,7 +2029,7 @@ class FeishuAdapter(BasePlatformAdapter):
}
actions = [_btn("✅ Allow Once", "approve_once", "primary")]
if not smart_denied:
if not smart_denied and allow_session:
actions.append(_btn("✅ Session", "approve_session"))
if allow_permanent:
actions.append(_btn("✅ Always", "approve_always"))

View File

@ -3802,6 +3802,7 @@ class SlackAdapter(BasePlatformAdapter):
description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
) -> SendResult:
"""Send a Block Kit approval prompt with interactive buttons.
@ -3838,7 +3839,7 @@ class SlackAdapter(BasePlatformAdapter):
"value": session_key,
},
]
if not smart_denied:
if not smart_denied and allow_session:
actions.append({
"type": "button",
"text": {"type": "plain_text", "text": "Allow Session"},

View File

@ -1099,6 +1099,7 @@ class TeamsAdapter(BasePlatformAdapter):
description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
) -> SendResult:
"""Send an Adaptive Card approval prompt with Allow/Deny buttons."""
@ -1117,7 +1118,7 @@ class TeamsAdapter(BasePlatformAdapter):
title="Allow Once", verb="hermes_approve",
data={**btn_data_base, "hermes_action": "approve_once"}, style="positive",
)]
if not smart_denied:
if not smart_denied and allow_session:
actions.append(ExecuteAction(
title="Allow Session", verb="hermes_approve",
data={**btn_data_base, "hermes_action": "approve_session"},

View File

@ -5021,6 +5021,7 @@ class TelegramAdapter(BasePlatformAdapter):
description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None,
allow_permanent: bool = True,
allow_session: bool = True,
smart_denied: bool = False,
) -> SendResult:
"""Send an inline-keyboard approval prompt with interactive buttons.
@ -5055,7 +5056,7 @@ class TelegramAdapter(BasePlatformAdapter):
buttons = [
InlineKeyboardButton("✅ Allow Once", callback_data=f"ea:once:{approval_id}")
]
if not smart_denied:
if not smart_denied and allow_session:
buttons.append(
InlineKeyboardButton("✅ Session", callback_data=f"ea:session:{approval_id}")
)