Skip to content

Add WhatsApp transport via openclaw/wacli + wp-admin pairing UI#6

Merged
lezama merged 3 commits intomainfrom
add/wacli-transport
May 8, 2026
Merged

Add WhatsApp transport via openclaw/wacli + wp-admin pairing UI#6
lezama merged 3 commits intomainfrom
add/wacli-transport

Conversation

@lezama
Copy link
Copy Markdown
Owner

@lezama lezama commented May 7, 2026

Summary

Connects openclaWP to WhatsApp by treating an inbound webhook from openclaw/wacli (whatsmeow-based linked-device CLI, brew install steipete/tap/wacli) as the entry point into the openclawp/chat ability. Each incoming message is HMAC-verified, forwarded to a configured agent, and the reply is posted back through wacli send.

This is the first public consumer of AgentsAPI\AI\Channels\WP_Agent_Channel. The portable agent-loop plumbing (validate, extract, dispatch, deliver, lifecycle hooks, session continuity) lives in agents-api; this PR only fills in wacli-specific I/O.

What's in it

Channel + transport

  • OpenclaWP_Wacli_Channel extends WP_Agent_Channel. The base class owns the agent loop; this concrete channel does payload parsing, allowlist, silent_skip for own-bot echoes / disallowed chats, and the wacli send text shell-out.
  • OpenclaWP_Wacli_Transport — REST endpoint /openclawp/v1/wacli/webhook. Verifies the X-Wacli-Signature: sha256=… HMAC, parses the wacli payload, constructs the channel, scopes a single-request add_filter('openclawp_chat_ability_permission', '__return_true') while running the chat ability, and returns 200/4xx/5xx based on the channel result.

Pair from wp-admin

  • OpenclaWP_Wacli_Process — process manager that spawns wacli auth --follow --qr-format text --events detached via nohup, reads the NDJSON event stream, exposes a state machine to wp-admin.
  • OpenclaWP_Wacli_Admin + assets/wacli-admin.{js,css} + assets/qrcode.min.js — wp-admin → openclaWP → WhatsApp page. Polls /openclawp/v1/wacli/state every 2s, renders a live QR in the browser as wacli rotates it, switches to a "Connected ✅" panel once paired. Settings card below for target agent (dropdown of registered agents), allowed chats (JID textarea), and wacli binary (auto-detected, overridable).
  • OpenclaWP_Wacli_Rest — admin REST routes (manage_options): GET /wacli/state, POST /wacli/connect, POST /wacli/disconnect, GET|POST /wacli/settings.

WP-CLI

  • wp openclawp wacli setup [--rotate] — prints the wacli sync --follow --webhook … --webhook-secret … command with an auto-generated HMAC secret.
  • wp openclawp wacli ping <jid> <text> — exercise the outbound path without a webhook in the loop.

Setup

brew install steipete/tap/wacli
wp option update openclawp_wacli_agent <slug-of-registered-agent>

Then wp-admin → openclaWP → WhatsApp → Connect WhatsApp, scan the QR with WhatsApp → Settings → Linked Devices. Configure the allowlist and agent in the Settings card on the same page.

The webhook URL and HMAC secret are exposed via wp openclawp wacli setup for users who'd rather configure wacli from the CLI.

Loop prevention + safety

  • Messages with from_me: true are dropped via silent_skip, so the agent never reacts to its own outgoing replies appearing on the next sync event.
  • openclawp_wacli_allowed_jids is a comma/newline-separated allowlist of chat JIDs. Empty allowlist = allow every inbound chat (called out as risky in the UI).
  • Send errors log to error_log instead of replying to the user's WhatsApp — the agent's failures don't bleed into the personal thread.

Test plan

  • 23 PHPUnit unit tests — HMAC verification (WacliTransportTest, 7 cases), settings normalizer (WacliSettingsTest, 4 cases), wacli process state machine (WacliProcessTest, 6 cases), plus 6 misc.
  • End-to-end smoke in a Studio WP 7.0-RC2 site:
    • POST /wacli/webhook with valid HMAC → 200 {ok: true, note: "replied"}.
    • POST with bad signature → 401 {error: "bad_signature"}.
    • POST with from_me: true200 {ok: true, note: "self_message"} (silent_skip path).
  • No regressions: existing 5 message-adapter tests still pass.

