wechatbridge-cli 1.6.1__tar.gz → 1.6.2__tar.gz

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.
Files changed (27) hide show
  1. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/PKG-INFO +36 -3
  2. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/README.md +35 -2
  3. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/__init__.py +1 -1
  4. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge_cli.egg-info/PKG-INFO +36 -3
  5. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/LICENSE +0 -0
  6. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/pyproject.toml +0 -0
  7. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/setup.cfg +0 -0
  8. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/tests/test_codex.py +0 -0
  9. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/tests/test_codex_auth.py +0 -0
  10. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/tests/test_dsh.py +0 -0
  11. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/tests/test_grok_auth.py +0 -0
  12. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/tests/test_hardening.py +0 -0
  13. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/__main__.py +0 -0
  14. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/agy.py +0 -0
  15. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/codex.py +0 -0
  16. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/config.py +0 -0
  17. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/dsh.py +0 -0
  18. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/grok.py +0 -0
  19. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/ilink.py +0 -0
  20. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/main.py +0 -0
  21. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/runner_common.py +0 -0
  22. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge/update_check.py +0 -0
  23. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge_cli.egg-info/SOURCES.txt +0 -0
  24. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge_cli.egg-info/dependency_links.txt +0 -0
  25. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge_cli.egg-info/entry_points.txt +0 -0
  26. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge_cli.egg-info/requires.txt +0 -0
  27. {wechatbridge_cli-1.6.1 → wechatbridge_cli-1.6.2}/wechatbridge_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wechatbridge-cli
3
- Version: 1.6.1
3
+ Version: 1.6.2
4
4
  Summary: Bridge WeChat messages to agy, Grok Build, or Codex CLIs — text/image/file/voice in, CLI replies and generated files back.
5
5
  Author: WeChatBridge contributors
6
6
  License: MIT
@@ -195,7 +195,7 @@ Key variables (all have defaults):
195
195
  | `AGY_TIMEOUT` | `600` | CLI run timeout in seconds (agy / grok / codex backends) |
196
196
  | `WECHATBRIDGE_MAX_OUTBOUND_BYTES` | `104857600` | max file size sent back to WeChat (100 MB) |
197
197
  | `WECHATBRIDGE_MAX_INBOUND_BYTES` | `20971520` | max inbound image/file after download (20 MB) |
