refactor(pi): standalone Zulip gateway service — replaces pi extension
CI / validate (push) Failing after 1s

Complete rewrite of the pi Zulip extension as a standalone Node.js service
with direct harness API calls. No longer depends on pi's session — survives
pi shutdown and restart.

Changes:
- src/index.ts: Complete rewrite — standalone process, not a pi extension
  - Direct harness API calls (POST /v1/chat/completions) with conversation memory
  - Per-sender conversation history (up to 50 messages)
  - Placeholder->edit streaming for Zulip DMs
  - Typing indicators via Zulip API
  - Health endpoint on :9200
  - Exponential backoff reconnection
  - Graceful shutdown on SIGINT/SIGTERM
- abiba-zulip.service: systemd service unit file
- README.md: Updated deployment instructions
- package.json/tsconfig.json: Updated for standalone app

Deploy: npm install -> npx tsc -> systemctl enable abiba-zulip
Closes issue #24 (Hermes parity for pi)
This commit is contained in:
root
2026-06-24 21:54:21 +00:00
parent 355123f204
commit a5bb81b44d
6 changed files with 565 additions and 2543 deletions
+71
View File
@@ -0,0 +1,71 @@
# Abiba Zulip Gateway — Standalone Service
**Replaces** the old pi extension (`~/.pi/agent/extensions/zulip/`).
Instead of injecting messages into pi's session (which dies when pi exits), this
runs as an independent Node.js **systemd service** that:
- Polls Zulip event queue for DMs
- Calls the harness inference API directly (no pi dependency)
- Maintains per-sender conversation memory
- Survives pi shutdown and restart
## Architecture
```
Zulip event queue → poll every 3s → DM detected
→ send typing indicator + placeholder
→ POST /v1/chat/completions with conversation history
→ edit placeholder with final response
```
## Requirements
- Node.js 22+
- `npm install` in this directory
## Config
All config via environment variables. Copy `abiba-zulip.service` to set up as a
systemd service, or run directly:
```bash
ZULIP_EMAIL=abiba-bot@chat.sysloggh.net \
ZULIP_API_KEY=your_key \
ZULIP_SITE=https://chat.sysloggh.net \
HARNESS_URL=http://192.168.68.116/v1/chat/completions \
HARNESS_API_KEY=sk-xxx \
HARNESS_MODEL=qwen3.6-35B-A3B \
node dist/index.js
```
## Deploy as a service
```bash
# 1. Install deps
cd /opt/abiba-zulip
npm install
# 2. Build
npx tsc
# 3. Install systemd service
cp abiba-zulip.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable abiba-zulip
systemctl start abiba-zulip
# 4. Check status
systemctl status abiba-zulip
journalctl -u abiba-zulip -f
# 5. Health check
curl http://127.0.0.1:9200/health
```
## Development
```bash
npm run dev # watch mode (tsc watch)
npm run build # compile
npm start # run compiled
```
+48
View File
@@ -0,0 +1,48 @@
[Unit]
Description=Abiba Zulip Gateway Service — Standalone Zulip bot for pi agent
Documentation=https://git.sysloggh.net/SyslogSolution/zulip-platform-plugins
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/abiba-zulip
# ── Zulip credentials ─────────────────────────────────────────────────────
Environment=ZULIP_EMAIL=abiba-bot@chat.sysloggh.net
Environment=ZULIP_API_KEY=cKTDMZAPW08dk3zl05sStzO7HRztzyn8
Environment=ZULIP_SITE=https://chat.sysloggh.net
# ── Agent identity ─────────────────────────────────────────────────────────
Environment=AGENT_NAME=abiba
Environment=AGENT_OWNER_EMAIL=jerome@sysloggh.com
# ── Harness API (inference) ────────────────────────────────────────────────
Environment=HARNESS_URL=http://192.168.68.116/v1/chat/completions
Environment=HARNESS_API_KEY=sk-856ffb0bbb-e5aaf78b10054eca608f8fbcbd73a889
Environment=HARNESS_MODEL=qwen3.6-35B-A3B
Environment=HARNESS_MAX_TOKENS=4096
# ── Service config ─────────────────────────────────────────────────────────
Environment=HEALTH_PORT=9200
Environment=POLL_INTERVAL_MS=3000
Environment=MAX_RETRIES=5
Environment=RETRY_DELAY_MS=5000
ExecStart=/usr/bin/node /opt/abiba-zulip/dist/index.js
Restart=always
RestartSec=5
# Logging
StandardOutput=journal
StandardError=journal
# Security hardening (optional — adjust as needed)
NoNewPrivileges=true
ProtectHome=read-only
ProtectSystem=full
PrivateTmp=true
[Install]
WantedBy=multi-user.target
+17 -1975
View File
File diff suppressed because it is too large Load Diff
+9 -18
View File
@@ -1,28 +1,19 @@
{ {
"name": "pi-zulip-extension", "name": "abiba-zulip-service",
"version": "0.1.0", "version": "2.0.0",
"description": "pi extension for Zulip agent communication — connects Abiba to the Sysloggh agent mesh", "description": "Standalone Zulip gateway service for Abiba (pi agent). Direct harness API, conversation memory, systemd service.",
"main": "src/index.ts",
"type": "module", "type": "module",
"main": "dist/index.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"check": "tsc --noEmit" "start": "node dist/index.js",
"dev": "node --watch dist/index.js"
}, },
"dependencies": { "dependencies": {
"yaml": "^2.9.0",
"zulip-js": "^2.0.0" "zulip-js": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
"@earendil-works/pi-coding-agent": "^0.80.2", "@types/node": "^22.0.0",
"typescript": "^5.0.0" "typescript": "^5.7.0"
}, }
"keywords": [
"pi",
"zulip",
"agent",
"abiba",
"sysloggh"
],
"license": "UNLICENSED",
"private": true
} }
File diff suppressed because it is too large Load Diff
+11 -7
View File
@@ -1,14 +1,18 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "target": "ES2022",
"module": "ESNext", "module": "ES2022",
"moduleResolution": "bundler", "moduleResolution": "node",
"strict": true, "outDir": "./dist",
"rootDir": "./src",
"strict": false,
"esModuleInterop": true, "esModuleInterop": true,
"outDir": "dist", "skipLibCheck": true,
"rootDir": "src", "forceConsistentCasingInFileNames": true,
"declaration": true, "declaration": true,
"skipLibCheck": true "declarationMap": true,
"sourceMap": true
}, },
"include": ["src/**/*.ts"] "include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
} }