Commit Graph
47 Commits
Author SHA1 Message Date
Abiba (pi) 2c4136daf8 fix(zulip): account for TRUNCATION_NOTICE overhead in _truncate
The truncation notice '[...truncated at Zulip limit]' was appended AFTER
slicing at MAX_ZULIP_MESSAGE (10000), causing the final message to exceed
Zulip's API limit. This fix subtracts the notice length from the slice so
the total stays within bounds.

(cherry picked from commit 19c52a9425)
2026-09-25 15:25:54 +00:00
Abiba (pi) 3db0d74620 security: remove hardcoded abiba-bot credentials from CI workflow
Replaced manual 'git clone' with password in URL with actions/checkout@v4.
Runner already auto-checkouts the repo - manual clone was redundant.
Also fixed YAML syntax issues in Config validation and No secrets check steps.
Credentials were exposed in git history since initial commit.

(cherry picked from commit aeb79c6286)
2026-09-25 15:25:54 +00:00
Abiba (pi) 33453b8204 feat(zulip): v3 resilience rewrite — circuit breaker, retry, watchdog, model fix
Root cause: Workers hung because default model was DeepSeek v4-pro (reasoning
model that produces empty content with normal token limits). pi RPC workers
waited forever for content that never arrived.

Changes:
- Circuit breaker (CLOSED→OPEN→HALF_OPEN) around Zulip API calls
- Retry with exponential backoff + jitter for transient errors
- Queue lifecycle management (idle_queue_timeout, auto re-register on BAD_EVENT_QUEUE_ID)
- External supervisor watchdog (restarts router after 3 health check failures)
- Busy worker timeout (SIGKILL after 5 min + error DM)
- PM2 hardening (max_restarts=100, max_memory_restart=500M)
- Crash handlers (uncaughtException + unhandledRejection → reconnect, not die)
- Fixed default model: deepseek-v4-pro → syslog-harness/syslog-auto
- Enhanced health endpoint with circuit breaker stats

Architecture: Research-backed from Zulip event system docs + Node.js resilience
patterns. Inline circuit breaker (no dependency). Separate watchdog process (Hermes pattern).

Verified: Circuit breaker trips on outage, recovers gracefully. End-to-end DM
processed in 4 seconds. Watchdog monitoring every 30s.
2026-07-13 21:29:25 +00:00
Abiba (pi) 40ce413fd8 contract: add host + Health URL columns, use health_url param
- Added Host column (Abiba: 192.168.68.24, Hermes agents: 192.168.68.123)
- Changed Health Port → Health URL with full http://host:port/health URLs
- Updated Check 1 to reference {{health_url}} instead of {{health_port}}
- Added .gitignore for .agents/ (OpenProse run state)
2026-07-02 17:18:40 +00:00
Abiba (pi) 15f94e3bcb feat: Gen 5 improvements — stream subscription check + auto-subscribe
Adds two improvements from pi Zulip extension lessons:

1. _ensure_stream_subscriptions() — checks at connect time if the bot
   is subscribed to the primary stream and general-chat; auto-subscribes
   if missing. Without this, bots with 0 subscriptions can't receive
   stream events (DMs only).

2. selftest check #9 — verifies stream subscriptions and reports
   count + names. Catches the 'zero subscriptions' failure mode that
   was a major debugging bottleneck in the pi extension.
2026-06-29 22:28:21 +00:00
Abiba (pi) 3bb7698f88 fix: clear stale errors on successful poll — prevents phantom error alerts
Same fix as applied to the pi Zulip extension: last_error_msg and
last_error_at are now cleared on every successful poll cycle, not
just on reconnect. Health monitors no longer show stale errors.
2026-06-29 22:22:59 +00:00
Abiba (pi) 0112c5bba7 feat: /zulip-test command + standardized health schema v1
- Added /zulip-test self-test command (7 checks: queue, identity,
  @all-bots, health, poll loop, echo prevention, API send)
- Standardized health endpoint to zulip-health/v1 schema
  (nested zulip{} object, checks{} map, platform/agent metadata)