198
- | `WECHATBRIDGE_MAX_CONCURRENT` | `4` | global concurrent process slots; same user serial (queue does not hold a slot); extras get a busy reply |
198
+ | `WECHATBRIDGE_MAX_CONCURRENT` | `4` | global concurrent process slots; same user serial (queue does not hold a slot); extras get a busy reply (on small RAM hosts, lower this to avoid concurrent CLI memory spikes; see [Memory limits](#memory-limits)) |
199
199
  | `WECHATBRIDGE_CONFIRM_TOKEN` | `y` | reply this token to approve a gated dangerous prompt |
200
200
  | `WECHATBRIDGE_ENABLE_MCP` | `true` | enable the `/mcp` help text command |
201
201
  | `WECHATBRIDGE_ENABLE_SUBAGENT` | `true` | enable the `/agent` prompt-rewrite command |
@@ -261,6 +261,39 @@ sudo systemctl enable --now wechatbridge@bot3
261
261
 
262
262
  Each instance reads its own config file (`~/.config/wechatbridge/bot2.env`) and keeps state under its own data directory (`~/.local/share/wechatbridge/bot2/`).
263
263
 
264
+ #### Memory limits
265
+
266
+ The multi-instance template (`deploy/wechatbridge@.service`) configures cgroup memory protection:
267
+
268
+ ```ini
269
+ MemoryAccounting=yes
270
+ MemoryHigh=450M
271
+ MemoryMax=512M
272
+ ```
273
+
274
+ - **Why these defaults?** Backend CLI child processes (such as `agy`) can reach ~276 MB+ RSS during a single turn, and spike even higher during image recognition or long conversational sessions. A tight hard cap (e.g. 300 MB) on ~1 GB RAM hosts triggers aggressive page thrashing, deep swap latency, and stream interruptions (`subscriber fell behind updates`). The `450M` soft limit (`MemoryHigh`) triggers gentle reclaim, while `512M` (`MemoryMax`) provides a hard ceiling preventing full-host OOM.
275
+ - **Overriding per-instance via drop-in:** Override memory limits without modifying the base unit template:
276
+ ```bash
277
+ sudo mkdir -p /etc/systemd/system/wechatbridge@bot2.service.d/
278
+ cat << 'EOF' | sudo tee /etc/systemd/system/wechatbridge@bot2.service.d/memory.conf
279
+ [Service]
280
+ # for hosts with >= 2 GB RAM
281
+ MemoryHigh=600M
282
+ MemoryMax=768M
283
+ EOF
284
+ sudo systemctl daemon-reload
285
+ sudo systemctl restart wechatbridge@bot2
286
+ ```
287
+ Verify active limits:
288
+ ```bash
289
+ systemctl show wechatbridge@bot2 -p MemoryHigh,MemoryMax
290
+ ```
291
+ - **Sizing guidance:** The defaults are tuned for small hosts (~1 GB RAM). On larger hosts with ample headroom, limits can be relaxed. When deploying multiple instances, note that each instance runs in its own service cgroup — budget for the worst-case combined ceiling of N × MemoryMax across running instances.
292
+ - **Interaction with concurrency:** Concurrent heavy turns (e.g. multiple users sending images at once) multiply backend CLI memory consumption. On memory-constrained hosts, prioritize lowering `WECHATBRIDGE_MAX_CONCURRENT` (e.g. to `2` or `1`) before increasing memory caps.
293
+ - **Swap configuration:** Avoid tightening `MemorySwapMax` — swap margin serves as a vital safety buffer that slows execution under pressure instead of triggering an immediate cgroup OOM kill.
294
+ - **Single-instance deployments:** The single-instance unit `deploy/wechatbridge.service` includes the same built-in memory limits (`MemoryHigh=450M` and `MemoryMax=512M`). To adjust them, override via a drop-in under `/etc/systemd/system/wechatbridge.service.d/` and replace the unit name in the `systemctl` commands above with `wechatbridge`. Takes effect when the unit file is installed or re-installed; `update.sh` upgrades do not touch unit files.
295
+ - **Platform scope:** These cgroup memory limits apply only to Linux systemd deployments; the macOS (launchd) and Windows (Task Scheduler) paths have no equivalent per-instance cap.
296
+
264
297
  ### macOS (launchd)
265
298
 
266
299
  ```bash
@@ -302,7 +335,7 @@ Other `/…` commands are either rejected (e.g. `/exit`), reported as unsupporte
302
335
  - **Danger gate is keyword-based**, not full intent understanding. Defaults target concrete patterns (`rm -rf /`, pipe-to-shell, `mkfs`, `format c:`, a few heavy Chinese phrases, …). Everyday wording like bare “delete” is **not** gated. Override list via `WECHATBRIDGE_CONFIRM_KEYWORDS`; approve with `WECHATBRIDGE_CONFIRM_TOKEN` (default `y`), TTL `WECHATBRIDGE_PENDING_TTL`.
303
336
  - **Inbound media** is size-capped (default 20 MB), streamed, and CDN hosts are allowlisted. Missing `aes_key` returns a clear error.
304
337
  - **Outbound artifacts** only leave the allowed per-user tree (agy: session scratch; grok: under session dir; dsh: under session dir), after `realpath` checks, and only if under `WECHATBRIDGE_MAX_OUTBOUND_BYTES`.
305
- - **Concurrency:** global process-slot cap (`WECHATBRIDGE_MAX_CONCURRENT`, default 4). Same user is serialized and does **not** hold a global slot while waiting on their previous message; different users can run in parallel up to the cap.
338
+ - **Concurrency:** global process-slot cap (`WECHATBRIDGE_MAX_CONCURRENT`, default 4). Same user is serialized and does **not** hold a global slot while waiting on their previous message; different users can run in parallel up to the cap (heavy concurrent tasks multiply subprocess memory; see [Memory limits](#memory-limits)).
306
339
  - **Long replies** are split into chunks (`WECHATBRIDGE_MESSAGE_CHUNK`, default 2000 characters).
307
340
  - **Data layout:** instance data under `~/.local/share/wechatbridge/<instance>/` (override with env). Runtime dirs prefer `0700`; token/QR files prefer `0600` (Unix; Windows relies on NTFS ACLs).
308
341
  - **Retention:** session temps vs dialogue history use separate TTLs (`WECHATBRIDGE_SESSION_RETENTION_DAYS`, `WECHATBRIDGE_HISTORY_RETENTION_DAYS`). Prefs/auth are kept.
@@ -163,7 +163,7 @@ Key variables (all have defaults):
163
163
  | `AGY_TIMEOUT` | `600` | CLI run timeout in seconds (agy / grok / codex backends) |
164
164
  | `WECHATBRIDGE_MAX_OUTBOUND_BYTES` | `104857600` | max file size sent back to WeChat (100 MB) |
165
165
  | `WECHATBRIDGE_MAX_INBOUND_BYTES` | `20971520` | max inbound image/file after download (20 MB) |
166
- | `WECHATBRIDGE_MAX_CONCURRENT` | `4` | global concurrent process slots; same user serial (queue does not hold a slot); extras get a busy reply |
166
+ | `WECHATBRIDGE_MAX_CONCURRENT` | `4` | global concurrent process slots; same user serial (queue does not hold a slot); extras get a busy reply (on small RAM hosts, lower this to avoid concurrent CLI memory spikes; see [Memory limits](#memory-limits)) |
167
167
  | `WECHATBRIDGE_CONFIRM_TOKEN` | `y` | reply this token to approve a gated dangerous prompt |
168
168
  | `WECHATBRIDGE_ENABLE_MCP` | `true` | enable the `/mcp` help text command |
169
169
  | `WECHATBRIDGE_ENABLE_SUBAGENT` | `true` | enable the `/agent` prompt-rewrite command |
@@ -229,6 +229,39 @@ sudo systemctl enable --now wechatbridge@bot3
229
229
 
230
230
  Each instance reads its own config file (`~/.config/wechatbridge/bot2.env`) and keeps state under its own data directory (`~/.local/share/wechatbridge/bot2/`).
231
231
 
232
+ #### Memory limits
233
+
234
+ The multi-instance template (`deploy/wechatbridge@.service`) configures cgroup memory protection:
235
+
236
+ ```ini
237
+ MemoryAccounting=yes
238
+ MemoryHigh=450M
239
+ MemoryMax=512M
240
+ ```
241
+
242
+ - **Why these defaults?** Backend CLI child processes (such as `agy`) can reach ~276 MB+ RSS during a single turn, and spike even higher during image recognition or long conversational sessions. A tight hard cap (e.g. 300 MB) on ~1 GB RAM hosts triggers aggressive page thrashing, deep swap latency, and stream interruptions (`subscriber fell behind updates`). The `450M` soft limit (`MemoryHigh`) triggers gentle reclaim, while `512M` (`MemoryMax`) provides a hard ceiling preventing full-host OOM.
243
+ - **Overriding per-instance via drop-in:** Override memory limits without modifying the base unit template:
244
+ ```bash
245
+ sudo mkdir -p /etc/systemd/system/wechatbridge@bot2.service.d/
246
+ cat << 'EOF' | sudo tee /etc/systemd/system/wechatbridge@bot2.service.d/memory.conf
247
+ [Service]
248
+ # for hosts with >= 2 GB RAM
249
+ MemoryHigh=600M
250
+ MemoryMax=768M
251
+ EOF
252
+ sudo systemctl daemon-reload
253
+ sudo systemctl restart wechatbridge@bot2
254
+ ```
255
+ Verify active limits:
256
+ ```bash
257
+ systemctl show wechatbridge@bot2 -p MemoryHigh,MemoryMax
258
+ ```
259
+ - **Sizing guidance:** The defaults are tuned for small hosts (~1 GB RAM). On larger hosts with ample headroom, limits can be relaxed. When deploying multiple instances, note that each instance runs in its own service cgroup — budget for the worst-case combined ceiling of N × MemoryMax across running instances.
260
+ - **Interaction with concurrency:** Concurrent heavy turns (e.g. multiple users sending images at once) multiply backend CLI memory consumption. On memory-constrained hosts, prioritize lowering `WECHATBRIDGE_MAX_CONCURRENT` (e.g. to `2` or `1`) before increasing memory caps.
261
+ - **Swap configuration:** Avoid tightening `MemorySwapMax` — swap margin serves as a vital safety buffer that slows execution under pressure instead of triggering an immediate cgroup OOM kill.
262
+ - **Single-instance deployments:** The single-instance unit `deploy/wechatbridge.service` includes the same built-in memory limits (`MemoryHigh=450M` and `MemoryMax=512M`). To adjust them, override via a drop-in under `/etc/systemd/system/wechatbridge.service.d/` and replace the unit name in the `systemctl` commands above with `wechatbridge`. Takes effect when the unit file is installed or re-installed; `update.sh` upgrades do not touch unit files.
263
+ - **Platform scope:** These cgroup memory limits apply only to Linux systemd deployments; the macOS (launchd) and Windows (Task Scheduler) paths have no equivalent per-instance cap.
264
+
232
265
  ### macOS (launchd)
233
266
 
234
267
  ```bash
@@ -270,7 +303,7 @@ Other `/…` commands are either rejected (e.g. `/exit`), reported as unsupporte
270
303
  - **Danger gate is keyword-based**, not full intent understanding. Defaults target concrete patterns (`rm -rf /`, pipe-to-shell, `mkfs`, `format c:`, a few heavy Chinese phrases, …). Everyday wording like bare “delete” is **not** gated. Override list via `WECHATBRIDGE_CONFIRM_KEYWORDS`; approve with `WECHATBRIDGE_CONFIRM_TOKEN` (default `y`), TTL `WECHATBRIDGE_PENDING_TTL`.
271
304
  - **Inbound media** is size-capped (default 20 MB), streamed, and CDN hosts are allowlisted. Missing `aes_key` returns a clear error.
272
305
  - **Outbound artifacts** only leave the allowed per-user tree (agy: session scratch; grok: under session dir; dsh: under session dir), after `realpath` checks, and only if under `WECHATBRIDGE_MAX_OUTBOUND_BYTES`.
273
- - **Concurrency:** global process-slot cap (`WECHATBRIDGE_MAX_CONCURRENT`, default 4). Same user is serialized and does **not** hold a global slot while waiting on their previous message; different users can run in parallel up to the cap.
306
+ - **Concurrency:** global process-slot cap (`WECHATBRIDGE_MAX_CONCURRENT`, default 4). Same user is serialized and does **not** hold a global slot while waiting on their previous message; different users can run in parallel up to the cap (heavy concurrent tasks multiply subprocess memory; see [Memory limits](#memory-limits)).
274
307
  - **Long replies** are split into chunks (`WECHATBRIDGE_MESSAGE_CHUNK`, default 2000 characters).
275
308
  - **Data layout:** instance data under `~/.local/share/wechatbridge/<instance>/` (override with env). Runtime dirs prefer `0700`; token/QR files prefer `0600` (Unix; Windows relies on NTFS ACLs).
276
309
  - **Retention:** session temps vs dialogue history use separate TTLs (`WECHATBRIDGE_SESSION_RETENTION_DAYS`, `WECHATBRIDGE_HISTORY_RETENTION_DAYS`). Prefs/auth are kept.
@@ -1,2 +1,2 @@
1
1
  """WeChatBridge — bridge WeChat messages to agy, Grok Build, or Codex CLIs."""
2
- __version__ = "1.6.1"
2
+ __version__ = "1.6.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wechatbridge-cli
3
- Version: 1.6.1
3
+ Version: 1.6.2
4
4
  Summary: Bridge WeChat messages to agy, Grok Build, or Codex CLIs — text/image/file/voice in, CLI replies and generated files back.
5
5
  Author: WeChatBridge contributors
6
6
  License: MIT
@@ -195,7 +195,7 @@ Key variables (all have defaults):
195
195
  | `AGY_TIMEOUT` | `600` | CLI run timeout in seconds (agy / grok / codex backends) |
196
196
  | `WECHATBRIDGE_MAX_OUTBOUND_BYTES` | `104857600` | max file size sent back to WeChat (100 MB) |
197
197
  | `WECHATBRIDGE_MAX_INBOUND_BYTES` | `20971520` | max inbound image/file after download (20 MB) |
198
- | `WECHATBRIDGE_MAX_CONCURRENT` | `4` | global concurrent process slots; same user serial (queue does not hold a slot); extras get a busy reply |
198
+ | `WECHATBRIDGE_MAX_CONCURRENT` | `4` | global concurrent process slots; same user serial (queue does not hold a slot); extras get a busy reply (on small RAM hosts, lower this to avoid concurrent CLI memory spikes; see [Memory limits](#memory-limits)) |
199
199
  | `WECHATBRIDGE_CONFIRM_TOKEN` | `y` | reply this token to approve a gated dangerous prompt |
200
200
  | `WECHATBRIDGE_ENABLE_MCP` | `true` | enable the `/mcp` help text command |
201
201
  | `WECHATBRIDGE_ENABLE_SUBAGENT` | `true` | enable the `/agent` prompt-rewrite command |
@@ -261,6 +261,39 @@ sudo systemctl enable --now wechatbridge@bot3
261
261
 
262
262
  Each instance reads its own config file (`~/.config/wechatbridge/bot2.env`) and keeps state under its own data directory (`~/.local/share/wechatbridge/bot2/`).
263
263
 
264
+ #### Memory limits
265
+
266
+ The multi-instance template (`deploy/wechatbridge@.service`) configures cgroup memory protection:
267
+
268
+ ```ini
269
+ MemoryAccounting=yes
270
+ MemoryHigh=450M
271
+ MemoryMax=512M
272
+ ```
273
+
274
+ - **Why these defaults?** Backend CLI child processes (such as `agy`) can reach ~276 MB+ RSS during a single turn, and spike even higher during image recognition or long conversational sessions. A tight hard cap (e.g. 300 MB) on ~1 GB RAM hosts triggers aggressive page thrashing, deep swap latency, and stream interruptions (`subscriber fell behind updates`). The `450M` soft limit (`MemoryHigh`) triggers gentle reclaim, while `512M` (`MemoryMax`) provides a hard ceiling preventing full-host OOM.
275
+ - **Overriding per-instance via drop-in:** Override memory limits without modifying the base unit template:
276
+ ```bash
277
+ sudo mkdir -p /etc/systemd/system/wechatbridge@bot2.service.d/
278
+ cat << 'EOF' | sudo tee /etc/systemd/system/wechatbridge@bot2.service.d/memory.conf
279
+ [Service]
280
+ # for hosts with >= 2 GB RAM
281
+ MemoryHigh=600M
282
+ MemoryMax=768M
283
+ EOF
284
+ sudo systemctl daemon-reload
285
+ sudo systemctl restart wechatbridge@bot2
286
+ ```
287
+ Verify active limits:
288
+ ```bash
289
+ systemctl show wechatbridge@bot2 -p MemoryHigh,MemoryMax
290
+ ```
291
+ - **Sizing guidance:** The defaults are tuned for small hosts (~1 GB RAM). On larger hosts with ample headroom, limits can be relaxed. When deploying multiple instances, note that each instance runs in its own service cgroup — budget for the worst-case combined ceiling of N × MemoryMax across running instances.
292
+ - **Interaction with concurrency:** Concurrent heavy turns (e.g. multiple users sending images at once) multiply backend CLI memory consumption. On memory-constrained hosts, prioritize lowering `WECHATBRIDGE_MAX_CONCURRENT` (e.g. to `2` or `1`) before increasing memory caps.
293
+ - **Swap configuration:** Avoid tightening `MemorySwapMax` — swap margin serves as a vital safety buffer that slows execution under pressure instead of triggering an immediate cgroup OOM kill.
294
+ - **Single-instance deployments:** The single-instance unit `deploy/wechatbridge.service` includes the same built-in memory limits (`MemoryHigh=450M` and `MemoryMax=512M`). To adjust them, override via a drop-in under `/etc/systemd/system/wechatbridge.service.d/` and replace the unit name in the `systemctl` commands above with `wechatbridge`. Takes effect when the unit file is installed or re-installed; `update.sh` upgrades do not touch unit files.
295
+ - **Platform scope:** These cgroup memory limits apply only to Linux systemd deployments; the macOS (launchd) and Windows (Task Scheduler) paths have no equivalent per-instance cap.
296
+
264
297
  ### macOS (launchd)
265
298
 
266
299
  ```bash
@@ -302,7 +335,7 @@ Other `/…` commands are either rejected (e.g. `/exit`), reported as unsupporte
302
335
  - **Danger gate is keyword-based**, not full intent understanding. Defaults target concrete patterns (`rm -rf /`, pipe-to-shell, `mkfs`, `format c:`, a few heavy Chinese phrases, …). Everyday wording like bare “delete” is **not** gated. Override list via `WECHATBRIDGE_CONFIRM_KEYWORDS`; approve with `WECHATBRIDGE_CONFIRM_TOKEN` (default `y`), TTL `WECHATBRIDGE_PENDING_TTL`.
303
336
  - **Inbound media** is size-capped (default 20 MB), streamed, and CDN hosts are allowlisted. Missing `aes_key` returns a clear error.
304
337
  - **Outbound artifacts** only leave the allowed per-user tree (agy: session scratch; grok: under session dir; dsh: under session dir), after `realpath` checks, and only if under `WECHATBRIDGE_MAX_OUTBOUND_BYTES`.
305
- - **Concurrency:** global process-slot cap (`WECHATBRIDGE_MAX_CONCURRENT`, default 4). Same user is serialized and does **not** hold a global slot while waiting on their previous message; different users can run in parallel up to the cap.
338
+ - **Concurrency:** global process-slot cap (`WECHATBRIDGE_MAX_CONCURRENT`, default 4). Same user is serialized and does **not** hold a global slot while waiting on their previous message; different users can run in parallel up to the cap (heavy concurrent tasks multiply subprocess memory; see [Memory limits](#memory-limits)).
306
339
  - **Long replies** are split into chunks (`WECHATBRIDGE_MESSAGE_CHUNK`, default 2000 characters).
307
340
  - **Data layout:** instance data under `~/.local/share/wechatbridge/<instance>/` (override with env). Runtime dirs prefer `0700`; token/QR files prefer `0600` (Unix; Windows relies on NTFS ACLs).
308
341
  - **Retention:** session temps vs dialogue history use separate TTLs (`WECHATBRIDGE_SESSION_RETENTION_DAYS`, `WECHATBRIDGE_HISTORY_RETENTION_DAYS`). Prefs/auth are kept.