feat: hermes-zulip-restore contract — restore Zulip on any Hermes agent
Deploys zulip-platform adapter to bundled plugin path, verifies env credentials, restarts gateway, and validates Zulip connection. Covers Mumuni (CT114), Tanko (CT112), Koby (CT111). Includes _strip_html slash-command fix, known failure modes, and owner-aware restart paths.
This commit is contained in:
@@ -0,0 +1,188 @@
|
|||||||
|
---
|
||||||
|
kind: function
|
||||||
|
name: hermes-zulip-restore
|
||||||
|
description: >
|
||||||
|
Restores Zulip connectivity for any Hermes agent (Mumuni CT114, Tanko CT112,
|
||||||
|
Koby CT111). Deploys the zulip-platform adapter to the correct bundled plugin
|
||||||
|
path, verifies env credentials, restarts the gateway, and confirms Zulip
|
||||||
|
connects. Run this whenever a Hermes agent stops responding on Zulip or after
|
||||||
|
a fresh agent deployment.
|
||||||
|
agent: abiba
|
||||||
|
version: 1.0.0
|
||||||
|
status: active
|
||||||
|
runtime_contract: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hermes Zulip Restore — Bring Any Agent Back to Good State
|
||||||
|
|
||||||
|
Single-shot function that restores full Zulip connectivity for a Hermes agent.
|
||||||
|
Covers adapter deployment, HTML stripping (slash command fix), env verification,
|
||||||
|
gateway restart, and connection validation.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
| Param | Type | Required | Default | Description |
|
||||||
|
|-------|------|----------|---------|-------------|
|
||||||
|
| `target` | string | yes | — | Agent name: `mumuni`, `tanko`, or `koby` |
|
||||||
|
|
||||||
|
## Maintains
|
||||||
|
|
||||||
|
- adapter_deployed: bool — Whether `_strip_html` adapter is at correct bundled path
|
||||||
|
- zulip_connected: bool — Whether gateway_state shows zulip.state = "connected"
|
||||||
|
- env_valid: bool — Whether .env has ZULIP_SITE, ZULIP_EMAIL, ZULIP_API_KEY
|
||||||
|
- gateway_running: bool — Whether gateway process is running
|
||||||
|
|
||||||
|
### Postconditions
|
||||||
|
|
||||||
|
- `_strip_html` function present in `<hermes-agent>/plugins/platforms/zulip/adapter.py`
|
||||||
|
- All three adapter files (__init__.py, adapter.py, plugin.yaml) present at bundled path
|
||||||
|
- Zulip env vars set in `~/.hermes/.env` (or `/home/jerome/.hermes/.env` for Tanko)
|
||||||
|
- Gateway restarted and zulip platform reports state `connected`
|
||||||
|
- HTML stripping enabled for `/approve` and `/deny` slash command support
|
||||||
|
|
||||||
|
## Requires
|
||||||
|
|
||||||
|
- SSH access to target host (direct or via amdpve for CTs)
|
||||||
|
- Git repo at `https://git.sysloggh.net/SyslogSolution/zulip-platform-plugins.git`
|
||||||
|
- Python 3 with `httpx` installed on target
|
||||||
|
- Zulip server accessible at `https://chat.sysloggh.net`
|
||||||
|
|
||||||
|
## Live-State Fields
|
||||||
|
|
||||||
|
| Host | CT | Proxmox | IP (direct) | Hermes Home | User |
|
||||||
|
|------|-----|---------|-------------|-------------|------|
|
||||||
|
| Mumuni | CT114 | — | 192.168.68.123 | /root/.hermes | root |
|
||||||
|
| Tanko | CT112 | amdpve | 192.168.68.122 | /home/jerome/.hermes | jerome |
|
||||||
|
| Koby | CT111 | amdpve | 192.168.68.129 | /root/.hermes | root |
|
||||||
|
|
||||||
|
| Field | Value | Trust |
|
||||||
|
|-------|-------|-------|
|
||||||
|
| Zulip server | https://chat.sysloggh.net | ✅ Verified |
|
||||||
|
| Git repo (zulip-platform) | `https://git.sysloggh.net/SyslogSolution/zulip-platform-plugins.git` | ✅ Verified |
|
||||||
|
| Bundled adapter path | `<HERMES_HOME>/hermes-agent/plugins/platforms/zulip/` | ✅ Verified |
|
||||||
|
| Git branch | `feat/zulip-streaming` | ✅ Verified (contains _strip_html fix) |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Step 1: Locate Target
|
||||||
|
|
||||||
|
Map `target` to connectivity parameters from the live-state table above.
|
||||||
|
For CT112 and CT111, route through `ssh root@amdpve` then `pct exec <id>`.
|
||||||
|
|
||||||
|
### Step 2: Deploy Zulip Adapter
|
||||||
|
|
||||||
|
On the target host:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone or update the plugin repo
|
||||||
|
mkdir -p /tmp/zulip-deploy
|
||||||
|
cd /tmp/zulip-deploy
|
||||||
|
if [ -d zulip-platform-plugins ]; then
|
||||||
|
cd zulip-platform-plugins && git pull origin feat/zulip-streaming
|
||||||
|
else
|
||||||
|
git clone --branch feat/zulip-streaming \
|
||||||
|
https://git.sysloggh.net/SyslogSolution/zulip-platform-plugins.git
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure bundled plugin directory exists
|
||||||
|
mkdir -p <HERMES_HOME>/hermes-agent/plugins/platforms/zulip
|
||||||
|
|
||||||
|
# Copy adapter files
|
||||||
|
cp zulip-platform-plugins/plugins/platforms/zulip/adapter.py \
|
||||||
|
zulip-platform-plugins/plugins/platforms/zulip/__init__.py \
|
||||||
|
zulip-platform-plugins/plugins/platforms/zulip/plugin.yaml \
|
||||||
|
<HERMES_HOME>/hermes-agent/plugins/platforms/zulip/
|
||||||
|
|
||||||
|
# Fix ownership (Tanko only)
|
||||||
|
chown -R jerome:jerome <HERMES_HOME>/hermes-agent/plugins/platforms/zulip/ # Tanko only
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -rf /tmp/zulip-deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Verify _strip_html is Present
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -q "_strip_html" <HERMES_HOME>/hermes-agent/plugins/platforms/zulip/adapter.py
|
||||||
|
```
|
||||||
|
Expected: exit code 0. If not found → adapter is stale, re-run Step 2 with fresh clone.
|
||||||
|
|
||||||
|
### Step 4: Verify Env Credentials
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -E "ZULIP_SITE|ZULIP_EMAIL|ZULIP_API_KEY" <HERMES_HOME>/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: all three variables set with non-empty values. If any missing:
|
||||||
|
- ZULIP_SITE: `https://chat.sysloggh.net`
|
||||||
|
- ZULIP_EMAIL: `<agent>-bot@chat.sysloggh.net`
|
||||||
|
- ZULIP_API_KEY: obtain from Zulip admin panel (Bots → show API key)
|
||||||
|
|
||||||
|
### Step 5: Restart Gateway
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd <HERMES_HOME>/hermes-agent
|
||||||
|
# Use venv if available
|
||||||
|
python3 -m hermes_cli.main gateway restart # or: venv/bin/python -m hermes_cli.main gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
Wait for the restart to complete (up to 45s). Check:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep "Gateway running" <HERMES_HOME>/logs/gateway.log | tail -1
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: "Gateway running with N platform(s)" where N > 1 (includes zulip).
|
||||||
|
|
||||||
|
### Step 6: Validate Zulip Connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "
|
||||||
|
import json
|
||||||
|
d = json.load(open('$HERMES_HOME/gateway_state.json'))
|
||||||
|
print('zulip:', d.get('platforms', {}).get('zulip', {}).get('state', 'NOT FOUND'))
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: `zulip: connected`. If not connected, check gateway log:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -E "zulip|Zulip|ZULIP" <HERMES_HOME>/logs/gateway.log | tail -10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 7: Report
|
||||||
|
|
||||||
|
Compile results: `{ adapter_deployed, zulip_connected, env_valid, gateway_running }`.
|
||||||
|
|
||||||
|
| State | Action |
|
||||||
|
|-------|--------|
|
||||||
|
| All true | ✅ Agent restored — relay success to user |
|
||||||
|
| `adapter_deployed: false` | Re-run Step 2 |
|
||||||
|
| `env_valid: false` | Prompt for missing credentials |
|
||||||
|
| `zulip_connected: false` | Check Zulip server reachability, verify API key |
|
||||||
|
| `gateway_running: false` | Check process logs for crash cause |
|
||||||
|
|
||||||
|
## Known Failure Modes
|
||||||
|
|
||||||
|
| Symptom | Root Cause | Recovery |
|
||||||
|
|---------|-----------|----------|
|
||||||
|
| Gateway running with 1 platform(s) | Adapter at wrong path (user plugins vs bundled) | Deploy to `<hermes-agent>/plugins/platforms/zulip/` not `~/.hermes/plugins/` |
|
||||||
|
| Queue expired / BAD_EVENT_QUEUE_ID | Idle for 10+ minutes → normal | Auto-reconnects — no action needed |
|
||||||
|
| No events received for N seconds | No DMs or @mentions sent to this bot | Normal if nobody messaged the agent |
|
||||||
|
| `httpx` not found | Missing dependency | `pip install httpx` in the Hermes venv or system Python |
|
||||||
|
| Slash commands not matching | Missing `_strip_html` — Zulip sends `<p>/approve</p>` | Verify `_strip_html` in adapter (Step 3) |
|
||||||
|
| Permission denied on gateway restart | Running as wrong user | Use `su - jerome` for Tanko; root for others |
|
||||||
|
|
||||||
|
## Git Branch Reference
|
||||||
|
|
||||||
|
The `_strip_html` fix lives on `feat/zulip-streaming` branch:
|
||||||
|
```
|
||||||
|
https://git.sysloggh.net/SyslogSolution/zulip-platform-plugins/src/branch/feat/zulip-streaming
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit `55ca15d` — `fix(zulip): add _strip_html for slash command matching`
|
||||||
|
Pull request #33 is the primary integration branch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last verified good state**: 2026-07-08 — Mumuni, Tanko, Koby all connected with `_strip_html` applied.
|
||||||
Reference in New Issue
Block a user