- Saved pi extension source to pi-zulip-extension/extension-src/
  (with all fixes: dynamic @all-bots, health sync, logging)
- Added extension src to repo for deployment tracking
- Updated vault with final contract status report
2026-06-29 03:45:34 +00:00
Abiba (pi) 1a6098f7fd contract: Zulip extension verification report + cross-platform contract
- Full contract verification for pi (TypeScript) and Hermes (Python) Zulip plugins
- Fixed @all-bots user_id (1→20) — now dynamically resolved from Zulip API
- Fixed last_event_id stale display in health endpoint (B2)
- Fixed display_recipient logging (B3)
- Added bot_user_id and all_bots_user_id to health endpoint
- Created OpenProse cross-platform verification contract (docs/contracts/)
- Created Hermes plugin deployment script (scripts/deploy-hermes-zulip.py)

Pi extension: 8/9 checks pass (LLM relay untested — no external DMs)
Hermes adapter: 9/10 checks pass (deployment pending)
2026-06-29 03:40:52 +00:00
Abiba (pi) 15adb30bc7 fix: raise MAX_CONSECUTIVE_EMPTY_POLLS 20→500 to prevent idle reconnection cycling
Bots with no incoming messages were reconnecting every ~60 seconds
(20 polls × 3s interval). Raised to 500 polls (~25min) so idle bots
don't cycle. Connection recovers immediately on BAD_EVENT_QUEUE_ID
regardless of this counter.
2026-06-28 11:28:57 +00:00
Abiba (pi) faf9566332 test: final CI verification 2026-06-28 00:52:21 +00:00
Abiba (pi) b0aeb81caf chore: add CI status doc 2026-06-28 00:51:44 +00:00
Abiba (pi) 3fbd31fbff chore: cleanup test workflows 2026-06-28 00:48:01 +00:00
Abiba (pi) a5dc880af1 ci: simplify workflow 2026-06-28 00:47:23 +00:00
Abiba (pi) 106048999f test: minimal workflow 2026-06-28 00:45:02 +00:00
Abiba (pi) 6afc46734a ci: debug checkout step 2026-06-28 00:43:43 +00:00
Abiba (pi) 9ee1919985 ci: add auth to git clone in workflows 2026-06-28 00:42:41 +00:00
Abiba (pi) c2f91f6e58 ci: replace actions/checkout with native git clone
CI / validate (pull_request) Failing after 4s
CI / deploy (pull_request) Has been skipped
2026-06-28 00:41:49 +00:00
Abiba (pi) 67b6f64a93 test: simple runner test without actions
CI / validate (pull_request) Failing after 1s
CI / deploy (pull_request) Has been skipped
2026-06-28 00:40:22 +00:00
Abiba (pi) 45e206019c chore: trigger CI debug test
CI / validate (pull_request) Failing after 14m26s
CI / deploy (pull_request) Has been skipped
2026-06-28 00:38:11 +00:00
Abiba (pi) 78c2d967ff chore: trigger CI test
CI / validate (pull_request) Failing after 4s
CI / deploy (pull_request) Has been skipped
2026-06-28 00:37:10 +00:00
Abiba (pi) 2d467ef02c ci: Gitea Actions runner + real deploy workflows + git hooks
CI / validate (push) Failing after 7s
CI / deploy (push) Has been skipped
2026-06-28 00:31:32 +00:00
Abiba (pi) d21c565ea5 feat(agent-zero): session memory + homelab project context
CI / validate (push) Failing after 1s
CI / deploy (push) Has been skipped
A2A server v2:
- Persistent conversation history per sender (20 msg limit)
- Homelab system prompt as default project context
- tasks/session/new and tasks/session/clear endpoints
- Session memory survives across multiple Zulip DMs