Dependency

composer.json pinned to automattic/agents-api: dev-add/agent-channel as dev-main so the PR can be installed against the open agents-api PR. Once Automattic/agents-api#99 merges, this can flip back to dev-main.

Architecture

The channel is the portable piece — anyone can write a Telegram, Email, or Slack channel by extending WP_Agent_Channel, and the agent loop behaves identically. See Automattic/agents-api#99 for the base class contract.

🤖 Generated with Claude Code

Connects openclaWP to WhatsApp by treating an inbound webhook from
openclaw/wacli (whatsmeow-based linked-device CLI) as the entry point
into the openclawp/chat ability. Each incoming message is HMAC-verified,
forwarded to a configured agent, and the reply is posted back through
wacli send.

## What's in it

- OpenclaWP_Wacli_Channel — extends AgentsAPI\AI\Channels\WP_Agent_Channel
  (introduced in agents-api PR #99). The base class owns the agent loop;
  this concrete channel only fills in wacli-specific I/O (payload parsing,
  allowlist, loop-prevention via `silent_skip`, `wacli send text` shell-out).

- OpenclaWP_Wacli_Transport — REST endpoint `/openclawp/v1/wacli/webhook`
  that verifies the HMAC signature wacli signs each event with, then
  hands the payload to the channel. Also owns the proc_open to wacli
  for outbound replies.

- OpenclaWP_Wacli_Process — long-running wacli process manager. Spawns
  `wacli auth --follow --qr-format text --events` detached via nohup,
  reads the NDJSON event stream, exposes a state machine to wp-admin
  for the QR pairing flow.

- OpenclaWP_Wacli_Admin + assets — wp-admin → openclaWP → WhatsApp page
  that pairs the device by rendering a live QR in the browser. Polls
  `/openclawp/v1/wacli/state` every 2s, refreshes the QR as wacli
  rotates it, switches to "Connected" once paired. Settings card under
  the state widget for agent slug, allowed-chat JID list, and an
  optional binary path override.

- WP-CLI helpers: `wp openclawp wacli setup` prints the wacli sync
  command to copy (with the auto-generated HMAC secret); `wp openclawp
  wacli ping <jid> <text>` exercises the outbound path without a
  webhook in the loop.

- 23 PHPUnit tests covering HMAC verification (8), settings normalizer
  (4), and the wacli process state machine (6). Plus 5 more for misc
  internals.

## Setup

```
brew install steipete/tap/wacli
wp option update openclawp_wacli_agent <slug-of-registered-agent>
```

Then **wp-admin → openclaWP → WhatsApp → Connect WhatsApp**, scan the QR.
Configure the allowlist + agent in the same page.

## Architecture

The channel is the portable piece — anyone can write a Telegram or Email
or Slack channel by extending the same base class, and the agent loop
behaves identically. See agents-api PR #99 for the base class contract.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
lezama and others added 2 commits May 7, 2026 21:49
Per the agents-api canonical contract landed in agents-api PR #105,
register openclaWP as the runtime that handles the stable `agents/chat`
ability. Channels and bridges that target `agents/chat` get routed
through openclaWP's existing `openclawp/chat` runner — no caller has to
know which plugin owns the runtime.

The mapping is intentionally a thin pass-through: the canonical input
(`agent`, `message`, `session_id`, `attachments`, `client_context`) is a
superset of what `openclawp/chat` accepts, and the ability already
ignores fields it doesn't read. When the runner starts using
attachments / client_context in a future change, no caller has to
update.

`openclawp/chat` stays registered for backwards compatibility — direct
callers and the existing block continue working.

Wacli webhook surface widens its short-lived permission grant to also
cover `agents_chat_permission`, since channels now reach the runner
through the canonical dispatcher.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Automattic/agents-api#105 merged, bringing WP_Agent_Channel and the
agents/chat dispatcher into main. The branch alias is no longer needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lezama lezama merged commit e373dd7 into main May 8, 2026
@lezama lezama deleted the add/wacli-transport branch May 8, 2026 00:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant