diff --git a/hermes-zulip-plugin.prose.md b/hermes-zulip-plugin.prose.md new file mode 100644 index 0000000..856a78a --- /dev/null +++ b/hermes-zulip-plugin.prose.md @@ -0,0 +1,207 @@ +--- +kind: function +name: hermes-zulip-plugin +description: > + Installs or updates the Zulip platform plugin for any Hermes agent from the + canonical zulip-platform-plugins repo. Ensures the agent runs the latest + adapter with all Zulip chat fixes (_strip_html, streaming, event recovery). + Runs a post-install verification and sends a relay success signal. +agent: abiba +version: 1.0.0 +status: active +runtime_contract: 2 +--- + +# Hermes Zulip Plugin — Install & Repair + +Single-shot function that pulls the latest zulip-platform plugin from the +canonical git repo, installs it to the correct Hermes bundled plugin path, +verifies the installation, and signals completion. + +Distinct from `hermes-zulip-restore`: this contract only touches the plugin +layer. It does NOT verify env credentials, restart the gateway, or validate +the live Zulip connection. Use `hermes-zulip-restore` for full connectivity +recovery. + +## Parameters + +| Param | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `target` | string | yes | — | Agent name: `mumuni`, `tanko`, or `koby` | +| `branch` | string | no | `feat/zulip-streaming` | Git branch to pull (overridable for pinning) | + +## Maintains + +- plugin_installed: bool — Whether all three adapter files exist at the bundled path +- plugin_version: string — Git commit SHA of the installed version +- strip_html_present: bool — Whether `_strip_html` fix is in the installed adapter +- signal_sent: bool — Whether relay success message was dispatched + +### Postconditions + +- All three adapter files (`__init__.py`, `adapter.py`, `plugin.yaml`) present in `/hermes-agent/plugins/platforms/zulip/` +- `_strip_html` function exists in `adapter.py` (slash-command fix, commit `55ca15d`+) +- Installed version matches HEAD of the requested branch +- Relay success signal sent to Hermes agent's inbox + +## 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 + +## 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 | +|-------|-------|-------| +| Git repo | `https://git.sysloggh.net/SyslogSolution/zulip-platform-plugins.git` | ✅ Verified | +| Default branch | `feat/zulip-streaming` | ✅ Verified | +| Bundled adapter path | `/hermes-agent/plugins/platforms/zulip/` | ✅ Verified | +| Adapter files | `__init__.py`, `adapter.py`, `plugin.yaml` | ✅ Verified | +| Strip-html commit | `55ca15d` (minimum) | ✅ Verified | + +## Execution + +### Step 1: Resolve Target + +Map `target` to host, CT ID, hermes_home, and user from the live-state table. +For CT112 and CT111, route through `ssh root@amdpve` then `pct exec `. + +### Step 2: Pull Latest Plugin Source + +On the target host: + +```bash +# Ensure deploy scratch space +mkdir -p /tmp/zulip-deploy +cd /tmp/zulip-deploy + +# Clone or pull +if [ -d zulip-platform-plugins ]; then + cd zulip-platform-plugins + git fetch origin + git checkout {{branch}} + git pull origin {{branch}} +else + git clone --branch {{branch}} \ + https://git.sysloggh.net/SyslogSolution/zulip-platform-plugins.git + cd zulip-platform-plugins +fi + +# Capture installed version +INSTALLED_SHA=$(git rev-parse HEAD) +echo "Installed SHA: $INSTALLED_SHA" + +# Verify we're at HEAD +HEAD_SHA=$(git rev-parse origin/{{branch}}) +if [ "$INSTALLED_SHA" = "$HEAD_SHA" ]; then + echo "At latest commit on {{branch}}" +else + echo "WARNING: not at HEAD — $INSTALLED_SHA vs $HEAD_SHA" +fi +``` + +### Step 3: Install Plugin Files + +```bash +# Ensure target directory exists +mkdir -p {{hermes_home}}/hermes-agent/plugins/platforms/zulip + +# Copy adapter files +cp plugins/platforms/zulip/adapter.py \ + plugins/platforms/zulip/__init__.py \ + plugins/platforms/zulip/plugin.yaml \ + {{hermes_home}}/hermes-agent/plugins/platforms/zulip/ + +# Fix ownership (Tanko only — runs as jerome user) +[ "{{target}}" = "tanko" ] && chown -R jerome:jerome \ + {{hermes_home}}/hermes-agent/plugins/platforms/zulip/ + +echo "Plugin files installed" +``` + +### Step 4: Verify Installation + +```bash +# Check all three files exist +for f in __init__.py adapter.py plugin.yaml; do + if [ -f "{{hermes_home}}/hermes-agent/plugins/platforms/zulip/$f" ]; then + echo "✅ $f present" + else + echo "❌ $f MISSING" + exit 1 + fi +done + +# Verify _strip_html fix +grep -q "_strip_html" {{hermes_home}}/hermes-agent/plugins/platforms/zulip/adapter.py \ + && echo "✅ _strip_html fix present" \ + || echo "❌ _strip_html MISSING — plugin may be stale" + +# Show installed plugin.yaml version +grep "^version:" {{hermes_home}}/hermes-agent/plugins/platforms/zulip/plugin.yaml || true +``` + +### Step 5: Send Relay Success Signal + +On the Abiba host (local), dispatch a relay message to the target agent: + +``` +ra-h-os-createRelayNode: + title: "Zulip plugin updated — {{target}}" + source: | + Plugin installed from {{branch}} @ {{INSTALLED_SHA}} + All 3 adapter files verified at {{hermes_home}}/hermes-agent/plugins/platforms/zulip/ + _strip_html fix: PRESENT + Timestamp: {{timestamp}} + + description: "hermes-zulip-plugin completed for {{target}} — plugin layer healthy" +``` + +### Step 6: Report + +Compile results into a single status block: + +| Field | Value | +|-------|-------| +| Target | `{{target}}` | +| Branch | `{{branch}}` | +| Commit SHA | `{{INSTALLED_SHA}}` | +| Files installed | `__init__.py`, `adapter.py`, `plugin.yaml` | +| `_strip_html` | `{{present|missing}}` | +| Signal sent | `{{yes|no}}` | + +## Known Failure Modes + +| Symptom | Root Cause | Recovery | +|---------|-----------|----------| +| Git clone fails | No network or repo unreachable | Check VPN/network, verify repo URL | +| Permission denied on copy | Wrong user for target | Use correct user (jerome for Tanko, root for others) | +| `_strip_html` missing after install | Branch doesn't include commit `55ca15d` | Switch to `feat/zulip-streaming` branch | +| Plugin files missing after copy | Target directory doesn't exist | Ensure `mkdir -p` ran successfully | +| Relay signal fails | MCP bridge unreachable | Signal manually via `ra-h-os-createRelayNode` | + +## Edge Differences from hermes-zulip-restore + +| Concern | hermes-zulip-restore | hermes-zulip-plugin | +|---------|---------------------|---------------------| +| Env credential check | ✅ Full ZULIP_* verification | ❌ Out of scope | +| Gateway restart | ✅ Restarts the gateway process | ❌ Out of scope | +| Live connection test | ✅ Validates `zulip.state = connected` | ❌ Out of scope | +| Plugin deploy | ✅ Includes deploy as one step | ✅ Primary purpose | +| Version tracking | ❌ Implicit | ✅ Explicit SHA capture | +| Signal dispatch | ❌ None | ✅ Relay message to target | + +For full connectivity recovery after a plugin install, chain this contract +with `hermes-zulip-restore` (skip its Step 2 to avoid redundant deploy). + +--- + +**Last updated**: 2026-07-08 — Created to separate plugin lifecycle from +restore workflow.