Adapter:
- Passes deterministic session_id per sender_name
- Conversations continue naturally without re-explaining context
2026-06-27 19:26:28 +00:00
Abiba (pi) 1f615bbb17 fix(agent-zero): queue expiry detection in adapter
CI / validate (push) Failing after 2s
CI / deploy (push) Has been skipped
Zulip event queues expire after ~600s of inactivity. The adapter now
detects 400/BAD_EVENT_QUEUE_ID responses and auto-reconnects instead of
silently returning empty results.
2026-06-27 19:12:47 +00:00
Abiba (pi) 4938bad199 ci: staggered deployment pipeline + runner setup guide
CI / validate (push) Failing after 0s
CI / deploy (push) Has been skipped
Three-track deployment:
- v*.*.*-rc.* → Tanko canary (test first)
- v*.*.* → All Hermes agents (after canary)
- az-v*.*.* → kagentz (Agent Zero track)

Runner setup: docs/RUNNER-SETUP.md — deploy on CT 116 via Docker
2026-06-27 19:08:34 +00:00
Abiba (pi) d30b480525 ci: update Actions workflow — validate all plugins, auto-deploy on tags
CI / validate (push) Failing after 1s
CI / deploy (push) Has been skipped
2026-06-27 19:04:09 +00:00
Abiba (pi) 7e6536ea57 fix(agent-zero): auto-date in system prompt, env vars for config
CI / validate (pull_request) Failing after 1s
CI / validate (push) Failing after 4s
2026-06-27 18:57:32 +00:00
Abiba (pi) 4db655d4f9 feat(agent-zero): uvicorn A2A server calling LiteLLM directly
CI / validate (pull_request) Failing after 1s
2026-06-27 18:44:56 +00:00
Abiba (pi) 1cf6c2f806 feat(agent-zero): working A2A server with Agent Zero core integration
CI / validate (pull_request) Failing after 2s
A2A server uses Agent Zero's AgentContext.communicate() directly
to process Zulip messages inside the container.

Full chain verified: Zulip DM -> adapter -> A2A -> Agent Zero -> response -> Zulip
2026-06-27 18:21:20 +00:00
Abiba (pi) c6beb650c4 feat(agent-zero): Zulip adapter with A2A protocol for kagentz
CI / validate (pull_request) Failing after 2s
New project: agent-zero-zulip/ — direct Zulip integration for Agent Zero
agents using A2A protocol internally.

Architecture:
  kagentz (CT 105) ┌──────────────────────┐
                    │ Zulip Adapter  ◄──►  │ Agent Zero
                    │ polls Zulip    A2A   │ Docker container
                    │ port 8001            │
                    └──────────────────────┘

- adapter.py: Zulip event queue poller + A2A client
- No intermediate bridge — direct kagentz ↔ Zulip
- Heartbeat, reconnection, placeholder→edit streaming
- Designed for re-use: scottdenya, future Agent Zero agents
2026-06-27 18:04:00 +00:00
Abiba (pi) 1b0f1c627a fix(zulip): connect() accepts **kwargs for is_reconnect flag
CI / validate (pull_request) Failing after 1s
Hermes Gateway passes is_reconnect=True to connect() on reconnection.
Without **kwargs, the adapter crashes on reconnect.
2026-06-27 15:21:49 +00:00
Abiba (pi) 3690a7f568 fix(zulip): use PATCH /api/v1/messages/{id} for message editing
CI / validate (pull_request) Failing after 2s
Zulip API edits messages via PATCH /api/v1/messages/{message_id}
(POST returns 405 on this endpoint). Also:
- edit_message() now returns SendResult (not bool) for Gateway compatibility
- Clean up duplicate gateway instances before restart
2026-06-27 14:20:14 +00:00
Abiba (pi) 870f64d8a9 fix(zulip): edit_message accepts **kwargs, returns SendResult; use POST for edits
CI / validate (pull_request) Failing after 2s
The Gateway streaming interface calls edit_message() with finalize=True
and expects SendResult. Fixed:
1. Added **kwargs to edit_message() signature — silently ignores finalize
2. Changed return type from bool to SendResult for Gateway compatibility
3. Switched from PATCH to POST /api/v1/messages/{id} — Zulip docs use
   POST for message editing (PATCH returns 405 Method Not Allowed)
4. Same fixes applied to delete_message() and send()

Discovered during Tanko Gen 4 testing.
2026-06-27 14:18:42 +00:00
Abiba (pi) 48bc66b42f feat(zulip): Gen 4 — connection pool recovery, heartbeat logging, log sanitization
CI / validate (pull_request) Failing after 6s
Fixes 4 critical reliability issues discovered during Zulip server outage recovery:

1. CLOSE-WAIT socket leak: _reconnect() now creates a fresh httpx.AsyncClient()
   when the queue expires, preventing stuck connection pool. Old client is
   explicitly aclose()'d.

2. Silent poll loop death: After sustained 502 errors, the poll loop was
   returning [] without logging — transport errors are swallowed by _api_call
   returning (None, 0). Added heartbeat logging every 5 minutes and silence
   detection warnings after 60s of no events.

3. HTTP client stuck pool: Added proactive client recreation after 20 consecutive
   empty polls (connection pool exhaustion detection) and periodic refresh every
   500 polls (connection reuse limit).

4. Log spam: 502 error responses from Netbird contain full HTML pages — now
   truncated to 80 chars with newlines stripped.

Also:
- Support ZULIP_URL env var (in addition to ZULIP_SITE)
- expose silence_seconds, consecutive_empty_polls, client_pool_resets in health stats
- Reduced health callback interval from 600s to 300s
2026-06-27 14:12:52 +00:00
Abiba (pi) 6bb438de6e fix(zulip): remove invalid kwargs from SendResult calls
SendResult only accepts: success, message_id, error, raw_response,
retryable. The adapter was passing platform=, chat_id=, and metadata=
which are not valid fields, causing TypeError on every send attempt.

This was the error Tanko was showing in Zulip:
'SendResult.__init__() got an unexpected keyword argument "platform"'
2026-06-27 11:34:14 +00:00
Abiba (pi) b1bef9024f fix(zulip): resolve bot user_id from /users/me for @mention detection
The /register endpoint doesn't always return user_id on all Zulip
server versions. Added _resolve_bot_user_id() that queries the
/users/me endpoint as a fallback. This ensures @mention detection
(is_direct_mention check) works even when register doesn't provide it.

Also bumped to v1.0.3 — cumulative fixes since v1.0.1:
- v1.0.2: BAD_EVENT_QUEUE_ID detection (silent queue expiry)
- v1.0.3: bot user_id resolution (@mention detection)
2026-06-27 05:51:12 +00:00
Abiba (pi) dcf2de0052 fix(zulip): detect BAD_EVENT_QUEUE_ID instead of silently swallowing it
Root cause of Tanko not responding to DMs after initial connection:
Zulip event queues expire after ~10min of dont_block polling. When the
queue expired, _api_call() returned None for all 400-level errors, which
_fetch_events() treated as 'no events' and returned []. The poll loop
never knew the queue died, so it never reconnected.

Fix:
1. _api_call() now returns (Optional[Dict], status_code) tuple
2. _fetch_events() explicitly checks for 400 status and raises
   RuntimeError('BAD_EVENT_QUEUE_ID') for the poll loop to catch
3. All callers updated to unpack the tuple

Now when a queue expires, the poll loop catches the error and
calls _reconnect() to register a fresh queue.
2026-06-27 05:50:13 +00:00
Abiba (pi) 715d54564f fix(zulip): pass Platform enum instead of string to BasePlatformAdapter.__init__
Root cause of 'str' object has no attribute 'value' error during connect():
the adapter passed a raw string 'zulip' to the base class's platform
parameter, which expects a Platform enum instance. When self.name was
accessed (calling self.platform.value.title()), the string had no .value
attribute.

Fix: import Platform from gateway.config and wrap with Platform('zulip').
2026-06-27 05:33:46 +00:00
Abiba (pi) 10e4e0374f feat: Gen 4 — deployment scripts, v1.0.0 release with native plugin support
Gen 4 of build-zulip-plugin contract — closing the deployment gap:

1. Rewrote deploy.sh for dual-mode deployment
   - --mode=native: deploys to ~/.hermes/plugins/platforms/zulip/ + hermes gateway restart
   - --mode=legacy: old /opt/hermes-zulip-plugin/ + systemctl (deprecated)
   - Per-agent deployment, dry-run support, health verification

