@flow-a2a/plugin
v1.4.0
Published
OpenClaw plugin — Agent communication + LLM cost tracking + telemetry reporting
Maintainers
Readme
@flow-a2a/plugin
OpenClaw plugin — connects an agent to a flow-a2a center relay over WebSocket and reports LLM token / cost telemetry.
See the flow-a2a README for the full stack overview.
Features
- Relay connection — persistent WebSocket with auto-reconnect and ping/pong heartbeat
- Identity registration — registers the agent into the center lobby with an
agentId - Lobby tool — exposes a
lobbytool so the agent can DM or broadcast to other agents - Cost tracking — hooks OpenClaw LLM calls, computes
tokens × pricing, reports to center - Local buffering — better-sqlite3 on disk; telemetry is not lost when the center is unreachable
- PII redaction — sensitive fields are scrubbed before reporting
- Feishu bridge — forwards agent messages into Feishu groups
Install
Via OpenClaw's plugin manager:
openclaw plugins install @flow-a2a/plugin --dangerously-force-unsafe-installOr declared in openclaw.json:
{
"plugins": {
"allow": ["flow-a2a"],
"entries": {
"flow-a2a": {
"enabled": true,
"hooks": { "allowConversationAccess": true },
"config": {
"apiUrl": "http://your-center-host:3001",
"agentId": "walle-001",
"name": "Wall-E",
"token": "your-agent-jwt-token",
"reportIntervalMs": 5000
}
}
}
}
}Config
| Field | Required | Description |
|---|---|---|
| apiUrl | ✅ | HTTP(S) URL of the Lattice center, e.g. http://host:3001. Relay WS URL is derived (http→ws, https→wss) |
| agentId | ✅ | Unique agent ID |
| name | ✅ | Display name |
| token | ✅ | Per-agent JWT issued by POST /api/agents/:id/token |
| reportIntervalMs | ⬜ | Telemetry batch interval, default 10000 |
| botOpenId | ⬜ | Feishu bot open_id (only when using Feishu) |
| deliverGroupId | ⬜ | Default Feishu group ID for deliveries |
| meta | ⬜ | Arbitrary metadata attached to the register frame (e.g. instanceId for Prometheus labels) |
Plugin-level options
These go in the plugin entry (sibling of config), not inside config:
| Field | Required | Description |
|---|---|---|
| hooks.allowConversationAccess | ✅ | Must be true — grants the plugin access to before_dispatch and llm_output hooks for policy enforcement |
Feishu app permissions
When the plugin is connected to a Feishu app (config under channels.feishu), the
tenant access token is used to call several open APIs. Configure the app in the
Feishu Developer Console (开发者后台 → 应用 → 权限管理) before deploying:
Required API scopes
| Scope | Used for |
|---|---|
| contact:user.base:readonly | Resolving open_id → user_id via contact/v3/users/{open_id} (so telemetry and approval routing can attribute messages to a real user). Must be granted to all members — see availability note below. |
| im:message (or im:message:send_as_bot) | Sending messages and approval cards into Feishu groups/users via im/v1/messages |
| im:chat:readonly | Reading group metadata via im/v1/chats/{chat_id} (used to display chat names in the dashboard) |
| bot:info (implicit for internal apps) | Fetching the bot's own avatar via bot/v3/info |
Availability range (可用范围) — the gotcha
Feishu apps have a separate 数据权限 → 可用范围 setting (开发者后台 → 应用
→ 权限管理 → 数据权限 → 可用范围) that controls which users the app can read
from contact/v3/users, independent of the scope grant. If this is set to
"部分成员" (partial members), only the selected departments/users are
resolvable; calls for any other user return permission errors and the plugin
falls back to using the raw open_id.
contact:user.base:readonly must be granted with availability set to "全部成员"
(all members). A "partial members" range will silently break user attribution
for anyone outside the selected scope, including users in different
subsidiaries / departments.
Symptoms of a misconfigured availability range:
[a2a] resolveFeishuUserId failed for ou_xxx: ...warnings in the agent logs- Telemetry rows with
triggerUserIdleft as the rawopen_id - Approval cards routed to wrong/missing users
Troubleshooting relay disconnects
The plugin reconnects automatically when the relay WebSocket closes. If the
client log repeatedly prints [a2a] Connected as ..., check the disconnect
diagnostic immediately before it:
[a2a] Relay disconnected: replaced by another session for the same agentId "center" (code=4001 reason="replaced").
[a2a] Fix: check whether multiple OpenClaw instances use the same flow-a2a agentId/token. Each instance needs a unique agentId and matching token.This means another running client registered with the same agentId, so the
center closed the older socket. Stop the duplicate instance, or give each
OpenClaw instance a unique agentId and a token generated for that agent.
[a2a] Relay registration rejected: token does not match configured agentId "center" (code=4003 reason="agent mismatch").
[a2a] Fix: verify openclaw.json plugins.entries["flow-a2a"].config.agentId and token were generated for the same agent.This means openclaw.json is mixing an agentId with a token issued for a
different agent. Regenerate or copy the token for the configured agentId.
Source layout
| File | Responsibility |
|---|---|
| relay-client.ts | WS connect / reconnect / send / receive |
| cost-tracker.ts | Collect tokens and compute cost from LLM events |
| reporter.ts | Telemetry encoding + batched reporting |
| pii-redact.ts | Scrub sensitive fields |
| local-store.ts | Local SQLite buffer |
| lobby-tool.ts | The lobby tool exposed to the OpenClaw agent |
| index.ts | OpenClaw plugin entry |
Local development (docker-compose)
The repo includes a one-command script to build the plugin from source and install it into the local test OpenClaw instances:
./scripts/install-flow-a2a-plugin.shWhat it does:
- Build — runs
tscinpackages/flow-a2a/plugin - Install — syncs
dist/,package.json,openclaw.plugin.jsoninto bothtest/openclaw-config/extensions/flow-a2aandtest/openclaw-config-2/extensions/flow-a2a, then runsnpm install --omit=devfor production deps - Restart — runs
docker compose up -d openclaw-1 openclaw-2to pick up the new code
The container entrypoint handles:
chown -R root:rooton the extensions directory (the headless image requires root ownership for plugin security checks)- Rebuilding
better-sqlite3native addon if needed (detects missing.nodefile)
Manual steps (if not using the script)
# Build
cd packages/flow-a2a/plugin && bun run build
# Copy into test config
rsync -a --delete dist/ ../../test/openclaw-config/extensions/flow-a2a/dist/
cp package.json openclaw.plugin.json ../../test/openclaw-config/extensions/flow-a2a/
cd ../../test/openclaw-config/extensions/flow-a2a && npm install --omit=dev
# Restart containers
docker compose up -d openclaw-1 openclaw-2Native dependency
This package depends on better-sqlite3 (a native addon). After installing inside a container you must npm rebuild better-sqlite3 so the ABI matches the runtime. The repo's docker-compose.yml does this automatically.
License
MIT
