@agent-wechat/agent-wechat 0.16.0 → 1.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -144
- package/dist/index.js +3121 -7106
- package/openclaw.plugin.json +241 -16
- package/package.json +110 -18
package/README.md
CHANGED
|
@@ -1,200 +1,170 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OpenClaw WeChat channel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The plugin uses the standalone native daemon. External mode connects by endpoint/token to an independently operated daemon, including a native server container; the Gateway needs neither `wx` nor Docker tools. Managed mode starts an owned local daemon and requires a separately installed `wx`. Plugin installation/startup never downloads or repairs a binary. Old container API configurations require explicit migration.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Once published, install the native beta with `openclaw plugins install @agent-wechat/agent-wechat@beta` or pin its exact version. The stable `latest` channel continues to select the old container plugin during the beta series.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Native external mode
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Configure plugin defaults under `plugins.entries.agent-wechat.config` (or the same `native` configuration in the channel):
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"native": {
|
|
14
|
+
"mode": "external",
|
|
15
|
+
"accountId": "example",
|
|
16
|
+
"endpoint": "http://127.0.0.1:6174",
|
|
17
|
+
"token": { "source": "env", "provider": "default", "id": "AGENT_WECHAT_TOKEN" },
|
|
18
|
+
"consumerStateDir": "/private/openclaw-native-consumer",
|
|
19
|
+
"readPolicy": "on_inbound"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
12
23
|
|
|
13
|
-
|
|
24
|
+
The external service remains independently owned. Plugin stop closes its stream and never stops/logs out the daemon or cancels its sends. Remote endpoints require verified HTTPS or an encrypted tunnel terminating at loopback. Set `AGENT_WECHAT_TOKEN` in the Gateway process environment, or use a configured OpenClaw secret provider. The plugin resolves `native.token` through the pinned OpenClaw SDK; `native.tokenFile` is removed. Tokens contain 32–1024 printable ASCII bytes without spaces. An explicit unresolved reference fails startup without falling back to another token. The API token needs read/media/send/login for the selected account, plus read-state only when automatic whole-thread read marking is enabled; do not grant admin solely to run the plugin. Normal sender/DM/group/mention policy remains in `channels.agent-wechat`; defaults remain closed.
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
## Native managed mode
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"native": {
|
|
31
|
+
"mode": "managed",
|
|
32
|
+
"accountId": "example",
|
|
33
|
+
"executable": "/opt/agent-wechat/bin/wx",
|
|
34
|
+
"listen": "127.0.0.1:6174",
|
|
35
|
+
"dataDir": "/private/native-store",
|
|
36
|
+
"token": { "source": "env", "provider": "default", "id": "AGENT_WECHAT_TOKEN" },
|
|
37
|
+
"consumerStateDir": "/private/openclaw-native-consumer",
|
|
38
|
+
"readPolicy": "never"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
18
42
|
|
|
19
|
-
|
|
43
|
+
Provision the native executable and account registration separately. Omitting `executable` resolves `wx` once from absolute trusted PATH entries. The plugin supports the package's tested npm launcher layout by selecting its already installed checksum-verified ELF payload directly. It never invokes an installer or shell. Unknown wrappers/old container CLIs are rejected. Managed startup refuses an occupied port and requires authenticated readiness matching the owned child PID, store path, server identity and process generation; it never adopts or terminates another process. Stop sends a graceful termination to its own child with bounded escalation. An unexpected child/stream failure is reported; automatic process crash restart is not implemented in this increment.
|
|
20
44
|
|
|
21
|
-
|
|
45
|
+
Managed startup sends the resolved token through a private stdin pipe; it creates no token file and does not put the token in arguments or the daemon environment. A managed `serverConfig` must omit `tokenFile`. Reload/restart the Gateway to change credentials or secret-provider configuration.
|
|
22
46
|
|
|
23
|
-
|
|
47
|
+
Keep `dataDir` and `consumerStateDir` on separate explicit persistent mounts. The daemon owns official-format profile/sidecar data; the consumer directory holds private intake/deduplication/model-handoff/read receipts bound to server and account identities. Executable upgrades do not remove either. One configured OpenClaw account maps to one selected native daemon account in this increment, while the daemon itself supports independent account slots.
|
|
24
48
|
|
|
25
|
-
|
|
26
|
-
npm install -g @agent-wechat/cli
|
|
27
|
-
wx up
|
|
28
|
-
```
|
|
49
|
+
## Native messaging lifecycle and validation limits
|
|
29
50
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```yaml
|
|
33
|
-
services:
|
|
34
|
-
agent-wechat:
|
|
35
|
-
image: ghcr.io/thisnick/agent-wechat:latest
|
|
36
|
-
container_name: agent-wechat
|
|
37
|
-
security_opt:
|
|
38
|
-
- seccomp=unconfined
|
|
39
|
-
cap_add:
|
|
40
|
-
- SYS_PTRACE
|
|
41
|
-
ports:
|
|
42
|
-
- "6174:6174"
|
|
43
|
-
volumes:
|
|
44
|
-
- agent-wechat-data:/data
|
|
45
|
-
- agent-wechat-home:/home/wechat
|
|
46
|
-
- ~/.config/agent-wechat/token:/data/auth-token:ro
|
|
47
|
-
restart: unless-stopped
|
|
48
|
-
|
|
49
|
-
volumes:
|
|
50
|
-
agent-wechat-data:
|
|
51
|
-
agent-wechat-home:
|
|
52
|
-
```
|
|
51
|
+
The adapter consumes account-scoped SSE using bearer headers and decimal-string journal cursors. It persists custody before acknowledging a cursor; acknowledgement is independent of unread state. Only explicitly live, incoming, committed resources can enter the model. Supported images, short voice, video, files and eligible stickers pass through bounded media retrieval and hash verification into OpenClaw-managed storage. Pending media retains custody without model dispatch or a read receipt. Expired/unsupported attachments retain an explicit status; raw XML never becomes an attachment. Catch-up/import/raw pending sync batches, unknown origin and outgoing messages do not become automatic model inputs. Group admission uses structured exact self-mention facts; unknown metadata or displayed `@names` never satisfy a mention gate. Contacts and groups use their separate catalogs.
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
Every ready attachment supplies its original OpenClaw-managed path in the message body and `Attachments` metadata, including images. File references wrap paths in angle brackets, such as `[Image file: <path>]` or `[Local file: <path>]`, so OpenClaw does not automatically load an original or a file with an image extension when scanning prompt text. A separately prepared image preview may occupy `MediaPath` while the original file remains accessible by its own path. Files, voice and video retain explicit local references; an available path does not claim that the model analyzed the file.
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
mkdir -p ~/.config/agent-wechat
|
|
58
|
-
openssl rand -hex 32 > ~/.config/agent-wechat/token
|
|
59
|
-
chmod 600 ~/.config/agent-wechat/token
|
|
60
|
-
docker compose up -d
|
|
61
|
-
```
|
|
55
|
+
In groups requiring a mention, sender-admitted unmentioned messages are retained as context without retrieval, transcription, a model turn or a read notification. The next admitted non-command message includes that context after checking the current sender policy again. `messages.groupChat.historyLimit` controls the per-group window (default 50, maximum 200; zero disables it). The consumer store retains at most 1,000 buffered group messages overall. Context survives a Gateway restart. At dispatch, the selected context is consumed atomically with the durable turn record, so an uncertain turn is not offered again automatically. Original mention facts remain unchanged; history is recorded separately from the triggering message.
|
|
62
56
|
|
|
63
|
-
|
|
57
|
+
Group catch-up retrieves media with at most three workers, keeps every verified original path and selects only the latest ready image's prepared representation for vision. Pending or unavailable historical attachments remain explicit statuses and do not block the current mention. The triggering attachment, when still pending, continues to hold its own dispatch. Voice transcription runs only after admission and activation. Commands do not drain buffered context.
|
|
64
58
|
|
|
65
|
-
|
|
59
|
+
By default, `readPolicy: "on_inbound"` marks a whole thread read after its incoming message has been durably stored, passed sender/group/mention checks and completed normal OpenClaw inbound handling. Built-in commands also qualify when no model is called. This policy does not inspect model inputs or require `hooks.allowConversationAccess`. Ignored messages, pending media and failed handling do not initiate a read. Read means handled by the integration; it does not claim that a model analyzed an attachment.
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
openclaw plugins install @agent-wechat/agent-wechat
|
|
69
|
-
```
|
|
61
|
+
A whole-thread read also clears earlier unread messages in that thread, including messages the model did not receive. The daemon freezes the latest local message boundary, checks the expected version, updates local unread state and submits one native read-status notification. Server acceptance alone does not prove that a phone badge changed. A lost notification response remains `needs_review` and is never automatically resubmitted. Pending reads survive a Gateway restart and retry independently of model execution or new SSE traffic. `readPolicy: "never"` disables both local and remote marking, including previously queued reads. Crashes during an unfinished dispatch retain `needs_review`; the plugin does not automatically duplicate model turns or uncertain outgoing sends.
|
|
70
62
|
|
|
71
|
-
|
|
63
|
+
The optional `readPolicy: "on_model_dispatch"` instead requires a confirmed model handoff. OpenClaw **2026.10.2** lacks `onPreparedAgentRunStart`, so its released public hooks provide a bounded compatibility path. Before dispatch, the adapter persists an immutable manifest binding the exact new message ID/version/content hash, policy hash, source and supplied asset hashes, and context roles. Text requires the exact current input and host-created message/account/session facts plus a matching `model_call_started`. Media additionally waits for the same run's `agent_end` snapshot and source-turn identity. Image bytes/MIME/length must match the frozen supplied representation; file/video/voice references must appear in the exact current body. Image count, model selection and reply delivery alone cannot confirm a model handoff. Unknown, omitted, rewritten, merged or ambiguous inputs remain unconfirmed. Host/provider history formats can prevent confirmation even when a reply was delivered; use `on_inbound` for ordinary bot read behavior.
|
|
72
64
|
|
|
73
|
-
|
|
74
|
-
# Uses defaults (localhost:6174, token from ~/.config/agent-wechat/token)
|
|
75
|
-
openclaw channels add --channel agent-wechat
|
|
65
|
+
The model-handoff compatibility path requires the operator's explicit host permission, `plugins.entries.agent-wechat.hooks.allowConversationAccess: true`. It observes only run IDs issued by this adapter and never enables that permission itself. Without the grant, or on an unsupported host, the explicit `on_model_dispatch` policy leaves messages unread and status reports `handoffUnconfirmed`. The older `onAgentRunStart` also reports direct tools and early lifecycle phases and is never used as proof. Access and mention gates still precede model dispatch; a rejected message does not itself trigger a read.
|
|
76
66
|
|
|
77
|
-
|
|
78
|
-
openclaw channels add --channel agent-wechat --url <url> --token <token>
|
|
79
|
-
```
|
|
67
|
+
Images exceeding the host's bounded representation, or formats requiring conversion, receive a separately stored JPEG still preview using the public SDK image processor. The original stays intact; both hashes are recorded and the model input labels the preview. Prepared images fit within 5 MiB and at most 1200 pixels per side (or a smaller configured limit). Files and videos can be supplied as available local references; this does not claim their contents were analyzed. Voice retains its verified file and includes configured transcription output or an explicit unavailable status. A placeholder does not confirm a model handoff under `on_model_dispatch`; default `on_inbound` can mark a thread after successfully handling an explicit expired/unsupported status.
|
|
80
68
|
|
|
81
|
-
|
|
69
|
+
## Message recalls
|
|
82
70
|
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
"channels": {
|
|
86
|
-
"agent-wechat": {
|
|
87
|
-
"enabled": true,
|
|
88
|
-
"serverUrl": "http://localhost:6174",
|
|
89
|
-
"dmPolicy": "open",
|
|
90
|
-
"allowFrom": ["*"],
|
|
91
|
-
"groupPolicy": "open"
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
71
|
+
`wechat_recall` recalls a message sent by the configured WeChat account in the current conversation. Pass `{"messageId":"<opaque stored ID>","serverId":"<exact decimal server ID>"}` using both values from the same verified send receipt or `wechat_history` entry. The server ID stays a string, including values larger than JavaScript's safe integer range. A local opaque ID can be reused, so an old reference with a different server ID is rejected. Send/job/operation IDs cannot replace a stored message ID.
|
|
96
72
|
|
|
97
|
-
|
|
73
|
+
The tool uses OpenClaw's trusted account, conversation and requester context. It accepts no account or thread selector, rechecks normal requester policy, and requires an own outgoing message; owner status and archive access do not widen recall permission. The daemon makes the final eligibility decision. The account token requires `read` and `send` permission. Normal OpenClaw tool policy can disable `wechat_recall`.
|
|
98
74
|
|
|
99
|
-
|
|
75
|
+
An exact recalled placeholder may lose its original sender attribution in the official profile. Repeating the tool with that placeholder inspects its retained recalled state through the daemon and cannot start another native recall; the result labels this `action: "inspect_recalled"`.
|
|
100
76
|
|
|
101
|
-
|
|
77
|
+
Every target gets a stable idempotency key and a durable operation. Only an exact successful receipt reports `recalled` or `already_recalled`. Rejections, `pending` and `needs_review` remain explicit. A lost response or cancellation does not cause another automatic POST or cancel the daemon's action. Retain the reported operation ID and key for inspection; do not submit a replacement action to resolve uncertainty.
|
|
102
78
|
|
|
103
|
-
|
|
79
|
+
Native text, image, file, video and voice delivery results return actual stored message IDs after account/thread/server-identity verification. `meta.messageIds` preserves each verified receipt for split voice notes, alongside the durable send/job ID. Accepted media jobs may publish their stored receipts slightly later; the adapter inspects the same job for up to ten seconds and requires every accepted voice chunk's receipt. If stored receipts remain unavailable, the result leaves `messageId` empty and reports `receiptState: "needs_review"` with `retryable: false`; acceptance is not a reason to resend. Failed, partial and uncertain jobs do not enter that receipt wait.
|
|
104
80
|
|
|
105
|
-
|
|
106
|
-
openclaw gateway restart
|
|
107
|
-
```
|
|
81
|
+
Incoming `message.recalled` events are consumed independently of slow media and model work, including recalls of own or unmentioned messages. The consumer persists the event cursor together with a tombstone, removes recalled body/quote content and attachment references from its custody records, and invalidates pending model proofs. Tombstones survive restart and suppress a late original. Attachment retrieval, transcription, model dispatch and later automatic reply delivery recheck the selected messages so late work cannot reinsert recalled input. A quote's unrelated comment remains available after its recalled original is removed.
|
|
108
82
|
|
|
109
|
-
|
|
83
|
+
If a source-local message ID later names a different exact thread/server target, the consumer admits that new message with a separate host turn and reply key. Completion callbacks, media results and read receipts captured for the old target cannot overwrite the new custody record.
|
|
110
84
|
|
|
111
|
-
|
|
85
|
+
Stored-history tools return a recalled placeholder; media tools return terminal `status: "recalled"`, `reason: "message_recalled"`, `retryable: false` without attachment paths or image blocks. Recall does not erase an already submitted model input, an existing OpenClaw transcript, a completed action, or attachment bytes retained by OpenClaw's storage policy. An affected in-flight turn is held for review and its later automatic reply is suppressed; the plugin does not claim retroactive transcript correction.
|
|
112
86
|
|
|
113
|
-
|
|
87
|
+
Old media handles whose stored source changed, or whose source identity cannot be verified, return terminal `unavailable` with `media_source_changed` or `media_identity_unavailable`. They expose no cached path and are not retried automatically. Fresh stored-message discovery can select the intended current message and its bound cached asset without resending a WeChat message.
|
|
114
88
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
openclaw channels login --channel agent-wechat
|
|
119
|
-
```
|
|
89
|
+
`node scripts/native-recall-model-smoke.mjs` builds the actual plugin and runs the pinned Gateway against synthetic native HTTP/SSE and a local provider. It checks registered tool discovery and exact-target execution, stable repeated keys, another thread's rejection, recall intake while provider/media requests are blocked, late-output suppression, out-of-order recall custody and one dispatch for a new exact occupant of a reused source ID. It makes no real account or native protocol exchange.
|
|
120
90
|
|
|
121
|
-
|
|
91
|
+
## Stored-history tools
|
|
122
92
|
|
|
123
|
-
|
|
93
|
+
The agent can look beyond the recent inbound buffer using the selected native account's local profile:
|
|
124
94
|
|
|
125
|
-
|
|
95
|
+
| Tool | Inputs (`?` means optional) | Output |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `wechat_history` | `{ limit?, cursor? }` | Stored messages, newest first, with `nextCursor`, `sourceGeneration` and `coverage` |
|
|
98
|
+
| `wechat_search` | `{ query, limit?, cursor? }` | Same page shape, filtered by case-insensitive substring |
|
|
99
|
+
| `wechat_message` | `{ messageId }` | One stored message |
|
|
100
|
+
| `wechat_media` | `{ messageId, download?, quality? }` | Media status, optional verified file path and image content |
|
|
126
101
|
|
|
127
|
-
|
|
102
|
+
A stored message includes `id`, `threadId`, nullable `serverId`, `senderId`, `direction` and `createdAt`, numeric `type`, bounded `body`, and `bodyTruncated`. Its optional `attachment` descriptor contains `kind`, `messageId` and `retrievalTool: "wechat_media"`. Message IDs and server IDs remain strings; use the opaque `id` for exact lookup and media retrieval.
|
|
128
103
|
|
|
129
|
-
|
|
104
|
+
A media result includes `messageId`, `threadId`, `status` and `retryable`, with optional `attachment`, actual `variant`, `jobId`, `reason` and display `body`. A ready attachment has `kind`, `filename`, verified local `path` and `mime`; image quality may be `full`, `standard` or `thumbnail`. Unavailable results never claim a local path. OpenClaw wraps structured outputs in `details` and text `content`; a ready image additionally supplies an image content block.
|
|
130
105
|
|
|
131
|
-
|
|
106
|
+
For example, the agent can call `wechat_search` with `{"query":"project notes"}` in the current conversation. These four default tools are restricted to the current WeChat account and thread derived from trusted OpenClaw context. Owners and agents with archive access have the same restriction when invoking a default tool. Default tools accept no thread/account selector, and the plugin rejects messages or assets from another conversation or account. Requester and historical sender policy are rechecked. Missing or conflicting trusted conversation context hides the default tools; webchat and other channels cannot use them.
|
|
132
107
|
|
|
133
|
-
|
|
108
|
+
History/search return opaque string message IDs, a bounded text preview, coverage metadata and `nextCursor`. Pass the cursor back with the same tool, query and thread to continue; the page limit is 1–50 (default 20). Search queries are limited to 256 UTF-8 bytes. Long message bodies are labeled as truncated. A sender-filtered page may be empty and still have a continuation. Search covers locally stored history and does not fetch missing remote history or maintain an official FTS index. Media messages receive a type/status description; these tools do not return raw transport XML or download attachments. History, search and exact-message lookup are read-only. Their attachment descriptions name `wechat_media` and the exact message ID without automatically retrieving media. None of these tools mark a conversation read, send messages or modify the inbound consumer cursor. Lookup requires the selected account's API `read` scope and remains subject to normal OpenClaw tool policy. With OpenClaw's compact tool catalog, the agent discovers history/search/message lookup through `tool_search` and invokes the returned tool with `tool_call`. The media tool stays directly callable as `wechat_media`: the pinned host's generic wrapper serializes nested image blocks as JSON text, while direct invocation preserves image content for vision.
|
|
134
109
|
|
|
135
|
-
|
|
110
|
+
`wechat_media` accepts `{"messageId":"<returned opaque ID>"}`. It verifies message/thread/sender admission before accessing assets, uses cached bytes first, checks identity, hash and size, and saves verified bytes to OpenClaw's managed media store. Files, videos and voice return local paths; images also return a bounded prepared preview as image content for vision. This retrieval does not itself transcribe voice or analyze files/videos. Cached bytes remain usable after CDN expiry.
|
|
136
111
|
|
|
137
|
-
-
|
|
112
|
+
Missing media requests a durable daemon transfer by default, with a two-second bounded wait. A pending result includes its `jobId` when known and no claimed local path; call the same tool with the same message ID later to check. The daemon owns and deduplicates the transfer, so cancelling a tool invocation does not cancel submitted work. Use `download: false` for cached media only. `quality` defaults to `best` (best cached image, normal video or decoded voice); explicit `original`, `standard`, `thumbnail`, `normal` and `encoded` variants are strict and may be unavailable. Results report the actual variant and explicit pending, expired, unsupported, unavailable or too-large status. Never automatically retry `retryable: false` or confirmed expiry. The media tool requires account-scoped API `read` and `media` permission, including for download requests. Raw XML, signed transport URLs and keys are never returned.
|
|
138
113
|
|
|
139
|
-
-
|
|
114
|
+
`node scripts/native-history-model-smoke.mjs` exercises the pinned Gateway with local account/media/model fixtures: durable group context, original file paths, one preview with a verified hash, a PNG sent as a file remaining a reference, direct image delivery, actual tool discovery, two paginated search calls, rejection of another thread's attachment before media access, one requested image download whose exact image bytes reach vision, and a cached file path reaching the provider. CI runs this check on both Linux architectures.
|
|
140
115
|
|
|
141
|
-
|
|
116
|
+
When asset metadata identifies an existing download job, retrieval polls that scoped job instead of submitting another transfer. A terminal job's reason and retryability are preserved, including in cache-only mode; this tool does not automatically retry failed jobs. A service rejection alone is not classified as expiry.
|
|
142
117
|
|
|
143
|
-
|
|
118
|
+
### Privileged archive tools
|
|
144
119
|
|
|
145
|
-
|
|
146
|
-
|-----|------|---------|-------------|
|
|
147
|
-
| `enabled` | boolean | `false` | Enable the WeChat channel |
|
|
148
|
-
| `serverUrl` | string | — | agent-wechat REST API URL |
|
|
149
|
-
| `token` | string | — | Auth token (auto-read from `~/.config/agent-wechat/token` for local setups) |
|
|
150
|
-
| `dmPolicy` | `"open" \| "allowlist" \| "disabled"` | `"disabled"` | Who can DM the bot |
|
|
151
|
-
| `allowFrom` | string[] | `[]` | DM senders and command owners: `wxid_...`, `"*"` for everyone, or `accessGroup:<name>`. With `dmPolicy: "open"`, use `["*"]` |
|
|
152
|
-
| `groupPolicy` | `"open" \| "allowlist" \| "disabled"` | `"disabled"` | Group message policy |
|
|
153
|
-
| `groupAllowFrom` | string[] | `[]` | Group senders who pass `allowlist` and may run commands in groups; falls back to `allowFrom` when empty |
|
|
154
|
-
| `groups` | object | `{}` | Per-group overrides (e.g. `{ "id@chatroom": { "requireMention": false, "enabled": true, "groupPolicy": "allowlist", "allowFrom": ["wxid_..."] } }`) |
|
|
155
|
-
| `pollIntervalMs` | integer | `1000` | Message polling interval |
|
|
156
|
-
| `authPollIntervalMs` | integer | `30000` | Auth status check interval |
|
|
120
|
+
Five optional tools read across every thread in the configured native WeChat account:
|
|
157
121
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
122
|
+
| Tool | Inputs (`?` means optional) | Output |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `wechat_archive_threads` | `{ query?, limit?, cursor? }` | Thread page: `id`, `peerId`, `kind`, `displayName`, `unreadCount`, plus pagination and coverage |
|
|
125
|
+
| `wechat_archive_search` | `{ query, threadId?, limit?, cursor? }` | Message page across all threads; optional `threadId` narrows it |
|
|
126
|
+
| `wechat_archive_history` | `{ threadId, limit?, cursor? }` | Message page for any selected thread, newest first |
|
|
127
|
+
| `wechat_archive_message` | `{ messageId }` | One message from any thread |
|
|
128
|
+
| `wechat_archive_media` | `{ messageId, download?, quality? }` | Verified attachment status/path and image content from any thread |
|
|
162
129
|
|
|
163
|
-
|
|
130
|
+
Message/page/media output shapes, bounds and download behavior match the default tools above. Archive attachment descriptors name `wechat_archive_media` as their retrieval tool. Thread queries match locally stored IDs and display names. Search uses the daemon's account-wide `GET /v1/accounts/{accountId}/messages?query=...` endpoint; its opaque cursor binds the account, query and thread selection and retains membership/order across local edits, deletions and new arrivals. Search reads the local profile with bounded snapshot capacity; it does not backfill remote history. An older daemon without that endpoint must be upgraded for account-wide search.
|
|
164
131
|
|
|
165
|
-
|
|
132
|
+
Configure access in OpenClaw's `~/.openclaw/openclaw.json`, merging into your existing named agent entries:
|
|
166
133
|
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"agents": {
|
|
137
|
+
"ownership": "explicit",
|
|
138
|
+
"entries": {
|
|
139
|
+
"main": {
|
|
140
|
+
"tools": { "alsoAllow": ["wechat_archive_*"] }
|
|
141
|
+
},
|
|
142
|
+
"group-bot": {
|
|
143
|
+
"tools": { "deny": ["wechat_archive_*"] }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
171
148
|
```
|
|
172
149
|
|
|
173
|
-
|
|
150
|
+
OpenClaw enforces these policies for tool execution and catalog discovery. The `main` name has no implicit privilege. Use individual tool names to grant a subset. If an agent already has `tools.allow`, add the archive names to that list instead of combining `allow` and `alsoAllow`. A global deny still wins over a per-agent allow. Optional tools are absent without a selecting policy, but `profile: "full"`, `*`, `group:plugins`, or an allow for the entire `agent-wechat` plugin also selects them; add an explicit per-agent deny for agents that should retain only current-thread access.
|
|
174
151
|
|
|
175
|
-
|
|
176
|
-
openclaw plugins install -l ./packages/openclaw-extension
|
|
177
|
-
```
|
|
152
|
+
There is no plugin permission list, owner exception or archive sender/thread filter. A host-authorized archive invocation can read the configured account from any channel, including a group invocation. Use OpenClaw's sender policies or route group traffic to an agent without archive tools when those runs should have narrower access. Archive tools stay bound to the configured account and verify message/asset identities, hashes and sizes. They never accept arbitrary file paths or another account selector. Normal account/token scope checks and invocation cancellation fences still apply.
|
|
178
153
|
|
|
179
|
-
|
|
154
|
+
History/search/message/thread reads never send messages, mark threads read or change the inbound cursor. Archive media can submit the same bounded, deduplicated download jobs as `wechat_media`; it stays directly callable so actual image blocks reach vision.
|
|
180
155
|
|
|
181
|
-
|
|
156
|
+
Run `node scripts/native-archive-tools-smoke.mjs` to check the pinned Gateway with three separately routed agents: no optional-tool grant, a full profile with an explicit deny, and an archive grant. It checks discovery and guessed-name invocation denial, cross-thread search pagination, other-sender history/media, unchanged default-tool scope, verified image bytes in provider input and a cached file path. All messages, account data and model responses in this test are synthetic. CI runs it on Linux x64 and ARM64.
|
|
182
157
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
agent-wechat container (port 6174)
|
|
195
|
-
│
|
|
196
|
-
▼
|
|
197
|
-
WeChat Desktop (in Xvfb)
|
|
198
|
-
```
|
|
158
|
+
Run `node scripts/native-model-smoke.mjs` and `node scripts/native-media-model-smoke.mjs` after installing workspace dependencies. They use the actual pinned Gateway, a local model and synthetic daemon/files. Checks cover default inbound reads without conversation permission, commands without a model call, current provider input, image hashes and prepared previews, reference/transcription delivery, whole-thread read requests, lost-response/reopen recovery, duplicate prevention, omitted images on a text-only model, explicit model-handoff permission, never-read policy and group routing behavior. Transcription uses the real SDK with an explicitly configured local deterministic CLI; it does not test speech recognition accuracy. Concurrent text inputs were serialized into distinct model turns; merged-input refusal has unit coverage for the explicit model policy. These checks establish local runtime behavior, not external provider acceptance, semantic analysis or consumption.
|
|
159
|
+
|
|
160
|
+
Native text/media sends retain daemon intent/job identity and caller idempotency. Text waits for accepted submission; media jobs preserve partial and uncertain outcomes rather than resending. Ordinary audio remains a file; only explicit `audioAsVoice` requests independently encoded voice. Sender/group/mention gates run before retrieval/transcription. Verified voice may use the configured OpenClaw transcription runtime after admission. Native QR login uses a durable daemon operation and authenticated PNG retrieval. Logout commits local inhibition before at most one authenticated native logout, reporting the remote outcome separately; saved-device revocation remains unverified. Explicitly confirmed forgetting removes local auth while retaining history; disconnect retains reusable trust.
|
|
161
|
+
|
|
162
|
+
## Optional Gateway facade
|
|
163
|
+
|
|
164
|
+
Set `native.gatewayFacade: {"enabled": true}` to register `/plugins/agent-wechat/native` on the Gateway. The default is disabled. A custom prefix must remain namespaced under `/plugins/...`; changing daemon configuration requires a Gateway reload.
|
|
165
|
+
|
|
166
|
+
The actual Gateway authenticates requests before the proxy runs. Its bearer is never forwarded to the daemon; the plugin uses its private daemon token reference. Routes are restricted to the configured account, with no daemon-wide token/account administration. Binary bodies, byte ranges, status codes and SSE are preserved within explicit size limits. Client disconnect/plugin shutdown closes upstream streaming work without cancelling durable sends.
|
|
167
|
+
|
|
168
|
+
`scripts/native-gateway-smoke.mjs` tests the built plugin on an isolated OpenClaw **2026.10.2** Gateway with synthetic loopback data: auth rejection, separate credentials, account boundaries, Range206, SSE and cancellation. This transport test is separate from the local model-boundary test above; neither uses an external model provider or a real WeChat account.
|
|
199
169
|
|
|
200
|
-
|
|
170
|
+
See [native deployment examples](../../deploy/native/README.md) and [native CLI installation](../cli/README.md).
|