2. Added verify-deployment.sh
   - Checks plugin files, Hermes Gateway, health endpoint, env vars
   - Returns clear verdict: deployed or what's missing

3. Updated ARCHITECTURE.md to v2
   - Documents native plugin as CURRENT, old systemd as DEPRECATED
   - Cross-references hermes-zulip-plugin/DEPRECATED.md

4. Tagged v1.0.0 — 14/14 Success Criteria met, deployable
2026-06-27 05:17:42 +00:00
Abiba (pi) 0acf1068bf feat(hermes): Zulip adapter Gen 3 — malformed message resilience, periodic @all-bots refresh, health callback
Gen 3 improvements from build-zulip-plugin contract run:

1. Malformed message resilience
   - Per-event try/except in poll loop — one bad event never kills adapter
   - malformed_events counter tracked in health stats
   - Fulfills Success Criterion: handles_malformed_messages == true

2. Periodic @all-bots refresh
   - _all_bots_refresh_forever() background task re-resolves every hour
   - Cancelled gracefully on disconnect()
   - all_bots_refreshes counter in health stats

3. Health stats callback mechanism
   - set_health_callback(callback, interval=600) — register external consumer
   - _report_health_if_callback() triggers on dedup cleanup cycle
   - Designed for Hermes Gateway to wire to RA-H OS knowledge graph logging
   - Cross-contract integration: get_all_bots_user_id() and get_bot_user_id()
     exposed for zulip-mention-reliability contract
2026-06-26 22:31:16 +00:00
Abiba (pi) 1c8f48fd77 feat(hermes): Zulip adapter Gen 2 — dedup cleanup task, self-test, health stats, dynamic all-bots
Gen 2 improvements from build-zulip-plugin contract run:

1. Background dedup maintenance task
   - _dedup_cleanup_forever() runs every 120s, pruning stale entries
   - _is_duplicate() is now pure O(1) — no per-call cleanup overhead
   - Cleanup is cancelled gracefully on disconnect

2. Self-test diagnostics (selftest())
   - 8 checks: connection, queue, HTTP client, bot identity, poll loop,
     dedup cleanup, echo prevention, @all-bots configuration
   - Returns structured verdict: healthy / degraded / critical_failure
   - Verifies every Success Criterion from the contract

3. Health stats tracking (get_health_stats())
   - Uptime, poll counts/errors, message routing counts, send stats
   - Error rate computation, dedup map size
   - Suitable for periodic RA-H OS knowledge graph logging

4. Dynamic @all-bots resolution
   - _resolve_all_bots_user_id() queries Zulip /api/v1/users on connect
   - Falls back to configured env var or hardcoded default (1)
   - Eliminates fragile hardcoded default in multi-bot deployments

5. Connection lifecycle now manages both poll_task and dedup_cleanup_task
   - Both cancelled gracefully on disconnect()
   - dedup task starts in connect() after queue registration
2026-06-26 22:23:38 +00:00
Abiba (pi) 2ee8a4d818 feat: Hermes Zulip native platform plugin — deprecates old standalone service
CI / validate (pull_request) Failing after 2s
Builds on the NousResearch Hermes Agent plugin system:

- plugins/platforms/zulip/ — full BasePlatformAdapter implementation
  - Zulip event queue polling (like pi-zulip-extension)
  - DM-first + @mention + @all-bots routing
  - Placeholder→edit streaming UX
  - Typing indicators
  - Deduplication and echo-loop prevention
  - Auto-registers via register(ctx) — zero core changes
  - plugin.yaml with full env var schema
- hermes-zulip-plugin/DEPRECATED.md — migration guide from old subprocess-based service

Configuration:
  Env vars: ZULIP_SITE, ZULIP_EMAIL, ZULIP_API_KEY (required)
  Config: platforms.zulip.extra in Hermes config.yaml
2026-06-25 21:22:52 +00:00
Abiba (pi) 397efef776 feat(hermes): full Zulip adapter with subprocess agent routing + streaming
CI / validate (pull_request) Failing after 1s
Complete rewrite of hermes-zulip-plugin following the same architecture
validated in pi-zulip-extension:

