feat: infrastructure-control network-verification + IP-first doctrine, plus 3 supporting contracts
- infrastructure-control: add Section 5.3 (Network Verification & Routing) with dual-path checks, NetBird ingress health, DNS drift detection, and routing regression alerts. Add Section 7 (Configuration Doctrine — IP-First) mandating LAN IPs for all configs/agents, URLs reserved for human browser access only. Also enrich access matrix (Mumuni, Koonimo, LiteLLM Admin, Grafana entries), reachability matrix, and CT 116 nginx routing + Prometheus targets. - build-zulip-plugin: fold Success Criteria into Maintains postconditions. - pi-approval-architecture: new reference doc — pi approval model vs Hermes, architectural limits, /approve and /deny stub commands. - zulip-approval-fix: new responsibility — documents the Zulip HTML/prefix bug that broke /approve and /deny, and the one-line fix applied.
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
---
|
||||
kind: responsibility
|
||||
name: zulip-approval-fix
|
||||
description: >
|
||||
Fixes broken /approve and /deny slash commands for Hermes agents in Zulip.
|
||||
Zulip delivers messages as HTML (<p>/approve</p>), 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**
|
||||
(`<p>/approve session</p>`), but the gateway's slash command prefix matcher
|
||||
checks `text.startswith("/approve")`. The `<p>` 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 `<p>/approve session</p>`
|
||||
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
|
||||
Reference in New Issue
Block a user