--- kind: responsibility name: zulip-approval-fix description: > Fixes broken /approve and /deny slash commands for Hermes agents in Zulip. Zulip delivers messages as HTML (
/approve
), but the gateway's slash command parser expects plain text. HTML tags prevent command matching. agent: abiba version: 2.0.0 status: deployed --- ## Root Cause The Hermes gateway ALREADY has native `/approve` and `/deny` handling via `slash_commands.py:_handle_approve_command()`. No custom approval interception is needed in the platform adapter. The bug: Zulip's API returns message content as **rendered HTML** (`/approve session
`), but the gateway's slash command prefix matcher checks `text.startswith("/approve")`. The `` tag prevents matching. ## Fix (Applied) One line added to `~/.hermes/plugins/platforms/zulip/adapter.py`: ```python content = msg.get("content", "") content = _strip_html(content) # Strip Zulip HTML for /commands ``` Plus a small `_strip_html()` helper that strips HTML tags and decodes entities. ## How the Full Flow Works 1. Agent tries dangerous command → `check_all_command_guards()` 2. Gateway's `_approval_notify_sync` sends approval prompt via `send()` 3. User sees: "⚠️ Dangerous command requires approval — Reply /approve or /deny" 4. User types `/approve session` in Zulip 5. Zulip API returns `
/approve session
` 6. Adapter strips HTML → `/approve session` 7. Gateway's slash command handler matches `/approve` 8. `_handle_approve_command` calls `resolve_gateway_approval(session_key, "session")` 9. Agent thread unblocks, command executes ## Gateway Slash Commands Supported | Command | Effect | |---------|--------| | `/approve` | Approve once | | `/approve session` | Approve for this session | | `/approve always` | Permanently approve this pattern | | `/approve all` | Approve ALL pending commands | | `/approve all session` | Approve all + remember session | | `/deny` | Deny oldest pending | | `/deny all` | Deny all pending | ## Files Changed | File | Host | Change | |------|------|--------| | `~/.hermes/plugins/platforms/zulip/adapter.py` | .122, .123 | +_strip_html() helper + HTML stripping in message handler | ## Deployed - ✅ Mumuni (.123) — patched + gateway restarted - ✅ Tanko (.122) — patched (jerome user), gateway restart pending ## Verification 1. Send DM: "run rm /tmp/test" to Mumuni/Tanko in Zulip 2. Agent should respond with approval prompt: "⚠️ Dangerous command requires approval" 3. Reply `/approve` — agent should execute and respond 4. Reply `/deny` — agent should block and explain why