- Zulip event queue registration + async poll loop (replaces deprecated
  call_on_each_message threaded approach)
- DM-first processing (ADR-001/ADR-002): all private messages routed to agent
- @mention and @all-bots detection (ADR-005/ADR-006) with mention cleanup
- Subprocess agent invocation (configurable via agent.command) with stdin
  message passing and stdout response capture
- Placeholder + edit pattern: sends 'Thinking...' immediately, edits with
  final response (like pi extension streaming)
- Typing indicators (start/stop) via REST API
- Typing indicator support via REST API
- 10K Zulip char limit with graceful truncation
- Error handling: timeout, FileNotFoundError, agent crash → graceful Zulip msg
- Health endpoint (:9200) in background thread
- CLI entry point: python3 -m hermes_zulip --config config.yaml
2026-06-24 20:47:47 +00:00
Abiba (pi) 0a7b69f386 fix: raise Zulip truncation limit from 1500 to 10000 (Zulip max)
CI / validate (pull_request) Failing after 1s
2026-06-24 20:33:55 +00:00
Abiba (pi) ad802100f3 feat(zulip): streaming responses + steering via session injection
CI / validate (pull_request) Failing after 2s
- Adds message_update handler that periodically edits the Zulip
  placeholder with partial response text (every ~2s during streaming)
- Sends a 'Thinking...' placeholder to Zulip immediately on DM receipt
- Detects agent busy state and uses deliverAs: 'steer' so subsequent
  Zulip DMs steer the conversation mid-stream (like terminal TUI)
- Adds editMessage() to the queue helper (Zulip PATCH /messages/{id})
- sendMessage() now returns the message ID for editing
- Turn_start / agent_end lifecycle tracks agent busy state
- Cleanup: streaming timer properly cleared on shutdown
2026-06-24 20:30:55 +00:00
Abiba (pi) ab3cb50eb4 fix(zulip): replace subprocess spawn with direct session injection
CI / validate (pull_request) Failing after 3s
- Removed spawnSync subprocess (pi -p --mode json) for Zulip DM processing
- Messages now inject directly into the current pi session via
  pi.sendUserMessage(), reusing the already-hot LLM — zero cold-start
- agent_end event handler captures assistant response and relays it to Zulip
- Added pending reply queue for ordered delivery of multiple Zulip messages
- Fixed zulip-js import (CJS default export, not named createClient)
- Added types.d.ts for zulip-js with default export declaration
- Updated package.json with yaml dependency, @earendil-works/pi-coding-agent
- Config supports both nested YAML and flat env-var formats
- Removed all subprocess timeout/buffer error paths (no longer needed)
2026-06-24 20:24:27 +00:00
Abiba (pi) ea262a38bb feat: add GitOps workflow — branch strategy, PR template, deploy/rollback scripts, CI skeleton
CI / validate (push) Failing after 11s
- docs/GITOPS.md: complete workflow from prototype to production
- .gitea/pull_request_template.md: pre-merge checklist for swarm PR review
- scripts/deploy.sh: tag-based deployment to all 6 CTs with health checks
- scripts/rollback.sh: rollback to previous tag
- .gitea/workflows/ci.yml: CI pipeline (activates when Actions runners available)
- .gitignore: prevent config.yaml and secrets from being committed
- .gitattributes: consistent LF line endings across platforms
2026-06-20 00:07:37 +00:00
Abiba (pi) 66c6d4966e fix: correct Abiba platform label (pi not openclaw), add plugin skeletons, shared config, fix health port
- Replace all 'openclaw' references with 'pi' (pi coding harness)
- Fix Git remote URL in CONTEXT.md
- Harmonize health port to 9200 across all docs
- Add config.yaml.example with full schema
- Add pi-zulip-extension skeleton (TypeScript, pi Extension API)
- Add hermes-zulip-plugin skeleton (Python, BasePlatformAdapter)
- Add agent-zero-plugin skeleton (Python, A0 plugin API)
- Update ADR-007 with pi extension docs reference
- Document private topic ACL v1 limitation in CONTEXT.md
2026-06-19 23:55:19 +00:00