mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 14:01:24 +08:00
Compare commits
439 Commits
codex/exec
...
v2026.3.31
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
213a704b71 | ||
|
|
44baf3bb2b | ||
|
|
107fefd255 | ||
|
|
6f111516ef | ||
|
|
f425ea06bf | ||
|
|
0a7024e209 | ||
|
|
418fa12dfa | ||
|
|
2a60e34f2a | ||
|
|
eee37bf836 | ||
|
|
fc169215d7 | ||
|
|
58ee76fc84 | ||
|
|
913e7d5eba | ||
|
|
a4f45c55b2 | ||
|
|
ffa2143d20 | ||
|
|
49458fc50e | ||
|
|
0d742c3c1b | ||
|
|
64091caf8f | ||
|
|
9d1b443542 | ||
|
|
1216ecbe58 | ||
|
|
6f5f0c065b | ||
|
|
2e530fc2e1 | ||
|
|
ce58f55fe0 | ||
|
|
2001603020 | ||
|
|
b441e59d25 | ||
|
|
cc278a76a4 | ||
|
|
a23b4dd5bc | ||
|
|
2a72a6d507 | ||
|
|
d0bcd86348 | ||
|
|
bb912daaed | ||
|
|
1816d6a4ed | ||
|
|
80ed55332d | ||
|
|
5fdde9b237 | ||
|
|
2d2c271cdf | ||
|
|
338d313043 | ||
|
|
8fa5ac5a96 | ||
|
|
6f7629995c | ||
|
|
91be36ca4f | ||
|
|
62a39381da | ||
|
|
13f1190149 | ||
|
|
0a891543c9 | ||
|
|
797a70fd95 | ||
|
|
b87f33c920 | ||
|
|
66413487c8 | ||
|
|
0abd143d37 | ||
|
|
b7013ec207 | ||
|
|
cb661122e2 | ||
|
|
e5537f8b64 | ||
|
|
6d59ce366e | ||
|
|
3ec143254f | ||
|
|
7cd0ff2d88 | ||
|
|
69fe999373 | ||
|
|
693d17c4a2 | ||
|
|
ae730d9a86 | ||
|
|
a1cb2bdc57 | ||
|
|
62e13bbf21 | ||
|
|
5e0e46f405 | ||
|
|
b4433a1bfe | ||
|
|
dd3796aef3 | ||
|
|
fcb802e826 | ||
|
|
1a313caff3 | ||
|
|
76b3235207 | ||
|
|
5c9408d3ca | ||
|
|
e039c72a76 | ||
|
|
bfba84a69d | ||
|
|
4b1c15d059 | ||
|
|
c03e2beca1 | ||
|
|
539ba0d244 | ||
|
|
b9f8bb6308 | ||
|
|
d794c5ca56 | ||
|
|
8ef81cc983 | ||
|
|
dc948bb4eb | ||
|
|
cebe697082 | ||
|
|
7672e48c19 | ||
|
|
1e17a96983 | ||
|
|
28673a9388 | ||
|
|
9e8129907e | ||
|
|
83038fcaf2 | ||
|
|
35c6b3f648 | ||
|
|
8bf8baef87 | ||
|
|
759d37635d | ||
|
|
6f74a572d9 | ||
|
|
8a563d603b | ||
|
|
6c679e5f04 | ||
|
|
aab7335236 | ||
|
|
78e74d4a64 | ||
|
|
1a4c9c3e85 | ||
|
|
7d2b4ed4e1 | ||
|
|
6eddd55393 | ||
|
|
a842e34f15 | ||
|
|
8f2e1194b7 | ||
|
|
43ef8a5a86 | ||
|
|
c416527df6 | ||
|
|
015ab98591 | ||
|
|
2a1db0c0f1 | ||
|
|
85611f0021 | ||
|
|
1ca12ec8bf | ||
|
|
fc5a2f9293 | ||
|
|
3bb02d3338 | ||
|
|
56b5ba0dcb | ||
|
|
1fcd179d8c | ||
|
|
bf96c67fd1 | ||
|
|
192484ed0a | ||
|
|
7dffd8160a | ||
|
|
a1e2d2bf42 | ||
|
|
c425ef3e74 | ||
|
|
5e30da3cad | ||
|
|
5aac609e08 | ||
|
|
ac6f025c43 | ||
|
|
b3a2734cc9 | ||
|
|
983891a603 | ||
|
|
461a3a4052 | ||
|
|
7c4bffdecd | ||
|
|
177687ae29 | ||
|
|
0d7f1e2c84 | ||
|
|
98c0c38186 | ||
|
|
da6e9bb76f | ||
|
|
e1da91791a | ||
|
|
9537094841 | ||
|
|
c75f4695b7 | ||
|
|
584db0aff2 | ||
|
|
2523e25c93 | ||
|
|
0ed7f1fd22 | ||
|
|
e8cb0b3659 | ||
|
|
d90b627e1b | ||
|
|
44b9936136 | ||
|
|
59866dd253 | ||
|
|
ba4116e6a9 | ||
|
|
9407ac87df | ||
|
|
8807b017d1 | ||
|
|
4fb373466e | ||
|
|
6936033e98 | ||
|
|
0711cb4a05 | ||
|
|
dc0e0b0f68 | ||
|
|
a4d72a83f0 | ||
|
|
c1ea0ae9c8 | ||
|
|
cbfeecfab4 | ||
|
|
0c83754246 | ||
|
|
0ed4f8a72b | ||
|
|
aaf6077f27 | ||
|
|
4ee742174f | ||
|
|
5fc8f6ca8f | ||
|
|
29b9310319 | ||
|
|
3be08454f4 | ||
|
|
91115cdf61 | ||
|
|
2df86cce1c | ||
|
|
5a93344d82 | ||
|
|
5b7443d175 | ||
|
|
e7e383b7cf | ||
|
|
ff36bc314d | ||
|
|
3f2fb73cfe | ||
|
|
dbe6663c34 | ||
|
|
8dbba7d17c | ||
|
|
27b9665871 | ||
|
|
d369c9373b | ||
|
|
37099dae3e | ||
|
|
35072c4751 | ||
|
|
675b80c4a4 | ||
|
|
4ea1ca4849 | ||
|
|
11590eb6ce | ||
|
|
3ceec929df | ||
|
|
7710579a82 | ||
|
|
b19e28a85e | ||
|
|
dba96e7507 | ||
|
|
25a3d37970 | ||
|
|
f8af407c86 | ||
|
|
4d912e0451 | ||
|
|
28bb8c600e | ||
|
|
305977571d | ||
|
|
e6441760d2 | ||
|
|
415e7d941b | ||
|
|
730ba40763 | ||
|
|
2ce44ca6a1 | ||
|
|
607076d164 | ||
|
|
f2d4089ca2 | ||
|
|
334085fbe9 | ||
|
|
5474796735 | ||
|
|
d8c68c8d42 | ||
|
|
62c28c0708 | ||
|
|
b4ac69c652 | ||
|
|
cd5179314d | ||
|
|
d077faab1a | ||
|
|
2bdf2fbf14 | ||
|
|
225dfe0094 | ||
|
|
8c0245f57b | ||
|
|
1243e2c0b6 | ||
|
|
e704323ff3 | ||
|
|
3d5af14984 | ||
|
|
44caf1ee3d | ||
|
|
57700d716f | ||
|
|
efe9183f9d | ||
|
|
cf3ae2612b | ||
|
|
da7f016db6 | ||
|
|
6b3f99a11f | ||
|
|
9abcfdadf5 | ||
|
|
9bc1f896c8 | ||
|
|
f45e5a6569 | ||
|
|
2194587d70 | ||
|
|
9023a0436c | ||
|
|
eb8de6715f | ||
|
|
57c47d8c7f | ||
|
|
4d038bb242 | ||
|
|
57fccca2dc | ||
|
|
330a9f98cb | ||
|
|
b9f857708c | ||
|
|
781775ec08 | ||
|
|
6be0c7ef09 | ||
|
|
7bd2761b92 | ||
|
|
cbc75f13b2 | ||
|
|
132208c01f | ||
|
|
c1269eddb8 | ||
|
|
eb84d91a80 | ||
|
|
df0e136bc7 | ||
|
|
e95f786aa2 | ||
|
|
a23c33a681 | ||
|
|
f288ff3f9f | ||
|
|
cd8d0881ed | ||
|
|
622bdfdad1 | ||
|
|
2befbc5e60 | ||
|
|
4e2a072b5b | ||
|
|
d27165f5de | ||
|
|
3a5042b6cc | ||
|
|
af37fca556 | ||
|
|
0b76d85509 | ||
|
|
72d1725fcf | ||
|
|
d15d7d0962 | ||
|
|
f865a5455e | ||
|
|
549169f746 | ||
|
|
d2dcd6fca6 | ||
|
|
bf6f506dfa | ||
|
|
f86e5c0a08 | ||
|
|
ab4ddff7f1 | ||
|
|
5707038e6c | ||
|
|
f96e150450 | ||
|
|
075645f5cb | ||
|
|
fcc2488579 | ||
|
|
34ae78bfee | ||
|
|
dfc124c772 | ||
|
|
0633406ff6 | ||
|
|
6eb42593fa | ||
|
|
788f56f30f | ||
|
|
8d942000c9 | ||
|
|
4e67e7c02c | ||
|
|
5ec362fe0b | ||
|
|
2feb83babb | ||
|
|
a6046c94f7 | ||
|
|
3bed73dc36 | ||
|
|
8dfbcaa200 | ||
|
|
3a87783632 | ||
|
|
8ef9e7f159 | ||
|
|
5ee054e9db | ||
|
|
b970187379 | ||
|
|
9f0845137a | ||
|
|
74830c7bac | ||
|
|
ff1ae5df22 | ||
|
|
641a6880cf | ||
|
|
1bf8fb26f4 | ||
|
|
4ab7947ec0 | ||
|
|
3059eadca2 | ||
|
|
aebdb8f8cf | ||
|
|
637f15375b | ||
|
|
6c6792446b | ||
|
|
d352bd050a | ||
|
|
ab8d999917 | ||
|
|
e42330eff7 | ||
|
|
aeee17a689 | ||
|
|
81b777c768 | ||
|
|
f7ced438f7 | ||
|
|
54c69414ad | ||
|
|
763d5cea44 | ||
|
|
235908c30e | ||
|
|
10ac6ead6b | ||
|
|
7516b423eb | ||
|
|
e89bd883d8 | ||
|
|
9d9ee0f313 | ||
|
|
28ede9a23e | ||
|
|
1346e6668e | ||
|
|
57003ffddf | ||
|
|
44674525f2 | ||
|
|
329d4bf1a8 | ||
|
|
082778df1a | ||
|
|
e394262bd8 | ||
|
|
d4cccda570 | ||
|
|
c918ab4faf | ||
|
|
697dddbeb6 | ||
|
|
d859746862 | ||
|
|
47136536c8 | ||
|
|
20481d424c | ||
|
|
ef6250d9a0 | ||
|
|
b3f894ea7e | ||
|
|
68e49fa791 | ||
|
|
1c95c41c37 | ||
|
|
2cb15255a7 | ||
|
|
d8d13f2bde | ||
|
|
4bec7622ab | ||
|
|
67bb3454ee | ||
|
|
3f1d6fe147 | ||
|
|
6b6ddcd2a6 | ||
|
|
f7285e0a9e | ||
|
|
1f6a964e57 | ||
|
|
5d8ca42c7d | ||
|
|
bf6d3176fc | ||
|
|
2412357bb7 | ||
|
|
9b6ebc1992 | ||
|
|
4f2df617fe | ||
|
|
121870a085 | ||
|
|
7ae1bb0c77 | ||
|
|
873549c8f1 | ||
|
|
fc4ef34478 | ||
|
|
8bcaf1a147 | ||
|
|
81e65e119f | ||
|
|
ee38d13f33 | ||
|
|
a966630a91 | ||
|
|
6d39209430 | ||
|
|
f8c7512ca5 | ||
|
|
afe4a4b260 | ||
|
|
d46f64199a | ||
|
|
cefa191417 | ||
|
|
82695bb24d | ||
|
|
da03d857f9 | ||
|
|
6ab0f62b3b | ||
|
|
7d70b1b51e | ||
|
|
72cb2a88f1 | ||
|
|
5fb19f296a | ||
|
|
1dda032531 | ||
|
|
020858647d | ||
|
|
a8ba6f2c03 | ||
|
|
82681ba215 | ||
|
|
56c9e2493b | ||
|
|
b878a34591 | ||
|
|
2ff7bb604c | ||
|
|
9590e2ccae | ||
|
|
7ec3674b46 | ||
|
|
ab0af5997d | ||
|
|
4892c60ee5 | ||
|
|
8d4040af58 | ||
|
|
31a4b45db0 | ||
|
|
eba41dae4f | ||
|
|
7b7d7cc743 | ||
|
|
61ae6d7201 | ||
|
|
f96e5bec39 | ||
|
|
af0c0862f2 | ||
|
|
a30214a624 | ||
|
|
91f7a6b0fd | ||
|
|
bd957a3a8b | ||
|
|
fa2e051bb6 | ||
|
|
e11b5d584c | ||
|
|
ca6432b0d9 | ||
|
|
bbd495ed63 | ||
|
|
2b2edaa01d | ||
|
|
dd9d0bdd8e | ||
|
|
9ff57ac479 | ||
|
|
e7e15b92bd | ||
|
|
b9f5d02f04 | ||
|
|
5cc0bc936c | ||
|
|
62d6cfedee | ||
|
|
9a94578d47 | ||
|
|
66777e140e | ||
|
|
07900facf6 | ||
|
|
30a1690323 | ||
|
|
910134b702 | ||
|
|
9c25544e6c | ||
|
|
4a6267bfe1 | ||
|
|
b96b1efc69 | ||
|
|
943163a419 | ||
|
|
8deb9522f3 | ||
|
|
e277a37f89 | ||
|
|
cfe1445953 | ||
|
|
f0af186726 | ||
|
|
2a75416634 | ||
|
|
ad77666054 | ||
|
|
e65c265e89 | ||
|
|
9d9cf0d8ff | ||
|
|
7590c22db7 | ||
|
|
8c83128fc3 | ||
|
|
8fdb19676a | ||
|
|
dd17dae3e5 | ||
|
|
1ea85a5d0b | ||
|
|
e8b0d57eb6 | ||
|
|
8746e2e216 | ||
|
|
ba7c98ab51 | ||
|
|
1b557ffe65 | ||
|
|
f849b8de97 | ||
|
|
3886b65ef2 | ||
|
|
6b38815f86 | ||
|
|
1ca4261d7e | ||
|
|
aff6883f93 | ||
|
|
c22edbb8ee | ||
|
|
555a4d896c | ||
|
|
4c45fc3575 | ||
|
|
17d0be02f2 | ||
|
|
1a75906a6f | ||
|
|
b7b46ad185 | ||
|
|
7a5c5f33d0 | ||
|
|
29cb1e3c7e | ||
|
|
ae703ab0e7 | ||
|
|
32a4a47d60 | ||
|
|
6d341cf366 | ||
|
|
09bb93c6e0 | ||
|
|
f011d0be28 | ||
|
|
c6f2db1506 | ||
|
|
3ad747e25f | ||
|
|
ab141df4b5 | ||
|
|
a3de1f5f55 | ||
|
|
08d365f481 | ||
|
|
fa150f8828 | ||
|
|
deead11dcd | ||
|
|
2dced6b4a0 | ||
|
|
e0281849c0 | ||
|
|
fec329ce8d | ||
|
|
4d369a3400 | ||
|
|
5cca380840 | ||
|
|
7e08669715 | ||
|
|
7a953a5227 | ||
|
|
8db20c1965 | ||
|
|
3216df7923 | ||
|
|
85647949a4 | ||
|
|
c5c10adc02 | ||
|
|
847912f3e2 | ||
|
|
3b9dab0ece | ||
|
|
a4e447a16e | ||
|
|
ee52f64226 | ||
|
|
a77928b108 | ||
|
|
9d5c5230c5 | ||
|
|
3d659fd356 | ||
|
|
fe2eb185ff | ||
|
|
8b88b927cb | ||
|
|
566fb73d9d | ||
|
|
3834d47099 | ||
|
|
bc3b05dce4 | ||
|
|
c4fa8635d0 | ||
|
|
348b094fe8 | ||
|
|
34b0a19a16 | ||
|
|
2fbd5e3f5f | ||
|
|
69916e8082 | ||
|
|
b7de04f23f | ||
|
|
85f3136cfc | ||
|
|
54f7221465 | ||
|
|
56be744a7a | ||
|
|
b0738210ff |
@@ -1,380 +0,0 @@
|
||||
---
|
||||
description: Update OpenClaw from upstream when branch has diverged (ahead/behind)
|
||||
---
|
||||
|
||||
# OpenClaw Upstream Sync Workflow
|
||||
|
||||
Use this workflow when your fork has diverged from upstream (e.g., "18 commits ahead, 29 commits behind").
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Check divergence status
|
||||
git fetch upstream && git rev-list --left-right --count main...upstream/main
|
||||
|
||||
# Full sync (rebase preferred)
|
||||
git fetch upstream && git rebase upstream/main && pnpm install && pnpm build && ./scripts/restart-mac.sh
|
||||
|
||||
# Check for Swift 6.2 issues after sync
|
||||
grep -r "FileManager\.default\|Thread\.isMainThread" src/ apps/ --include="*.swift"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Assess Divergence
|
||||
|
||||
```bash
|
||||
git fetch upstream
|
||||
git log --oneline --left-right main...upstream/main | head -20
|
||||
```
|
||||
|
||||
This shows:
|
||||
|
||||
- `<` = your local commits (ahead)
|
||||
- `>` = upstream commits you're missing (behind)
|
||||
|
||||
**Decision point:**
|
||||
|
||||
- Few local commits, many upstream → **Rebase** (cleaner history)
|
||||
- Many local commits or shared branch → **Merge** (preserves history)
|
||||
|
||||
---
|
||||
|
||||
## Step 2A: Rebase Strategy (Preferred)
|
||||
|
||||
Replays your commits on top of upstream. Results in linear history.
|
||||
|
||||
```bash
|
||||
# Ensure working tree is clean
|
||||
git status
|
||||
|
||||
# Rebase onto upstream
|
||||
git rebase upstream/main
|
||||
```
|
||||
|
||||
### Handling Rebase Conflicts
|
||||
|
||||
```bash
|
||||
# When conflicts occur:
|
||||
# 1. Fix conflicts in the listed files
|
||||
# 2. Stage resolved files
|
||||
git add <resolved-files>
|
||||
|
||||
# 3. Continue rebase
|
||||
git rebase --continue
|
||||
|
||||
# If a commit is no longer needed (already in upstream):
|
||||
git rebase --skip
|
||||
|
||||
# To abort and return to original state:
|
||||
git rebase --abort
|
||||
```
|
||||
|
||||
### Common Conflict Patterns
|
||||
|
||||
| File | Resolution |
|
||||
| ---------------- | ------------------------------------------------ |
|
||||
| `package.json` | Take upstream deps, keep local scripts if needed |
|
||||
| `pnpm-lock.yaml` | Accept upstream, regenerate with `pnpm install` |
|
||||
| `*.patch` files | Usually take upstream version |
|
||||
| Source files | Merge logic carefully, prefer upstream structure |
|
||||
|
||||
---
|
||||
|
||||
## Step 2B: Merge Strategy (Alternative)
|
||||
|
||||
Preserves all history with a merge commit.
|
||||
|
||||
```bash
|
||||
git merge upstream/main --no-edit
|
||||
```
|
||||
|
||||
Resolve conflicts same as rebase, then:
|
||||
|
||||
```bash
|
||||
git add <resolved-files>
|
||||
git commit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Rebuild Everything
|
||||
|
||||
After sync completes:
|
||||
|
||||
```bash
|
||||
# Install dependencies (regenerates lock if needed)
|
||||
pnpm install
|
||||
|
||||
# Build TypeScript
|
||||
pnpm build
|
||||
|
||||
# Build UI assets
|
||||
pnpm ui:build
|
||||
|
||||
# Run diagnostics
|
||||
pnpm clawdbot doctor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Rebuild macOS App
|
||||
|
||||
```bash
|
||||
# Full rebuild, sign, and launch
|
||||
./scripts/restart-mac.sh
|
||||
|
||||
# Or just package without restart
|
||||
pnpm mac:package
|
||||
```
|
||||
|
||||
### Install to /Applications
|
||||
|
||||
```bash
|
||||
# Kill running app
|
||||
pkill -x "OpenClaw" || true
|
||||
|
||||
# Move old version
|
||||
mv /Applications/OpenClaw.app /tmp/OpenClaw-backup.app
|
||||
|
||||
# Install new build
|
||||
cp -R dist/OpenClaw.app /Applications/
|
||||
|
||||
# Launch
|
||||
open /Applications/OpenClaw.app
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4A: Verify macOS App & Agent
|
||||
|
||||
After rebuilding the macOS app, always verify it works correctly:
|
||||
|
||||
```bash
|
||||
# Check gateway health
|
||||
pnpm clawdbot health
|
||||
|
||||
# Verify no zombie processes
|
||||
ps aux | grep -E "(clawdbot|gateway)" | grep -v grep
|
||||
|
||||
# Test agent functionality by sending a verification message
|
||||
pnpm clawdbot agent --message "Verification: macOS app rebuild successful - agent is responding." --session-id YOUR_TELEGRAM_SESSION_ID
|
||||
|
||||
# Confirm the message was received on Telegram
|
||||
# (Check your Telegram chat with the bot)
|
||||
```
|
||||
|
||||
**Important:** Always wait for the Telegram verification message before proceeding. If the agent doesn't respond, troubleshoot the gateway or model configuration before pushing.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Handle Swift/macOS Build Issues (Common After Upstream Sync)
|
||||
|
||||
Upstream updates may introduce Swift 6.2 / macOS 26 SDK incompatibilities. Use analyze-mode for systematic debugging:
|
||||
|
||||
### Analyze-Mode Investigation
|
||||
|
||||
```bash
|
||||
# Gather context with parallel agents
|
||||
morph-mcp_warpgrep_codebase_search search_string="Find deprecated FileManager.default and Thread.isMainThread usages in Swift files" repo_path="/Volumes/Main SSD/Developer/clawdis"
|
||||
morph-mcp_warpgrep_codebase_search search_string="Locate Peekaboo submodule and macOS app Swift files with concurrency issues" repo_path="/Volumes/Main SSD/Developer/clawdis"
|
||||
```
|
||||
|
||||
### Common Swift 6.2 Fixes
|
||||
|
||||
**FileManager.default Deprecation:**
|
||||
|
||||
```bash
|
||||
# Search for deprecated usage
|
||||
grep -r "FileManager\.default" src/ apps/ --include="*.swift"
|
||||
|
||||
# Replace with proper initialization
|
||||
# OLD: FileManager.default
|
||||
# NEW: FileManager()
|
||||
```
|
||||
|
||||
**Thread.isMainThread Deprecation:**
|
||||
|
||||
```bash
|
||||
# Search for deprecated usage
|
||||
grep -r "Thread\.isMainThread" src/ apps/ --include="*.swift"
|
||||
|
||||
# Replace with modern concurrency check
|
||||
# OLD: Thread.isMainThread
|
||||
# NEW: await MainActor.run { ... } or DispatchQueue.main.sync { ... }
|
||||
```
|
||||
|
||||
### Peekaboo Submodule Fixes
|
||||
|
||||
```bash
|
||||
# Check Peekaboo for concurrency issues
|
||||
cd src/canvas-host/a2ui
|
||||
grep -r "Thread\.isMainThread\|FileManager\.default" . --include="*.swift"
|
||||
|
||||
# Fix and rebuild submodule
|
||||
cd /Volumes/Main SSD/Developer/clawdis
|
||||
pnpm canvas:a2ui:bundle
|
||||
```
|
||||
|
||||
### macOS App Concurrency Fixes
|
||||
|
||||
```bash
|
||||
# Check macOS app for issues
|
||||
grep -r "Thread\.isMainThread\|FileManager\.default" apps/macos/ --include="*.swift"
|
||||
|
||||
# Clean and rebuild after fixes
|
||||
cd apps/macos && rm -rf .build .swiftpm
|
||||
./scripts/restart-mac.sh
|
||||
```
|
||||
|
||||
### Model Configuration Updates
|
||||
|
||||
If upstream introduced new model configurations:
|
||||
|
||||
```bash
|
||||
# Check for OpenRouter API key requirements
|
||||
grep -r "openrouter\|OPENROUTER" src/ --include="*.ts" --include="*.js"
|
||||
|
||||
# Update openclaw.json with fallback chains
|
||||
# Add model fallback configurations as needed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Verify & Push
|
||||
|
||||
```bash
|
||||
# Verify everything works
|
||||
pnpm clawdbot health
|
||||
pnpm test
|
||||
|
||||
# Push (force required after rebase)
|
||||
git push origin main --force-with-lease
|
||||
|
||||
# Or regular push after merge
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails After Sync
|
||||
|
||||
```bash
|
||||
# Clean and rebuild
|
||||
rm -rf node_modules dist
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Type Errors (Bun/Node Incompatibility)
|
||||
|
||||
Common issue: `fetch.preconnect` type mismatch. Fix by using `FetchLike` type instead of `typeof fetch`.
|
||||
|
||||
### macOS App Crashes on Launch
|
||||
|
||||
Usually resource bundle mismatch. Full rebuild required:
|
||||
|
||||
```bash
|
||||
cd apps/macos && rm -rf .build .swiftpm
|
||||
./scripts/restart-mac.sh
|
||||
```
|
||||
|
||||
### Patch Failures
|
||||
|
||||
```bash
|
||||
# Check patch status
|
||||
pnpm install 2>&1 | grep -i patch
|
||||
|
||||
# If patches fail, they may need updating for new dep versions
|
||||
# Check patches/ directory against package.json patchedDependencies
|
||||
```
|
||||
|
||||
### Swift 6.2 / macOS 26 SDK Build Failures
|
||||
|
||||
**Symptoms:** Build fails with deprecation warnings about `FileManager.default` or `Thread.isMainThread`
|
||||
|
||||
**Search-Mode Investigation:**
|
||||
|
||||
```bash
|
||||
# Exhaustive search for deprecated APIs
|
||||
morph-mcp_warpgrep_codebase_search search_string="Find all Swift files using deprecated FileManager.default or Thread.isMainThread" repo_path="/Volumes/Main SSD/Developer/clawdis"
|
||||
```
|
||||
|
||||
**Quick Fix Commands:**
|
||||
|
||||
```bash
|
||||
# Find all affected files
|
||||
find . -name "*.swift" -exec grep -l "FileManager\.default\|Thread\.isMainThread" {} \;
|
||||
|
||||
# Replace FileManager.default with FileManager()
|
||||
find . -name "*.swift" -exec sed -i '' 's/FileManager\.default/FileManager()/g' {} \;
|
||||
|
||||
# For Thread.isMainThread, need manual review of each usage
|
||||
grep -rn "Thread\.isMainThread" --include="*.swift" .
|
||||
```
|
||||
|
||||
**Rebuild After Fixes:**
|
||||
|
||||
```bash
|
||||
# Clean all build artifacts
|
||||
rm -rf apps/macos/.build apps/macos/.swiftpm
|
||||
rm -rf src/canvas-host/a2ui/.build
|
||||
|
||||
# Rebuild Peekaboo bundle
|
||||
pnpm canvas:a2ui:bundle
|
||||
|
||||
# Full macOS rebuild
|
||||
./scripts/restart-mac.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Automation Script
|
||||
|
||||
Save as `scripts/sync-upstream.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "==> Fetching upstream..."
|
||||
git fetch upstream
|
||||
|
||||
echo "==> Current divergence:"
|
||||
git rev-list --left-right --count main...upstream/main
|
||||
|
||||
echo "==> Rebasing onto upstream/main..."
|
||||
git rebase upstream/main
|
||||
|
||||
echo "==> Installing dependencies..."
|
||||
pnpm install
|
||||
|
||||
echo "==> Building..."
|
||||
pnpm build
|
||||
pnpm ui:build
|
||||
|
||||
echo "==> Running doctor..."
|
||||
pnpm clawdbot doctor
|
||||
|
||||
echo "==> Rebuilding macOS app..."
|
||||
./scripts/restart-mac.sh
|
||||
|
||||
echo "==> Verifying gateway health..."
|
||||
pnpm clawdbot health
|
||||
|
||||
echo "==> Checking for Swift 6.2 compatibility issues..."
|
||||
if grep -r "FileManager\.default\|Thread\.isMainThread" src/ apps/ --include="*.swift" --quiet; then
|
||||
echo "⚠️ Found potential Swift 6.2 deprecated API usage"
|
||||
echo " Run manual fixes or use analyze-mode investigation"
|
||||
else
|
||||
echo "✅ No obvious Swift deprecation issues found"
|
||||
fi
|
||||
|
||||
echo "==> Testing agent functionality..."
|
||||
# Note: Update YOUR_TELEGRAM_SESSION_ID with actual session ID
|
||||
pnpm clawdbot agent --message "Verification: Upstream sync and macOS rebuild completed successfully." --session-id YOUR_TELEGRAM_SESSION_ID || echo "Warning: Agent test failed - check Telegram for verification message"
|
||||
|
||||
echo "==> Done! Check Telegram for verification message, then run 'git push --force-with-lease' when ready."
|
||||
```
|
||||
@@ -16,6 +16,7 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
|
||||
- Pass `--json` for machine-readable summaries.
|
||||
- Per-phase logs land under `/tmp/openclaw-parallels-*`.
|
||||
- Do not run local and gateway agent turns in parallel on the same fresh workspace or session.
|
||||
- If `main` is moving under active multi-agent work, prefer a detached worktree pinned to one commit for long Parallels suites. The smoke scripts now verify the packed tgz commit instead of live `git rev-parse HEAD`, but a pinned worktree still avoids noisy rebuild/version drift during reruns.
|
||||
- For `prlctl exec`, pass the VM name before `--current-user` (`prlctl exec "$VM" --current-user ...`), not the other way around.
|
||||
- If the workflow installs OpenClaw from a repo checkout instead of the site installer/npm release, finish by installing a real guest CLI shim and verifying it in a fresh guest shell. `pnpm openclaw ...` inside the repo is not enough for handoff parity.
|
||||
- On macOS guests, prefer a user-global install plus a stable PATH-visible shim:
|
||||
@@ -32,6 +33,8 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
|
||||
- On Windows same-guest update checks, restart the gateway after the npm upgrade before `gateway status` / `agent`; in-place global npm updates can otherwise leave stale hashed `dist/*` module imports alive in the running service.
|
||||
- For Windows same-guest update checks, prefer the done-file/log-drain PowerShell runner pattern over one long-lived `prlctl exec ... powershell -EncodedCommand ...` transport. The guest can finish successfully while the outer `prlctl exec` still hangs.
|
||||
- Linux same-guest update verification should also export `HOME=/root`, pass `OPENAI_API_KEY` via `prlctl exec ... /usr/bin/env`, and use `openclaw agent --local`; the fresh Linux baseline does not rely on persisted gateway credentials.
|
||||
- The npm-update wrapper now prints per-lane progress from the nested log files. If a lane still looks stuck, inspect the nested logs in `runDir` first (`macos-fresh.log`, `windows-fresh.log`, `linux-fresh.log`, `macos-update.log`, `windows-update.log`, `linux-update.log`) instead of assuming the outer wrapper hung.
|
||||
- If the wrapper fails a lane, read the auto-dumped tail first, then the full nested lane log under `/tmp/openclaw-parallels-npm-update.*`.
|
||||
|
||||
## CLI invocation footgun
|
||||
|
||||
@@ -43,6 +46,7 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
|
||||
- Default to the snapshot closest to `macOS 26.3.1 latest`.
|
||||
- On Peter's Tahoe VM, `fresh-latest-march-2026` can hang in `prlctl snapshot-switch`; if restore times out there, rerun with `--snapshot-hint 'macOS 26.3.1 latest'` before blaming auth or the harness.
|
||||
- The macOS smoke should include a dashboard load phase after gateway health: resolve the tokenized URL with `openclaw dashboard --no-open`, verify the served HTML contains the Control UI title/root shell, then open Safari and require an established localhost TCP connection from Safari to the gateway port.
|
||||
- If a packaged install regresses with `500` on `/`, `/healthz`, or `__openclaw/control-ui-config.json` after `fresh.install-main` or `upgrade.install-main`, suspect bundled plugin runtime deps resolving from the package root `node_modules` rather than `dist/extensions/*/node_modules`. Repro quickly with a real `npm pack`/global install lane before blaming dashboard auth or Safari.
|
||||
- `prlctl exec` is fine for deterministic repo commands, but use the guest Terminal or `prlctl enter` when installer parity or shell-sensitive behavior matters.
|
||||
- Multi-word `openclaw agent --message ...` checks should go through a guest shell wrapper (`guest_current_user_sh` / `guest_current_user_cli` or `/bin/sh -lc ...`), not raw `prlctl exec ... node openclaw.mjs ...`, or the message can be split into extra argv tokens and Commander reports `too many arguments for 'agent'`.
|
||||
- When ref-mode onboarding stores `OPENAI_API_KEY` as an env secret ref, the post-onboard agent verification should also export `OPENAI_API_KEY` for the guest command. The gateway can still reject with pairing-required and fall back to embedded execution, and that fallback needs the env-backed credential available in the shell.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
name: openclaw-test-heap-leaks
|
||||
description: Investigate `pnpm test` memory growth, Vitest worker OOMs, and suspicious RSS increases in OpenClaw using the `scripts/test-parallel.mjs` heap snapshot tooling. Use when Codex needs to reproduce test-lane memory growth, collect repeated `.heapsnapshot` files, compare snapshots from the same worker PID, distinguish transformed-module retention from real data leaks, and fix or reduce the impact by patching cleanup logic or isolating hotspot tests.
|
||||
description: Investigate `pnpm test` memory growth, Vitest worker OOMs, and suspicious RSS increases in OpenClaw using the `scripts/test-parallel.mjs` heap snapshot tooling. Use when Codex needs to reproduce test-lane memory growth, collect repeated `.heapsnapshot` files, compare snapshots from the same worker PID, triage likely transformed-module retention versus likely runtime leaks, and fix or reduce the impact by patching cleanup logic or isolating hotspot tests.
|
||||
---
|
||||
|
||||
# OpenClaw Test Heap Leaks
|
||||
|
||||
Use this skill for test-memory investigations. Do not guess from RSS alone when heap snapshots are available.
|
||||
Use this skill for test-memory investigations. Do not guess from RSS alone when heap snapshots are available. Treat snapshot-name deltas as triage evidence, not proof, until retainers or dominators support the call.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -14,19 +14,23 @@ Use this skill for test-memory investigations. Do not guess from RSS alone when
|
||||
- `pnpm canvas:a2ui:bundle && OPENCLAW_TEST_MEMORY_TRACE=1 OPENCLAW_TEST_HEAPSNAPSHOT_INTERVAL_MS=60000 OPENCLAW_TEST_HEAPSNAPSHOT_DIR=.tmp/heapsnap OPENCLAW_TEST_WORKERS=2 OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB=6144 pnpm test`
|
||||
- Keep `OPENCLAW_TEST_MEMORY_TRACE=1` enabled so the wrapper prints per-file RSS summaries alongside the snapshots.
|
||||
- If the report is about a specific shard or worker budget, preserve that shape.
|
||||
- Before you analyze snapshots, identify the real lane names from `[test-parallel] start ...` lines or `pnpm test --plan`. Do not assume a single `unit-fast` lane; local plans often split into `unit-fast-batch-*`.
|
||||
|
||||
2. Wait for repeated snapshots before concluding anything.
|
||||
- Take at least two intervals from the same lane.
|
||||
- Compare snapshots from the same PID inside one lane directory such as `.tmp/heapsnap/unit-fast/`.
|
||||
- Use `scripts/heapsnapshot-delta.mjs` to compare either two files directly or the earliest/latest pair per PID in one lane directory.
|
||||
- Compare snapshots from the same PID inside the real lane directory such as `.tmp/heapsnap/unit-fast-batch-2/`.
|
||||
- Use `.agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs` to compare either two files directly or the earliest/latest pair per PID in one lane directory.
|
||||
- If the helper suggests transformed-module retention, confirm the top entries in DevTools retainers/dominators before calling it solved.
|
||||
|
||||
3. Classify the growth before choosing a fix.
|
||||
- If growth is dominated by Vite/Vitest transformed source strings, `Module`, `system / Context`, bytecode, descriptor arrays, or property maps, treat it as retained module graph growth in long-lived workers.
|
||||
- If growth is dominated by Vite/Vitest transformed source strings, `Module`, `system / Context`, bytecode, descriptor arrays, or property maps, treat it as likely retained module graph growth in long-lived workers.
|
||||
- If growth is dominated by app objects, caches, buffers, server handles, timers, mock state, sqlite state, or similar runtime objects, treat it as a likely cleanup or lifecycle leak.
|
||||
- If the names are ambiguous, stop short of a confident label and inspect retainers/dominators in DevTools for the top deltas.
|
||||
|
||||
4. Fix the right layer.
|
||||
- For retained transformed-module growth in shared workers:
|
||||
- Move hotspot files out of `unit-fast` by updating `test/fixtures/test-parallel.behavior.json`.
|
||||
- For likely retained transformed-module growth in shared workers:
|
||||
- Prefer timing and hotspot-driven scheduling fixes first. Check whether the file is already represented in `test/fixtures/test-timings.unit.json` and whether `scripts/test-update-memory-hotspots.mjs` should refresh the measured hotspot manifest before hand-editing behavior overrides.
|
||||
- Move hotspot files out of the real shared lane by updating `test/fixtures/test-parallel.behavior.json` only when timing-driven peeling is insufficient.
|
||||
- Prefer `singletonIsolated` for files that are safe alone but inflate shared worker heaps.
|
||||
- If the file should already have been peeled out by timings but is absent from `test/fixtures/test-timings.unit.json`, call that out explicitly. Missing timings are a scheduling blind spot.
|
||||
- For real leaks:
|
||||
@@ -40,24 +44,24 @@ Use this skill for test-memory investigations. Do not guess from RSS alone when
|
||||
|
||||
## Heuristics
|
||||
|
||||
- Do not call everything a leak. In this repo, large `unit-fast` growth can be a worker-lifetime problem rather than an application object leak.
|
||||
- Do not call everything a leak. In this repo, large `unit-fast` or `unit-fast-batch-*` growth can be a worker-lifetime problem rather than an application object leak.
|
||||
- `scripts/test-parallel.mjs` and `scripts/test-parallel-memory.mjs` are the primary control points for wrapper diagnostics.
|
||||
- The lane names printed by `[test-parallel] start ...` and `[test-parallel][mem] summary ...` tell you where to focus.
|
||||
- When one or two files account for most of the delta and they are missing from timings, reducing impact by isolating them is usually the first pragmatic fix.
|
||||
- When the same retained object families grow across multiple intervals in the same worker PID, trust the snapshots over intuition.
|
||||
- When the same retained object families grow across multiple intervals in the same worker PID, trust the snapshots over intuition, then confirm ambiguous calls with retainer evidence.
|
||||
|
||||
## Snapshot Comparison
|
||||
|
||||
- Direct comparison:
|
||||
- `node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs before.heapsnapshot after.heapsnapshot`
|
||||
- Auto-select earliest/latest snapshots per PID within one lane:
|
||||
- `node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs --lane-dir .tmp/heapsnap/unit-fast`
|
||||
- `node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs --lane-dir .tmp/heapsnap/unit-fast-batch-2`
|
||||
- Useful flags:
|
||||
- `--top 40`
|
||||
- `--min-kb 32`
|
||||
- `--pid 16133`
|
||||
|
||||
Read the top positive deltas first. Large positive growth in module-transform artifacts suggests lane isolation; large positive growth in runtime objects suggests a real leak.
|
||||
Read the top positive deltas first. Large positive growth in module-transform artifacts suggests lane isolation; large positive growth in runtime objects suggests a real leak. If the names alone do not settle it, open the same snapshot pair in DevTools and inspect retainers/dominators for the top rows before declaring root cause.
|
||||
|
||||
## Output Expectations
|
||||
|
||||
@@ -66,6 +70,6 @@ When using this skill, report:
|
||||
- The exact reproduce command.
|
||||
- Which lane and PID were compared.
|
||||
- The dominant retained object families from the snapshot delta.
|
||||
- Whether the issue is a real leak or shared-worker retained module growth.
|
||||
- Whether the issue is a likely real leak or likely shared-worker retained module growth, plus whether retainers/dominators confirmed it.
|
||||
- The concrete fix or impact-reduction patch.
|
||||
- What you verified, and what snapshot overhead prevented you from verifying.
|
||||
|
||||
@@ -64,6 +64,243 @@ function parseArgs(argv) {
|
||||
return options;
|
||||
}
|
||||
|
||||
class JsonStreamScanner {
|
||||
constructor(filePath) {
|
||||
this.stream = fs.createReadStream(filePath, {
|
||||
encoding: "utf8",
|
||||
highWaterMark: 1024 * 1024,
|
||||
});
|
||||
this.iterator = this.stream[Symbol.asyncIterator]();
|
||||
this.buffer = "";
|
||||
this.offset = 0;
|
||||
this.done = false;
|
||||
}
|
||||
|
||||
compactBuffer() {
|
||||
if (this.offset > 65536) {
|
||||
this.buffer = this.buffer.slice(this.offset);
|
||||
this.offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
async ensureAvailable(count = 1) {
|
||||
while (!this.done && this.buffer.length - this.offset < count) {
|
||||
const next = await this.iterator.next();
|
||||
if (next.done) {
|
||||
this.done = true;
|
||||
break;
|
||||
}
|
||||
this.buffer += next.value;
|
||||
}
|
||||
}
|
||||
|
||||
async peek() {
|
||||
await this.ensureAvailable(1);
|
||||
return this.buffer[this.offset] ?? null;
|
||||
}
|
||||
|
||||
async next() {
|
||||
await this.ensureAvailable(1);
|
||||
if (this.offset >= this.buffer.length) {
|
||||
return null;
|
||||
}
|
||||
const char = this.buffer[this.offset];
|
||||
this.offset += 1;
|
||||
this.compactBuffer();
|
||||
return char;
|
||||
}
|
||||
|
||||
async skipWhitespace() {
|
||||
while (true) {
|
||||
const char = await this.peek();
|
||||
if (char === null || !/\s/u.test(char)) {
|
||||
return;
|
||||
}
|
||||
await this.next();
|
||||
}
|
||||
}
|
||||
|
||||
async expectChar(expected) {
|
||||
const char = await this.next();
|
||||
if (char !== expected) {
|
||||
fail(`Expected ${expected} but found ${char ?? "<eof>"}`);
|
||||
}
|
||||
}
|
||||
|
||||
async find(sequence) {
|
||||
let matched = 0;
|
||||
while (true) {
|
||||
const char = await this.next();
|
||||
if (char === null) {
|
||||
fail(`Could not find ${sequence}`);
|
||||
}
|
||||
if (char === sequence[matched]) {
|
||||
matched += 1;
|
||||
if (matched === sequence.length) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
matched = char === sequence[0] ? 1 : 0;
|
||||
if (matched === sequence.length) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async readBalancedObject() {
|
||||
const start = await this.next();
|
||||
if (start !== "{") {
|
||||
fail(`Expected { but found ${start ?? "<eof>"}`);
|
||||
}
|
||||
let text = "{";
|
||||
let depth = 1;
|
||||
let inString = false;
|
||||
let escaped = false;
|
||||
while (depth > 0) {
|
||||
const char = await this.next();
|
||||
if (char === null) {
|
||||
fail("Unexpected EOF while reading JSON object");
|
||||
}
|
||||
text += char;
|
||||
if (inString) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (char === "\\") {
|
||||
escaped = true;
|
||||
} else if (char === '"') {
|
||||
inString = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (char === '"') {
|
||||
inString = true;
|
||||
} else if (char === "{") {
|
||||
depth += 1;
|
||||
} else if (char === "}") {
|
||||
depth -= 1;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
async parseNumberArray(onValue) {
|
||||
await this.skipWhitespace();
|
||||
await this.expectChar("[");
|
||||
await this.skipWhitespace();
|
||||
if ((await this.peek()) === "]") {
|
||||
await this.next();
|
||||
return;
|
||||
}
|
||||
|
||||
let token = "";
|
||||
let index = 0;
|
||||
const flush = () => {
|
||||
if (token.length === 0) {
|
||||
fail("Unexpected empty number token");
|
||||
}
|
||||
const value = Number.parseInt(token, 10);
|
||||
if (!Number.isFinite(value)) {
|
||||
fail(`Invalid numeric token: ${token}`);
|
||||
}
|
||||
onValue(value, index);
|
||||
index += 1;
|
||||
token = "";
|
||||
};
|
||||
|
||||
while (true) {
|
||||
const char = await this.next();
|
||||
if (char === null) {
|
||||
fail("Unexpected EOF while reading number array");
|
||||
}
|
||||
if (char === "]") {
|
||||
flush();
|
||||
return;
|
||||
}
|
||||
if (char === ",") {
|
||||
flush();
|
||||
continue;
|
||||
}
|
||||
if (/\s/u.test(char)) {
|
||||
continue;
|
||||
}
|
||||
token += char;
|
||||
}
|
||||
}
|
||||
|
||||
async readJsonString() {
|
||||
await this.expectChar('"');
|
||||
let value = "";
|
||||
while (true) {
|
||||
const char = await this.next();
|
||||
if (char === null) {
|
||||
fail("Unexpected EOF while reading JSON string");
|
||||
}
|
||||
if (char === '"') {
|
||||
return value;
|
||||
}
|
||||
if (char !== "\\") {
|
||||
value += char;
|
||||
continue;
|
||||
}
|
||||
const escaped = await this.next();
|
||||
if (escaped === null) {
|
||||
fail("Unexpected EOF while reading JSON string escape");
|
||||
}
|
||||
if (escaped === "u") {
|
||||
let hex = "";
|
||||
for (let index = 0; index < 4; index += 1) {
|
||||
const hexChar = await this.next();
|
||||
if (hexChar === null) {
|
||||
fail("Unexpected EOF while reading JSON unicode escape");
|
||||
}
|
||||
hex += hexChar;
|
||||
}
|
||||
value += String.fromCharCode(Number.parseInt(hex, 16));
|
||||
continue;
|
||||
}
|
||||
value +=
|
||||
escaped === "b"
|
||||
? "\b"
|
||||
: escaped === "f"
|
||||
? "\f"
|
||||
: escaped === "n"
|
||||
? "\n"
|
||||
: escaped === "r"
|
||||
? "\r"
|
||||
: escaped === "t"
|
||||
? "\t"
|
||||
: escaped;
|
||||
}
|
||||
}
|
||||
|
||||
async parseStringArray(onValue) {
|
||||
await this.skipWhitespace();
|
||||
await this.expectChar("[");
|
||||
await this.skipWhitespace();
|
||||
if ((await this.peek()) === "]") {
|
||||
await this.next();
|
||||
return;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
while (true) {
|
||||
const value = await this.readJsonString();
|
||||
onValue(value, index);
|
||||
index += 1;
|
||||
await this.skipWhitespace();
|
||||
const separator = await this.next();
|
||||
if (separator === "]") {
|
||||
return;
|
||||
}
|
||||
if (separator !== ",") {
|
||||
fail(`Expected , or ] but found ${separator ?? "<eof>"}`);
|
||||
}
|
||||
await this.skipWhitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseHeapFilename(filePath) {
|
||||
const base = path.basename(filePath);
|
||||
const match = base.match(
|
||||
@@ -151,38 +388,89 @@ function resolvePair(options) {
|
||||
};
|
||||
}
|
||||
|
||||
function loadSummary(filePath) {
|
||||
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
const meta = data.snapshot?.meta;
|
||||
async function parseSnapshotMeta(scanner) {
|
||||
await scanner.find('"snapshot":');
|
||||
await scanner.skipWhitespace();
|
||||
const metaObjectText = await scanner.readBalancedObject();
|
||||
const parsed = JSON.parse(metaObjectText);
|
||||
return parsed?.meta ?? null;
|
||||
}
|
||||
|
||||
async function buildSummary(filePath) {
|
||||
const scanner = new JsonStreamScanner(filePath);
|
||||
const meta = await parseSnapshotMeta(scanner);
|
||||
if (!meta) {
|
||||
fail(`Invalid heap snapshot: ${filePath}`);
|
||||
}
|
||||
|
||||
const nodeFieldCount = meta.node_fields.length;
|
||||
const typeNames = meta.node_types[0];
|
||||
const strings = data.strings;
|
||||
const typeIndex = meta.node_fields.indexOf("type");
|
||||
const nameIndex = meta.node_fields.indexOf("name");
|
||||
const selfSizeIndex = meta.node_fields.indexOf("self_size");
|
||||
if (typeIndex === -1 || nameIndex === -1 || selfSizeIndex === -1) {
|
||||
fail(`Unsupported heap snapshot schema: ${filePath}`);
|
||||
}
|
||||
|
||||
const summary = new Map();
|
||||
for (let offset = 0; offset < data.nodes.length; offset += nodeFieldCount) {
|
||||
const type = typeNames[data.nodes[offset + typeIndex]];
|
||||
const name = strings[data.nodes[offset + nameIndex]];
|
||||
const selfSize = data.nodes[offset + selfSizeIndex];
|
||||
const key = `${type}\t${name}`;
|
||||
const current = summary.get(key) ?? {
|
||||
type,
|
||||
name,
|
||||
const summaryByIndex = new Map();
|
||||
let nodeCount = 0;
|
||||
let currentTypeId = 0;
|
||||
let currentNameId = 0;
|
||||
let currentSelfSize = 0;
|
||||
await scanner.find('"nodes":');
|
||||
await scanner.parseNumberArray((value, index) => {
|
||||
const fieldIndex = index % nodeFieldCount;
|
||||
if (fieldIndex === typeIndex) {
|
||||
currentTypeId = value;
|
||||
return;
|
||||
}
|
||||
if (fieldIndex === nameIndex) {
|
||||
currentNameId = value;
|
||||
return;
|
||||
}
|
||||
if (fieldIndex === selfSizeIndex) {
|
||||
currentSelfSize = value;
|
||||
}
|
||||
if (fieldIndex !== nodeFieldCount - 1) {
|
||||
return;
|
||||
}
|
||||
const key = `${currentTypeId}\t${currentNameId}`;
|
||||
const current = summaryByIndex.get(key) ?? {
|
||||
typeId: currentTypeId,
|
||||
nameId: currentNameId,
|
||||
selfSize: 0,
|
||||
count: 0,
|
||||
};
|
||||
current.selfSize += selfSize;
|
||||
current.selfSize += currentSelfSize;
|
||||
current.count += 1;
|
||||
summary.set(key, current);
|
||||
summaryByIndex.set(key, current);
|
||||
nodeCount += 1;
|
||||
});
|
||||
|
||||
const requiredNameIds = new Set(
|
||||
Array.from(summaryByIndex.values(), (entry) => entry.nameId).filter((value) => value >= 0),
|
||||
);
|
||||
const nameStrings = new Map();
|
||||
await scanner.find('"strings":');
|
||||
await scanner.parseStringArray((value, index) => {
|
||||
if (requiredNameIds.has(index)) {
|
||||
nameStrings.set(index, value);
|
||||
}
|
||||
});
|
||||
|
||||
const summary = new Map();
|
||||
for (const entry of summaryByIndex.values()) {
|
||||
const key = `${typeNames[entry.typeId] ?? "unknown"}\t${nameStrings.get(entry.nameId) ?? ""}`;
|
||||
summary.set(key, {
|
||||
type: typeNames[entry.typeId] ?? "unknown",
|
||||
name: nameStrings.get(entry.nameId) ?? "",
|
||||
selfSize: entry.selfSize,
|
||||
count: entry.count,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
nodeCount: data.snapshot.node_count,
|
||||
nodeCount,
|
||||
summary,
|
||||
};
|
||||
}
|
||||
@@ -205,11 +493,11 @@ function truncate(text, maxLength) {
|
||||
return text.length <= maxLength ? text : `${text.slice(0, maxLength - 1)}…`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
async function main() {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const pair = resolvePair(options);
|
||||
const before = loadSummary(pair.before);
|
||||
const after = loadSummary(pair.after);
|
||||
const before = await buildSummary(pair.before);
|
||||
const after = await buildSummary(pair.after);
|
||||
const minBytes = options.minKb * 1024;
|
||||
|
||||
const rows = [];
|
||||
@@ -262,4 +550,4 @@ function main() {
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
await main();
|
||||
|
||||
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@@ -59,6 +59,11 @@
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/nostr/**"
|
||||
- "docs/channels/nostr.md"
|
||||
"channel: qqbot":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/qqbot/**"
|
||||
- "docs/channels/qqbot.md"
|
||||
"channel: signal":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -302,11 +302,17 @@ jobs:
|
||||
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
|
||||
env:
|
||||
TASK: ${{ matrix.task }}
|
||||
SHARD_COUNT: ${{ matrix.shard_count || '' }}
|
||||
SHARD_INDEX: ${{ matrix.shard_index || '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
case "$TASK" in
|
||||
extensions)
|
||||
if [ -n "$SHARD_COUNT" ] && [ -n "$SHARD_INDEX" ]; then
|
||||
export OPENCLAW_TEST_SHARDS="$SHARD_COUNT"
|
||||
export OPENCLAW_TEST_SHARD_INDEX="$SHARD_INDEX"
|
||||
fi
|
||||
pnpm test:extensions
|
||||
;;
|
||||
contracts|contracts-protocol)
|
||||
@@ -459,6 +465,8 @@ jobs:
|
||||
use-sticky-disk: "false"
|
||||
|
||||
- name: Check types and lint and oxfmt
|
||||
env:
|
||||
OPENCLAW_LOCAL_CHECK: "0"
|
||||
run: pnpm check
|
||||
|
||||
- name: Strict TS build smoke
|
||||
|
||||
2
.github/workflows/openclaw-npm-release.yml
vendored
2
.github/workflows/openclaw-npm-release.yml
vendored
@@ -71,6 +71,8 @@ jobs:
|
||||
echo "Publishing openclaw@${PACKAGE_VERSION}"
|
||||
|
||||
- name: Check
|
||||
env:
|
||||
OPENCLAW_LOCAL_CHECK: "0"
|
||||
run: pnpm check
|
||||
|
||||
- name: Build
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"globs": ["docs/**/*.md", "docs/**/*.mdx", "README.md"],
|
||||
"ignores": [
|
||||
"docs/zh-CN/**",
|
||||
"docs/.i18n/**",
|
||||
"docs/reference/templates/**",
|
||||
"**/.local/**"
|
||||
],
|
||||
"ignores": ["docs/zh-CN/**", "docs/.i18n/**", "docs/reference/templates/**", "**/.local/**"],
|
||||
"config": {
|
||||
"default": true,
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
- Type-check/build: `pnpm build`
|
||||
- TypeScript checks: `pnpm tsgo`
|
||||
- Lint/format: `pnpm check`
|
||||
- Local agent/dev shells default to lower-memory `OPENCLAW_LOCAL_CHECK=1` behavior for `pnpm tsgo` and `pnpm lint`; set `OPENCLAW_LOCAL_CHECK=0` in CI/shared runs.
|
||||
- Format check: `pnpm format` (oxfmt --check)
|
||||
- Format fix: `pnpm format:fix` (oxfmt --write)
|
||||
- Terminology:
|
||||
@@ -179,6 +180,10 @@
|
||||
- When tests need example Anthropic/OpenAI model constants, prefer `sonnet-4.6` and `gpt-5.4`; update older Anthropic/GPT examples when you touch those tests.
|
||||
- Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic.
|
||||
- Write tests to clean up timers, env, globals, mocks, sockets, temp dirs, and module state so `--isolate=false` stays green.
|
||||
- Test performance guardrail: do not put `vi.resetModules()` plus `await import(...)` in `beforeEach`/per-test loops for heavy modules unless module state truly requires it. Prefer static imports or one-time `beforeAll` imports, then reset mocks/runtime state directly.
|
||||
- Test performance guardrail: inside an extension package, prefer a thin local seam (`./api.ts`, `./runtime-api.ts`, or a narrower local `*.runtime-api.ts`) over direct `openclaw/plugin-sdk/*` imports for internal production code. Keep local seams curated and lightweight; only reach for direct `plugin-sdk/*` imports when you are crossing a real package boundary or when no suitable local seam exists yet.
|
||||
- Test performance guardrail: keep expensive runtime fallback work such as snapshotting, migration, installs, or bootstrap behind dedicated `*.runtime.ts` boundaries so tests can mock the seam instead of accidentally invoking real work.
|
||||
- Test performance guardrail: for import-only/runtime-wrapper tests, keep the wrapper lazy. Do not eagerly load heavy verification/bootstrap/runtime modules at module top level if the exported function can import them on demand.
|
||||
- Agents MUST NOT modify baseline, inventory, ignore, snapshot, or expected-failure files to silence failing checks without explicit approval in this chat.
|
||||
- For targeted/local debugging, keep using the wrapper: `pnpm test -- <path-or-filter> [vitest args...]` (for example `pnpm test -- src/commands/onboard-search.test.ts -t "shows registered plugin providers"`); do not default to raw `pnpm vitest run ...` because it bypasses wrapper config/profile/pool routing.
|
||||
- Do not set test workers above 16; tried already.
|
||||
|
||||
445
CHANGELOG.md
445
CHANGELOG.md
@@ -2,112 +2,336 @@
|
||||
|
||||
Docs: https://docs.openclaw.ai
|
||||
|
||||
## Unreleased
|
||||
## 2026.3.31
|
||||
|
||||
### Breaking
|
||||
|
||||
- Nodes/exec: remove the duplicated `nodes.run` shell wrapper from the CLI and agent `nodes` tool so node shell execution always goes through `exec host=node`, keeping node-specific capabilities on `nodes invoke` and the dedicated media/location/notify actions.
|
||||
- Background tasks: replace the old JSON task ledger with the SQLite-backed task store, so undocumented tooling or scripts that read `tasks/runs.json` directly must switch to the supported `openclaw tasks` surfaces instead of depending on state-dir internals. Thanks @vincentkoc and @mbelinky.
|
||||
- Plugin SDK: deprecate the legacy provider compat subpaths plus the older bundled provider setup and channel-runtime compatibility shims, emit migration warnings, and keep the current documented `openclaw/plugin-sdk/*` entrypoints plus local `api.ts` / `runtime-api.ts` barrels as the forward path ahead of a future major-release removal.
|
||||
- Skills/install and Plugins/install: built-in dangerous-code `critical` findings and install-time scan failures now fail closed by default, so plugin installs and gateway-backed skill dependency installs that previously succeeded may now require an explicit dangerous override such as `--dangerously-force-unsafe-install` to proceed.
|
||||
- Gateway/auth: `trusted-proxy` now rejects mixed shared-token configs, and local-direct fallback requires the configured token instead of implicitly authenticating same-host callers. Thanks @zhangning-agent, @jacobtomlinson, and @vincentkoc.
|
||||
- Gateway/node commands: node commands now stay disabled until node pairing is approved, so device pairing alone is no longer enough to expose declared node commands. (#57777) Thanks @jacobtomlinson.
|
||||
- Gateway/node events: node-originated runs now stay on a reduced trusted surface, so notification-driven or node-triggered flows that previously relied on broader host/session tool access may need adjustment. (#57691) Thanks @jacobtomlinson.
|
||||
|
||||
### Changes
|
||||
|
||||
- LINE/outbound media: add LINE image, video, and audio outbound sends on the LINE-specific delivery path, including explicit preview/tracking handling for videos while keeping generic media sends on the existing image-only route. (#45826) Thanks @masatohoshino.
|
||||
- WhatsApp/reactions: agents can now react with emoji on incoming WhatsApp messages, enabling more natural conversational interactions like acknowledging a photo with ❤️ instead of typing a reply. Thanks @mcaxtr.
|
||||
- MCP: add remote HTTP/SSE server support for `mcp.servers` URL configs, including auth headers and safer config redaction for MCP credentials. (#50396) Thanks @dhananjai1729.
|
||||
- Agents/MCP: materialize bundle MCP tools with provider-safe names (`serverName__toolName`), support optional `streamable-http` transport selection plus per-server connection timeouts, and preserve real tool results from aborted/error turns unless truncation explicitly drops them. (#49505) Thanks @ziomancer.
|
||||
- Plugins/hooks: add a `before_install` hook with structured request provenance, built-in scan status, and install-target metadata so external security scanners and policy engines can review and block skill, plugin package, plugin bundle, and single-file plugin installs. (#56050) thanks @odysseus0.
|
||||
- ACP/plugins: add an explicit default-off ACPX plugin-tools MCP bridge config, document the trust boundary, and harden the built-in bridge packaging/logging path so global installs and stdio MCP sessions work reliably. (#56867) Thanks @joe2643.
|
||||
- Agents/LLM: add a configurable idle-stream timeout for embedded runner requests so stalled model streams abort cleanly instead of hanging until the broader run timeout fires. (#55072) Thanks @liuy.
|
||||
- OpenAI/Responses: forward configured `text.verbosity` across Responses HTTP and WebSocket transports, surface it in `/status`, and keep per-agent verbosity precedence aligned with runtime behavior. (#47106) Thanks @merc1305 and @vincentkoc.
|
||||
- Agents/MCP: materialize bundle MCP tools with provider-safe names (`serverName__toolName`), support optional `streamable-http` transport selection plus per-server connection timeouts, and preserve real tool results from aborted/error turns unless truncation explicitly drops them. (#49505) Thanks @ziomancer.
|
||||
- Android/notifications: add notification-forwarding controls with package filtering, quiet hours, rate limiting, and safer picker behavior for forwarded notification events. (#40175) Thanks @nimbleenigma.
|
||||
- Background tasks: turn tasks into a real shared background-run control plane instead of ACP-only bookkeeping by unifying ACP, subagent, cron, and background CLI execution under one SQLite-backed ledger, routing detached lifecycle updates through the executor seam, adding audit/maintenance/status visibility, tightening auto-cleanup and lost-run recovery, improving task awareness in internal status/tool surfaces, and clarifying the split between heartbeat/main-session automation and detached scheduled runs. Thanks @mbelinky and @vincentkoc.
|
||||
- Background tasks: add the first linear task flow control surface with `openclaw flows list|show|cancel`, keep manual multi-task flows separate from one-task auto-sync flows, and surface doctor recovery hints for obviously orphaned or broken flow/task linkage. Thanks @mbelinky and @vincentkoc.
|
||||
- Channels/QQ Bot: add QQ Bot as a bundled channel plugin with multi-account setup, SecretRef-aware credentials, slash commands, reminders, and media send/receive support. (#52986) Thanks @sliverp.
|
||||
- Diffs: skip unused viewer-versus-file SSR preload work so `diffs` view-only and file-only runs do less render work while keeping mode outputs aligned. (#57909) thanks @gumadeiras.
|
||||
- Tasks: add a minimal SQLite-backed task flow registry plus task-to-flow linkage scaffolding, so orchestrated work can start gaining a first-class parent record without changing current task delivery behavior. Thanks @mbelinky and @vincentkoc.
|
||||
- Tasks: persist blocked state on one-task task flows and let the same flow reopen cleanly on retry, so blocked detached work can carry a parent-level reason and continue without fragmenting into a new job. Thanks @mbelinky and @vincentkoc.
|
||||
- Tasks: route one-task ACP and subagent updates through a parent task-flow owner context, so detached work can emerge back through the intended parent thread/session instead of speaking only as a raw child task. Thanks @mbelinky and @vincentkoc.
|
||||
- LINE/outbound media: add LINE image, video, and audio outbound sends on the LINE-specific delivery path, including explicit preview/tracking handling for videos while keeping generic media sends on the existing image-only route. (#45826) Thanks @masatohoshino.
|
||||
- Matrix/history: add optional room history context for Matrix group triggers via `channels.matrix.historyLimit`, with per-agent watermarks and retry-safe snapshots so failed trigger retries do not drift into newer room messages. (#57022) thanks @chain710.
|
||||
- Matrix/network: add explicit `channels.matrix.proxy` config for routing Matrix traffic through an HTTP(S) proxy, including account-level overrides and matching probe/runtime behavior. (#56931) thanks @patrick-yingxi-pan.
|
||||
- Background tasks: turn tasks into a real shared background-run control plane instead of ACP-only bookkeeping by unifying ACP, subagent, cron, and background CLI execution under one SQLite-backed ledger, routing detached lifecycle updates through the executor seam, adding audit/maintenance/status visibility, tightening auto-cleanup and lost-run recovery, improving task awareness in internal status/tool surfaces, and clarifying the split between heartbeat/main-session automation and detached scheduled runs. Thanks @vincentkoc and @mbelinky.
|
||||
- Matrix/streaming: add draft streaming so partial Matrix replies update the same message in place instead of sending a new message for each chunk. (#56387) Thanks @jrusz.
|
||||
- Matrix/threads: add per-DM `threadReplies` overrides and keep thread session isolation aligned with the effective room or DM thread policy from the triggering message onward. (#57995) thanks @teconomix.
|
||||
- MCP: add remote HTTP/SSE server support for `mcp.servers` URL configs, including auth headers and safer config redaction for MCP credentials. (#50396) Thanks @dhananjai1729.
|
||||
- Memory/QMD: add per-agent `memorySearch.qmd.extraCollections` so agents can opt into cross-agent session search without flattening every transcript collection into one shared QMD namespace. Thanks @vincentkoc.
|
||||
- Microsoft Teams/member info: add a Graph-backed member info action so Teams automations and tools can resolve channel member details directly from Microsoft Graph. (#57528) Thanks @sudie-codes.
|
||||
- Nostr/inbound DMs: verify inbound event signatures before pairing or sender-authorization side effects, so forged DM events no longer create pairing requests or trigger reply attempts. Thanks @smaeljaish771 and @vincentkoc.
|
||||
- OpenAI/Responses: forward configured `text.verbosity` across Responses HTTP and WebSocket transports, surface it in `/status`, and keep per-agent verbosity precedence aligned with runtime behavior. (#47106) Thanks @merc1305 and @vincentkoc.
|
||||
- Pi/Codex: add native Codex web search support for embedded Pi runs, including config/docs/wizard coverage and managed-tool suppression when native Codex search is active. (#46579) Thanks @Evizero.
|
||||
- Slack/exec approvals: add native Slack approval routing and approver authorization so exec approval prompts can stay in Slack instead of falling back to the Web UI or terminal. Thanks @vincentkoc.
|
||||
- TTS: Add structured provider diagnostics and fallback attempt analytics. (#57954) Thanks @joshavant.
|
||||
- WhatsApp/reactions: agents can now react with emoji on incoming WhatsApp messages, enabling more natural conversational interactions like acknowledging a photo with ❤️ instead of typing a reply. Thanks @mcaxtr.
|
||||
- Agents/BTW: force `/btw` side questions to disable provider reasoning so Anthropic adaptive-thinking sessions stop failing with `No BTW response generated`. Fixes #55376. Thanks @Catteres and @vincentkoc.
|
||||
- CLI/onboarding: reset the remote gateway URL prompt to the safe loopback default after declining a discovered endpoint, so onboarding does not keep a previously rejected remote URL. (#57828)
|
||||
- Agents/exec defaults: honor per-agent `tools.exec` defaults when no inline directive or session override is present, so configured exec host, security, ask, and node settings actually apply. (#57689)
|
||||
- Sandbox/networking: sanitize SSH subprocess env vars through the shared sandbox policy and route marketplace archive downloads plus Ollama discovery, auth, and pull requests through the guarded fetch path so sandboxed execution and remote fetches follow the repo's trust boundaries. (#57848, #57850)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Image generation/build: write stable runtime alias files into `dist/` and route provider-auth runtime lookups through those aliases so image-generation providers keep resolving auth/runtime modules after rebuilds instead of crashing on missing hashed chunk files.
|
||||
- Config/runtime: pin the first successful config load in memory for the running process and refresh that snapshot on successful writes/reloads, so hot paths stop reparsing `openclaw.json` between watcher-driven swaps.
|
||||
- Config/legacy cleanup: stop probing obsolete alternate legacy config names and service labels during local config/service detection, while keeping the active `~/.openclaw/openclaw.json` path canonical.
|
||||
- Slack: stop retry-driven duplicate replies when draft-finalization edits fail ambiguously, and log configured allowlisted users/channels by readable name instead of raw IDs.
|
||||
- Agents/OpenAI Responses: normalize raw bundled MCP tool schemas on the WebSocket/Responses path so bare-object, object-ish, and top-level union MCP tools no longer get rejected by OpenAI during tool registration. (#58299) Thanks @yelog.
|
||||
- ACP/security: replace ACP's dangerous-tool name override with semantic approval classes, so only narrow readonly reads/searches can auto-approve while indirect exec-capable and control-plane tools always require explicit prompt approval. Thanks @vincentkoc.
|
||||
- ACP/sessions_spawn: register ACP child runs for completion tracking and lifecycle cleanup, and make registration-failure cleanup explicitly best-effort so callers do not assume an already-started ACP turn was fully aborted. (#40885) Thanks @xaeon2026 and @vincentkoc.
|
||||
- ACP/tasks: mark cleanly exited ACP runs as blocked when they end on deterministic write or authorization blockers, and wake the parent session with a follow-up instead of falsely reporting success.
|
||||
- ACPX/runtime: derive the bundled ACPX expected version from the extension package metadata instead of hardcoding a separate literal, so plugin-local ACPX installs stop drifting out of health-check parity after version bumps. (#49089) Thanks @jiejiesks and @vincentkoc.
|
||||
- Gateway/auth: make local-direct `trusted-proxy` fallback require the configured shared token instead of silently authenticating same-host callers, while keeping same-host reverse proxy identity-header flows on the normal trusted-proxy path. Thanks @zhangning-agent and @vincentkoc.
|
||||
- Memory/QMD: send MCP `query` collection filters as the upstream `collections` array instead of the legacy singular `collection` field, so mcporter-backed QMD 1.1+ searches still scope correctly after the unified `query` tool migration. (#54728) Thanks @armanddp and @vincentkoc.
|
||||
- Memory/QMD: keep `qmd embed` active in `search` mode too, so BM25-first setups still build a complete index for later vector and hybrid retrieval. (#54509) Thanks @hnshah and @vincentkoc.
|
||||
- Memory/QMD: point `QMD_CONFIG_DIR` at the nested `xdg-config/qmd` directory so per-agent collection config resolves correctly. (#39078) Thanks @smart-tinker and @vincentkoc.
|
||||
- Memory/QMD: include deduplicated default plus per-agent `memorySearch.extraPaths` when building QMD custom collections, so shared and agent-specific extra roots both get indexed consistently. (#57315) Thanks @Vitalcheffe and @vincentkoc.
|
||||
- Memory/session indexer: include `.jsonl.reset.*` and `.jsonl.deleted.*` transcripts in the memory host session scan while still excluding `.jsonl.bak.*` compaction backups and lock files, so memory search sees archived session history without duplicating stale snapshots. Thanks @hclsys and @vincentkoc.
|
||||
- Agents/sandbox: honor `tools.sandbox.tools.alsoAllow`, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman.
|
||||
- LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn ... --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels.
|
||||
- LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997.
|
||||
- TTS/Microsoft: auto-switch the default Edge voice to Chinese for CJK-dominant text without overriding explicitly selected Microsoft voices. (#52355) Thanks @extrasmall0.
|
||||
- Agents/context pruning: count supplementary-plane CJK characters with the shared code-point-aware estimator so context pruning stops underestimating Japanese and Chinese text that uses Extension B ideographs. (#39985) Thanks @Edward-Qiang-2024.
|
||||
- Slack/status reactions: add a reaction lifecycle for queued, thinking, tool, done, and error phases in Slack monitors, with safer cleanup so queued ack reactions stay correct across silent runs, pre-reply failures, and delayed transitions. (#56430) Thanks @hsiaoa.
|
||||
- macOS/local gateway: stop OpenClaw.app from killing healthy local gateway listeners after startup by recognizing the current `openclaw-gateway` process title and using the current `openclaw gateway` launch shape.
|
||||
- Gateway/OpenAI compatibility: accept flat Responses API function tool definitions on `/v1/responses` and preserve `strict` when normalizing hosted tools into the embedded runner, so spec-compliant clients like Codex no longer fail validation or silently lose strict tool enforcement. Thanks @malaiwah and @vincentkoc.
|
||||
- Memory/QMD: resolve slugified `memory_search` file hints back to the indexed filesystem path before returning search hits, so `memory_get` works again for mixed-case and spaced paths. (#50313) Thanks @erra9x.
|
||||
- OpenAI/Codex fast mode: map `/fast` to priority processing on native OpenAI and Codex Responses endpoints instead of rewriting reasoning settings, and document the exact endpoint and override behavior.
|
||||
- Memory/QMD: weight CJK-heavy text correctly when estimating chunk sizes, preserve surrogate-pair characters during fine splits, and keep long Latin lines on the old chunk boundaries so memory indexing produces better-sized chunks for CJK notes. (#40271) Thanks @AaronLuo00.
|
||||
- Security/LINE: make webhook signature validation run the timing-safe compare even when the supplied signature length is wrong, closing a small timing side-channel. (#55663) Thanks @gavyngong.
|
||||
- LINE/status: stop `openclaw status` from warning about missing credentials when sanitized LINE snapshots are already configured, while still surfacing whether the missing field is the token or secret. (#45701) Thanks @tamaosamu.
|
||||
- Gateway/health: carry webhook-vs-polling account mode from channel descriptors into runtime snapshots so passive channels like LINE and BlueBubbles skip false stale-socket health failures. (#47488) Thanks @karesansui-u.
|
||||
- Agents/MCP: reuse bundled MCP runtimes across turns in the same session, while recreating them when MCP config changes and disposing stale runtimes cleanly on session rollover. (#55090) Thanks @allan0509.
|
||||
- Memory/QMD: honor `memory.qmd.update.embedInterval` even when regular QMD update cadence is disabled or slower by arming a dedicated embed-cadence maintenance timer, while avoiding redundant timers when regular updates are already frequent enough. (#37326) Thanks @barronlroth.
|
||||
- Memory/QMD: add `memory.qmd.searchTool` as an exact mcporter tool override, so custom QMD MCP tools such as `hybrid_search` can be used without weakening the validated `searchMode` config surface. (#27801) Thanks @keramblock.
|
||||
- Memory/QMD: keep reset and deleted session transcripts in QMD session export so daily session resets do not silently drop most historical recall from `memory_search`. (#30220) Thanks @pushkarsingh32.
|
||||
- Memory/QMD: rebind collections when QMD reports a changed pattern but omits path metadata, so config pattern changes stop being silently ignored on restart. (#49897) Thanks @Madruru.
|
||||
- Memory/QMD: warn explicitly when `memory.backend=qmd` is configured but the `qmd` binary is missing, so doctor and runtime fallback no longer fail as a silent builtin downgrade. (#50439) Thanks @Jimmy-xuzimo and @vincentkoc.
|
||||
- Memory/QMD: pass a direct-session key on `openclaw memory search` so CLI QMD searches no longer get denied as `session=<none>` under direct-only scope defaults. (#43517) Thanks @waynecc-at and @vincentkoc.
|
||||
- Memory/QMD: keep `memory_search` session-hit paths roundtrip-safe when exported session markdown lives under the workspace `qmd/` directory, so `memory_get` can read the exact returned path instead of failing on the generic `qmd/sessions/...` alias. (#43519) Thanks @holgergruenhagen and @vincentkoc.
|
||||
- Agents/memory flush: keep daily memory flush files append-only during embedded attempts so compaction writes do not overwrite earlier notes. (#53725) Thanks @HPluseven.
|
||||
- Web UI/markdown: stop bare auto-links from swallowing adjacent CJK text while preserving valid mixed-script path and query characters in rendered links. (#48410) Thanks @jnuyao.
|
||||
- BlueBubbles/iMessage: coalesce URL-only inbound messages with their link-preview balloon again so sharing a bare link no longer drops the URL from agent context. Thanks @vincentkoc.
|
||||
- Sandbox/browser: install `fonts-noto-cjk` in the sandbox browser image so screenshots render Chinese, Japanese, and Korean text correctly instead of tofu boxes. Fixes #35597. Thanks @carrotRakko and @vincentkoc.
|
||||
- Memory/FTS: add configurable trigram tokenization plus short-CJK substring fallback so memory search can find Chinese, Japanese, and Korean text without breaking mixed long-and-short queries. Thanks @carrotRakko.
|
||||
- Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
|
||||
- TUI/chat: keep optimistic outbound user messages visible during active runs by deferring local-run binding until the first gateway chat event reveals the real run id, preventing premature history reloads from wiping pending local sends. (#54722) Thanks @seanturner001.
|
||||
- TUI/model picker: keep searchable `/model` and `/models` input mode from hijacking `j`/`k` as navigation keys, and harden width bounds under `m`-filtered model lists so search no longer crashes on long rows. (#30156) Thanks @briannicholls.
|
||||
- Agents/Kimi: preserve already-valid Anthropic-compatible tool call argument objects while still clearing cached repairs when later trailing junk exceeds the repair allowance. (#54491) Thanks @yuanaichi.
|
||||
- Docker/setup: force BuildKit for local image builds (including sandbox image builds) so `./docker-setup.sh` no longer fails on `RUN --mount=...` when hosts default to Docker's legacy builder. (#56681) Thanks @zhanghui-china.
|
||||
- Agents/Anthropic failover: treat Anthropic `api_error` payloads with `An unexpected error occurred while processing the response` as transient so retry/fallback can engage instead of surfacing a terminal failure. (#57441) Thanks @zijiess and @vincentkoc.
|
||||
- Agents/compaction: keep late compaction-retry completions from double-resolving finished compaction futures, so interrupted or timed-out compactions stop surfacing spurious second-completion races. (#57796) Thanks @joshavant.
|
||||
- Agents/disabled providers: make disabled providers disappear from default model selection and embedded provider fallback, while letting explicitly pinned disabled providers fail with a clear config error instead of silently taking traffic. (#57735) Thanks @rileybrown-dev and @vincentkoc.
|
||||
- Agents/OAuth output: force exec-host OAuth output readers through the gateway fs policy so embedded gateway runs stop crashing when provider auth writes land outside the current sandbox workspace. (#58249) Thanks @joshavant.
|
||||
- Agents/system prompt: fix `agent.name` interpolation in the embedded runtime system prompt and make provider/model fallback text reflect the effective runtime selection after start. (#57625) Thanks @StllrSvr and @vincentkoc.
|
||||
- Android/device info: read the app's version metadata from the package manager instead of hidden APIs so Android 15+ onboarding and device info no longer fail to compile or report placeholder values. (#58126) Thanks @L3ER0Y.
|
||||
- Android/pairing: stop appending duplicate push receiver entries to `gateway-service.conf` on repeated QR pairing and keep push registration bounded to the current successful pairing, so Android push delivery stays healthy across re-pair and token rotation. (#58256) Thanks @surrealroad.
|
||||
- App install smoke: pin the latest-release lookup to `latest`, cache the first stable install version across the rerun, and relax prerelease package assertions so the Parallels smoke lane can validate stable-to-main upgrades even when `beta` moves ahead or the guest starts from an older stable. (#58177) Thanks @vincentkoc.
|
||||
- Auth/profiles: keep the last successful config load in memory for the running process and refresh that snapshot on successful writes/reloads, so hot paths stop reparsing `openclaw.json` between watcher-driven swaps.
|
||||
- Config/SecretRef + Control UI: harden SecretRef redaction round-trip restore, block unsafe raw fallback (force Form mode when raw is unavailable), and preflight submitted-config SecretRefs before config write RPC persistence. (#58044) Thanks @joshavant.
|
||||
- Config/Telegram: migrate removed `channels.telegram.groupMentionsOnly` into `channels.telegram.groups[\"*\"].requireMention` on load so legacy configs no longer crash at startup. (#55336) thanks @jameslcowan.
|
||||
- Config/update: stop `openclaw doctor` write-backs from persisting plugin-injected channel defaults, so `openclaw update` no longer seeds config keys that later break service refresh validation. (#56834) Thanks @openperf.
|
||||
- Control UI/agents: auto-load agent workspace files on initial Files panel open, and populate overview model/workspace/fallbacks from effective runtime agent metadata so defaulted models no longer show as `Not set`. (#56637) Thanks @dxsx84.
|
||||
- Control UI/slash commands: make `/steer` and `/redirect` work from the chat command palette with visible pending state for active-run `/steer`, correct redirected-run tracking, and a single canonical `/steer` entry in the command menu. (#54625) Thanks @fuller-stack-dev.
|
||||
- Cron/announce: preserve all deliverable text payloads for announce mode instead of collapsing to the last chunk, so multi-line cron reports deliver in full to Telegram forum topics.
|
||||
- Cron/isolated sessions: carry the full live-session provider, model, and auth-profile selection across retry restarts so cron jobs with model overrides no longer fail or loop on mid-run model-switch requests. (#57972) Thanks @issaba1.
|
||||
- Diffs/config: preserve schema-shaped plugin config parsing from `diffsPluginConfigSchema.safeParse()`, so direct callers keep `defaults` and `security` sections instead of receiving flattened tool defaults. (#57904) Thanks @gumadeiras.
|
||||
- Diffs: fall back to plain text when `lang` hints are invalid during diff render and viewer hydration, so bad or stale language values no longer break the diff viewer. (#57902) Thanks @gumadeiras.
|
||||
- Discord/voice: enforce the same guild channel and member allowlist checks on spoken voice ingress before transcription, so joined voice channels no longer accept speech from users outside the configured Discord access policy. Thanks @cyjhhh and @vincentkoc.
|
||||
- Docker/setup: force BuildKit for local image builds (including sandbox image builds) so `./docker-setup.sh` no longer fails on `RUN --mount=...` when hosts default to Docker's legacy builder. (#56681) Thanks @zhanghui-china.
|
||||
- Docs/anchors: fix broken English docs links and make Mint anchor audits run against the English-source docs tree. (#57039) thanks @velvet-shark.
|
||||
- Doctor/plugins: skip false Matrix legacy-helper warnings when no migration plans exist, and keep bundled `enabledByDefault` plugins in the gateway startup set. (#57931) Thanks @dinakars777.
|
||||
- Exec approvals/macOS: unwrap `arch` and `xcrun` before deriving shell payloads and allow-always patterns, so wrapper approvals stay bound to the carried command instead of the outer carrier. Thanks @tdjackey and @vincentkoc.
|
||||
- Exec approvals: unwrap `caffeinate` and `sandbox-exec` before persisting allow-always trust so later shell payload changes still require a fresh approval. Thanks @tdjackey and @vincentkoc.
|
||||
- Exec/approvals: infer Discord and Telegram exec approvers from existing owner config when `execApprovals.approvers` is unset, extend the default approval window to 30 minutes, and clarify approval-unavailable guidance so approvals do not appear to silently disappear.
|
||||
- Pi/TUI: flush message-boundary replies at `message_end` so turns stop looking stuck until the next nudge when the final reply was already ready. Thanks @vincentkoc.
|
||||
- Exec/approvals: keep `awk` and `sed` family binaries out of the low-risk `safeBins` fast path, and stop doctor profile scaffolding from treating them like ordinary custom filters. Thanks @vincentkoc.
|
||||
- Exec/env: block proxy, TLS, and Docker endpoint env overrides in host execution so request-scoped commands cannot silently reroute outbound traffic or trust attacker-supplied certificate settings. Thanks @AntAISecurityLab.
|
||||
- Exec/env: block Python package index override variables from request-scoped host exec environment sanitization so package fetches cannot be redirected through a caller-supplied index. Thanks @nexrin and @vincentkoc.
|
||||
- Exec/node: stop gateway-side workdir fallback from rewriting explicit `host=node` cwd values to the gateway filesystem, so remote node exec approval and runs keep using the intended node-local directory. (#50961) Thanks @openperf.
|
||||
- Exec/runtime: default implicit exec to `host=auto`, resolve that target to sandbox only when a sandbox runtime exists, keep explicit `host=sandbox` fail-closed without sandbox, and show `/exec` effective host state in runtime status/docs.
|
||||
- Exec: fail closed when the implicit sandbox host has no sandbox runtime, and stop denied async approval followups from reusing prior command output from the same session. (#56800) Thanks @scoootscooob.
|
||||
- Exec/approvals: infer Discord and Telegram exec approvers from existing owner config when `execApprovals.approvers` is unset, extend the default approval window to 30 minutes, and clarify approval-unavailable guidance so approvals do not appear to silently disappear.
|
||||
- Exec/node: stop gateway-side workdir fallback from rewriting explicit `host=node` cwd values to the gateway filesystem, so remote node exec approval and runs keep using the intended node-local directory. (#50961) Thanks @openperf.
|
||||
- Plugins/ClawHub: sanitize temporary archive filenames for scoped package names and slash-containing skill slugs so `openclaw plugins install @scope/name` no longer fails with `ENOENT` during archive download. (#56452) Thanks @soimy.
|
||||
- Telegram/polling: keep the watchdog from aborting long-running reply delivery by treating recent non-polling API activity as bounded liveness instead of a hard stall. (#56343) Thanks @openperf.
|
||||
- Memory/FTS: keep provider-less keyword hits visible at the default memory-search threshold, so FTS-only recall works without requiring `--min-score 0`. (#56473) Thanks @opriz.
|
||||
- Memory/LanceDB: resolve runtime dependency manifest lookup from the bundled `extensions/memory-lancedb` path (including flattened dist chunks) so startup no longer fails with a missing `@lancedb/lancedb` dependency error. (#56623) Thanks @LUKSOAgent.
|
||||
- Tools/web_search: localize the shared search cache to module scope so same-process global symbol lookups can no longer inspect or mutate cached web-search responses. Thanks @vincentkoc.
|
||||
- Agents/silent turns: fail closed on silent memory-flush runs so narrated `NO_REPLY` self-talk cannot stream or finalize into external replies even when block streaming is enabled. (#52593)
|
||||
- Browser/plugins: auto-enable the bundled browser plugin when browser config or browser tool policy already references it, and show a clearer CLI error when `plugins.allow` excludes `browser`.
|
||||
- Matrix/plugin loading: ship and source-load the crypto bootstrap runtime sidecar correctly so current `main` stops warning about failed Matrix bootstrap loads and `matrix/index` plugin-id mismatches on every invocation. (#53298) thanks @keithce.
|
||||
- iOS/Live Activities: mark the `ActivityKit` import in `LiveActivityManager.swift` as `@preconcurrency` so Xcode 26.4 / Swift 6 builds stop failing on strict concurrency checks. (#57180) Thanks @ngutman.
|
||||
- Plugins/Matrix: mirror the Matrix crypto WASM runtime dependency into the root packaged install and enforce root/plugin dependency parity so bundled Matrix E2EE crypto resolves correctly in shipped builds. (#57163) Thanks @gumadeiras.
|
||||
- Plugins/CLI: add descriptor-backed lazy plugin CLI registration so Matrix can keep its CLI module lazy-loaded without dropping `openclaw matrix ...` from parse-time command registration. (#57165) Thanks @gumadeiras.
|
||||
- Plugins/CLI: collect root-help plugin descriptors through a dedicated non-activating CLI metadata path so enabled plugins keep validated config semantics without triggering runtime-only plugin registration work, while preserving runtime CLI command registration for legacy channel plugins that still wire commands from full registration. (#57294) thanks @gumadeiras.
|
||||
- Anthropic/OAuth: inject `/fast` `service_tier` hints for direct `sk-ant-oat-*` requests so OAuth-authenticated Anthropic runs stop missing the same overload-routing signal as API-key traffic. Fixes #55758. Thanks @Cypherm and @vincentkoc.
|
||||
- Anthropic/service tiers: support explicit `serviceTier` model params for direct Anthropic requests and let them override `/fast` defaults when both are set. (#45453) Thanks @vincentkoc.
|
||||
- Auto-reply/fast: accept `/fast status` on the directive-only path, align help/status text with the documented `status|on|off` syntax, and keep current-state replies consistent across command surfaces. Fixes #46095. Thanks @weissfl and @vincentkoc.
|
||||
- Telegram/native commands: prefix native command menu callback payloads and preserve `CommandSource: "native"` when Telegram replays them through callback queries, so `/fast` and other native command menus keep working even when text-command routing is disabled. Thanks @vincentkoc.
|
||||
- Docs/anchors: fix broken English docs links and make Mint anchor audits run against the English-source docs tree. (#57039) thanks @velvet-shark.
|
||||
- Cron/announce: preserve all deliverable text payloads for announce mode instead of collapsing to the last chunk, so multi-line cron reports deliver in full to Telegram forum topics.
|
||||
- Feishu/groups: keep quoted replies and topic bootstrap context aligned with group sender allowlists so only allowlisted thread messages seed agent context. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Gateway/attachments: offload large inbound images without leaking `media://` markers into text-only runs, preserve mixed attachment order for model input/transcripts, and fail closed when model image capability cannot be resolved. (#55513) Thanks @Syysean.
|
||||
- Gateway/auth: keep shared-auth rate limiting active during WebSocket handshake attempts even when callers also send device-token candidates, so bogus device-token fields no longer suppress shared-secret brute-force tracking. Thanks @kexinoh and @vincentkoc.
|
||||
- Gateway/auth: reject mismatched browser `Origin` headers on trusted-proxy HTTP operator requests while keeping origin-less headless proxy clients working. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Gateway/device tokens: disconnect active device sessions after token rotation so newly rotated credentials revoke existing live connections immediately instead of waiting for those sockets to close naturally. Thanks @zsxsoft and @vincentkoc.
|
||||
- Gateway/health: carry webhook-vs-polling account mode from channel descriptors into runtime snapshots so passive channels like LINE and BlueBubbles skip false stale-socket health failures. (#47488) Thanks @karesansui-u.
|
||||
- Gateway/pairing: restore QR bootstrap onboarding handoff so fresh `/pair qr` iPhone setup can auto-approve the initial node pairing, receive a reusable node device token, and stop retrying with spent bootstrap auth. (#58382) Thanks @ngutman.
|
||||
- Gateway/OpenAI compatibility: accept flat Responses API function tool definitions on `/v1/responses` and preserve `strict` when normalizing hosted tools into the embedded runner, so spec-compliant clients like Codex no longer fail validation or silently lose strict tool enforcement. Thanks @malaiwah and @vincentkoc.
|
||||
- Gateway/OpenAI HTTP: restore default operator scopes for bearer-authenticated requests that omit `x-openclaw-scopes`, so headless `/v1/chat/completions` and session-history callers work again after the recent method-scope hardening. (#57596) Thanks @openperf.
|
||||
- Gateway/plugins: scope plugin-auth HTTP route runtime clients to read-only access and keep gateway-authenticated plugin routes on write scope, so plugin-owned webhook handlers do not inherit write-capable runtime access by default. Thanks @davidluzsilva and @vincentkoc.
|
||||
- Gateway/SecretRef: resolve restart token drift checks with merged service/runtime env sources and hard-fail unsupported mutable SecretRef plus OAuth-profile combinations so restart warnings and policy enforcement match runtime behavior. (#58141) Thanks @joshavant.
|
||||
- Gateway/tools HTTP: tighten HTTP tool-invoke authorization so owner-only tools stay off HTTP invoke paths. (#57773) Thanks @jacobtomlinson.
|
||||
- Harden async approval followup delivery in webchat-only sessions (#57359) Thanks @joshavant.
|
||||
- Status: fix cache hit rate exceeding 100% by deriving denominator from prompt-side token fields instead of potentially undersized totalTokens. Fixes #26643.
|
||||
- Config/update: stop `openclaw doctor` write-backs from persisting plugin-injected channel defaults, so `openclaw update` no longer seeds config keys that later break service refresh validation. (#56834) Thanks @openperf.
|
||||
- Agents/Anthropic failover: treat Anthropic `api_error` payloads with `An unexpected error occurred while processing the response` as transient so retry/fallback can engage instead of surfacing a terminal failure. (#57441) Thanks @zijiess and @vincentkoc.
|
||||
- Agents/compaction: keep late compaction-retry rejections handled after the aggregate timeout path wins without swallowing real pre-timeout wait failures, so timed-out retries no longer surface an unhandled rejection on later unsubscribe. (#57451) Thanks @mpz4life and @vincentkoc.
|
||||
- Matrix/delivery recovery: treat Synapse `User not in room` replay failures as permanent during startup recovery so poisoned queued messages move to `failed/` instead of crash-looping Matrix after restart. (#57426) thanks @dlardo.
|
||||
- Plugins/facades: guard bundled plugin facade loads with a cache-first sentinel so circular re-entry stops crashing `xai`, `sglang`, and `vllm` during gateway plugin startup. (#57508) Thanks @openperf.
|
||||
- Agents/MCP: dispose bundled MCP runtimes after one-shot `openclaw agent --local` runs finish, while preserving bundled MCP state across in-run retries so local JSON runs exit cleanly without restarting stateful MCP tools mid-run.
|
||||
- Heartbeat/auth: prevent exec-event heartbeat runs from inheriting owner-only tool access from the session delivery target, so node exec output stays on the non-owner tool surface even when the target session belongs to the owner. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
|
||||
- Hooks/session routing: rebind hook-triggered `agent:` session keys to the actual target agent before isolated dispatch so dedicated hook agents keep their own session-scoped tool and plugin identity. Thanks @kexinoh and @vincentkoc.
|
||||
- Host exec/env: block additional request-scoped env overrides that can redirect Docker endpoints, trust roots, compiler include paths, package resolution, or Python environment roots during approved host runs. Thanks @tdjackey and @vincentkoc.
|
||||
- Image generation/build: write stable runtime alias files into `dist/` and route provider-auth runtime lookups through those aliases so image-generation providers keep resolving auth/runtime modules after rebuilds instead of crashing on missing hashed chunk files.
|
||||
- iOS/Live Activities: mark the `ActivityKit` import in `LiveActivityManager.swift` as `@preconcurrency` so Xcode 26.4 / Swift 6 builds stop failing on strict concurrency checks. (#57180) Thanks @ngutman.
|
||||
- LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn ... --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels.
|
||||
- LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997.
|
||||
- Agents/failover: make overloaded same-provider retry count and retry delay configurable via `auth.cooldowns`, default to one retry with no delay, and document the model-fallback behavior.
|
||||
|
||||
## 2026.3.28
|
||||
## 2026.3.31-beta.1
|
||||
|
||||
### Breaking
|
||||
|
||||
- Providers/Qwen: remove the deprecated `qwen-portal-auth` OAuth integration for `portal.qwen.ai`; migrate to Model Studio with `openclaw onboard --auth-choice modelstudio-api-key`. (#52709) Thanks @pomelo-nwu.
|
||||
- Config/Doctor: drop automatic config migrations older than two months; very old legacy keys now fail validation instead of being rewritten on load or by `openclaw doctor`.
|
||||
- Nodes/exec: remove the duplicated `nodes.run` shell wrapper from the CLI and agent `nodes` tool so node shell execution always goes through `exec host=node`, keeping node-specific capabilities on `nodes invoke` and the dedicated media/location/notify actions.
|
||||
- Plugin SDK: deprecate the legacy provider compat subpaths plus the older bundled provider setup and channel-runtime compatibility shims, emit migration warnings, and keep the current documented `openclaw/plugin-sdk/*` entrypoints plus local `api.ts` / `runtime-api.ts` barrels as the forward path ahead of a future major-release removal.
|
||||
- Skills/install and Plugins/install: built-in dangerous-code `critical` findings and install-time scan failures now fail closed by default, so plugin installs and gateway-backed skill dependency installs that previously succeeded may now require an explicit dangerous override such as `--dangerously-force-unsafe-install` to proceed.
|
||||
- Gateway/auth: `trusted-proxy` now rejects mixed shared-token configs, and local-direct fallback requires the configured token instead of implicitly authenticating same-host callers. Thanks @zhangning-agent, @jacobtomlinson, and @vincentkoc.
|
||||
- Gateway/node commands: node commands now stay disabled until node pairing is approved, so device pairing alone is no longer enough to expose declared node commands. (#57777) Thanks @jacobtomlinson.
|
||||
- Gateway/node events: node-originated runs now stay on a reduced trusted surface, so notification-driven or node-triggered flows that previously relied on broader host/session tool access may need adjustment. (#57691) Thanks @jacobtomlinson.
|
||||
|
||||
### Changes
|
||||
|
||||
- ACP/plugins: add an explicit default-off ACPX plugin-tools MCP bridge config, document the trust boundary, and harden the built-in bridge packaging/logging path so global installs and stdio MCP sessions work reliably. (#56867) Thanks @joe2643.
|
||||
- Agents/LLM: add a configurable idle-stream timeout for embedded runner requests so stalled model streams abort cleanly instead of hanging until the broader run timeout fires. (#55072) Thanks @liuy.
|
||||
- Agents/MCP: materialize bundle MCP tools with provider-safe names (`serverName__toolName`), support optional `streamable-http` transport selection plus per-server connection timeouts, and preserve real tool results from aborted/error turns unless truncation explicitly drops them. (#49505) Thanks @ziomancer.
|
||||
- Android/notifications: add notification-forwarding controls with package filtering, quiet hours, rate limiting, and safer picker behavior for forwarded notification events. (#40175) Thanks @nimbleenigma.
|
||||
- Background tasks: turn tasks into a real shared background-run control plane instead of ACP-only bookkeeping by unifying ACP, subagent, cron, and background CLI execution under one SQLite-backed ledger, routing detached lifecycle updates through the executor seam, adding audit/maintenance/status visibility, tightening auto-cleanup and lost-run recovery, improving task awareness in internal status/tool surfaces, and clarifying the split between heartbeat/main-session automation and detached scheduled runs. Thanks @mbelinky and @vincentkoc.
|
||||
- Background tasks: add the first linear task flow control surface with `openclaw flows list|show|cancel`, keep manual multi-task flows separate from one-task auto-sync flows, and surface doctor recovery hints for obviously orphaned or broken flow/task linkage. Thanks @mbelinky and @vincentkoc.
|
||||
- Channels/QQ Bot: add QQ Bot as a bundled channel plugin with multi-account setup, SecretRef-aware credentials, slash commands, reminders, and media send/receive support. (#52986) Thanks @sliverp.
|
||||
- Diffs: skip unused viewer-versus-file SSR preload work so `diffs` view-only and file-only runs do less render work while keeping mode outputs aligned. (#57909) thanks @gumadeiras.
|
||||
- Tasks: add a minimal SQLite-backed task flow registry plus task-to-flow linkage scaffolding, so orchestrated work can start gaining a first-class parent record without changing current task delivery behavior. Thanks @mbelinky and @vincentkoc.
|
||||
- Tasks: persist blocked state on one-task task flows and let the same flow reopen cleanly on retry, so blocked detached work can carry a parent-level reason and continue without fragmenting into a new job. Thanks @mbelinky and @vincentkoc.
|
||||
- Tasks: route one-task ACP and subagent updates through a parent task-flow owner context, so detached work can emerge back through the intended parent thread/session instead of speaking only as a raw child task. Thanks @mbelinky and @vincentkoc.
|
||||
- LINE/outbound media: add LINE image, video, and audio outbound sends on the LINE-specific delivery path, including explicit preview/tracking handling for videos while keeping generic media sends on the existing image-only route. (#45826) Thanks @masatohoshino.
|
||||
- Matrix/history: add optional room history context for Matrix group triggers via `channels.matrix.historyLimit`, with per-agent watermarks and retry-safe snapshots so failed trigger retries do not drift into newer room messages. (#57022) thanks @chain710.
|
||||
- Matrix/network: add explicit `channels.matrix.proxy` config for routing Matrix traffic through an HTTP(S) proxy, including account-level overrides and matching probe/runtime behavior. (#56931) thanks @patrick-yingxi-pan.
|
||||
- Matrix/streaming: add draft streaming so partial Matrix replies update the same message in place instead of sending a new message for each chunk. (#56387) Thanks @jrusz.
|
||||
- Matrix/threads: add per-DM `threadReplies` overrides and keep thread session isolation aligned with the effective room or DM thread policy from the triggering message onward. (#57995) thanks @teconomix.
|
||||
- MCP: add remote HTTP/SSE server support for `mcp.servers` URL configs, including auth headers and safer config redaction for MCP credentials. (#50396) Thanks @dhananjai1729.
|
||||
- Memory/QMD: add per-agent `memorySearch.qmd.extraCollections` so agents can opt into cross-agent session search without flattening every transcript collection into one shared QMD namespace. Thanks @vincentkoc.
|
||||
- Microsoft Teams/member info: add a Graph-backed member info action so Teams automations and tools can resolve channel member details directly from Microsoft Graph. (#57528) Thanks @sudie-codes.
|
||||
- Nostr/inbound DMs: verify inbound event signatures before pairing or sender-authorization side effects, so forged DM events no longer create pairing requests or trigger reply attempts. Thanks @smaeljaish771 and @vincentkoc.
|
||||
- OpenAI/Responses: forward configured `text.verbosity` across Responses HTTP and WebSocket transports, surface it in `/status`, and keep per-agent verbosity precedence aligned with runtime behavior. (#47106) Thanks @merc1305 and @vincentkoc.
|
||||
- Pi/Codex: add native Codex web search support for embedded Pi runs, including config/docs/wizard coverage and managed-tool suppression when native Codex search is active. (#46579) Thanks @Evizero.
|
||||
- Slack/exec approvals: add native Slack approval routing and approver authorization so exec approval prompts can stay in Slack instead of falling back to the Web UI or terminal. Thanks @vincentkoc.
|
||||
- TTS: Add structured provider diagnostics and fallback attempt analytics. (#57954) Thanks @joshavant.
|
||||
- WhatsApp/reactions: agents can now react with emoji on incoming WhatsApp messages, enabling more natural conversational interactions like acknowledging a photo with ❤️ instead of typing a reply. Thanks @mcaxtr.
|
||||
- Agents/BTW: force `/btw` side questions to disable provider reasoning so Anthropic adaptive-thinking sessions stop failing with `No BTW response generated`. Fixes #55376. Thanks @Catteres and @vincentkoc.
|
||||
- CLI/onboarding: reset the remote gateway URL prompt to the safe loopback default after declining a discovered endpoint, so onboarding does not keep a previously rejected remote URL. (#57828)
|
||||
- Agents/exec defaults: honor per-agent `tools.exec` defaults when no inline directive or session override is present, so configured exec host, security, ask, and node settings actually apply. (#57689)
|
||||
- Sandbox/networking: sanitize SSH subprocess env vars through the shared sandbox policy and route marketplace archive downloads plus Ollama discovery, auth, and pull requests through the guarded fetch path so sandboxed execution and remote fetches follow the repo's trust boundaries. (#57848, #57850)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Agents/OpenAI Responses: normalize raw bundled MCP tool schemas on the WebSocket/Responses path so bare-object, object-ish, and top-level union MCP tools no longer get rejected by OpenAI during tool registration. (#58299) Thanks @yelog.
|
||||
- ACP/security: replace ACP's dangerous-tool name override with semantic approval classes, so only narrow readonly reads/searches can auto-approve while indirect exec-capable and control-plane tools always require explicit prompt approval. Thanks @vincentkoc.
|
||||
- ACP/sessions_spawn: register ACP child runs for completion tracking and lifecycle cleanup, and make registration-failure cleanup explicitly best-effort so callers do not assume an already-started ACP turn was fully aborted. (#40885) Thanks @xaeon2026 and @vincentkoc.
|
||||
- ACP/tasks: mark cleanly exited ACP runs as blocked when they end on deterministic write or authorization blockers, and wake the parent session with a follow-up instead of falsely reporting success.
|
||||
- ACPX/runtime: derive the bundled ACPX expected version from the extension package metadata instead of hardcoding a separate literal, so plugin-local ACPX installs stop drifting out of health-check parity after version bumps. (#49089) Thanks @jiejiesks and @vincentkoc.
|
||||
- Agents/Anthropic failover: treat Anthropic `api_error` payloads with `An unexpected error occurred while processing the response` as transient so retry/fallback can engage instead of surfacing a terminal failure. (#57441) Thanks @zijiess and @vincentkoc.
|
||||
- Agents/compaction: keep late compaction-retry rejections handled after the aggregate timeout path wins without swallowing real pre-timeout wait failures, so timed-out retries no longer surface an unhandled rejection on later unsubscribe. (#57451) Thanks @mpz4life and @vincentkoc.
|
||||
- Agents/context pruning: count supplementary-plane CJK characters with the shared code-point-aware estimator so context pruning stops underestimating Japanese and Chinese text that uses Extension B ideographs. (#39985) Thanks @Edward-Qiang-2024.
|
||||
- Agents/Kimi: preserve already-valid Anthropic-compatible tool call argument objects while still clearing cached repairs when later trailing junk exceeds the repair allowance. (#54491) Thanks @yuanaichi.
|
||||
- Agents/MCP: dispose bundled MCP runtimes after one-shot `openclaw agent --local` runs finish, while preserving bundled MCP state across in-run retries so local JSON runs exit cleanly without restarting stateful MCP tools mid-run.
|
||||
- Agents/MCP: reuse bundled MCP runtimes across turns in the same session, while recreating them when MCP config changes and disposing stale runtimes cleanly on session rollover. (#55090) Thanks @allan0509.
|
||||
- Agents/memory flush: keep daily memory flush files append-only during embedded attempts so compaction writes do not overwrite earlier notes. (#53725) Thanks @HPluseven.
|
||||
- Agents/sandbox: honor `tools.sandbox.tools.alsoAllow`, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman.
|
||||
- Agents/sandbox: make remote FS bridge reads pin the parent path and open the file atomically in the helper so read access cannot race path resolution. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Agents/silent turns: fail closed on silent memory-flush runs so narrated `NO_REPLY` self-talk cannot stream or finalize into external replies even when block streaming is enabled. (#52593)
|
||||
- Agents/subagents: fix interim subagent runtime display so `/subagents list` and `/subagents info` stop inflating short runtimes and show second-level durations correctly. (#57739) Thanks @samzong.
|
||||
- Anthropic/OAuth: inject `/fast` `service_tier` hints for direct `sk-ant-oat-*` requests so OAuth-authenticated Anthropic runs stop missing the same overload-routing signal as API-key traffic. Fixes #55758. Thanks @Cypherm and @vincentkoc.
|
||||
- Anthropic/service tiers: support explicit `serviceTier` model params for direct Anthropic requests and let them override `/fast` defaults when both are set. (#45453) Thanks @vincentkoc.
|
||||
- Auto-reply/fast: accept `/fast status` on the directive-only path, align help/status text with the documented `status|on|off` syntax, and keep current-state replies consistent across command surfaces. Fixes #46095. Thanks @weissfl and @vincentkoc.
|
||||
- Azure OpenAI/custom providers: use the `azure-openai-responses` path for Azure custom providers so Azure OpenAI endpoints stay on the correct Responses integration surface. (#50851) Thanks @kunalk16.
|
||||
- BlueBubbles/iMessage: coalesce URL-only inbound messages with their link-preview balloon again so sharing a bare link no longer drops the URL from agent context. Thanks @vincentkoc.
|
||||
- Browser/plugins: auto-enable the bundled browser plugin when browser config or browser tool policy already references it, and show a clearer CLI error when `plugins.allow` excludes `browser`.
|
||||
- CI/dev checks: default local `pnpm check` to a lower-memory typecheck/lint path while keeping CI on the normal parallel path, and harden Telegram test typing/literals around native TypeScript-Go tooling crashes.
|
||||
- Tasks: add a small task-flow runtime substrate for authoring layers with persisted wait targets and output bags, plus bundled skills/Lobster examples and richer `flows show` / `doctor` recovery hints for multi-task flow state. (#58336) Thanks @mbelinky and @vincentkoc.
|
||||
- Config/legacy cleanup: stop probing obsolete alternate legacy config names and service labels during local config/service detection, while keeping the active `~/.openclaw/openclaw.json` path canonical.
|
||||
- Config/runtime: pin the first successful config load in memory for the running process and refresh that snapshot on successful writes/reloads, so hot paths stop reparsing `openclaw.json` between watcher-driven swaps.
|
||||
- Config/SecretRef + Control UI: harden SecretRef redaction round-trip restore, block unsafe raw fallback (force Form mode when raw is unavailable), and preflight submitted-config SecretRefs before config write RPC persistence. (#58044) Thanks @joshavant.
|
||||
- Config/Telegram: migrate removed `channels.telegram.groupMentionsOnly` into `channels.telegram.groups["*"].requireMention` on load so legacy configs no longer crash at startup. (#55336) thanks @jameslcowan.
|
||||
- Config/update: stop `openclaw doctor` write-backs from persisting plugin-injected channel defaults, so `openclaw update` no longer seeds config keys that later break service refresh validation. (#56834) Thanks @openperf.
|
||||
- Control UI/agents: auto-load agent workspace files on initial Files panel open, and populate overview model/workspace/fallbacks from effective runtime agent metadata so defaulted models no longer show as `Not set`. (#56637) Thanks @dxsx84.
|
||||
- Control UI/slash commands: make `/steer` and `/redirect` work from the chat command palette with visible pending state for active-run `/steer`, correct redirected-run tracking, and a single canonical `/steer` entry in the command menu. (#54625) Thanks @fuller-stack-dev.
|
||||
- Cron/announce: preserve all deliverable text payloads for announce mode instead of collapsing to the last chunk, so multi-line cron reports deliver in full to Telegram forum topics.
|
||||
- Cron/isolated sessions: carry the full live-session provider, model, and auth-profile selection across retry restarts so cron jobs with model overrides no longer fail or loop on mid-run model-switch requests. (#57972) Thanks @issaba1.
|
||||
- Diffs/config: preserve schema-shaped plugin config parsing from `diffsPluginConfigSchema.safeParse()`, so direct callers keep `defaults` and `security` sections instead of receiving flattened tool defaults. (#57904) Thanks @gumadeiras.
|
||||
- Diffs: fall back to plain text when `lang` hints are invalid during diff render and viewer hydration, so bad or stale language values no longer break the diff viewer. (#57902) Thanks @gumadeiras.
|
||||
- Discord/voice: enforce the same guild channel and member allowlist checks on spoken voice ingress before transcription, so joined voice channels no longer accept speech from users outside the configured Discord access policy. Thanks @cyjhhh and @vincentkoc.
|
||||
- Docker/setup: force BuildKit for local image builds (including sandbox image builds) so `./docker-setup.sh` no longer fails on `RUN --mount=...` when hosts default to Docker's legacy builder. (#56681) Thanks @zhanghui-china.
|
||||
- Docs/anchors: fix broken English docs links and make Mint anchor audits run against the English-source docs tree. (#57039) thanks @velvet-shark.
|
||||
- Doctor/plugins: skip false Matrix legacy-helper warnings when no migration plans exist, and keep bundled `enabledByDefault` plugins in the gateway startup set. (#57931) Thanks @dinakars777.
|
||||
- Exec approvals/macOS: unwrap `arch` and `xcrun` before deriving shell payloads and allow-always patterns, so wrapper approvals stay bound to the carried command instead of the outer carrier. Thanks @tdjackey and @vincentkoc.
|
||||
- Exec approvals: unwrap `caffeinate` and `sandbox-exec` before persisting allow-always trust so later shell payload changes still require a fresh approval. Thanks @tdjackey and @vincentkoc.
|
||||
- Exec/approvals: infer Discord and Telegram exec approvers from existing owner config when `execApprovals.approvers` is unset, extend the default approval window to 30 minutes, and clarify approval-unavailable guidance so approvals do not appear to silently disappear.
|
||||
- Exec/approvals: keep `awk` and `sed` family binaries out of the low-risk `safeBins` fast path, and stop doctor profile scaffolding from treating them like ordinary custom filters. Thanks @vincentkoc.
|
||||
- Exec/env: block proxy, TLS, and Docker endpoint env overrides in host execution so request-scoped commands cannot silently reroute outbound traffic or trust attacker-supplied certificate settings. Thanks @AntAISecurityLab.
|
||||
- Exec/env: block Python package index override variables from request-scoped host exec environment sanitization so package fetches cannot be redirected through a caller-supplied index. Thanks @nexrin and @vincentkoc.
|
||||
- Exec/node: stop gateway-side workdir fallback from rewriting explicit `host=node` cwd values to the gateway filesystem, so remote node exec approval and runs keep using the intended node-local directory. (#50961) Thanks @openperf.
|
||||
- Exec/runtime: default implicit exec to `host=auto`, resolve that target to sandbox only when a sandbox runtime exists, keep explicit `host=sandbox` fail-closed without sandbox, and show `/exec` effective host state in runtime status/docs.
|
||||
- Exec: fail closed when the implicit sandbox host has no sandbox runtime, and stop denied async approval followups from reusing prior command output from the same session. (#56800) Thanks @scoootscooob.
|
||||
- Feishu/groups: keep quoted replies and topic bootstrap context aligned with group sender allowlists so only allowlisted thread messages seed agent context. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Gateway/attachments: offload large inbound images without leaking `media://` markers into text-only runs, preserve mixed attachment order for model input/transcripts, and fail closed when model image capability cannot be resolved. (#55513) Thanks @Syysean.
|
||||
- Gateway/auth: keep shared-auth rate limiting active during WebSocket handshake attempts even when callers also send device-token candidates, so bogus device-token fields no longer suppress shared-secret brute-force tracking. Thanks @kexinoh and @vincentkoc.
|
||||
- Gateway/auth: reject mismatched browser `Origin` headers on trusted-proxy HTTP operator requests while keeping origin-less headless proxy clients working. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Gateway/device tokens: disconnect active device sessions after token rotation so newly rotated credentials revoke existing live connections immediately instead of waiting for those sockets to close naturally. Thanks @zsxsoft and @vincentkoc.
|
||||
- Gateway/health: carry webhook-vs-polling account mode from channel descriptors into runtime snapshots so passive channels like LINE and BlueBubbles skip false stale-socket health failures. (#47488) Thanks @karesansui-u.
|
||||
- Gateway/pairing: restore QR bootstrap onboarding handoff so fresh `/pair qr` iPhone setup can auto-approve the initial node pairing, receive a reusable node device token, and stop retrying with spent bootstrap auth. (#58382) Thanks @ngutman.
|
||||
- Gateway/OpenAI compatibility: accept flat Responses API function tool definitions on `/v1/responses` and preserve `strict` when normalizing hosted tools into the embedded runner, so spec-compliant clients like Codex no longer fail validation or silently lose strict tool enforcement. Thanks @malaiwah and @vincentkoc.
|
||||
- Gateway/OpenAI HTTP: restore default operator scopes for bearer-authenticated requests that omit `x-openclaw-scopes`, so headless `/v1/chat/completions` and session-history callers work again after the recent method-scope hardening. (#57596) Thanks @openperf.
|
||||
- Gateway/plugins: scope plugin-auth HTTP route runtime clients to read-only access and keep gateway-authenticated plugin routes on write scope, so plugin-owned webhook handlers do not inherit write-capable runtime access by default. Thanks @davidluzsilva and @vincentkoc.
|
||||
- Gateway/SecretRef: resolve restart token drift checks with merged service/runtime env sources and hard-fail unsupported mutable SecretRef plus OAuth-profile combinations so restart warnings and policy enforcement match runtime behavior. (#58141) Thanks @joshavant.
|
||||
- Gateway/tools HTTP: tighten HTTP tool-invoke authorization so owner-only tools stay off HTTP invoke paths. (#57773) Thanks @jacobtomlinson.
|
||||
- Harden async approval followup delivery in webchat-only sessions (#57359) Thanks @joshavant.
|
||||
- Heartbeat/auth: prevent exec-event heartbeat runs from inheriting owner-only tool access from the session delivery target, so node exec output stays on the non-owner tool surface even when the target session belongs to the owner. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
|
||||
- Hooks/session routing: rebind hook-triggered `agent:` session keys to the actual target agent before isolated dispatch so dedicated hook agents keep their own session-scoped tool and plugin identity. Thanks @kexinoh and @vincentkoc.
|
||||
- Host exec/env: block additional request-scoped env overrides that can redirect Docker endpoints, trust roots, compiler include paths, package resolution, or Python environment roots during approved host runs. Thanks @tdjackey and @vincentkoc.
|
||||
- Image generation/build: write stable runtime alias files into `dist/` and route provider-auth runtime lookups through those aliases so image-generation providers keep resolving auth/runtime modules after rebuilds instead of crashing on missing hashed chunk files.
|
||||
- iOS/Live Activities: mark the `ActivityKit` import in `LiveActivityManager.swift` as `@preconcurrency` so Xcode 26.4 / Swift 6 builds stop failing on strict concurrency checks. (#57180) Thanks @ngutman.
|
||||
- LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn ... --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels.
|
||||
- LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997.
|
||||
- LINE/status: stop `openclaw status` from warning about missing credentials when sanitized LINE snapshots are already configured, while still surfacing whether the missing field is the token or secret. (#45701) Thanks @tamaosamu.
|
||||
- macOS/local gateway: stop OpenClaw.app from killing healthy local gateway listeners after startup by recognizing the current `openclaw-gateway` process title and using the current `openclaw gateway` launch shape.
|
||||
- macOS/wide-area discovery: switch gateway discovery to Tailscale MagicDNS names so Mac clients recover more reliably across changing tailnet IPs. (#57833) Thanks @jacobtomlinson.
|
||||
- Matrix/CLI send: start one-off Matrix send clients before outbound delivery so `openclaw message send --channel matrix` restores E2EE in encrypted rooms instead of sending plain events. (#57936) Thanks @gumadeiras.
|
||||
- Matrix/context: filter fetched room context by sender allowlists so reply and thread context lookup no longer pulls non-allowlisted messages into agent context. (#58376) Thanks @jacobtomlinson.
|
||||
- Matrix/delivery recovery: treat Synapse `User not in room` replay failures as permanent during startup recovery so poisoned queued messages move to `failed/` instead of crash-looping Matrix after restart. (#57426) thanks @dlardo.
|
||||
- Matrix/direct rooms: recover fresh auto-joined 1:1 DMs without eagerly persisting invite-only `m.direct` mappings, while keeping named, aliased, and explicitly configured rooms on the room path. (#58024) Thanks @gumadeiras.
|
||||
- Matrix/direct rooms: stop trusting remote `is_direct`, honor explicit local `is_direct: false` for discovered DM candidates, and avoid extra member-state lookups for shared rooms so DM routing and repair stay aligned. (#57124) Thanks @w-sss.
|
||||
- Matrix/DM threads: keep strict unnamed fresh-invite rooms promotable even when Matrix omits the optional direct hint, preserve repair-failed local DM promotions while still revalidating later room metadata, and keep both bound and thread-isolated Matrix sessions reporting the correct route policy. (#58099) Thanks @gumadeiras.
|
||||
- Matrix/plugin loading: ship and source-load the crypto bootstrap runtime sidecar correctly so current `main` stops warning about failed Matrix bootstrap loads and `matrix/index` plugin-id mismatches on every invocation. (#53298) thanks @keithce.
|
||||
- Mattermost/websocket: detect stale Mattermost WebSocket sessions after bot disable/enable cycles so monitoring reconnects cleanly instead of silently staying stale. (#53604) Thanks @Qinsam.
|
||||
- Media/downloads: stop forwarding auth and cookie headers across cross-origin redirects during media saves, while preserving safe request headers for same-origin redirect chains. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Media/images: reject oversized decoded image inputs before metadata and resize backends run, so tiny compressed image bombs fail early instead of exhausting gateway memory. (#58226) Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Memory/doctor: probe QMD availability from the agent workspace too, so `openclaw doctor` no longer falsely reports relative `memory.qmd.command` configs as broken while runtime search still works. Thanks @vincentkoc.
|
||||
- Memory/doctor: suppress the orphan transcript cleanup warning when QMD session indexing is enabled, so doctor no longer suggests deleting transcript history that QMD still uses for recall. (#40584) Thanks @Gyarados4157 and @vincentkoc.
|
||||
- Memory/FTS: add configurable trigram tokenization plus short-CJK substring fallback so memory search can find Chinese, Japanese, and Korean text without breaking mixed long-and-short queries. Thanks @carrotRakko.
|
||||
- Memory/FTS: keep provider-less keyword hits visible at the default memory-search threshold, so FTS-only recall works without requiring `--min-score 0`. (#56473) Thanks @opriz.
|
||||
- Memory/LanceDB: resolve runtime dependency manifest lookup from the bundled `extensions/memory-lancedb` path (including flattened dist chunks) so startup no longer fails with a missing `@lancedb/lancedb` dependency error. (#56623) Thanks @LUKSOAgent.
|
||||
- Memory/QMD: add `memory.qmd.searchTool` as an exact mcporter tool override, so custom QMD MCP tools such as `hybrid_search` can be used without weakening the validated `searchMode` config surface. (#27801) Thanks @keramblock.
|
||||
- Memory/QMD: honor `memory.qmd.update.embedInterval` even when regular QMD update cadence is disabled or slower by arming a dedicated embed-cadence maintenance timer, while avoiding redundant timers when regular updates are already frequent enough. (#37326) Thanks @barronlroth.
|
||||
- Memory/QMD: include deduplicated default plus per-agent `memorySearch.extraPaths` when building QMD custom collections, so shared and agent-specific extra roots both get indexed consistently. (#57315) Thanks @Vitalcheffe and @vincentkoc.
|
||||
- Memory/QMD: keep `memory_search` session-hit paths roundtrip-safe when exported session markdown lives under the workspace `qmd/` directory, so `memory_get` can read the exact returned path instead of failing on the generic `qmd/sessions/...` alias. (#43519) Thanks @holgergruenhagen and @vincentkoc.
|
||||
- Memory/QMD: keep `qmd embed` active in `search` mode too, so BM25-first setups still build a complete index for later vector and hybrid retrieval. (#54509) Thanks @hnshah and @vincentkoc.
|
||||
- Memory/QMD: keep reset and deleted session transcripts in QMD session export so daily session resets do not silently drop most historical recall from `memory_search`. (#30220) Thanks @pushkarsingh32.
|
||||
- Memory/QMD: pass a direct-session key on `openclaw memory search` so CLI QMD searches no longer get denied as `session=<none>` under direct-only scope defaults. (#43517) Thanks @waynecc-at and @vincentkoc.
|
||||
- Memory/QMD: point `QMD_CONFIG_DIR` at the nested `xdg-config/qmd` directory so per-agent collection config resolves correctly. (#39078) Thanks @smart-tinker and @vincentkoc.
|
||||
- Memory/QMD: preserve explicit `start_line` and `end_line` metadata from mcporter query results so `memory search` hits keep the real snippet offsets instead of falling back to the snippet header. (#47960) Thanks @vincentkoc.
|
||||
- Memory/QMD: rebind collections when QMD reports a changed pattern but omits path metadata, so config pattern changes stop being silently ignored on restart. (#49897) Thanks @Madruru.
|
||||
- Memory/QMD: resolve slugified `memory_search` file hints back to the indexed filesystem path before returning search hits, so `memory_get` works again for mixed-case and spaced paths. (#50313) Thanks @erra9x.
|
||||
- Memory/QMD: send MCP `query` collection filters as the upstream `collections` array instead of the legacy singular `collection` field, so mcporter-backed QMD 1.1+ searches still scope correctly after the unified `query` tool migration. (#54728) Thanks @armanddp and @vincentkoc.
|
||||
- Memory/QMD: serialize cross-process `qmd embed` runs behind a shared lock and stagger periodic embed timers so multi-agent QMD collections stop thundering-herding on startup and every maintenance interval. Thanks @vincentkoc.
|
||||
- Memory/QMD: stop rewriting Han/CJK BM25 queries before `qmd search`, so OpenClaw search semantics match direct QMD results for mixed and spaced Chinese queries. Thanks @vincentkoc.
|
||||
- Memory/QMD: surface degraded vector status from `qmd status` so `openclaw memory status --deep` warns when semantic search is unavailable because the index still has `0` vectors. Fixes #28169. Thanks @vincentkoc.
|
||||
- Memory/QMD: treat null-byte collection corruption the same when QMD surfaces it as `ENOENT`, so managed-collection repair still rebuilds and retries instead of leaving QMD stuck on a broken path. Thanks @vincentkoc.
|
||||
- Memory/QMD: warn explicitly when `memory.backend=qmd` is configured but the `qmd` binary is missing, so doctor and runtime fallback no longer fail as a silent builtin downgrade. (#50439) Thanks @Jimmy-xuzimo and @vincentkoc.
|
||||
- Memory/QMD: weight CJK-heavy text correctly when estimating chunk sizes, preserve surrogate-pair characters during fine splits, and keep long Latin lines on the old chunk boundaries so memory indexing produces better-sized chunks for CJK notes. (#40271) Thanks @AaronLuo00.
|
||||
- Memory/session indexer: include `.jsonl.reset.*` and `.jsonl.deleted.*` transcripts in the memory host session scan while still excluding `.jsonl.bak.*` compaction backups and lock files, so memory search sees archived session history without duplicating stale snapshots. Thanks @hclsys and @vincentkoc.
|
||||
- Microsoft Teams/threads: filter fetched thread history by sender allowlists so thread context seeding no longer pulls messages from disallowed users. (#57723) Thanks @jacobtomlinson.
|
||||
- OpenAI/Codex fast mode: map `/fast` to priority processing on native OpenAI and Codex Responses endpoints instead of rewriting reasoning settings, and document the exact endpoint and override behavior.
|
||||
- Outbound media/local files: piggyback host-local `MEDIA:` reads on the configured fs policy instead of a separate media-root check, so generated files outside the workspace can send when `tools.fs.workspaceOnly=false` while plaintext-like host files stay blocked by the outbound media allowlist.
|
||||
- Pairing: enforce pending request limits per account instead of per shared channel queue, so one account's outstanding pairing challenges no longer block new pairing on other accounts. Thanks @smaeljaish771 and @vincentkoc.
|
||||
- Plugins/ClawHub: sanitize temporary archive filenames for scoped package names and slash-containing skill slugs so `openclaw plugins install @scope/name` no longer fails with `ENOENT` during archive download. (#56452) Thanks @soimy.
|
||||
- Plugins/CLI: add descriptor-backed lazy plugin CLI registration so Matrix can keep its CLI module lazy-loaded without dropping `openclaw matrix ...` from parse-time command registration. (#57165) Thanks @gumadeiras.
|
||||
- Plugins/CLI: collect root-help plugin descriptors through a dedicated non-activating CLI metadata path so enabled plugins keep validated config semantics without triggering runtime-only plugin registration work, while preserving runtime CLI command registration for legacy channel plugins that still wire commands from full registration. (#57294) thanks @gumadeiras.
|
||||
- Plugins/facades: guard bundled plugin facade loads with a cache-first sentinel so circular re-entry stops crashing `xai`, `sglang`, and `vllm` during gateway plugin startup. (#57508) Thanks @openperf.
|
||||
- Plugins/Matrix: mirror the Matrix crypto WASM runtime dependency into the root packaged install and enforce root/plugin dependency parity so bundled Matrix E2EE crypto resolves correctly in shipped builds. (#57163) Thanks @gumadeiras.
|
||||
- Plugins/startup: block workspace `.env` from overriding `OPENCLAW_BUNDLED_PLUGINS_DIR`, so bundled plugin trust roots only come from inherited runtime env or package resolution instead of repo-local dotenv files. Thanks @nexrin and @vincentkoc.
|
||||
- Sandbox/browser: install `fonts-noto-cjk` in the sandbox browser image so screenshots render Chinese, Japanese, and Korean text correctly instead of tofu boxes. Fixes #35597. Thanks @carrotRakko and @vincentkoc.
|
||||
- Security/LINE: make webhook signature validation run the timing-safe compare even when the supplied signature length is wrong, closing a small timing side-channel. (#55663) Thanks @gavyngong.
|
||||
- Sessions/Feishu: preserve conversation ids that legitimately embed `:topic:` in shared session helper parsing, while keeping Telegram topic session parsing intact. (#58100) Thanks @gumadeiras.
|
||||
- Slack/status reactions: add a reaction lifecycle for queued, thinking, tool, done, and error phases in Slack monitors, with safer cleanup so queued ack reactions stay correct across silent runs, pre-reply failures, and delayed transitions. (#56430) Thanks @hsiaoa.
|
||||
- Status/node-only hosts: teach `openclaw status` to handle node-only hosts on current `main` without the old mixed gateway assumptions. (#56718) Thanks @ImLukeF.
|
||||
- Status: fix cache hit rate exceeding 100% by deriving denominator from prompt-side token fields instead of potentially undersized totalTokens. Fixes #26643.
|
||||
- Telegram/audio: transcode Telegram voice-note `.ogg` attachments before the local `whisper-cli` auto fallback runs, and keep mention-preflight transcription enabled in auto mode when `tools.media.audio` is unset.
|
||||
- Telegram/forum topics: restore reply routing to the active topic and keep ACP `sessions_spawn(..., thread=true, mode="session")` bound to that same topic instead of falling back to root chat or losing follow-up routing. (#56060) Thanks @one27001.
|
||||
- Telegram/media: allow RFC 2544 benchmark-range Telegram CDN resolutions during media downloads, so voice messages, PDFs, and other attachments no longer fail with `Failed to download media`. (#57624) Thanks @MoerAI.
|
||||
- Telegram/native commands: prefix native command menu callback payloads and preserve `CommandSource: "native"` when Telegram replays them through callback queries, so `/fast` and other native command menus keep working even when text-command routing is disabled. Thanks @vincentkoc.
|
||||
- Telegram/polling: keep the watchdog from aborting long-running reply delivery by treating recent non-polling API activity as bounded liveness instead of a hard stall. (#56343) Thanks @openperf.
|
||||
- Tools/web_fetch: add an explicit trusted env-proxy path for proxy-only installs while keeping strict SSRF fetches on the pinned direct path, so trusted proxy routing does not weaken strict destination binding. (#50650) Thanks @kkav004.
|
||||
- Tools/web_search: localize the shared search cache to module scope so same-process global symbol lookups can no longer inspect or mutate cached web-search responses. Thanks @vincentkoc.
|
||||
- TTS/Microsoft: auto-switch the default Edge voice to Chinese for CJK-dominant text without overriding explicitly selected Microsoft voices. (#52355) Thanks @extrasmall0.
|
||||
- TTS: Restore 3.28 schema compatibility and fallback observability. (#57953) Thanks @joshavant.
|
||||
- TUI/chat: keep optimistic outbound user messages visible during active runs by deferring local-run binding until the first gateway chat event reveals the real run id, preventing premature history reloads from wiping pending local sends. (#54722) Thanks @seanturner001.
|
||||
- TUI/model picker: keep searchable `/model` and `/models` input mode from hijacking `j`/`k` as navigation keys, and harden width bounds under `m`-filtered model lists so search no longer crashes on long rows. (#30156) Thanks @briannicholls.
|
||||
- Voice Call/media stream: cap inbound WebSocket frame size before `start` validation so oversized pre-start frames are dropped before JSON parsing. Thanks @Kazamayc and @vincentkoc.
|
||||
- Voice call/Plivo: pin stored callback bases to the configured public webhook URL so later call-control redirects stay on the intended origin even if webhook transport metadata differs. Thanks @zsxsoft and @vincentkoc.
|
||||
- Web UI/markdown: stop bare auto-links from swallowing adjacent CJK text while preserving valid mixed-script path and query characters in rendered links. (#48410) Thanks @jnuyao.
|
||||
- Approvals/UI: keep the newest pending approval at the front of the Control UI queue so approving one request does not accidentally target an older expired id. Thanks @vincentkoc.
|
||||
- Auth profiles/OAuth: refresh runtime auth snapshots when saving rotated credentials so OAuth providers do not reuse consumed refresh tokens after the first token rotation. Fixes #55389. Thanks @sam26880 and @vincentkoc.
|
||||
- Browser/screenshot: use `fromSurface: false` in raw CDP screenshots to avoid a Chromium compositor bug that drops cross-origin image textures (QR codes, CDN assets), and preserve pre-existing device emulation state across full-page viewport expansion. (#54358) Thanks @FMLS.
|
||||
- ClawDock/docs: move the helper scripts to `scripts/clawdock`, publish ClawDock as a first-class docs page on the docs site, and document reinstalling local helper copies from the new raw GitHub path. (#23912) thanks @Olshansk.
|
||||
- Control UI/gateway: clear queued browser connect timeouts on client stop so aborted or replaced gateway clients do not send delayed connect requests after shutdown. (#57338) thanks @gumadeiras.
|
||||
- Control UI/gateway: reconnect the browser client when gateway event sequence gaps are detected, so stale non-chat state recovers automatically instead of only telling the user to refresh. (#23912) thanks @Olshansk.
|
||||
- Exec approvals/channels: unify Discord and Telegram exec approval runtime handling, move approval buttons onto the shared interactive reply model, and fix Telegram approval buttons and typed `/approve` commands so configured approvers can resolve requests reliably again. (#57516) Thanks @scoootscooob.
|
||||
- Gateway/SQLite transient handling: keep unhandled `SQLITE_CANTOPEN`, `SQLITE_BUSY`, `SQLITE_LOCKED`, and `SQLITE_IOERR` failures non-fatal in the global rejection handler so macOS LaunchAgent restarts do not enter a crash-throttle loop. (#57018)
|
||||
- Hooks/plugins/skills: block workspace `.env` overrides for bundled root directories so workspace startup cannot redirect bundled trust roots away from the packaged defaults. Thanks @nexrin and @vincentkoc.
|
||||
- LINE/webhooks: cap shared concurrent pre-verify webhook body reads so excess requests are rejected before entering the LINE body handler. Thanks @nexrin and @vincentkoc.
|
||||
- Memory/QMD: preserve explicit custom collection names for shared paths outside the agent workspace so `memory_search` stops appending `-<agentId>` to externally managed QMD collections. (#52539) Thanks @lobsrice and @vincentkoc.
|
||||
- Memory/builtin: keep memory-file indexing active in FTS-only mode (no embedding provider) so forced reindexes no longer swap in an empty index and wipe existing memory chunks. (#42714) Thanks @asamimei.
|
||||
- Nostr/config: redact `channels.nostr.privateKey` in config snapshots and Control UI config views, so Nostr signing keys no longer appear in plain text. Thanks @ccreater222.
|
||||
- Plugin approvals: accept unique short approval-id prefixes on `plugin.approval.resolve`, matching exec approvals and restoring `/approve` fallback flows on chat approval surfaces. Thanks @vincentkoc.
|
||||
- SSH sandbox/upload: reject workspace symlinks that resolve outside the uploaded tree before syncing to the remote sandbox, so later agent writes cannot be redirected through escaped links. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Slack/interactive replies: resolve Slack Block Kit reply delivery from both authored `channelData.slack.blocks` payloads and directive-generated interactive replies, and keep Slack button styles mapped onto valid Block Kit button rendering so interactive replies stop dropping on Slack-specific delivery paths. Thanks @vincentkoc.
|
||||
- Subagents/announcements: preserve the requester agent id for inline deterministic tool spawns so named agents without channel bindings can still announce completions through the correct owner session. (#55437) Thanks @kAIborg24.
|
||||
- Telegram/Anthropic streaming: replace raw invalid stream-order provider errors with a safe retry message so internal `message_start/message_stop` failures do not leak into chats. (#55408) Thanks @imydal.
|
||||
- Tlon/media: route inbound image downloads through the shared media store, cap each download at 6 MB, and stop after 8 images per message so large Tlon posts no longer balloon local media storage. Thanks @AntAISecurityLab and @vincentkoc.
|
||||
- Agents/live switch: stop transient cron and subagent model overrides from being misread as persisted live-session switches, so isolated runs no longer fail with `LiveSessionModelSwitchError`. Thanks @vincentkoc.
|
||||
- Agents/tool-call repair: recover malformed Kimi/OpenRouter tool-call argument streams when provider preambles appear before JSON payloads, and fail closed on non-tool leading text so fragment strings do not leak into filesystem path arguments during sub-agent runs. (#56560) Thanks @Originalwhite.
|
||||
- Gateway/startup: keep configured primary-model warmup on the static model path so container boots do not snapshot-load provider runtime graphs just to validate a configured model. Thanks @vincentkoc.
|
||||
- OpenAI/Responses: omit disabled reasoning payloads across OpenAI, Codex, and Azure OpenAI request builders so GPT-5 endpoints no longer reject `reasoning.effort: "none"`. (#58208) Thanks @jalehman.
|
||||
- WhatsApp/outbound: restore runtime send/action routing and outbound compatibility after the recent channel seam refactor so outbound sends, reactions, and related media actions keep reaching the active session.
|
||||
- xAI/Responses: normalize image-bearing tool results for xAI responses payloads, including OpenResponses-style `input_image.source` parts, so image tool replays no longer 422 on the follow-up turn. (#58017) Thanks @neeravmakwana.
|
||||
- Zalo/webhooks: scope replay dedupe to the authenticated target so one configured account can no longer cause same-id inbound events for another target to be dropped. Thanks @smaeljaish771 and @vincentkoc.
|
||||
- Plugins/prompt build: preserve the highest-priority `systemPrompt` when merging `before_prompt_build` hook results instead of letting lower-priority hooks overwrite it. (#58375)
|
||||
- Tlon/settings migration: preserve explicit empty-array settings during migration so restart-time reseeding no longer restores older file-config values. (#58370)
|
||||
- Plugins/marketplace: harden marketplace archive installs by surfacing guarded download failures as structured install errors and deriving temp filenames from the final resolved URL. (#58267)
|
||||
- UI/DOM safety: build the chat delete-confirm popover and Canvas Host fallback status line with DOM nodes instead of injected HTML strings. (#58269, #58266)
|
||||
- Media/file handling: create image temp work directories under the shared OpenClaw temp root and stop widening allowed local media roots from reply or tool source paths, keeping local media access limited to configured and agent-scoped roots. (#58270, #57770)
|
||||
- Telegram/security: migrate legacy pairing `allowFrom` state to the `default` account only and gate group voice-message preflight transcription on sender authorization so unauthorized senders cannot trigger paid transcription before being dropped. (#58165, #57566)
|
||||
- Exec approvals: stop shell init-file flags from matching persisted script-path approvals, detect command-carrier inline eval in tools like `awk`, `find`, `xargs`, `make`, and `sed`, and treat the awk family as interpreter-like so allow-always decisions no longer persist interpreter paths. (#58369, #57842, #57772)
|
||||
- Voice call/Telnyx: canonicalize verified webhook signatures before deriving replay keys so equivalent Base64 and Base64URL encodings dedupe correctly. (#57829)
|
||||
- Gateway/HTTP trust boundaries: ignore self-declared bearer scopes, deny dangerous HTTP tool-invoke paths by default, require write scope on `/v1/embeddings`, and bind OpenAI-compatible `/v1/chat/completions` plus `/v1/responses` ingress as non-owner so external HTTP callers cannot self-upgrade access. (#57783, #57771, #57721, #57769, #57778)
|
||||
- Gateway/trusted config and bootstrap: clear self-declared scopes for device-less `trusted-proxy` WebSocket connects and trim the Control UI bootstrap payload to only the fields needed before the normal handshake. (#57692, #57727)
|
||||
- Skills and workspace safety: replace raw skill-file reads with a symlink-safe, root-confined loader and pin workspace-only `apply_patch` delete and directory-creation mutations to verified workspace roots so path rebind races fail closed. (#57519, #56016)
|
||||
- Env and filesystem hardening: block credential and gateway auth env vars from workspace `.env` files, always strip dangerous inherited host env vars such as `BROWSER` and `GIT_EDITOR`, and keep sensitive host paths and OpenClaw state roots blocked even when external sandbox bind sources are allowed. (#57767, #57559, #56024)
|
||||
- OpenShell and ACP file boundaries: skip symlinks and preserve trusted host-only directories during OpenShell mirror sync, and route ACP attachment reads through the shared attachment cache so local files outside allowed roots are no longer forwarded. (#57693, #57690)
|
||||
- Channel/webhook authorization: skip Discord audio preflight transcription for unauthorized guild senders, add a shared pre-auth in-flight guard to Synology Chat webhooks, and validate Microsoft Teams bearer auth before JSON body parsing. (#57695, #57722, #57686)
|
||||
- Infra/randomness: replace `Math.random()` in affected identifier and delay-jitter paths with shared secure-random helpers, including UI UUID generation. (#57744)
|
||||
|
||||
## 2026.3.28
|
||||
|
||||
### Changes
|
||||
|
||||
@@ -128,12 +352,11 @@ Docs: https://docs.openclaw.ai
|
||||
- Memory/plugins: move the pre-compaction memory flush plan behind the active memory plugin contract so `memory-core` owns flush prompts and target-path policy instead of hardcoded core logic.
|
||||
- MiniMax: trim model catalog to M2.7 only, removing legacy M2, M2.1, M2.5, and VL-01 models. (#54487) Thanks @liyuan97.
|
||||
- Plugins/runtime: expose `runHeartbeatOnce` in the plugin runtime `system` namespace so plugins can trigger a single heartbeat cycle with an explicit delivery target override (e.g. `heartbeat: { target: "last" }`). (#40299) Thanks @loveyana.
|
||||
- Background tasks: keep durable lifecycle records for ACP/subagent spawned work and deliver ACP completion/failure updates through the real requester chat path instead of session-only stream events.
|
||||
- Background tasks: keep durable lifecycle records for ACP/subagent spawned work and deliver ACP completion/failure updates through the real requester chat path instead of session-only stream events. Thanks @mbelinky and @vincentkoc.
|
||||
- Agents/compaction: preserve the post-compaction AGENTS refresh on stale-usage preflight compaction for both immediate replies and queued followups. (#49479) Thanks @jared596.
|
||||
- Agents/compaction: surface safeguard-specific cancel reasons and relabel benign manual `/compact` no-op cases as skipped instead of failed. (#51072) Thanks @afurm.
|
||||
- Docs: add `pnpm docs:check-links:anchors` for Mintlify anchor validation while keeping `scripts/docs-link-audit.mjs` as the stable link-audit entrypoint. (#55912) Thanks @velvet-shark.
|
||||
- Tavily: mark outbound API requests with `X-Client-Source: openclaw` so Tavily can attribute OpenClaw-originated traffic. (#55335) Thanks @lakshyaag-tavily.
|
||||
- Matrix/streaming: add `streaming: "partial"` draft replies that stay on a single editable preview message, stop preview streaming once text no longer fits one Matrix event, and clear stale previews before media-only finals. (#56387) thanks @jrusz.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -145,8 +368,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Telegram/splitting: replace proportional text estimate with verified HTML-length search so long messages split at word boundaries instead of mid-word; gracefully degrade when tag overhead exceeds the limit. (#56595)
|
||||
- Telegram/delivery: skip whitespace-only and hook-blanked text replies in bot delivery to prevent GrammyError 400 empty-text crashes. (#56620)
|
||||
- Telegram/send: validate `replyToMessageId` at all four API sinks with a shared normalizer that rejects non-numeric, NaN, and mixed-content strings. (#56587)
|
||||
- Approvals/UI: keep the newest pending approval at the front of the Control UI queue so approving one request does not accidentally target an older expired id. Thanks @vincentkoc.
|
||||
- Plugin approvals: accept unique short approval-id prefixes on `plugin.approval.resolve`, matching exec approvals and restoring `/approve` fallback flows on chat approval surfaces. Thanks @vincentkoc.
|
||||
- Mistral: normalize OpenAI-compatible request flags so official Mistral API runs no longer fail with remaining `422 status code (no body)` chat errors.
|
||||
- Control UI/config: keep sensitive raw config hidden by default, replace the blank blocked editor with an explicit reveal-to-edit state, and restore raw JSON editing without auto-exposing secrets. Fixes #55322.
|
||||
- CLI/zsh: defer `compdef` registration until `compinit` is available so zsh completion loads cleanly with plugin managers and manual setups. (#56555)
|
||||
@@ -162,8 +383,6 @@ Docs: https://docs.openclaw.ai
|
||||
- iMessage: stop leaking inline `[[reply_to:...]]` tags into delivered text by sending `reply_to` as RPC metadata and stripping stray directive tags from outbound messages. (#39512) Thanks @mvanhorn.
|
||||
- CLI/plugins: make routed commands use the same auto-enabled bundled-channel snapshot as gateway startup, so configured bundled channels like Slack load without requiring a prior config rewrite. (#54809) Thanks @neeravmakwana.
|
||||
- CLI/message send: write manual `openclaw message send` deliveries into the resolved agent session transcript again by always threading the default CLI agent through outbound mirroring. (#54187) Thanks @KevInTheCloud5617.
|
||||
- Agents/live switch: stop transient cron and subagent model overrides from being misread as persisted live-session switches, so isolated runs no longer fail with `LiveSessionModelSwitchError`. Thanks @vincentkoc.
|
||||
- Gateway/startup: keep configured primary-model warmup on the static model path so container boots do not snapshot-load provider runtime graphs just to validate a configured model. Thanks @vincentkoc.
|
||||
- CLI/onboarding: show the Kimi Code API key option again in the Moonshot setup menu so the interactive picker includes all Kimi setup paths together. Fixes #54412 Thanks @sparkyrider
|
||||
- Agents/status: use provider-aware context window lookup for fresh Anthropic 4.6 model overrides so `/status` shows the correct 1.0m window instead of an underreported shared-cache minimum. (#54796) Thanks @neeravmakwana.
|
||||
- OpenAI/WebSocket: preserve reasoning replay metadata and tool-call item ids on WebSocket tool turns, and start a fresh response chain when full-context resend is required. (#53856) Thanks @xujingchen1996.
|
||||
@@ -185,8 +404,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Message tool/buttons: keep the shared `buttons` schema optional in merged tool definitions so plain `action=send` calls stop failing validation when no buttons are provided. (#54418) Thanks @adzendo.
|
||||
- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026.
|
||||
- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.
|
||||
- Subagents/announcements: preserve the requester agent id for inline deterministic tool spawns so named agents without channel bindings can still announce completions through the correct owner session. (#55437) Thanks @kAIborg24.
|
||||
- Telegram/Anthropic streaming: replace raw invalid stream-order provider errors with a safe retry message so internal `message_start/message_stop` failures do not leak into chats. (#55408) Thanks @imydal.
|
||||
- Plugins/context engines: retry strict legacy `assemble()` calls without the new `prompt` field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.
|
||||
- CLI/update status: explicitly say `up to date` when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.
|
||||
- Daemon/Linux: stop flagging non-gateway systemd services as duplicate gateways just because their unit files mention OpenClaw, reducing false-positive doctor/log noise. (#45328) Thanks @gregretkowski.
|
||||
@@ -229,7 +446,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/diffs: load bundled Pierre themes without JSON module imports so diff rendering keeps working on newer Node builds. (#45869) thanks @NickHood1984.
|
||||
- Plugins/uninstall: remove owned `channels.<id>` config when uninstalling channel plugins, and keep the uninstall preview aligned with explicit channel ownership so built-in channels and shared keys stay intact. (#35915) Thanks @wbxl2000.
|
||||
- Plugins/Matrix: prefer explicit DM signals when choosing outbound direct rooms and routing unmapped verification summaries, so strict 2-person fallback rooms do not outrank the real DM. (#56076) thanks @gumadeiras
|
||||
- Slack/interactive replies: resolve Slack Block Kit reply delivery from both authored `channelData.slack.blocks` payloads and directive-generated interactive replies, and keep Slack button styles mapped onto valid Block Kit button rendering so interactive replies stop dropping on Slack-specific delivery paths. Thanks @vincentkoc.
|
||||
- Plugins/Matrix: resolve env-backed `accessToken` and `password` SecretRefs against the active Matrix config env path during startup, and officially accept SecretRef `accessToken` config values. (#54980) thanks @kakahu2015.
|
||||
- Microsoft Teams/proactive DMs: prefer the freshest personal conversation reference for `user:<aadObjectId>` sends when multiple stored references exist, so replies stop targeting stale DM threads. (#54702) Thanks @gumclaw.
|
||||
- Gateway/plugins: reuse the session workspace when building HTTP `/tools/invoke` tool lists and harden tool construction to infer the session agent workspace by default, so workspace plugins do not re-register on repeated HTTP tool calls. (#56101) thanks @neeravmakwana
|
||||
@@ -242,13 +458,11 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/Matrix: encrypt E2EE image thumbnails with `thumbnail_file` while keeping unencrypted-room previews on `thumbnail_url`, so encrypted Matrix image events keep thumbnail metadata without leaking plaintext previews. (#54711) thanks @frischeDaten.
|
||||
- Telegram/forum topics: keep native `/new` and `/reset` routed to the active topic by preserving the topic target on forum-thread command context. (#35963)
|
||||
- Status/port diagnostics: treat single-process dual-stack loopback gateway listeners as healthy in `openclaw status --all`, suppressing false "port already in use" conflict warnings. (#53398) Thanks @DanWebb1949.
|
||||
- Memory/builtin: keep memory-file indexing active in FTS-only mode (no embedding provider) so forced reindexes no longer swap in an empty index and wipe existing memory chunks. (#42714) Thanks @asamimei.
|
||||
- CLI/status: detect node-only hosts in `openclaw status` and `openclaw status --all`, show the configured remote gateway target instead of a false local `ECONNREFUSED`, and suppress contradictory local-gateway diagnosis output.
|
||||
- Gateway/SQLite transient handling: keep unhandled `SQLITE_CANTOPEN`, `SQLITE_BUSY`, `SQLITE_LOCKED`, and `SQLITE_IOERR` failures non-fatal in the global rejection handler so macOS LaunchAgent restarts do not enter a crash-throttle loop. (#57018)
|
||||
- Control UI/gateway: reconnect the browser client when gateway event sequence gaps are detected, so stale non-chat state recovers automatically instead of only telling the user to refresh. (#23912) thanks @Olshansk.
|
||||
- ClawDock/docs: move the helper scripts to `scripts/clawdock`, publish ClawDock as a first-class docs page on the docs site, and document reinstalling local helper copies from the new raw GitHub path. (#23912) thanks @Olshansk.
|
||||
- Control UI/gateway: clear queued browser connect timeouts on client stop so aborted or replaced gateway clients do not send delayed connect requests after shutdown. (#57338) thanks @gumadeiras.
|
||||
- Mattermost: detect stale websocket sessions after bot disable/enable cycles by polling the bot account `update_at` and forcing a reconnect when it changes. (#53604) Thanks @Qinsam.
|
||||
|
||||
### Breaking
|
||||
|
||||
- Providers/Qwen: remove the deprecated `qwen-portal-auth` OAuth integration for `portal.qwen.ai`; migrate to Model Studio with `openclaw onboard --auth-choice modelstudio-api-key`. (#52709) Thanks @pomelo-nwu.
|
||||
- Config/Doctor: drop automatic config migrations older than two months; very old legacy keys now fail validation instead of being rewritten on load or by `openclaw doctor`.
|
||||
|
||||
## 2026.3.24
|
||||
|
||||
@@ -274,8 +488,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Control UI/agents: add a "Not set" placeholder to the default agent model selector dropdown. (#53411) Thanks @BunsDev.
|
||||
- Runtime/install: lower the supported Node 22 floor to `22.14+` while continuing to recommend Node 24, so npm installs and self-updates do not strand Node 22.14 users on older releases.
|
||||
- CLI/update: preflight the target npm package `engines.node` before `openclaw update` runs a global package install, so outdated Node runtimes fail with a clear upgrade message instead of attempting an unsupported latest release.
|
||||
- Agents/BTW: force `/btw` side questions to disable provider reasoning so Anthropic adaptive-thinking sessions stop failing with `No BTW response generated`. Fixes #55376. Thanks @Catteres and @vincentkoc.
|
||||
- Auth profiles/OAuth: refresh runtime auth snapshots when saving rotated credentials so OAuth providers do not reuse consumed refresh tokens after the first token rotation. Fixes #55389. Thanks @sam26880 and @vincentkoc.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -389,7 +601,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/session policy: require sender ownership for `/send` policy changes so command-authorized non-owners cannot rewrite owner-only session delivery policy.
|
||||
- Security/bash stop: route `/bash stop` through the hardened process-tree killer so invalid or attacker-influenced SIGKILL targets cannot escape the intended bash-session scope.
|
||||
- Security/installer: hide staged project `.npmrc` files during skill and package installs so npm registry and git settings inside the stage directory cannot hijack trusted installs.
|
||||
- Agents/tool-call repair: recover malformed Kimi/OpenRouter tool-call argument streams when provider preambles appear before JSON payloads, and fail closed on non-tool leading text so fragment strings do not leak into filesystem path arguments during sub-agent runs. (#56560) Thanks @Originalwhite.
|
||||
|
||||
## 2026.3.23
|
||||
|
||||
@@ -627,9 +838,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Onboarding/custom providers: keep Azure AI Foundry `*.services.ai.azure.com` custom endpoints on the selected compatibility path instead of forcing Responses, so chat-completions Foundry models still work after setup. Fixes #50528. (#50535) Thanks @obviyus.
|
||||
- make `openclaw update status` explicitly say `up to date` when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.
|
||||
- Agents/embedded transport errors: distinguish common network failures like connection refused, DNS lookup failure, and interrupted sockets from true timeouts in embedded-run user messaging and lifecycle diagnostics. (#51419) Thanks @scoootscooob.
|
||||
- Security/pairing: bind iOS setup codes to the intended node profile and reject first-use bootstrap redemption that asks for broader roles or scopes. Thanks @tdjackey.
|
||||
- Nostr/security: enforce inbound DM policy before decrypt, route Nostr DMs through the standard reply pipeline, and add pre-crypto rate and size guards so unknown senders cannot bypass pairing or force unbounded crypto work. Thanks @kuranikaran.
|
||||
- Synology Chat/security: keep reply delivery bound to stable numeric `user_id` by default, and gate mutable username/nickname recipient lookup behind `dangerouslyAllowNameMatching` with new regression coverage. Thanks @nexrin.
|
||||
- Browser/node proxy: enforce `nodeHost.browserProxy.allowProfiles` across `query.profile` and `body.profile`, block proxy-side profile create/delete when the allowlist is set, and keep the default full proxy surface when the allowlist is empty.
|
||||
- Security/device pairing: harden `device.token.rotate` deny handling by keeping public failures generic while logging internal deny reasons and preserving approved-baseline enforcement. (`GHSA-7jrw-x62h-64p8`)
|
||||
- Security/exec safe bins: remove `jq` from the default safe-bin allowlist and fail closed on the `jq` `env` builtin when operators explicitly opt `jq` back in, so `jq -n env` cannot dump host secrets without an explicit trust path. Thanks @gladiator9797 for reporting.
|
||||
@@ -639,31 +847,15 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/network: harden explicit-proxy SSRF pinning by translating target-hop transport hints onto HTTPS proxy tunnels and failing closed for plain HTTP guarded fetches that cannot preserve pinned DNS.
|
||||
- Security/Synology Chat: require explicit per-account webhook paths for multi-account setups by default, reject duplicate exact webhook paths fail-closed, and keep inherited-path behavior behind an explicit dangerous opt-in so shared routes can no longer collapse DM policy contexts across accounts. Thanks @tdjackey for reporting.
|
||||
- Browser/remote CDP: honor strict browser SSRF policy during remote CDP reachability and `/json/version` discovery checks, redact sensitive `cdpUrl` tokens from status output, and warn when remote CDP targets private/internal hosts.
|
||||
- Telegram/replies: set `allow_sending_without_reply` on reply-targeted sends and media-error notices so deleted parent messages no longer drop otherwise valid replies. (#52524) Thanks @moltbot886.
|
||||
- Gateway/status: resolve env-backed `gateway.auth.*` SecretRefs before read-only probe auth checks so status no longer reports false probe failures when auth is configured through SecretRef. (#52513) Thanks @CodeForgeNet.
|
||||
- Agents/exec: return plain-text failed tool output for timeouts and other non-success exec outcomes so models no longer parrot raw JSON error payloads back to users. (#52508) Thanks @martingarramon.
|
||||
- CLI/startup: lazy-load channel add and root help startup paths to trim avoidable RSS and help latency on constrained hosts. (#46784) Thanks @vincentkoc.
|
||||
- CLI/onboarding: import static provider definitions directly for onboarding model/config helpers so those paths no longer pull provider discovery just for built-in defaults. (#47467) Thanks @vincentkoc.
|
||||
- CLI/configure: clarify fresh-setup memory-search warnings so they say semantic recall needs at least one embedding provider, and scope the initial model allowlist picker to the provider selected in configure. Thanks @vincentkoc.
|
||||
- CLI/auth choice: lazy-load plugin/provider fallback resolution so mapped auth choices stay on the static path and only unknown choices pay the heavy provider load. (#47495) Thanks @vincentkoc.
|
||||
- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc.
|
||||
- CLI/status: keep `status --json` stdout clean by skipping plugin compatibility scans that were not rendered in the JSON payload. (#52449) Thanks @cgdusek.
|
||||
- Agents/Telegram: avoid rebuilding the full model catalog on ordinary inbound replies so Telegram message handling no longer pays multi-second core startup latency before reply generation. Thanks @vincentkoc.
|
||||
- Media/security: bound remote-media error-body snippets with the same streaming caps and idle timeouts as successful downloads, so malicious HTTP error responses cannot force unbounded buffering before OpenClaw throws.
|
||||
- Gateway/auth: ignore spoofed loopback hops in trusted forwarding chains and block device approvals that request scopes above the caller session. (#46800) Thanks @vincentkoc.
|
||||
- Gateway/auth: clear self-declared scopes for device-less trusted-proxy Control UI sessions so proxy-authenticated connects cannot claim admin or secrets scopes without a bound device identity.
|
||||
- Hardening: refresh stale device pairing requests and pending metadata (#50695) Thanks @smaeljaish771 and @joshavant.
|
||||
- Gateway/auth: add regression coverage that keeps device-less trusted-proxy Control UI sessions off privileged pairing approval RPCs. Thanks @vincentkoc.
|
||||
- Media/Windows security: block remote-host `file://` media URLs and UNC/network paths before local filesystem resolution in core media loading and adjacent prompt/sandbox attachment seams, so the next release no longer allows structured local-media inputs to trigger outbound SMB credential handshakes on Windows. Thanks @RacerZ-fighting for reporting.
|
||||
- Web tools/Exa: align the bundled Exa plugin with the current Exa API by supporting newer search types and richer `contents` options, while fixing the result-count cap to honor Exa's higher limit. Thanks @vincentkoc.
|
||||
- Agents/default timeout: raise the shared default agent timeout from `600s` to `48h` so long-running ACP and agent sessions do not fail unless you configure a shorter limit.
|
||||
- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc.
|
||||
- Agents/Telegram: avoid rebuilding the full model catalog on ordinary inbound replies so Telegram message handling no longer pays multi-second core startup latency before reply generation. Thanks @vincentkoc.
|
||||
- Agents/models: cache `models.json` readiness by config and auth-file state so embedded runner turns stop paying repeated model-catalog startup work before replies. Thanks @vincentkoc.
|
||||
- Gateway/status: tolerate network interface discovery failures in status, onboarding control-UI links, and self-presence display paths so those surfaces fall back cleanly instead of crashing. (#52195) Thanks @meng-clb.
|
||||
- Gateway/Linux: auto-detect nvm-managed Node TLS CA bundle needs before CLI startup and refresh installed services that are missing `NODE_EXTRA_CA_CERTS`. (#51146) Thanks @GodsBoy.
|
||||
- Google auth/Node 25: patch `gaxios` to use native fetch without injecting `globalThis.window`, while translating proxy and mTLS transport settings so Google Vertex and Google Chat auth keep working on Node 25. (#47914) Thanks @pdd-cli.
|
||||
- Gateway/status: resolve env-backed `gateway.auth.*` SecretRefs before read-only probe auth checks so status no longer reports false probe failures when auth is configured through SecretRef. (#52513) Thanks @CodeForgeNet.
|
||||
- Gateway/plugins: pin runtime webhook routes to the gateway startup registry so channel webhooks keep working across plugin-registry churn, and make plugin auth + dispatch resolve routes from the same live HTTP-route registry. (#47902) Fixes #46924 and #47041. Thanks @steipete.
|
||||
- Gateway/restart: defer externally signaled unmanaged restarts through the in-process idle drain, and preserve the restored subagent run as remap fallback during orphan recovery so resumed sessions do not duplicate work. (#47719) Thanks @joeykrug.
|
||||
- Telegram/setup: seed fresh setups with `channels.telegram.groups["*"].requireMention=true` so new bots stay mention-gated in groups unless you explicitly open them up. Thanks @vincentkoc.
|
||||
@@ -757,7 +949,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord/ACP: forward worker abort signals into ACP turns so timed-out Discord jobs cancel the running turn instead of silently leaving the bound ACP session working in the background.
|
||||
- ACP/Codex session replay: preserve hidden assistant thinking when loading or rebinding existing ACP sessions so stored thought chunks do not replay into visible assistant text. Thanks @vincentkoc.
|
||||
- Gateway/commands: keep internal `chat.send` slash-command UX while requiring `operator.admin` before internal callers can persist `/exec` defaults or mutate `phone-control` node policy through `/phone arm|disarm`.
|
||||
- Plugins/Matrix: move bundled plugin `KeyedAsyncQueue` imports onto the stable `plugin-sdk/core` surface so Matrix Docker/runtime builds do not depend on the brittle keyed-async-queue subpath. Thanks @ecohash-co and @vincentkoc.
|
||||
- Plugins/context engines: enforce owner-aware context-engine registration on both loader and public SDK paths so plugins cannot spoof privileged ownership, claim the core `legacy` engine id, or overwrite an existing engine id through direct SDK imports. (#47595) Thanks @vincentkoc.
|
||||
- Plugins/bundler TDZ: fix `RESERVED_COMMANDS` temporal dead zone error that prevented device-pair, phone-control, and talk-voice plugins from registering when the bundler placed the commands module after call sites in the same output chunk. Thanks @BunsDev.
|
||||
- Plugins/imports: fix stale googlechat runtime-api import paths and signal SDK circular re-exports broken by recent plugin-sdk refactors. Thanks @BunsDev.
|
||||
@@ -780,11 +971,9 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/context engines: retry strict legacy `assemble()` calls without the new `prompt` field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.
|
||||
- Plugins/runtime state: share plugin-facing infra singleton state across duplicate module graphs and keep session-binding adapter ownership stable until the active owner unregisters. (#50725) thanks @huntharo.
|
||||
- Discord/pickers: keep `/codex_resume --browse-projects` picker callbacks alive in Discord by sharing component callback state across duplicate module graphs, preserving callback fallbacks, and acknowledging matched plugin interactions before dispatch. (#51260) Thanks @huntharo.
|
||||
- Memory/core tools: register `memory_search` and `memory_get` independently so one unavailable memory tool no longer suppresses the other in new sessions. (#50198) Thanks @artwalker.
|
||||
- Telegram/Mattermost message tool: keep plugin button schemas optional in isolated and cron sessions so plain sends do not fail validation when no current channel is active. (#52589) Thanks @tylerliu612.
|
||||
- Release/npm publish: fail the npm release check when `dist/control-ui/index.html` is missing from the packed tarball, so broken Control UI asset releases are blocked before publish. Fixes #52808. (#52852) Thanks @kevinheinrichs.
|
||||
- Slack/embedded delivery: suppress transcript-only `delivery-mirror` assistant messages before embedded re-delivery and raise the default Slack chunk fallback so messages just over 4000 characters stay in a single post. (#45489) Thanks @theo674.
|
||||
- Slack/embedded delivery: suppress transcript-only `delivery-mirror` assistant messages before embedded re-delivery and raise the default Slack chunk fallback so messages just over 4000 characters stay in a single post. (#45489) Thanks @theo674.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -846,7 +1035,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/exec approvals: unwrap `env` dispatch wrappers inside shell-segment allowlist resolution on macOS so `env FOO=bar /path/to/bin` resolves against the effective executable instead of the wrapper token.
|
||||
- Security/exec approvals: treat backslash-newline as shell line continuation during macOS shell-chain parsing so line-continued `$(` substitutions fail closed instead of slipping past command-substitution checks.
|
||||
- Security/exec approvals: bind macOS skill auto-allow trust to both executable name and resolved path so same-basename binaries no longer inherit trust from unrelated skill bins.
|
||||
- Build/plugin-sdk bundling: bundle plugin-sdk subpath entries in one shared build pass so published packages stop duplicating shared chunks and avoid the recent plugin-sdk memory blow-up. (#45426) Thanks @TarasShyn.
|
||||
- Cron/isolated sessions: route nested cron-triggered embedded runner work onto the nested lane so isolated cron jobs no longer deadlock when compaction or other queued inner work runs. Thanks @vincentkoc.
|
||||
- Agents/OpenAI-compatible compat overrides: respect explicit user `models[].compat` opt-ins for non-native `openai-completions` endpoints so usage-in-streaming capability overrides no longer get forced off when the endpoint actually supports them. (#44432) Thanks @cheapestinference.
|
||||
- Agents/Azure OpenAI startup prompts: rephrase the built-in `/new`, `/reset`, and post-compaction startup instruction so Azure OpenAI deployments no longer hit HTTP 400 false positives from the content filter. (#43403) Thanks @xingsy97.
|
||||
@@ -942,13 +1130,8 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/failover: classify z.ai `network_error` stop reasons as retryable timeouts so provider connectivity failures trigger fallback instead of surfacing raw unhandled-stop-reason errors. (#43884) Thanks @hougangdev.
|
||||
- Config/Anthropic startup: inline Anthropic alias normalization during config load so gateway startup no longer crashes on dated Anthropic model refs like `anthropic/claude-sonnet-4-20250514`. (#45520) Thanks @BunsDev.
|
||||
- Memory/session sync: add mode-aware post-compaction session reindexing with `agents.defaults.compaction.postIndexSync` plus `agents.defaults.memorySearch.sync.sessions.postCompactionForce`, so compacted session memory can refresh immediately without forcing every deployment into synchronous reindexing. (#25561) thanks @rodrigouroz.
|
||||
- Telegram/model picker: make inline model button selections persist the chosen session model correctly, clear overrides when selecting the configured default, and include effective fallback models in `/models` button validation. (#40105) Thanks @avirweb.
|
||||
- Telegram/native command sync: suppress expected `BOT_COMMANDS_TOO_MUCH` retry error noise, add a final fallback summary log, and document the difference between command-menu overflow and real Telegram network failures.
|
||||
- Mattermost/reply media delivery: pass agent-scoped `mediaLocalRoots` through shared reply delivery so allowed local files upload correctly from button, slash-command, and model-picker replies. (#44021) Thanks @LyleLiu666.
|
||||
- Plugins/env-scoped roots: fix plugin discovery/load caches and provenance tracking so same-process `HOME`/`OPENCLAW_HOME` changes no longer reuse stale plugin state or misreport `~/...` plugins as untracked. (#44046) thanks @gumadeiras.
|
||||
- Gateway/session discovery: discover disk-only and retired ACP session stores under custom templated `session.store` roots so ACP reconciliation, session-id/session-label targeting, and run-id fallback keep working after restart. (#44176) thanks @gumadeiras.
|
||||
- Browser/existing-session: stop reporting fake CDP ports/URLs for live attached Chrome sessions, render `transport: chrome-mcp` in CLI/status output instead of `port: 0`, and keep timeout diagnostics transport-aware when no direct CDP URL exists.
|
||||
- Models/OpenRouter native ids: canonicalize native OpenRouter model keys across config writes, runtime lookups, fallback management, and `models list --plain`, and migrate legacy duplicated `openrouter/openrouter/...` config entries forward on write.
|
||||
- Feishu/event dedupe: keep early duplicate suppression aligned with the shared Feishu message-id contract and release the pre-queue dedupe marker after failed dispatch so retried events can recover instead of being dropped until the short TTL expires. (#43762) Thanks @yunweibang.
|
||||
- Gateway/hooks: bucket hook auth failures by forwarded client IP behind trusted proxies and warn when `hooks.allowedAgentIds` leaves hook routing unrestricted.
|
||||
- Agents/compaction: skip the post-compaction `cache-ttl` marker write when a compaction completed in the same attempt, preventing the next turn from immediately triggering a second tiny compaction. (#28548) thanks @MoerAI.
|
||||
@@ -1086,12 +1269,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/node pending drain followup: keep `hasMore` true when the deferred baseline status item still needs delivery, and avoid allocating empty pending-work state for drain-only nodes with no queued work. (#41429) Thanks @mbelinky.
|
||||
- Protocol/Swift model sync: regenerate pending node work Swift bindings after the landed `node.pending.*` schema additions so generated protocol artifacts are consistent again. (#41477) Thanks @mbelinky.
|
||||
- Cron/subagent followup: do not misclassify empty or `NO_REPLY` cron responses as interim acknowledgements that need a rerun, so deliberately silent cron jobs are no longer retried. (#41383) thanks @jackal092927.
|
||||
- CLI/memory teardown: close cached memory search/index managers in the one-shot CLI shutdown path so watcher-backed memory caches no longer keep completed CLI runs alive after output finishes. (#40389) thanks @Julbarth.
|
||||
- Tools/web search: treat Brave `llm-context` grounding snippets as plain strings so `web_search` no longer returns empty snippet arrays in LLM Context mode. (#41387) thanks @zheliu2.
|
||||
- ACP/run-mode delivery: restore inline delivery for one-shot ACP run spawns from non-subagent (main) requester sessions so completions reach the originating Discord/Telegram/etc. channel again. Subagent orchestrators continue to use stream-to-parent when an active heartbeat relay route is available. (#52426) Thanks @distractedCoding.
|
||||
- Telegram/exec approvals: reject `/approve` commands aimed at other bots, keep deterministic approval prompts visible when tool-result delivery fails, and stop resolved exact IDs from matching other pending approvals by prefix. (#37233) Thanks @huntharo.
|
||||
- Control UI/Sessions: restore single-column session table collapse on narrow viewport or container widths by moving the responsive table override next to the base grid rule and enabling inline-size container queries. (#12175) Thanks @benjipeng.
|
||||
- Telegram/final preview delivery: split active preview lifecycle from cleanup retention so missing archived preview edits avoid duplicate fallback sends without clearing the live preview or blocking later in-place finalization. (#41662) thanks @hougangdev.
|
||||
- Cron/state errors: record `lastErrorReason` in cron job state and keep the gateway schema aligned with the full failover-reason set, including regression coverage for protocol conformance. (#14382) thanks @futuremind2026.
|
||||
- Browser/Browserbase 429 handling: surface stable no-retry rate-limit guidance without buffering discarded HTTP 429 response bodies from remote browser services. (#40491) thanks @mvanhorn.
|
||||
- CI/CodeQL Swift toolchain: select Xcode 26.1 before installing Swift build tools so the CodeQL Swift job uses Swift tools 6.2 on `macos-latest`. (#41787) thanks @BunsDev.
|
||||
@@ -1100,10 +1278,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Telegram/direct delivery: bridge direct delivery sends to internal `message:sent` hooks so internal hook listeners observe successful Telegram deliveries. (#40185) Thanks @vincentkoc.
|
||||
- Dependencies: refresh workspace dependencies except the pinned Carbon package, and harden ACP session-config writes against non-string SDK values so newer ACP clients fail fast instead of tripping type/runtime mismatches.
|
||||
- Telegram/polling restarts: clear bounded cleanup timeout handles after `runner.stop()` and `bot.stop()` settle so stall recovery no longer leaves stray 15-second timers behind on clean shutdown. (#43188) thanks @kyohwang.
|
||||
- Gateway/config errors: surface up to three validation issues in top-level `config.set`, `config.patch`, and `config.apply` error messages while preserving structured issue details. (#42664) Thanks @huntharo.
|
||||
- Hooks/plugin context parity followup: pass `trigger` and `channelId` through embedded `llm_input`, `agent_end`, and `llm_output` hook contexts so plugins receive the same agent metadata across hook phases. (#42362) Thanks @zhoulf1006.
|
||||
- Status/context windows: normalize provider-qualified override cache keys so `/status` resolves the active provider's configured context window even when `models.providers` keys use mixed case or surrounding whitespace. (#36389) Thanks @haoruilee.
|
||||
- ACP/main session aliases: canonicalize `main` before ACP session lookup so restarted ACP main sessions rehydrate instead of failing closed with `Session is not ACP-enabled: main`. (#43285, fixes #25692)
|
||||
- Agents/embedded runner: recover canonical allowlisted tool names from malformed `toolCallId` and malformed non-blank tool-name variants before dispatch, while failing closed on ambiguous matches. (#34485) thanks @yuweuii.
|
||||
- Agents/failover: classify ZenMux quota-refresh `402` responses as `rate_limit` so model fallback retries continue instead of stopping on a temporary subscription window. (#43917) thanks @bwjoke.
|
||||
- Agents/failover: classify HTTP 422 malformed-request responses as `format` and recognize OpenRouter "requires more credits" billing errors so provider fallback triggers instead of surfacing raw errors. (#43823) thanks @jnMetaCode.
|
||||
|
||||
13
SECURITY.md
13
SECURITY.md
@@ -57,7 +57,10 @@ These are frequently reported but are typically closed with no code change:
|
||||
- Reports that only show a malicious plugin executing privileged actions after a trusted operator installs/enables it.
|
||||
- Reports that assume per-user multi-tenant authorization on a shared gateway host/config.
|
||||
- Reports that treat the Gateway HTTP compatibility endpoints (`POST /v1/chat/completions`, `POST /v1/responses`) as if they implemented scoped operator auth (`operator.write` vs `operator.admin`). These endpoints authenticate the shared Gateway bearer secret/password and are documented full operator-access surfaces, not per-user/per-scope boundaries.
|
||||
- Reports that assume `x-openclaw-scopes` can reduce or redefine shared-secret bearer auth on the OpenAI-compatible HTTP endpoints. For shared-secret auth (`gateway.auth.mode="token"` or `"password"`), those endpoints ignore narrower bearer-declared scopes and restore the full default operator scope set plus owner semantics.
|
||||
- Reports that treat `POST /tools/invoke` under shared-secret bearer auth (`gateway.auth.mode="token"` or `"password"`) as a narrower per-request/per-scope authorization surface. That endpoint is designed as the same trusted-operator HTTP boundary: shared-secret bearer auth is full operator access there, narrower `x-openclaw-scopes` values do not reduce that path, and owner-only tool policy follows the shared-secret operator contract.
|
||||
- Reports that only show differences in heuristic detection/parity (for example obfuscation-pattern detection on one exec path but not another, such as `node.invoke -> system.run` parity gaps) without demonstrating bypass of auth, approvals, allowlist enforcement, sandboxing, or other documented trust boundaries.
|
||||
- Reports that only show an ACP tool can indirectly execute, mutate, orchestrate sessions, or reach another tool/runtime without demonstrating bypass of ACP prompt/approval, allowlist enforcement, sandboxing, or another documented trust boundary. ACP silent approval is intentionally limited to narrow readonly classes; parity-only indirect-command findings are hardening, not vulnerabilities.
|
||||
- ReDoS/DoS claims that require trusted operator configuration input (for example catastrophic regex in `sessionFilter` or `logging.redactPatterns`) without a trust-boundary bypass.
|
||||
- Archive/install extraction claims that require pre-existing local filesystem priming in trusted state (for example planting symlink/hardlink aliases under destination directories such as skills/tools paths) without showing an untrusted path that can create/control that primitive.
|
||||
- Reports that depend on replacing or rewriting an already-approved executable path on a trusted host (same-path inode/content swap) without showing an untrusted path to perform that write.
|
||||
@@ -93,7 +96,14 @@ When patching a GHSA via `gh api`, include `X-GitHub-Api-Version: 2022-11-28` (o
|
||||
OpenClaw does **not** model one gateway as a multi-tenant, adversarial user boundary.
|
||||
|
||||
- Authenticated Gateway callers are treated as trusted operators for that gateway instance.
|
||||
- The HTTP compatibility endpoints (`POST /v1/chat/completions`, `POST /v1/responses`) are in that same trusted-operator bucket. Passing Gateway bearer auth there is equivalent to operator access for that gateway; they do not implement a narrower `operator.write` vs `operator.admin` trust split.
|
||||
- The HTTP compatibility endpoints (`POST /v1/chat/completions`, `POST /v1/responses`) and direct tool endpoint (`POST /tools/invoke`) are in that same trusted-operator bucket. Passing Gateway bearer auth there is equivalent to operator access for that gateway; they do not implement a narrower `operator.write` vs `operator.admin` trust split.
|
||||
- Concretely, on the OpenAI-compatible HTTP surface:
|
||||
- shared-secret bearer auth (`token` / `password`) authenticates possession of the gateway operator secret
|
||||
- those requests receive the full default operator scope set (`operator.admin`, `operator.read`, `operator.write`, `operator.approvals`, `operator.pairing`)
|
||||
- chat-turn endpoints (`/v1/chat/completions`, `/v1/responses`) also treat those shared-secret callers as owner senders for owner-only tool policy
|
||||
- `POST /tools/invoke` follows that same shared-secret rule and also treats those callers as owner senders for owner-only tool policy
|
||||
- narrower `x-openclaw-scopes` headers are ignored for that shared-secret path
|
||||
- only identity-bearing HTTP modes (for example trusted proxy auth or `gateway.auth.mode="none"` on private ingress) honor declared per-request operator scopes
|
||||
- Session identifiers (`sessionKey`, session IDs, labels) are routing controls, not per-user authorization boundaries.
|
||||
- If one operator can view data from another operator on the same gateway, that is expected in this trust model.
|
||||
- OpenClaw can technically run multiple gateway instances on one machine, but recommended operations are clean separation by trust boundary.
|
||||
@@ -129,6 +139,7 @@ Plugins/extensions are part of OpenClaw's trusted computing base for a gateway.
|
||||
- Any report whose only claim is that an operator-enabled `dangerous*`/`dangerously*` config option weakens defaults (these are explicit break-glass tradeoffs by design)
|
||||
- Reports that depend on trusted operator-supplied configuration values to trigger availability impact (for example custom regex patterns). These may still be fixed as defense-in-depth hardening, but are not security-boundary bypasses.
|
||||
- Reports whose only claim is heuristic/parity drift in command-risk detection (for example obfuscation-pattern checks) across exec surfaces, without a demonstrated trust-boundary bypass. These are hardening-only findings and are not vulnerabilities; triage may close them as `invalid`/`no-action` or track them separately as low/informational hardening.
|
||||
- Reports whose only claim is that an ACP-exposed tool can indirectly execute commands, mutate host state, or reach another privileged tool/runtime without demonstrating a bypass of ACP prompt/approval, allowlist enforcement, sandboxing, or another documented trust boundary. These are hardening-only findings, not vulnerabilities.
|
||||
- Reports whose only claim is that exec approvals do not semantically model every interpreter/runtime loader form, subcommand, flag combination, package script, or transitive module/config import. Exec approvals bind exact request context and best-effort direct local file operands; they are not a complete semantic model of everything a runtime may load.
|
||||
- Exposed secrets that are third-party/user-controlled credentials (not OpenClaw-owned and not granting access to OpenClaw-operated infrastructure/services) without demonstrated OpenClaw impact
|
||||
- Reports whose only claim is host-side exec when sandbox runtime is disabled/unavailable (documented default behavior in the trusted-operator model), without a boundary bypass.
|
||||
|
||||
@@ -65,8 +65,8 @@ android {
|
||||
applicationId = "ai.openclaw.app"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 2026033000
|
||||
versionName = "2026.3.30"
|
||||
versionCode = 2026033100
|
||||
versionName = "2026.3.31"
|
||||
ndk {
|
||||
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
|
||||
@@ -91,6 +91,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
|
||||
val manualPort: StateFlow<Int> = prefs.manualPort
|
||||
val manualTls: StateFlow<Boolean> = prefs.manualTls
|
||||
val gatewayToken: StateFlow<String> = prefs.gatewayToken
|
||||
val gatewayBootstrapToken: StateFlow<String> = prefs.gatewayBootstrapToken
|
||||
val onboardingCompleted: StateFlow<Boolean> = prefs.onboardingCompleted
|
||||
val canvasDebugStatusEnabled: StateFlow<Boolean> = prefs.canvasDebugStatusEnabled
|
||||
val speakerEnabled: StateFlow<Boolean> = prefs.speakerEnabled
|
||||
@@ -261,6 +262,22 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
|
||||
ensureRuntime().connect(endpoint)
|
||||
}
|
||||
|
||||
fun connect(
|
||||
endpoint: GatewayEndpoint,
|
||||
token: String?,
|
||||
bootstrapToken: String?,
|
||||
password: String?,
|
||||
) {
|
||||
ensureRuntime().connect(
|
||||
endpoint,
|
||||
NodeRuntime.GatewayConnectAuth(
|
||||
token = token,
|
||||
bootstrapToken = bootstrapToken,
|
||||
password = password,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun connectManual() {
|
||||
ensureRuntime().connectManual()
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@ class NodeRuntime(
|
||||
context: Context,
|
||||
val prefs: SecurePrefs = SecurePrefs(context.applicationContext),
|
||||
) {
|
||||
data class GatewayConnectAuth(
|
||||
val token: String?,
|
||||
val bootstrapToken: String?,
|
||||
val password: String?,
|
||||
)
|
||||
|
||||
private val appContext = context.applicationContext
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private val deviceAuthStore = DeviceAuthStore(prefs)
|
||||
@@ -775,28 +781,51 @@ class NodeRuntime(
|
||||
}
|
||||
operatorStatusText = "Connecting…"
|
||||
updateStatus()
|
||||
val token = prefs.loadGatewayToken()
|
||||
val bootstrapToken = prefs.loadGatewayBootstrapToken()
|
||||
val password = prefs.loadGatewayPassword()
|
||||
connectWithAuth(endpoint = endpoint, auth = resolveGatewayConnectAuth(), reconnect = true)
|
||||
}
|
||||
|
||||
private fun connectWithAuth(
|
||||
endpoint: GatewayEndpoint,
|
||||
auth: GatewayConnectAuth,
|
||||
reconnect: Boolean = false,
|
||||
) {
|
||||
val tls = connectionManager.resolveTlsParams(endpoint)
|
||||
operatorSession.connect(
|
||||
endpoint,
|
||||
token,
|
||||
bootstrapToken,
|
||||
password,
|
||||
connectionManager.buildOperatorConnectOptions(),
|
||||
tls,
|
||||
)
|
||||
val connectOperator =
|
||||
shouldConnectOperatorSession(
|
||||
auth.token,
|
||||
auth.bootstrapToken,
|
||||
auth.password,
|
||||
loadStoredRoleDeviceToken("operator"),
|
||||
)
|
||||
if (!connectOperator) {
|
||||
operatorConnected = false
|
||||
operatorStatusText = "Offline"
|
||||
operatorSession.disconnect()
|
||||
updateStatus()
|
||||
} else {
|
||||
operatorSession.connect(
|
||||
endpoint,
|
||||
auth.token,
|
||||
auth.bootstrapToken,
|
||||
auth.password,
|
||||
connectionManager.buildOperatorConnectOptions(),
|
||||
tls,
|
||||
)
|
||||
}
|
||||
nodeSession.connect(
|
||||
endpoint,
|
||||
token,
|
||||
bootstrapToken,
|
||||
password,
|
||||
auth.token,
|
||||
auth.bootstrapToken,
|
||||
auth.password,
|
||||
connectionManager.buildNodeConnectOptions(),
|
||||
tls,
|
||||
)
|
||||
operatorSession.reconnect()
|
||||
nodeSession.reconnect()
|
||||
if (reconnect && connectOperator) {
|
||||
operatorSession.reconnect()
|
||||
}
|
||||
if (reconnect) {
|
||||
nodeSession.reconnect()
|
||||
}
|
||||
}
|
||||
|
||||
fun connect(endpoint: GatewayEndpoint) {
|
||||
@@ -818,25 +847,27 @@ class NodeRuntime(
|
||||
operatorStatusText = "Connecting…"
|
||||
nodeStatusText = "Connecting…"
|
||||
updateStatus()
|
||||
val token = prefs.loadGatewayToken()
|
||||
val bootstrapToken = prefs.loadGatewayBootstrapToken()
|
||||
val password = prefs.loadGatewayPassword()
|
||||
operatorSession.connect(
|
||||
endpoint,
|
||||
token,
|
||||
bootstrapToken,
|
||||
password,
|
||||
connectionManager.buildOperatorConnectOptions(),
|
||||
tls,
|
||||
)
|
||||
nodeSession.connect(
|
||||
endpoint,
|
||||
token,
|
||||
bootstrapToken,
|
||||
password,
|
||||
connectionManager.buildNodeConnectOptions(),
|
||||
tls,
|
||||
)
|
||||
connectWithAuth(endpoint = endpoint, auth = resolveGatewayConnectAuth())
|
||||
}
|
||||
|
||||
fun connect(
|
||||
endpoint: GatewayEndpoint,
|
||||
auth: GatewayConnectAuth,
|
||||
) {
|
||||
connectedEndpoint = endpoint
|
||||
operatorStatusText = "Connecting…"
|
||||
nodeStatusText = "Connecting…"
|
||||
updateStatus()
|
||||
connectWithAuth(endpoint = endpoint, auth = resolveGatewayConnectAuth(auth))
|
||||
}
|
||||
|
||||
internal fun resolveGatewayConnectAuth(explicitAuth: GatewayConnectAuth? = null): GatewayConnectAuth {
|
||||
return explicitAuth
|
||||
?: GatewayConnectAuth(
|
||||
token = prefs.loadGatewayToken(),
|
||||
bootstrapToken = prefs.loadGatewayBootstrapToken(),
|
||||
password = prefs.loadGatewayPassword(),
|
||||
)
|
||||
}
|
||||
|
||||
fun acceptGatewayTrustPrompt() {
|
||||
@@ -868,6 +899,11 @@ class NodeRuntime(
|
||||
connect(GatewayEndpoint.manual(host = host, port = port))
|
||||
}
|
||||
|
||||
private fun loadStoredRoleDeviceToken(role: String): String? {
|
||||
val deviceId = identityStore.loadOrCreate().deviceId
|
||||
return deviceAuthStore.loadToken(deviceId, role)
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
connectedEndpoint = null
|
||||
_pendingGatewayTrust.value = null
|
||||
@@ -1197,6 +1233,20 @@ class NodeRuntime(
|
||||
|
||||
}
|
||||
|
||||
internal fun shouldConnectOperatorSession(
|
||||
token: String?,
|
||||
bootstrapToken: String?,
|
||||
password: String?,
|
||||
storedOperatorToken: String?,
|
||||
): Boolean {
|
||||
return (
|
||||
!token.isNullOrBlank() ||
|
||||
!bootstrapToken.isNullOrBlank() ||
|
||||
!password.isNullOrBlank() ||
|
||||
!storedOperatorToken.isNullOrBlank()
|
||||
)
|
||||
}
|
||||
|
||||
private enum class HomeCanvasGatewayState {
|
||||
Connected,
|
||||
Connecting,
|
||||
|
||||
@@ -53,6 +53,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ai.openclaw.app.MainViewModel
|
||||
import ai.openclaw.app.gateway.GatewayEndpoint
|
||||
import ai.openclaw.app.ui.mobileCardSurface
|
||||
|
||||
private enum class ConnectInputMode {
|
||||
@@ -71,6 +72,7 @@ fun ConnectTabScreen(viewModel: MainViewModel) {
|
||||
val manualTls by viewModel.manualTls.collectAsState()
|
||||
val manualEnabled by viewModel.manualEnabled.collectAsState()
|
||||
val gatewayToken by viewModel.gatewayToken.collectAsState()
|
||||
val gatewayBootstrapToken by viewModel.gatewayBootstrapToken.collectAsState()
|
||||
val pendingTrust by viewModel.pendingGatewayTrust.collectAsState()
|
||||
|
||||
var advancedOpen by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -240,9 +242,13 @@ fun ConnectTabScreen(viewModel: MainViewModel) {
|
||||
resolveGatewayConnectConfig(
|
||||
useSetupCode = inputMode == ConnectInputMode.SetupCode,
|
||||
setupCode = setupCode,
|
||||
manualHost = manualHostInput,
|
||||
manualPort = manualPortInput,
|
||||
manualTls = manualTlsInput,
|
||||
savedManualHost = manualHost,
|
||||
savedManualPort = manualPort.toString(),
|
||||
savedManualTls = manualTls,
|
||||
manualHostInput = manualHostInput,
|
||||
manualPortInput = manualPortInput,
|
||||
manualTlsInput = manualTlsInput,
|
||||
fallbackBootstrapToken = gatewayBootstrapToken,
|
||||
fallbackToken = gatewayToken,
|
||||
fallbackPassword = passwordInput,
|
||||
)
|
||||
@@ -269,7 +275,12 @@ fun ConnectTabScreen(viewModel: MainViewModel) {
|
||||
viewModel.setGatewayToken("")
|
||||
}
|
||||
viewModel.setGatewayPassword(config.password)
|
||||
viewModel.connectManual()
|
||||
viewModel.connect(
|
||||
GatewayEndpoint.manual(host = config.host, port = config.port),
|
||||
token = config.token.ifEmpty { null },
|
||||
bootstrapToken = config.bootstrapToken.ifEmpty { null },
|
||||
password = config.password.ifEmpty { null },
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().height(52.dp),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
|
||||
@@ -37,9 +37,13 @@ private val gatewaySetupJson = Json { ignoreUnknownKeys = true }
|
||||
internal fun resolveGatewayConnectConfig(
|
||||
useSetupCode: Boolean,
|
||||
setupCode: String,
|
||||
manualHost: String,
|
||||
manualPort: String,
|
||||
manualTls: Boolean,
|
||||
savedManualHost: String,
|
||||
savedManualPort: String,
|
||||
savedManualTls: Boolean,
|
||||
manualHostInput: String,
|
||||
manualPortInput: String,
|
||||
manualTlsInput: Boolean,
|
||||
fallbackBootstrapToken: String,
|
||||
fallbackToken: String,
|
||||
fallbackPassword: String,
|
||||
): GatewayConnectConfig? {
|
||||
@@ -69,13 +73,23 @@ internal fun resolveGatewayConnectConfig(
|
||||
)
|
||||
}
|
||||
|
||||
val manualUrl = composeGatewayManualUrl(manualHost, manualPort, manualTls) ?: return null
|
||||
val manualUrl = composeGatewayManualUrl(manualHostInput, manualPortInput, manualTlsInput) ?: return null
|
||||
val parsed = parseGatewayEndpoint(manualUrl) ?: return null
|
||||
val savedManualEndpoint =
|
||||
composeGatewayManualUrl(savedManualHost, savedManualPort, savedManualTls)
|
||||
?.let(::parseGatewayEndpoint)
|
||||
val preserveBootstrapToken =
|
||||
savedManualEndpoint != null &&
|
||||
savedManualEndpoint.host == parsed.host &&
|
||||
savedManualEndpoint.port == parsed.port &&
|
||||
savedManualEndpoint.tls == parsed.tls &&
|
||||
fallbackToken.isBlank() &&
|
||||
fallbackPassword.isBlank()
|
||||
return GatewayConnectConfig(
|
||||
host = parsed.host,
|
||||
port = parsed.port,
|
||||
tls = parsed.tls,
|
||||
bootstrapToken = "",
|
||||
bootstrapToken = if (preserveBootstrapToken) fallbackBootstrapToken.trim() else "",
|
||||
token = fallbackToken.trim(),
|
||||
password = fallbackPassword.trim(),
|
||||
)
|
||||
|
||||
@@ -96,6 +96,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import ai.openclaw.app.BuildConfig
|
||||
import ai.openclaw.app.LocationMode
|
||||
import ai.openclaw.app.MainViewModel
|
||||
import ai.openclaw.app.gateway.GatewayEndpoint
|
||||
import ai.openclaw.app.node.DeviceNotificationListenerService
|
||||
import com.google.mlkit.vision.barcode.common.Barcode
|
||||
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
|
||||
@@ -211,6 +212,7 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
val statusText by viewModel.statusText.collectAsState()
|
||||
val isConnected by viewModel.isConnected.collectAsState()
|
||||
val isNodeConnected by viewModel.isNodeConnected.collectAsState()
|
||||
val serverName by viewModel.serverName.collectAsState()
|
||||
val remoteAddress by viewModel.remoteAddress.collectAsState()
|
||||
val persistedGatewayToken by viewModel.gatewayToken.collectAsState()
|
||||
@@ -227,6 +229,7 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||
var manualTls by rememberSaveable { mutableStateOf(false) }
|
||||
var gatewayError by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var attemptedConnect by rememberSaveable { mutableStateOf(false) }
|
||||
val canFinishOnboarding = canFinishOnboarding(isConnected = isConnected, isNodeConnected = isNodeConnected)
|
||||
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val qrScannerOptions =
|
||||
@@ -732,7 +735,7 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||
FinalStep(
|
||||
parsedGateway = parseGatewayEndpoint(gatewayUrl),
|
||||
statusText = statusText,
|
||||
isConnected = isConnected,
|
||||
isConnected = canFinishOnboarding,
|
||||
serverName = serverName,
|
||||
remoteAddress = remoteAddress,
|
||||
attemptedConnect = attemptedConnect,
|
||||
@@ -848,7 +851,7 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||
}
|
||||
}
|
||||
OnboardingStep.FinalCheck -> {
|
||||
if (isConnected) {
|
||||
if (canFinishOnboarding) {
|
||||
Button(
|
||||
onClick = { viewModel.setOnboardingCompleted(true) },
|
||||
modifier = Modifier.weight(1f).height(52.dp),
|
||||
@@ -882,7 +885,17 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||
viewModel.setGatewayToken("")
|
||||
}
|
||||
viewModel.setGatewayPassword(password)
|
||||
viewModel.connectManual()
|
||||
viewModel.connect(
|
||||
GatewayEndpoint.manual(host = parsed.host, port = parsed.port),
|
||||
token = token.ifEmpty { null },
|
||||
bootstrapToken =
|
||||
if (gatewayInputMode == GatewayInputMode.SetupCode) {
|
||||
decodeGatewaySetupCode(setupCode)?.bootstrapToken?.trim()?.ifEmpty { null }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
password = password.ifEmpty { null },
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f).height(52.dp),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
@@ -898,6 +911,10 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun canFinishOnboarding(isConnected: Boolean, isNodeConnected: Boolean): Boolean {
|
||||
return isConnected && isNodeConnected
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun onboardingPrimaryButtonColors() =
|
||||
ButtonDefaults.buttonColors(
|
||||
@@ -1677,21 +1694,22 @@ private fun FinalStep(
|
||||
)
|
||||
}
|
||||
}
|
||||
Text("Status", style = onboardingCaption1Style.copy(fontWeight = FontWeight.Bold), color = onboardingTextSecondary)
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = onboardingCommandBg,
|
||||
border = BorderStroke(1.dp, onboardingCommandBorder),
|
||||
) {
|
||||
Text(
|
||||
statusLabel,
|
||||
modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp),
|
||||
style = onboardingCalloutStyle.copy(fontFamily = FontFamily.Monospace),
|
||||
color = onboardingCommandText,
|
||||
)
|
||||
}
|
||||
if (showDiagnostics) {
|
||||
Text("Error", style = onboardingCaption1Style.copy(fontWeight = FontWeight.Bold), color = onboardingTextSecondary)
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = onboardingCommandBg,
|
||||
border = BorderStroke(1.dp, onboardingCommandBorder),
|
||||
) {
|
||||
Text(
|
||||
statusLabel,
|
||||
modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp),
|
||||
style = onboardingCalloutStyle.copy(fontFamily = FontFamily.Monospace),
|
||||
color = onboardingCommandText,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
"OpenClaw Android ${openClawAndroidVersionLabel()}",
|
||||
style = onboardingCaption1Style,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package ai.openclaw.app
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
import java.util.UUID
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class GatewayBootstrapAuthTest {
|
||||
@Test
|
||||
fun connectsOperatorSessionWhenBootstrapAuthExists() {
|
||||
assertTrue(shouldConnectOperatorSession(token = "", bootstrapToken = "bootstrap-1", password = "", storedOperatorToken = ""))
|
||||
assertTrue(shouldConnectOperatorSession(token = null, bootstrapToken = "bootstrap-1", password = null, storedOperatorToken = null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skipsOperatorSessionOnlyWhenNoSharedBootstrapOrStoredAuthExists() {
|
||||
assertTrue(shouldConnectOperatorSession(token = "shared-token", bootstrapToken = "bootstrap-1", password = null, storedOperatorToken = null))
|
||||
assertTrue(shouldConnectOperatorSession(token = null, bootstrapToken = "bootstrap-1", password = "shared-password", storedOperatorToken = null))
|
||||
assertTrue(shouldConnectOperatorSession(token = null, bootstrapToken = null, password = null, storedOperatorToken = "stored-token"))
|
||||
assertFalse(shouldConnectOperatorSession(token = null, bootstrapToken = "", password = null, storedOperatorToken = null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveGatewayConnectAuth_prefersExplicitSetupAuthOverStoredPrefs() {
|
||||
val app = RuntimeEnvironment.getApplication()
|
||||
val securePrefs =
|
||||
app.getSharedPreferences(
|
||||
"openclaw.node.secure.test.${UUID.randomUUID()}",
|
||||
android.content.Context.MODE_PRIVATE,
|
||||
)
|
||||
val prefs = SecurePrefs(app, securePrefsOverride = securePrefs)
|
||||
prefs.setGatewayToken("stale-shared-token")
|
||||
prefs.setGatewayBootstrapToken("")
|
||||
prefs.setGatewayPassword("stale-password")
|
||||
val runtime = NodeRuntime(app, prefs)
|
||||
|
||||
val auth =
|
||||
runtime.resolveGatewayConnectAuth(
|
||||
NodeRuntime.GatewayConnectAuth(
|
||||
token = null,
|
||||
bootstrapToken = "setup-bootstrap-token",
|
||||
password = null,
|
||||
),
|
||||
)
|
||||
|
||||
assertNull(auth.token)
|
||||
assertEquals("setup-bootstrap-token", auth.bootstrapToken)
|
||||
assertNull(auth.password)
|
||||
}
|
||||
}
|
||||
@@ -155,9 +155,13 @@ class GatewayConfigResolverTest {
|
||||
resolveGatewayConnectConfig(
|
||||
useSetupCode = true,
|
||||
setupCode = setupCode,
|
||||
manualHost = "",
|
||||
manualPort = "",
|
||||
manualTls = true,
|
||||
savedManualHost = "",
|
||||
savedManualPort = "",
|
||||
savedManualTls = true,
|
||||
manualHostInput = "",
|
||||
manualPortInput = "",
|
||||
manualTlsInput = true,
|
||||
fallbackBootstrapToken = "",
|
||||
fallbackToken = "shared-token",
|
||||
fallbackPassword = "shared-password",
|
||||
)
|
||||
@@ -179,9 +183,13 @@ class GatewayConfigResolverTest {
|
||||
resolveGatewayConnectConfig(
|
||||
useSetupCode = true,
|
||||
setupCode = setupCode,
|
||||
manualHost = "",
|
||||
manualPort = "",
|
||||
manualTls = true,
|
||||
savedManualHost = "",
|
||||
savedManualPort = "",
|
||||
savedManualTls = true,
|
||||
manualHostInput = "",
|
||||
manualPortInput = "",
|
||||
manualTlsInput = true,
|
||||
fallbackBootstrapToken = "",
|
||||
fallbackToken = "shared-token",
|
||||
fallbackPassword = "shared-password",
|
||||
)
|
||||
@@ -194,6 +202,74 @@ class GatewayConfigResolverTest {
|
||||
assertNull(resolved?.password?.takeIf { it.isNotEmpty() })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveGatewayConnectConfigManualPreservesBootstrapTokenWhenNoReplacementAuthExists() {
|
||||
val resolved =
|
||||
resolveGatewayConnectConfig(
|
||||
useSetupCode = false,
|
||||
setupCode = "",
|
||||
savedManualHost = "192.168.31.100",
|
||||
savedManualPort = "18789",
|
||||
savedManualTls = false,
|
||||
manualHostInput = "192.168.31.100",
|
||||
manualPortInput = "18789",
|
||||
manualTlsInput = false,
|
||||
fallbackBootstrapToken = "bootstrap-1",
|
||||
fallbackToken = "",
|
||||
fallbackPassword = "",
|
||||
)
|
||||
|
||||
assertEquals("192.168.31.100", resolved?.host)
|
||||
assertEquals(18789, resolved?.port)
|
||||
assertEquals(false, resolved?.tls)
|
||||
assertEquals("bootstrap-1", resolved?.bootstrapToken)
|
||||
assertEquals("", resolved?.token)
|
||||
assertEquals("", resolved?.password)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveGatewayConnectConfigManualDropsBootstrapTokenWhenReplacementPasswordExists() {
|
||||
val resolved =
|
||||
resolveGatewayConnectConfig(
|
||||
useSetupCode = false,
|
||||
setupCode = "",
|
||||
savedManualHost = "192.168.31.100",
|
||||
savedManualPort = "18789",
|
||||
savedManualTls = false,
|
||||
manualHostInput = "192.168.31.100",
|
||||
manualPortInput = "18789",
|
||||
manualTlsInput = false,
|
||||
fallbackBootstrapToken = "bootstrap-1",
|
||||
fallbackToken = "",
|
||||
fallbackPassword = "password-1",
|
||||
)
|
||||
|
||||
assertEquals("", resolved?.bootstrapToken)
|
||||
assertEquals("", resolved?.token)
|
||||
assertEquals("password-1", resolved?.password)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveGatewayConnectConfigManualDropsBootstrapTokenWhenEndpointChanges() {
|
||||
val resolved =
|
||||
resolveGatewayConnectConfig(
|
||||
useSetupCode = false,
|
||||
setupCode = "",
|
||||
savedManualHost = "192.168.31.100",
|
||||
savedManualPort = "18789",
|
||||
savedManualTls = false,
|
||||
manualHostInput = "192.168.31.101",
|
||||
manualPortInput = "18789",
|
||||
manualTlsInput = false,
|
||||
fallbackBootstrapToken = "bootstrap-1",
|
||||
fallbackToken = "",
|
||||
fallbackPassword = "",
|
||||
)
|
||||
|
||||
assertEquals("", resolved?.bootstrapToken)
|
||||
assertEquals("192.168.31.101", resolved?.host)
|
||||
}
|
||||
|
||||
private fun encodeSetupCode(payloadJson: String): String {
|
||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(payloadJson.toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package ai.openclaw.app.ui
|
||||
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class OnboardingFlowLogicTest {
|
||||
@Test
|
||||
fun blocksFinishWhenOnlyOperatorIsConnected() {
|
||||
assertFalse(canFinishOnboarding(isConnected = true, isNodeConnected = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blocksFinishWhenDisconnected() {
|
||||
assertFalse(canFinishOnboarding(isConnected = false, isNodeConnected = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blocksFinishWhenOnlyNodeIsConnected() {
|
||||
assertFalse(canFinishOnboarding(isConnected = false, isNodeConnected = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun allowsFinishOnlyWhenOperatorAndNodeAreConnected() {
|
||||
assertTrue(canFinishOnboarding(isConnected = true, isNodeConnected = true))
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Shared iOS version defaults.
|
||||
// Generated overrides live in build/Version.xcconfig (git-ignored).
|
||||
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.3.30
|
||||
OPENCLAW_MARKETING_VERSION = 2026.3.30
|
||||
OPENCLAW_BUILD_VERSION = 2026033000
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.3.31
|
||||
OPENCLAW_MARKETING_VERSION = 2026.3.31
|
||||
OPENCLAW_BUILD_VERSION = 2026033190
|
||||
|
||||
#include? "../build/Version.xcconfig"
|
||||
|
||||
@@ -65,9 +65,9 @@ Release behavior:
|
||||
- Beta release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, and `OpenClawPushAPNsEnvironment=production`.
|
||||
- The beta flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
|
||||
- Root `package.json.version` is the only version source for iOS.
|
||||
- A root version like `2026.3.30-beta.1` becomes:
|
||||
- `CFBundleShortVersionString = 2026.3.30`
|
||||
- `CFBundleVersion = next TestFlight build number for 2026.3.30`
|
||||
- A root version like `2026.3.31-beta.1` becomes:
|
||||
- `CFBundleShortVersionString = 2026.3.31`
|
||||
- `CFBundleVersion = next TestFlight build number for 2026.3.31`
|
||||
|
||||
Required env for beta builds:
|
||||
|
||||
|
||||
@@ -69,6 +69,13 @@ enum GatewaySettingsStore {
|
||||
account: self.preferredGatewayStableIDAccount)
|
||||
}
|
||||
|
||||
static func clearPreferredGatewayStableID(defaults: UserDefaults = .standard) {
|
||||
_ = KeychainStore.delete(
|
||||
service: self.gatewayService,
|
||||
account: self.preferredGatewayStableIDAccount)
|
||||
defaults.removeObject(forKey: self.preferredGatewayStableIDDefaultsKey)
|
||||
}
|
||||
|
||||
static func loadLastDiscoveredGatewayStableID() -> String? {
|
||||
if let value = KeychainStore.loadString(
|
||||
service: self.gatewayService,
|
||||
@@ -89,6 +96,13 @@ enum GatewaySettingsStore {
|
||||
account: self.lastDiscoveredGatewayStableIDAccount)
|
||||
}
|
||||
|
||||
static func clearLastDiscoveredGatewayStableID(defaults: UserDefaults = .standard) {
|
||||
_ = KeychainStore.delete(
|
||||
service: self.gatewayService,
|
||||
account: self.lastDiscoveredGatewayStableIDAccount)
|
||||
defaults.removeObject(forKey: self.lastDiscoveredGatewayStableIDDefaultsKey)
|
||||
}
|
||||
|
||||
static func loadGatewayToken(instanceId: String) -> String? {
|
||||
let account = self.gatewayTokenAccount(instanceId: instanceId)
|
||||
let token = KeychainStore.loadString(service: self.gatewayService, account: account)?
|
||||
@@ -119,6 +133,12 @@ enum GatewaySettingsStore {
|
||||
account: self.gatewayBootstrapTokenAccount(instanceId: instanceId))
|
||||
}
|
||||
|
||||
static func clearGatewayBootstrapToken(instanceId: String) {
|
||||
_ = KeychainStore.delete(
|
||||
service: self.gatewayService,
|
||||
account: self.gatewayBootstrapTokenAccount(instanceId: instanceId))
|
||||
}
|
||||
|
||||
static func loadGatewayPassword(instanceId: String) -> String? {
|
||||
KeychainStore.loadString(
|
||||
service: self.gatewayService,
|
||||
|
||||
@@ -1697,14 +1697,24 @@ extension NodeAppModel {
|
||||
password: password,
|
||||
nodeOptions: connectOptions)
|
||||
self.prepareForGatewayConnect(url: url, stableID: effectiveStableID)
|
||||
self.startOperatorGatewayLoop(
|
||||
url: url,
|
||||
stableID: effectiveStableID,
|
||||
if self.shouldStartOperatorGatewayLoop(
|
||||
token: token,
|
||||
bootstrapToken: bootstrapToken,
|
||||
password: password,
|
||||
nodeOptions: connectOptions,
|
||||
sessionBox: sessionBox)
|
||||
stableID: effectiveStableID)
|
||||
{
|
||||
self.startOperatorGatewayLoop(
|
||||
url: url,
|
||||
stableID: effectiveStableID,
|
||||
token: token,
|
||||
bootstrapToken: bootstrapToken,
|
||||
password: password,
|
||||
nodeOptions: connectOptions,
|
||||
sessionBox: sessionBox)
|
||||
} else {
|
||||
self.operatorGatewayTask = nil
|
||||
Task { await self.operatorGateway.disconnect() }
|
||||
}
|
||||
self.startNodeGatewayLoop(
|
||||
url: url,
|
||||
stableID: effectiveStableID,
|
||||
@@ -1785,6 +1795,86 @@ private extension NodeAppModel {
|
||||
self.apnsLastRegisteredTokenHex = nil
|
||||
}
|
||||
|
||||
func shouldStartOperatorGatewayLoop(
|
||||
token: String?,
|
||||
bootstrapToken: String?,
|
||||
password: String?,
|
||||
stableID _: String) -> Bool
|
||||
{
|
||||
Self.shouldStartOperatorGatewayLoop(
|
||||
token: token,
|
||||
bootstrapToken: bootstrapToken,
|
||||
password: password,
|
||||
hasStoredOperatorToken: self.hasStoredGatewayRoleToken("operator"))
|
||||
}
|
||||
|
||||
func hasStoredGatewayRoleToken(_ role: String) -> Bool {
|
||||
let identity = DeviceIdentityStore.loadOrCreate()
|
||||
return DeviceAuthStore.loadToken(deviceId: identity.deviceId, role: role) != nil
|
||||
}
|
||||
|
||||
static func shouldStartOperatorGatewayLoop(
|
||||
token: String?,
|
||||
bootstrapToken: String?,
|
||||
password: String?,
|
||||
hasStoredOperatorToken: Bool) -> Bool
|
||||
{
|
||||
let trimmedToken = token?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if !trimmedToken.isEmpty {
|
||||
return true
|
||||
}
|
||||
let trimmedPassword = password?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if !trimmedPassword.isEmpty {
|
||||
return true
|
||||
}
|
||||
let trimmedBootstrapToken = bootstrapToken?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if !trimmedBootstrapToken.isEmpty {
|
||||
return false
|
||||
}
|
||||
return hasStoredOperatorToken
|
||||
}
|
||||
|
||||
static func clearingBootstrapToken(in config: GatewayConnectConfig?) -> GatewayConnectConfig? {
|
||||
guard let config else { return nil }
|
||||
let trimmedBootstrapToken = config.bootstrapToken?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard !trimmedBootstrapToken.isEmpty else { return config }
|
||||
return GatewayConnectConfig(
|
||||
url: config.url,
|
||||
stableID: config.stableID,
|
||||
tls: config.tls,
|
||||
token: config.token,
|
||||
bootstrapToken: nil,
|
||||
password: config.password,
|
||||
nodeOptions: config.nodeOptions)
|
||||
}
|
||||
|
||||
func currentGatewayReconnectAuth(
|
||||
fallbackToken: String?,
|
||||
fallbackBootstrapToken: String?,
|
||||
fallbackPassword: String?) -> (token: String?, bootstrapToken: String?, password: String?)
|
||||
{
|
||||
if let cfg = self.activeGatewayConnectConfig {
|
||||
return (cfg.token, cfg.bootstrapToken, cfg.password)
|
||||
}
|
||||
return (fallbackToken, fallbackBootstrapToken, fallbackPassword)
|
||||
}
|
||||
|
||||
func clearPersistedGatewayBootstrapTokenIfNeeded() {
|
||||
// Always drop the in-memory bootstrap token after the first successful
|
||||
// bootstrap connect so reconnect loops cannot reuse a spent token.
|
||||
self.activeGatewayConnectConfig = Self.clearingBootstrapToken(in: self.activeGatewayConnectConfig)
|
||||
|
||||
let trimmedInstanceId = UserDefaults.standard.string(forKey: "node.instanceId")?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard !trimmedInstanceId.isEmpty else { return }
|
||||
guard
|
||||
GatewaySettingsStore.loadGatewayBootstrapToken(instanceId: trimmedInstanceId) != nil
|
||||
else { return }
|
||||
|
||||
GatewaySettingsStore.clearGatewayBootstrapToken(instanceId: trimmedInstanceId)
|
||||
}
|
||||
|
||||
func refreshBackgroundReconnectSuppressionIfNeeded(source: String) {
|
||||
guard self.isBackgrounded else { return }
|
||||
guard !self.backgroundReconnectSuppressed else { return }
|
||||
@@ -1841,11 +1931,15 @@ private extension NodeAppModel {
|
||||
displayName: nodeOptions.clientDisplayName)
|
||||
|
||||
do {
|
||||
let reconnectAuth = self.currentGatewayReconnectAuth(
|
||||
fallbackToken: token,
|
||||
fallbackBootstrapToken: bootstrapToken,
|
||||
fallbackPassword: password)
|
||||
try await self.operatorGateway.connect(
|
||||
url: url,
|
||||
token: token,
|
||||
bootstrapToken: bootstrapToken,
|
||||
password: password,
|
||||
token: reconnectAuth.token,
|
||||
bootstrapToken: reconnectAuth.bootstrapToken,
|
||||
password: reconnectAuth.password,
|
||||
connectOptions: operatorOptions,
|
||||
sessionBox: sessionBox,
|
||||
onConnected: { [weak self] in
|
||||
@@ -1948,12 +2042,16 @@ private extension NodeAppModel {
|
||||
|
||||
do {
|
||||
let epochMs = Int(Date().timeIntervalSince1970 * 1000)
|
||||
let reconnectAuth = self.currentGatewayReconnectAuth(
|
||||
fallbackToken: token,
|
||||
fallbackBootstrapToken: bootstrapToken,
|
||||
fallbackPassword: password)
|
||||
GatewayDiagnostics.log("connect attempt epochMs=\(epochMs) url=\(url.absoluteString)")
|
||||
try await self.nodeGateway.connect(
|
||||
url: url,
|
||||
token: token,
|
||||
bootstrapToken: bootstrapToken,
|
||||
password: password,
|
||||
token: reconnectAuth.token,
|
||||
bootstrapToken: reconnectAuth.bootstrapToken,
|
||||
password: reconnectAuth.password,
|
||||
connectOptions: currentOptions,
|
||||
sessionBox: sessionBox,
|
||||
onConnected: { [weak self] in
|
||||
@@ -1965,6 +2063,30 @@ private extension NodeAppModel {
|
||||
self.screen.errorText = nil
|
||||
UserDefaults.standard.set(true, forKey: "gateway.autoconnect")
|
||||
}
|
||||
let usedBootstrapToken =
|
||||
reconnectAuth.token?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty != false &&
|
||||
reconnectAuth.bootstrapToken?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.isEmpty == false
|
||||
if usedBootstrapToken {
|
||||
await MainActor.run {
|
||||
self.clearPersistedGatewayBootstrapTokenIfNeeded()
|
||||
if self.operatorGatewayTask == nil && self.shouldStartOperatorGatewayLoop(
|
||||
token: reconnectAuth.token,
|
||||
bootstrapToken: nil,
|
||||
password: reconnectAuth.password,
|
||||
stableID: stableID)
|
||||
{
|
||||
self.startOperatorGatewayLoop(
|
||||
url: url,
|
||||
stableID: stableID,
|
||||
token: reconnectAuth.token,
|
||||
bootstrapToken: nil,
|
||||
password: reconnectAuth.password,
|
||||
nodeOptions: currentOptions,
|
||||
sessionBox: sessionBox)
|
||||
}
|
||||
}
|
||||
}
|
||||
let relayData = await MainActor.run {
|
||||
(
|
||||
sessionKey: self.mainSessionKey,
|
||||
@@ -1975,8 +2097,8 @@ private extension NodeAppModel {
|
||||
ShareGatewayRelaySettings.saveConfig(
|
||||
ShareGatewayRelayConfig(
|
||||
gatewayURLString: url.absoluteString,
|
||||
token: token,
|
||||
password: password,
|
||||
token: reconnectAuth.token,
|
||||
password: reconnectAuth.password,
|
||||
sessionKey: relayData.sessionKey,
|
||||
deliveryChannel: relayData.deliveryChannel,
|
||||
deliveryTo: relayData.deliveryTo))
|
||||
@@ -3015,6 +3137,20 @@ extension NodeAppModel {
|
||||
static func _test_currentDeepLinkKey() -> String {
|
||||
self.expectedDeepLinkKey()
|
||||
}
|
||||
|
||||
static func _test_shouldStartOperatorGatewayLoop(
|
||||
token: String?,
|
||||
bootstrapToken: String?,
|
||||
password: String?,
|
||||
hasStoredOperatorToken: Bool) -> Bool
|
||||
{
|
||||
self.shouldStartOperatorGatewayLoop(
|
||||
token: token,
|
||||
bootstrapToken: bootstrapToken,
|
||||
password: password,
|
||||
hasStoredOperatorToken: hasStoredOperatorToken)
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
// swiftlint:enable type_body_length file_length
|
||||
|
||||
@@ -1008,6 +1008,11 @@ struct SettingsTab: View {
|
||||
|
||||
// Reset onboarding state + clear saved gateway connection (the two things RootCanvas checks).
|
||||
GatewaySettingsStore.clearLastGatewayConnection()
|
||||
GatewaySettingsStore.clearPreferredGatewayStableID()
|
||||
GatewaySettingsStore.clearLastDiscoveredGatewayStableID()
|
||||
// Resetting onboarding should also forget trusted gateway TLS fingerprints.
|
||||
// Otherwise a restarted dev gateway can stay stuck in a local TLS cancel loop.
|
||||
GatewayTLSStore.clearAllFingerprints()
|
||||
OnboardingStateStore.reset()
|
||||
|
||||
// RootCanvas also short-circuits onboarding when these are true.
|
||||
|
||||
@@ -5,6 +5,7 @@ import Testing
|
||||
@testable import OpenClaw
|
||||
|
||||
@Suite(.serialized) struct GatewayConnectionSecurityTests {
|
||||
@MainActor
|
||||
private func makeController() -> GatewayConnectionController {
|
||||
GatewayConnectionController(appModel: NodeAppModel(), startDiscovery: false)
|
||||
}
|
||||
@@ -32,8 +33,7 @@ import Testing
|
||||
}
|
||||
|
||||
private func clearTLSFingerprint(stableID: String) {
|
||||
let suite = UserDefaults(suiteName: "ai.openclaw.shared") ?? .standard
|
||||
suite.removeObject(forKey: "gateway.tls.\(stableID)")
|
||||
GatewayTLSStore.clearFingerprint(stableID: stableID)
|
||||
}
|
||||
|
||||
@Test @MainActor func discoveredTLSParams_prefersStoredPinOverAdvertisedTXT() async {
|
||||
@@ -126,4 +126,21 @@ import Testing
|
||||
#expect(controller._test_resolveManualPort(host: "device.sample.ts.net.", port: 0, useTLS: true) == 443)
|
||||
#expect(controller._test_resolveManualPort(host: "device.sample.ts.net", port: 18789, useTLS: true) == 18789)
|
||||
}
|
||||
|
||||
@Test @MainActor func clearAllTLSFingerprints_removesStoredPins() async {
|
||||
let stableID1 = "test|\(UUID().uuidString)"
|
||||
let stableID2 = "test|\(UUID().uuidString)"
|
||||
defer { GatewayTLSStore.clearAllFingerprints() }
|
||||
|
||||
GatewayTLSStore.saveFingerprint("11", stableID: stableID1)
|
||||
GatewayTLSStore.saveFingerprint("22", stableID: stableID2)
|
||||
|
||||
#expect(GatewayTLSStore.loadFingerprint(stableID: stableID1) == "11")
|
||||
#expect(GatewayTLSStore.loadFingerprint(stableID: stableID2) == "22")
|
||||
|
||||
GatewayTLSStore.clearAllFingerprints()
|
||||
|
||||
#expect(GatewayTLSStore.loadFingerprint(stableID: stableID1) == nil)
|
||||
#expect(GatewayTLSStore.loadFingerprint(stableID: stableID2) == nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,64 @@ private final class MockWatchMessagingService: @preconcurrency WatchMessagingSer
|
||||
#expect(appModel.mainSessionKey == "agent:agent-123:main")
|
||||
}
|
||||
|
||||
@Test func operatorLoopWaitsForBootstrapHandoffBeforeUsingStoredToken() {
|
||||
#expect(
|
||||
!NodeAppModel._test_shouldStartOperatorGatewayLoop(
|
||||
token: nil,
|
||||
bootstrapToken: "fresh-bootstrap-token",
|
||||
password: nil,
|
||||
hasStoredOperatorToken: true)
|
||||
)
|
||||
#expect(
|
||||
!NodeAppModel._test_shouldStartOperatorGatewayLoop(
|
||||
token: nil,
|
||||
bootstrapToken: nil,
|
||||
password: nil,
|
||||
hasStoredOperatorToken: false)
|
||||
)
|
||||
#expect(
|
||||
NodeAppModel._test_shouldStartOperatorGatewayLoop(
|
||||
token: nil,
|
||||
bootstrapToken: nil,
|
||||
password: nil,
|
||||
hasStoredOperatorToken: true)
|
||||
)
|
||||
#expect(
|
||||
NodeAppModel._test_shouldStartOperatorGatewayLoop(
|
||||
token: "shared-token",
|
||||
bootstrapToken: "fresh-bootstrap-token",
|
||||
password: nil,
|
||||
hasStoredOperatorToken: false)
|
||||
)
|
||||
}
|
||||
|
||||
@Test func clearingBootstrapTokenStripsReconnectConfigEvenWithoutPersistence() {
|
||||
let config = GatewayConnectConfig(
|
||||
url: URL(string: "wss://gateway.example")!,
|
||||
stableID: "test-gateway",
|
||||
tls: nil,
|
||||
token: nil,
|
||||
bootstrapToken: "spent-bootstrap-token",
|
||||
password: nil,
|
||||
nodeOptions: GatewayConnectOptions(
|
||||
role: "node",
|
||||
scopes: [],
|
||||
caps: [],
|
||||
commands: [],
|
||||
permissions: [:],
|
||||
clientId: "openclaw-ios",
|
||||
clientMode: "node",
|
||||
clientDisplayName: nil))
|
||||
|
||||
let cleared = NodeAppModel.clearingBootstrapToken(in: config)
|
||||
#expect(cleared?.bootstrapToken == nil)
|
||||
#expect(cleared?.url == config.url)
|
||||
#expect(cleared?.stableID == config.stableID)
|
||||
#expect(cleared?.token == config.token)
|
||||
#expect(cleared?.password == config.password)
|
||||
#expect(cleared?.nodeOptions.role == config.nodeOptions.role)
|
||||
}
|
||||
|
||||
@Test @MainActor func handleInvokeRejectsBackgroundCommands() async {
|
||||
let appModel = NodeAppModel()
|
||||
appModel.setScenePhase(.background)
|
||||
|
||||
@@ -558,12 +558,16 @@ extension GatewayConnection {
|
||||
func skillsInstall(
|
||||
name: String,
|
||||
installId: String,
|
||||
dangerouslyForceUnsafeInstall: Bool? = nil,
|
||||
timeoutMs: Int? = nil) async throws -> SkillInstallResult
|
||||
{
|
||||
var params: [String: AnyCodable] = [
|
||||
"name": AnyCodable(name),
|
||||
"installId": AnyCodable(installId),
|
||||
]
|
||||
if let dangerouslyForceUnsafeInstall {
|
||||
params["dangerouslyForceUnsafeInstall"] = AnyCodable(dangerouslyForceUnsafeInstall)
|
||||
}
|
||||
if let timeoutMs {
|
||||
params["timeoutMs"] = AnyCodable(timeoutMs)
|
||||
}
|
||||
|
||||
@@ -16,9 +16,20 @@ enum HostEnvSecurityPolicy {
|
||||
"RUBYOPT",
|
||||
"BASH_ENV",
|
||||
"ENV",
|
||||
"BROWSER",
|
||||
"GIT_EDITOR",
|
||||
"GIT_EXTERNAL_DIFF",
|
||||
"GIT_EXEC_PATH",
|
||||
"GIT_SEQUENCE_EDITOR",
|
||||
"GIT_TEMPLATE_DIR",
|
||||
"GIT_SSL_NO_VERIFY",
|
||||
"GIT_SSL_CAINFO",
|
||||
"GIT_SSL_CAPATH",
|
||||
"CC",
|
||||
"CXX",
|
||||
"CARGO_BUILD_RUSTC",
|
||||
"CMAKE_C_COMPILER",
|
||||
"CMAKE_CXX_COMPILER",
|
||||
"SHELL",
|
||||
"SHELLOPTS",
|
||||
"PS4",
|
||||
@@ -46,6 +57,9 @@ enum HostEnvSecurityPolicy {
|
||||
"GIT_SSH",
|
||||
"GIT_PROXY_COMMAND",
|
||||
"GIT_ASKPASS",
|
||||
"GIT_SSL_NO_VERIFY",
|
||||
"GIT_SSL_CAINFO",
|
||||
"GIT_SSL_CAPATH",
|
||||
"SSH_ASKPASS",
|
||||
"LESSOPEN",
|
||||
"LESSCLOSE",
|
||||
@@ -74,6 +88,52 @@ enum HostEnvSecurityPolicy {
|
||||
"PHP_INI_SCAN_DIR",
|
||||
"DENO_DIR",
|
||||
"BUN_CONFIG_REGISTRY",
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"ALL_PROXY",
|
||||
"NO_PROXY",
|
||||
"NODE_TLS_REJECT_UNAUTHORIZED",
|
||||
"NODE_EXTRA_CA_CERTS",
|
||||
"SSL_CERT_FILE",
|
||||
"SSL_CERT_DIR",
|
||||
"REQUESTS_CA_BUNDLE",
|
||||
"CURL_CA_BUNDLE",
|
||||
"DOCKER_HOST",
|
||||
"DOCKER_TLS_VERIFY",
|
||||
"DOCKER_CERT_PATH",
|
||||
"PIP_INDEX_URL",
|
||||
"PIP_PYPI_URL",
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_CONFIG_FILE",
|
||||
"PIP_FIND_LINKS",
|
||||
"PIP_TRUSTED_HOST",
|
||||
"UV_INDEX",
|
||||
"UV_INDEX_URL",
|
||||
"UV_EXTRA_INDEX_URL",
|
||||
"UV_DEFAULT_INDEX",
|
||||
"DOCKER_HOST",
|
||||
"DOCKER_TLS_VERIFY",
|
||||
"DOCKER_CERT_PATH",
|
||||
"DOCKER_CONTEXT",
|
||||
"LIBRARY_PATH",
|
||||
"CPATH",
|
||||
"C_INCLUDE_PATH",
|
||||
"CPLUS_INCLUDE_PATH",
|
||||
"OBJC_INCLUDE_PATH",
|
||||
"NODE_EXTRA_CA_CERTS",
|
||||
"SSL_CERT_FILE",
|
||||
"SSL_CERT_DIR",
|
||||
"REQUESTS_CA_BUNDLE",
|
||||
"CURL_CA_BUNDLE",
|
||||
"GOPROXY",
|
||||
"GONOSUMCHECK",
|
||||
"GONOSUMDB",
|
||||
"GONOPROXY",
|
||||
"GOPRIVATE",
|
||||
"GOENV",
|
||||
"GOPATH",
|
||||
"PYTHONUSERBASE",
|
||||
"VIRTUAL_ENV",
|
||||
"LUA_PATH",
|
||||
"LUA_CPATH",
|
||||
"GEM_HOME",
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.3.30</string>
|
||||
<string>2026.3.31</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2026033000</string>
|
||||
<string>2026033190</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -14,10 +14,11 @@ struct WideAreaGatewayBeacon: Equatable {
|
||||
}
|
||||
|
||||
enum WideAreaGatewayDiscovery {
|
||||
private static let maxCandidates = 40
|
||||
private static let digPath = "/usr/bin/dig"
|
||||
private static let defaultTimeoutSeconds: TimeInterval = 0.2
|
||||
private static let nameserverProbeConcurrency = 6
|
||||
// Security: wide-area discovery must trust only the Tailscale MagicDNS resolver.
|
||||
// Probing arbitrary tailnet peers lets the fastest responder become DNS-SD authority.
|
||||
private static let tailscaleDNSResolver = "100.100.100.100"
|
||||
|
||||
struct DiscoveryContext {
|
||||
var tailscaleStatus: @Sendable () -> String?
|
||||
@@ -39,27 +40,16 @@ enum WideAreaGatewayDiscovery {
|
||||
timeoutSeconds - Date().timeIntervalSince(startedAt)
|
||||
}
|
||||
|
||||
guard let ips = collectTailnetIPv4s(
|
||||
statusJson: context.tailscaleStatus()).nonEmpty else { return [] }
|
||||
var candidates = Array(ips.prefix(self.maxCandidates))
|
||||
guard let nameserver = findNameserver(
|
||||
candidates: &candidates,
|
||||
guard let statusJson = context.tailscaleStatus(),
|
||||
!collectTailnetIPv4s(statusJson: statusJson).isEmpty,
|
||||
let discovery = loadWideAreaPtrRecords(
|
||||
remaining: remaining,
|
||||
dig: context.dig)
|
||||
else {
|
||||
return []
|
||||
}
|
||||
else { return [] }
|
||||
|
||||
guard let domain = OpenClawBonjour.wideAreaGatewayServiceDomain else { return [] }
|
||||
let domainTrimmed = domain.trimmingCharacters(in: CharacterSet(charactersIn: "."))
|
||||
let probeName = "_openclaw-gw._tcp.\(domainTrimmed)"
|
||||
guard let ptrLines = context.dig(
|
||||
["+short", "+time=1", "+tries=1", "@\(nameserver)", probeName, "PTR"],
|
||||
min(defaultTimeoutSeconds, remaining()))?.split(whereSeparator: \.isNewline),
|
||||
!ptrLines.isEmpty
|
||||
else {
|
||||
return []
|
||||
}
|
||||
let domainTrimmed = discovery.domainTrimmed
|
||||
let ptrLines = discovery.ptrLines
|
||||
let nameserver = self.tailscaleDNSResolver
|
||||
|
||||
var beacons: [WideAreaGatewayBeacon] = []
|
||||
for raw in ptrLines {
|
||||
@@ -148,68 +138,26 @@ enum WideAreaGatewayDiscovery {
|
||||
return output
|
||||
}
|
||||
|
||||
private static func findNameserver(
|
||||
candidates: inout [String],
|
||||
private static func loadWideAreaPtrRecords(
|
||||
remaining: () -> TimeInterval,
|
||||
dig: @escaping @Sendable (_ args: [String], _ timeout: TimeInterval) -> String?) -> String?
|
||||
dig: @escaping @Sendable (_ args: [String], _ timeout: TimeInterval) -> String?)
|
||||
-> (domainTrimmed: String, ptrLines: [Substring])?
|
||||
{
|
||||
guard let domain = OpenClawBonjour.wideAreaGatewayServiceDomain else { return nil }
|
||||
let domainTrimmed = domain.trimmingCharacters(in: CharacterSet(charactersIn: "."))
|
||||
let probeName = "_openclaw-gw._tcp.\(domainTrimmed)"
|
||||
let budget = max(0, remaining())
|
||||
if budget <= 0 { return nil }
|
||||
|
||||
let ips = candidates
|
||||
candidates.removeAll(keepingCapacity: true)
|
||||
if ips.isEmpty { return nil }
|
||||
|
||||
final class ProbeState: @unchecked Sendable {
|
||||
let lock = NSLock()
|
||||
var nextIndex = 0
|
||||
var found: String?
|
||||
guard let stdout = dig(
|
||||
["+short", "+time=1", "+tries=1", "@\(self.tailscaleDNSResolver)", probeName, "PTR"],
|
||||
min(defaultTimeoutSeconds, budget)),
|
||||
let ptrLines = stdout.split(whereSeparator: \.isNewline).nonEmpty
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let state = ProbeState()
|
||||
let deadline = Date().addingTimeInterval(max(0, remaining()))
|
||||
let workerCount = min(self.nameserverProbeConcurrency, ips.count)
|
||||
let group = DispatchGroup()
|
||||
|
||||
for _ in 0..<workerCount {
|
||||
group.enter()
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
defer { group.leave() }
|
||||
|
||||
while Date() < deadline {
|
||||
state.lock.lock()
|
||||
if state.found != nil {
|
||||
state.lock.unlock()
|
||||
return
|
||||
}
|
||||
let i = state.nextIndex
|
||||
state.nextIndex += 1
|
||||
state.lock.unlock()
|
||||
|
||||
if i >= ips.count { return }
|
||||
let ip = ips[i]
|
||||
let budget = deadline.timeIntervalSinceNow
|
||||
if budget <= 0 { return }
|
||||
|
||||
if let stdout = dig(
|
||||
["+short", "+time=1", "+tries=1", "@\(ip)", probeName, "PTR"],
|
||||
min(defaultTimeoutSeconds, budget)),
|
||||
stdout.split(whereSeparator: \.isNewline).isEmpty == false
|
||||
{
|
||||
state.lock.lock()
|
||||
if state.found == nil {
|
||||
state.found = ip
|
||||
}
|
||||
state.lock.unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = group.wait(timeout: .now() + max(0.0, remaining()))
|
||||
return state.found
|
||||
return (domainTrimmed, ptrLines)
|
||||
}
|
||||
|
||||
private static func runDig(args: [String], timeout: TimeInterval) -> String? {
|
||||
|
||||
@@ -1,10 +1,37 @@
|
||||
import Darwin
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import OpenClawDiscovery
|
||||
|
||||
private final class NameserverQueryLog: @unchecked Sendable {
|
||||
private let lock = NSLock()
|
||||
private var nameservers: [String] = []
|
||||
|
||||
func record(_ nameserver: String) {
|
||||
self.lock.lock()
|
||||
defer { self.lock.unlock() }
|
||||
self.nameservers.append(nameserver)
|
||||
}
|
||||
|
||||
func count(matching nameserver: String) -> Int {
|
||||
self.lock.lock()
|
||||
defer { self.lock.unlock() }
|
||||
return self.nameservers.filter { $0 == nameserver }.count
|
||||
}
|
||||
}
|
||||
|
||||
@Suite(.serialized)
|
||||
struct WideAreaGatewayDiscoveryTests {
|
||||
@Test func `discovers beacon from tailnet dns sd fallback`() {
|
||||
let originalWideAreaDomain = getenv("OPENCLAW_WIDE_AREA_DOMAIN").map { String(cString: $0) }
|
||||
setenv("OPENCLAW_WIDE_AREA_DOMAIN", "openclaw.internal", 1)
|
||||
defer {
|
||||
if let originalWideAreaDomain {
|
||||
setenv("OPENCLAW_WIDE_AREA_DOMAIN", originalWideAreaDomain, 1)
|
||||
} else {
|
||||
unsetenv("OPENCLAW_WIDE_AREA_DOMAIN")
|
||||
}
|
||||
}
|
||||
let statusJson = """
|
||||
{
|
||||
"Self": { "TailscaleIPs": ["100.69.232.64"] },
|
||||
@@ -20,7 +47,7 @@ struct WideAreaGatewayDiscoveryTests {
|
||||
let recordType = args.last ?? ""
|
||||
let nameserver = args.first(where: { $0.hasPrefix("@") }) ?? ""
|
||||
if recordType == "PTR" {
|
||||
if nameserver == "@100.123.224.76" {
|
||||
if nameserver == "@100.100.100.100" {
|
||||
return "steipetacstudio-gateway._openclaw-gw._tcp.openclaw.internal.\n"
|
||||
}
|
||||
return ""
|
||||
@@ -47,4 +74,55 @@ struct WideAreaGatewayDiscoveryTests {
|
||||
#expect(beacon.tailnetDns == "peters-mac-studio-1.sheep-coho.ts.net")
|
||||
#expect(beacon.cliPath == "/Users/steipete/openclaw/src/entry.ts")
|
||||
}
|
||||
|
||||
@Test func `attacker peer cannot become nameserver`() {
|
||||
let originalWideAreaDomain = getenv("OPENCLAW_WIDE_AREA_DOMAIN").map { String(cString: $0) }
|
||||
setenv("OPENCLAW_WIDE_AREA_DOMAIN", "openclaw.internal", 1)
|
||||
defer {
|
||||
if let originalWideAreaDomain {
|
||||
setenv("OPENCLAW_WIDE_AREA_DOMAIN", originalWideAreaDomain, 1)
|
||||
} else {
|
||||
unsetenv("OPENCLAW_WIDE_AREA_DOMAIN")
|
||||
}
|
||||
}
|
||||
let statusJson = """
|
||||
{
|
||||
"Self": { "TailscaleIPs": ["100.64.0.1"] },
|
||||
"Peer": {
|
||||
"attacker": { "TailscaleIPs": ["100.64.0.2"] }
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let queriedNameservers = NameserverQueryLog()
|
||||
let context = WideAreaGatewayDiscovery.DiscoveryContext(
|
||||
tailscaleStatus: { statusJson },
|
||||
dig: { args, _ in
|
||||
let nameserver = args.first(where: { $0.hasPrefix("@") }) ?? ""
|
||||
queriedNameservers.record(nameserver)
|
||||
|
||||
let recordType = args.last ?? ""
|
||||
if recordType == "PTR" {
|
||||
if nameserver == "@100.64.0.2" {
|
||||
return "evil._openclaw-gw._tcp.openclaw.internal.\n"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if recordType == "SRV" {
|
||||
return "0 0 443 evil.ts.net."
|
||||
}
|
||||
if recordType == "TXT" {
|
||||
return "\"displayName=Evil\""
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
let beacons = WideAreaGatewayDiscovery.discover(
|
||||
timeoutSeconds: 2.0,
|
||||
context: context)
|
||||
|
||||
#expect(queriedNameservers.count(matching: "@100.64.0.2") == 0)
|
||||
#expect(queriedNameservers.count(matching: "@100.100.100.100") == 1)
|
||||
#expect(beacons.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,25 @@ public enum GatewayTLSStore {
|
||||
_ = GenericPasswordKeychainStore.saveString(value, service: self.keychainService, account: stableID)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func clearFingerprint(stableID: String) -> Bool {
|
||||
let removedKeychain = GenericPasswordKeychainStore.delete(
|
||||
service: self.keychainService,
|
||||
account: stableID)
|
||||
self.clearLegacyFingerprint(stableID: stableID)
|
||||
return removedKeychain
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func clearAllFingerprints() -> Bool {
|
||||
let removedKeychain = SecItemDelete([
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: self.keychainService,
|
||||
] as CFDictionary)
|
||||
self.clearAllLegacyFingerprints()
|
||||
return removedKeychain == errSecSuccess || removedKeychain == errSecItemNotFound
|
||||
}
|
||||
|
||||
// MARK: - Migration
|
||||
|
||||
/// On first Keychain read for a given stableID, move any legacy UserDefaults
|
||||
@@ -53,6 +72,18 @@ public enum GatewayTLSStore {
|
||||
}
|
||||
defaults.removeObject(forKey: legacyKey)
|
||||
}
|
||||
|
||||
private static func clearLegacyFingerprint(stableID: String) {
|
||||
guard let defaults = UserDefaults(suiteName: self.legacySuiteName) else { return }
|
||||
defaults.removeObject(forKey: self.legacyKeyPrefix + stableID)
|
||||
}
|
||||
|
||||
private static func clearAllLegacyFingerprints() {
|
||||
guard let defaults = UserDefaults(suiteName: self.legacySuiteName) else { return }
|
||||
for key in defaults.dictionaryRepresentation().keys where key.hasPrefix(self.legacyKeyPrefix) {
|
||||
defaults.removeObject(forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class GatewayTLSPinningSession: NSObject, WebSocketSessioning, URLSessionDelegate, @unchecked Sendable {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5593}
|
||||
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5720}
|
||||
{"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true}
|
||||
{"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true}
|
||||
{"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -144,7 +144,7 @@
|
||||
{"recordType":"path","path":"agents.defaults.heartbeat.prompt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.heartbeat.session","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.heartbeat.suppressToolErrorWarnings","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["automation"],"label":"Heartbeat Suppress Tool Error Warnings","help":"Suppress tool error warning payloads during heartbeat runs.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.heartbeat.target","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["automation"],"help":"Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, synology-chat, tlon, twitch, zalo, zalouser.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.heartbeat.target","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["automation"],"help":"Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, qqbot, synology-chat, tlon, twitch, zalo, zalouser.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.heartbeat.to","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.humanDelay","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.humanDelay.maxMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Human Delay Max (ms)","help":"Maximum delay in ms for custom humanDelay (default: 2500).","hasChildren":false}
|
||||
@@ -159,6 +159,8 @@
|
||||
{"recordType":"path","path":"agents.defaults.imageModel.fallbacks","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["media","models","reliability"],"label":"Image Model Fallbacks","help":"Ordered fallback image models (provider/model).","hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.imageModel.fallbacks.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.imageModel.primary","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","models"],"label":"Image Model","help":"Optional image model (provider/model) used when the primary model lacks image input.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.llm","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.llm.idleTimeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.maxConcurrent","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.mediaMaxMb","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search","help":"Vector search over MEMORY.md and memory/*.md (per-agent overrides supported).","hasChildren":true}
|
||||
@@ -185,6 +187,12 @@
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.multimodal.modalities.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.outputDimensionality","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search Output Dimensionality","help":"Gemini embedding-2 only: chooses the output vector size for memory embeddings. Use 768, 1536, or 3072 (default), and expect a full reindex when you change it because stored vector dimensions must stay consistent.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search Provider","help":"Selects the embedding backend used to build/query memory vectors: \"openai\", \"gemini\", \"voyage\", \"mistral\", \"ollama\", or \"local\". Keep your most reliable provider here and configure fallback for resilience.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.qmd","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search QMD Collections","help":"Use this when one agent should query another agent's transcript collections; QMD-specific extra collections let you opt into cross-agent memory search without flattening everything into one shared namespace.","hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.qmd.extraCollections","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"QMD Extra Collections","help":"Use this when you need directional transcript search across agents; add collections here to scope QMD recalls without creating a shared global transcript namespace.","hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.qmd.extraCollections.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.qmd.extraCollections.*.name","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.qmd.extraCollections.*.path","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.qmd.extraCollections.*.pattern","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.query","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.query.hybrid","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.query.hybrid.candidateMultiplier","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search Hybrid Candidate Multiplier","help":"Expands the candidate pool before reranking (default: 4). Raise this for better recall on noisy corpora, but expect more compute and slightly slower searches.","hasChildren":false}
|
||||
@@ -204,7 +212,7 @@
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Remote Embedding Base URL","help":"Overrides the embedding API endpoint, such as an OpenAI-compatible proxy or custom Gemini base URL. Use this only when routing through your own gateway or vendor endpoint; keep provider defaults otherwise.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","url-secret"],"label":"Remote Embedding Base URL","help":"Overrides the embedding API endpoint, such as an OpenAI-compatible proxy or custom Gemini base URL. Use this only when routing through your own gateway or vendor endpoint; keep provider defaults otherwise.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.batch","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.batch.concurrency","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Remote Batch Concurrency","help":"Limits how many embedding batch jobs run at the same time during indexing (default: 2). Increase carefully for faster bulk indexing, but watch provider rate limits and queue errors.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.remote.batch.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Remote Batch Embedding Enabled","help":"Enables provider batch APIs for embedding jobs when supported (OpenAI/Gemini), improving throughput on larger index runs. Keep this enabled unless debugging provider batch failures or running very small workloads.","hasChildren":false}
|
||||
@@ -342,6 +350,7 @@
|
||||
{"recordType":"path","path":"agents.defaults.subagents.model.fallbacks","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.subagents.model.fallbacks.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.subagents.model.primary","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.subagents.requireAgentId","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.subagents.runTimeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.subagents.thinking","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.thinkingDefault","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -377,7 +386,7 @@
|
||||
{"recordType":"path","path":"agents.list.*.heartbeat.prompt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.heartbeat.session","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.heartbeat.suppressToolErrorWarnings","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["automation"],"label":"Agent Heartbeat Suppress Tool Error Warnings","help":"Suppress tool error warning payloads during heartbeat runs.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.heartbeat.target","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["automation"],"help":"Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, synology-chat, tlon, twitch, zalo, zalouser.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.heartbeat.target","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["automation"],"help":"Delivery target (\"last\", \"none\", or a channel id). Known channels: telegram, whatsapp, discord, irc, googlechat, slack, signal, imessage, line, bluebubbles, feishu, matrix, mattermost, msteams, nextcloud-talk, nostr, qqbot, synology-chat, tlon, twitch, zalo, zalouser.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.heartbeat.to","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.humanDelay","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.humanDelay.maxMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -413,6 +422,12 @@
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.multimodal.modalities.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.outputDimensionality","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.qmd","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.qmd.extraCollections","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.qmd.extraCollections.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.qmd.extraCollections.*.name","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.qmd.extraCollections.*.path","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.qmd.extraCollections.*.pattern","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.query","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.query.hybrid","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.query.hybrid.candidateMultiplier","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -432,7 +447,7 @@
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.batch","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.batch.concurrency","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.remote.batch.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -566,6 +581,7 @@
|
||||
{"recordType":"path","path":"agents.list.*.subagents.model.fallbacks","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.subagents.model.fallbacks.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.subagents.model.primary","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.subagents.requireAgentId","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.subagents.thinking","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.thinkingDefault","kind":"core","type":"string","required":false,"enumValues":["off","minimal","low","medium","high","xhigh","adaptive"],"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent Thinking Default","help":"Optional per-agent default thinking level. Overrides agents.defaults.thinkingDefault for this agent when no per-message or session override is set.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -599,7 +615,7 @@
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.ask","kind":"core","type":"string","required":false,"enumValues":["off","on-miss","always"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.backgroundMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.cleanupMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.host","kind":"core","type":"string","required":false,"enumValues":["sandbox","gateway","node"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.host","kind":"core","type":"string","required":false,"enumValues":["auto","sandbox","gateway","node"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.node","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.notifyOnExit","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.tools.exec.notifyOnExitEmptySuccess","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -681,6 +697,8 @@
|
||||
{"recordType":"path","path":"auth.cooldowns.billingBackoffHoursByProvider.*","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"auth.cooldowns.billingMaxHours","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["access","auth","performance"],"label":"Billing Backoff Cap (hours)","help":"Cap (hours) for billing backoff (default: 24).","hasChildren":false}
|
||||
{"recordType":"path","path":"auth.cooldowns.failureWindowHours","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["access","auth"],"label":"Failover Window (hours)","help":"Failure window (hours) for backoff counters (default: 24).","hasChildren":false}
|
||||
{"recordType":"path","path":"auth.cooldowns.overloadedBackoffMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["access","auth","reliability","storage"],"label":"Overloaded Backoff (ms)","help":"Fixed delay in milliseconds before retrying an overloaded provider/profile rotation (default: 0).","hasChildren":false}
|
||||
{"recordType":"path","path":"auth.cooldowns.overloadedProfileRotations","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["access","auth","storage"],"label":"Overloaded Profile Rotations","help":"Maximum same-provider auth-profile rotations allowed for overloaded errors before switching to model fallback (default: 1).","hasChildren":false}
|
||||
{"recordType":"path","path":"auth.order","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["access","auth"],"label":"Auth Profile Order","help":"Ordered auth profile IDs per provider (used for automatic failover).","hasChildren":true}
|
||||
{"recordType":"path","path":"auth.order.*","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"auth.order.*.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -779,7 +797,7 @@
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.groups.*.tools.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.mediaLocalRoots","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.mediaLocalRoots.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.accounts.*.mediaMaxMb","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -829,7 +847,7 @@
|
||||
{"recordType":"path","path":"channels.bluebubbles.groups.*.tools.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.bluebubbles.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.mediaLocalRoots","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.bluebubbles.mediaLocalRoots.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.bluebubbles.mediaMaxMb","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -999,7 +1017,7 @@
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.intents.guildMembers","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.intents.presence","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.maxLinesPerMessage","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -1225,7 +1243,7 @@
|
||||
{"recordType":"path","path":"channels.discord.intents.guildMembers","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Discord Guild Members Intent","help":"Enable the Guild Members privileged intent. Must also be enabled in the Discord Developer Portal. Default: false.","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.intents.presence","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Discord Presence Intent","help":"Enable the Guild Presences privileged intent. Must also be enabled in the Discord Developer Portal. Allows tracking user activities (e.g. Spotify). Default: false.","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.discord.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.maxLinesPerMessage","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network","performance"],"label":"Discord Max Lines Per Message","help":"Soft max line count per Discord message (default: 17).","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.discord.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -1659,7 +1677,7 @@
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.includeAttachments","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.mediaMaxMb","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.accounts.*.region","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -1722,7 +1740,7 @@
|
||||
{"recordType":"path","path":"channels.imessage.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.includeAttachments","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.imessage.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.mediaMaxMb","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.imessage.region","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -1782,7 +1800,7 @@
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.host","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.mentionPatterns","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.irc.accounts.*.mentionPatterns.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -1851,7 +1869,7 @@
|
||||
{"recordType":"path","path":"channels.irc.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.host","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.irc.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.irc.mentionPatterns","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.irc.mentionPatterns.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -1969,6 +1987,7 @@
|
||||
{"recordType":"path","path":"channels.matrix.dm.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.dm.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.dm.policy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.dm.threadReplies","kind":"channel","type":"string","required":false,"enumValues":["off","inbound","always"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.encryption","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.groupAllowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -1993,16 +2012,18 @@
|
||||
{"recordType":"path","path":"channels.matrix.groups.*.tools.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.groups.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.matrix.groups.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.homeserver","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.initialSyncLimit","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.matrix.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.password","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.matrix.password.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.password.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.password.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.proxy","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.reactionNotifications","kind":"channel","type":"string","required":false,"enumValues":["off","own"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.replyToMode","kind":"channel","type":"string","required":false,"enumValues":["off","first","all"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2081,7 +2102,7 @@
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.interactions.allowedSourceIps.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.interactions.callbackBaseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.oncharPrefixes","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.mattermost.accounts.*.oncharPrefixes.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2131,7 +2152,7 @@
|
||||
{"recordType":"path","path":"channels.mattermost.interactions.allowedSourceIps.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.interactions.callbackBaseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.mattermost.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.mattermost.oncharPrefixes","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.mattermost.oncharPrefixes.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2179,7 +2200,7 @@
|
||||
{"recordType":"path","path":"channels.msteams.heartbeat.useIndicator","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.msteams.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.msteams.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.msteams.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.msteams.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.msteams.mediaAllowHosts","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.msteams.mediaAllowHosts.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.msteams.mediaAuthAllowHosts","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -2270,7 +2291,7 @@
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.groupPolicy","kind":"channel","type":"string","required":true,"enumValues":["open","disabled","allowlist"],"defaultValue":"allowlist","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2328,7 +2349,7 @@
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.groupPolicy","kind":"channel","type":"string","required":true,"enumValues":["open","disabled","allowlist"],"defaultValue":"allowlist","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nextcloud-talk.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2360,9 +2381,12 @@
|
||||
{"recordType":"path","path":"channels.nostr.dmPolicy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.nostr.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.privateKey","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.privateKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["channels","network","security"],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.nostr.privateKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.privateKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.privateKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.profile","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.nostr.profile.about","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.profile.banner","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2374,6 +2398,73 @@
|
||||
{"recordType":"path","path":"channels.nostr.profile.website","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.nostr.relays","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.nostr.relays.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"QQ Bot","help":"connect to QQ via official QQ Bot API with group chat and direct message support.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.appId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.audioFormatPolicy","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.audioFormatPolicy.sttDirectFormats","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.audioFormatPolicy.sttDirectFormats.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.audioFormatPolicy.transcodeEnabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.audioFormatPolicy.uploadDirectFormats","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.audioFormatPolicy.uploadDirectFormats.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.clientSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.clientSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.clientSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.clientSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.clientSecretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.markdownSupport","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.systemPrompt","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.upgradeMode","kind":"channel","type":"string","required":false,"enumValues":["doc","hot-reload"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.upgradeUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.urlDirectUpload","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.voiceDirectUploadFormats","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.accounts.*.voiceDirectUploadFormats.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.appId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.audioFormatPolicy","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.audioFormatPolicy.sttDirectFormats","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.audioFormatPolicy.sttDirectFormats.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.audioFormatPolicy.transcodeEnabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.audioFormatPolicy.uploadDirectFormats","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.audioFormatPolicy.uploadDirectFormats.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.clientSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.clientSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.clientSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.clientSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.clientSecretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.defaultAccount","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.markdownSupport","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.stt","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.stt.apiKey","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.stt.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.stt.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.stt.model","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.stt.provider","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.systemPrompt","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.apiKey","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.authStyle","kind":"channel","type":"string","required":false,"enumValues":["bearer","api-key"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.model","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.provider","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.queryParams","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.queryParams.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.speed","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.tts.voice","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.upgradeMode","kind":"channel","type":"string","required":false,"enumValues":["doc","hot-reload"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.upgradeUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.urlDirectUpload","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.qqbot.voiceDirectUploadFormats","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.qqbot.voiceDirectUploadFormats.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Signal","help":"signal-cli linked device; more setup (David Reagans: \"Hop on Discord.\").","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.signal.account","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Signal Account","help":"Signal account identifier (phone/number handle) used to bind this channel config to a specific Signal identity. Keep this aligned with your linked device/session state.","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -2436,7 +2527,7 @@
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.ignoreAttachments","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.ignoreStories","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.mediaMaxMb","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.accounts.*.reactionAllowlist","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -2506,7 +2597,7 @@
|
||||
{"recordType":"path","path":"channels.signal.ignoreAttachments","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.ignoreStories","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.signal.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.mediaMaxMb","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.signal.reactionAllowlist","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -2598,6 +2689,15 @@
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.dms.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.dms.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.agentFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.agentFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.approvers.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.sessionFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.sessionFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.execApprovals.target","kind":"channel","type":"string","required":false,"enumValues":["dm","channel","both"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.groupPolicy","kind":"channel","type":"string","required":false,"enumValues":["open","disabled","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.healthMonitor","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.healthMonitor.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2607,7 +2707,7 @@
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.heartbeat.useIndicator","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.mode","kind":"channel","type":"string","required":false,"enumValues":["socket","http"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2723,6 +2823,15 @@
|
||||
{"recordType":"path","path":"channels.slack.dms.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.dms.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Slack Exec Approvals","help":"Slack-native exec approval routing and approver authorization. Enable this only when Slack should act as an explicit exec-approval client for the selected workspace account.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.agentFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Slack Exec Approval Agent Filter","help":"Optional allowlist of agent IDs eligible for Slack exec approvals, for example `[\"main\", \"ops-agent\"]`. Use this to keep approval prompts scoped to the agents you actually operate from Slack.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.agentFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Slack Exec Approval Approvers","help":"Slack user IDs allowed to approve exec requests for this workspace account. Use Slack user IDs or user targets such as `U123`, `user:U123`, or `<@U123>`. If you leave this unset, OpenClaw falls back to owner IDs inferred from channels.slack.allowFrom, channels.slack.dm.allowFrom, and defaultTo when possible.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.approvers.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Slack Exec Approvals Enabled","help":"Enable Slack exec approvals for this account. When false or unset, Slack messages/buttons cannot approve exec requests.","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.sessionFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network","storage"],"label":"Slack Exec Approval Session Filter","help":"Optional session-key filters matched as substring or regex-style patterns before Slack approval routing is used. Use narrow patterns so Slack approvals only appear for intended sessions.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.sessionFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.execApprovals.target","kind":"channel","type":"string","required":false,"enumValues":["dm","channel","both"],"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Slack Exec Approval Target","help":"Controls where Slack approval prompts are sent: \"dm\" sends to approver DMs (default), \"channel\" sends to the originating Slack chat/thread, and \"both\" sends to both. Channel delivery exposes the command text to the chat, so only use it in trusted channels.","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.groupPolicy","kind":"channel","type":"string","required":true,"enumValues":["open","disabled","allowlist"],"defaultValue":"allowlist","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.healthMonitor","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.healthMonitor.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2732,7 +2841,7 @@
|
||||
{"recordType":"path","path":"channels.slack.heartbeat.useIndicator","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.slack.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.mode","kind":"channel","type":"string","required":true,"enumValues":["socket","http"],"defaultValue":"socket","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.slack.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -2924,7 +3033,7 @@
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.linkPreview","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.accounts.*.network","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -3053,7 +3162,7 @@
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Telegram Exec Approvals","help":"Telegram-native exec approval routing and approver authorization. Enable this only when Telegram should act as an explicit exec-approval client for the selected bot account.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals.agentFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Telegram Exec Approval Agent Filter","help":"Optional allowlist of agent IDs eligible for Telegram exec approvals, for example `[\"main\", \"ops-agent\"]`. Use this to keep approval prompts scoped to the agents you actually operate from Telegram.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals.agentFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Telegram Exec Approval Approvers","help":"Telegram user IDs allowed to approve exec requests for this bot account. Use numeric Telegram user IDs; prompts are only delivered to these approvers when target includes dm.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Telegram Exec Approval Approvers","help":"Telegram user IDs allowed to approve exec requests for this bot account. Use numeric Telegram user IDs. If you leave this unset, OpenClaw falls back to numeric owner IDs inferred from channels.telegram.allowFrom and direct-message defaultTo when possible.","hasChildren":true}
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals.approvers.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Telegram Exec Approvals Enabled","help":"Enable Telegram exec approvals for this account. When false or unset, Telegram messages/buttons cannot approve exec requests.","hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.execApprovals.sessionFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network","storage"],"label":"Telegram Exec Approval Session Filter","help":"Optional session-key filters matched as substring or regex-style patterns before Telegram approval routing is used. Use narrow patterns so Telegram approvals only appear for intended sessions.","hasChildren":true}
|
||||
@@ -3109,7 +3218,7 @@
|
||||
{"recordType":"path","path":"channels.telegram.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.linkPreview","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.telegram.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.telegram.network","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -3218,7 +3327,7 @@
|
||||
{"recordType":"path","path":"channels.twitch.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.twitch.expiresIn","kind":"channel","type":["null","number"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.twitch.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.twitch.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["bullets","code","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.twitch.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["block","bullets","code","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.twitch.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.twitch.obtainmentTimestamp","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.twitch.refreshToken","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3281,7 +3390,7 @@
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.heartbeat.useIndicator","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.mediaMaxMb","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.messagePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3346,7 +3455,7 @@
|
||||
{"recordType":"path","path":"channels.whatsapp.heartbeat.useIndicator","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.whatsapp.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.mediaMaxMb","kind":"channel","type":"integer","required":true,"defaultValue":50,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.messagePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.whatsapp.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3368,7 +3477,7 @@
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.groupAllowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.groupPolicy","kind":"channel","type":"string","required":false,"enumValues":["open","disabled","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.accounts.*.proxy","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3393,7 +3502,7 @@
|
||||
{"recordType":"path","path":"channels.zalo.groupAllowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.groupPolicy","kind":"channel","type":"string","required":false,"enumValues":["open","disabled","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.zalo.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalo.proxy","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3430,7 +3539,7 @@
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.groups.*.tools.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.messagePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.accounts.*.profile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3458,7 +3567,7 @@
|
||||
{"recordType":"path","path":"channels.zalouser.groups.*.tools.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.zalouser.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code","block"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.messagePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.zalouser.profile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3647,7 +3756,7 @@
|
||||
{"recordType":"path","path":"gateway.push","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Gateway Push Delivery","help":"Push-delivery settings used by the gateway when it needs to wake or notify paired devices. Configure relay-backed APNs here for official iOS builds; direct APNs auth remains env-based for local/manual builds.","hasChildren":true}
|
||||
{"recordType":"path","path":"gateway.push.apns","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Gateway APNs Delivery","help":"APNs delivery settings for iOS devices paired to this gateway. Use relay settings for official/TestFlight builds that register through the external push relay.","hasChildren":true}
|
||||
{"recordType":"path","path":"gateway.push.apns.relay","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Gateway APNs Relay","help":"External relay settings for relay-backed APNs sends. The gateway uses this relay for push.test, wake nudges, and reconnect wakes after a paired official iOS build publishes a relay-backed registration.","hasChildren":true}
|
||||
{"recordType":"path","path":"gateway.push.apns.relay.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","network"],"label":"Gateway APNs Relay Base URL","help":"Base HTTPS URL for the external APNs relay service used by official/TestFlight iOS builds. Keep this aligned with the relay URL baked into the iOS build so registration and send traffic hit the same deployment.","hasChildren":false}
|
||||
{"recordType":"path","path":"gateway.push.apns.relay.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","network","url-secret"],"label":"Gateway APNs Relay Base URL","help":"Base HTTPS URL for the external APNs relay service used by official/TestFlight iOS builds. Keep this aligned with the relay URL baked into the iOS build so registration and send traffic hit the same deployment.","hasChildren":false}
|
||||
{"recordType":"path","path":"gateway.push.apns.relay.timeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance"],"label":"Gateway APNs Relay Timeout (ms)","help":"Timeout in milliseconds for relay send requests from the gateway to the APNs relay (default: 10000). Increase for slower relays or networks, or lower to fail wake attempts faster.","hasChildren":false}
|
||||
{"recordType":"path","path":"gateway.reload","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network","reliability"],"label":"Config Reload","help":"Live config-reload policy for how edits are applied and when full restarts are triggered. Keep hybrid behavior for safest operational updates unless debugging reload internals.","hasChildren":true}
|
||||
{"recordType":"path","path":"gateway.reload.debounceMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["network","performance","reliability"],"label":"Config Reload Debounce (ms)","help":"Debounce window (ms) before applying config changes.","hasChildren":false}
|
||||
@@ -3796,7 +3905,9 @@
|
||||
{"recordType":"path","path":"mcp.servers.*.cwd","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"mcp.servers.*.env","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"mcp.servers.*.env.*","kind":"core","type":["boolean","number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"mcp.servers.*.url","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"mcp.servers.*.headers","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"mcp.servers.*.headers.*","kind":"core","type":["boolean","number","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["security"],"hasChildren":false}
|
||||
{"recordType":"path","path":"mcp.servers.*.url","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"mcp.servers.*.workingDirectory","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"media","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Media","help":"Top-level media behavior shared across providers and tools that handle inbound files. Keep defaults unless you need stable filenames for external processing pipelines or longer-lived inbound media retention.","hasChildren":true}
|
||||
{"recordType":"path","path":"media.preserveFilenames","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Preserve Media Filenames","help":"When enabled, uploaded media keeps its original filename instead of a generated temp-safe name. Turn this on when downstream automations depend on stable names, and leave off to reduce accidental filename leakage.","hasChildren":false}
|
||||
@@ -3832,6 +3943,7 @@
|
||||
{"recordType":"path","path":"memory.qmd.scope.rules.*.match.keyPrefix","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"memory.qmd.scope.rules.*.match.rawKeyPrefix","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"memory.qmd.searchMode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"QMD Search Mode","help":"Selects the QMD retrieval path: \"query\" uses standard query flow, \"search\" uses search-oriented retrieval, and \"vsearch\" emphasizes vector retrieval. Keep default unless tuning relevance quality.","hasChildren":false}
|
||||
{"recordType":"path","path":"memory.qmd.searchTool","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"QMD Search Tool Override","help":"Overrides the exact mcporter tool name used for QMD searches while preserving `searchMode` as the semantic retrieval mode. Use this only when your QMD MCP server exposes a custom tool such as `hybrid_search` and keep it unset for the normal built-in tool mapping.","hasChildren":false}
|
||||
{"recordType":"path","path":"memory.qmd.sessions","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"memory.qmd.sessions.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"QMD Session Indexing","help":"Indexes session transcripts into QMD so recall can include prior conversation content (experimental, default: false). Enable only when transcript memory is required and you accept larger index churn.","hasChildren":false}
|
||||
{"recordType":"path","path":"memory.qmd.sessions.exportDir","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"QMD Session Export Directory","help":"Overrides where sanitized session exports are written before QMD indexing. Use this when default state storage is constrained or when exports must land on a managed volume.","hasChildren":false}
|
||||
@@ -3937,14 +4049,14 @@
|
||||
{"recordType":"path","path":"models.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Catalog Mode","help":"Controls provider catalog behavior: \"merge\" keeps built-ins and overlays your custom providers, while \"replace\" uses only your configured providers. In \"merge\", matching provider IDs preserve non-empty agent models.json baseUrl values, while apiKey values are preserved only when the provider is not SecretRef-managed in current config/auth-profile context; SecretRef-managed providers refresh apiKey from current source markers, and matching model contextWindow/maxTokens use the higher value between explicit and implicit entries.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Providers","help":"Provider map keyed by provider ID containing connection/auth settings and concrete model definitions. Use stable provider keys so references from agents and tooling remain portable across environments.","hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*.api","kind":"core","type":"string","required":false,"enumValues":["openai-completions","openai-responses","openai-codex-responses","anthropic-messages","google-generative-ai","github-copilot","bedrock-converse-stream","ollama"],"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider API Adapter","help":"Provider API adapter selection controlling request/response compatibility handling for model calls. Use the adapter that matches your upstream provider protocol to avoid feature mismatch.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.api","kind":"core","type":"string","required":false,"enumValues":["openai-completions","openai-responses","openai-codex-responses","anthropic-messages","google-generative-ai","github-copilot","bedrock-converse-stream","ollama","azure-openai-responses"],"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider API Adapter","help":"Provider API adapter selection controlling request/response compatibility handling for model calls. Use the adapter that matches your upstream provider protocol to avoid feature mismatch.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","models","security"],"label":"Model Provider API Key","help":"Provider credential used for API-key based authentication when the provider requires direct key auth. Use secret/env substitution and avoid storing real keys in committed config files.","hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.auth","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider Auth Mode","help":"Selects provider auth style: \"api-key\" for API key auth, \"token\" for bearer token auth, \"oauth\" for OAuth credentials, and \"aws-sdk\" for AWS credential resolution. Match this to your provider requirements.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.authHeader","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider Authorization Header","help":"When true, credentials are sent via the HTTP Authorization header even if alternate auth is possible. Use this only when your provider or proxy explicitly requires Authorization forwarding.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.baseUrl","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider Base URL","help":"Base URL for the provider endpoint used to serve model requests for that provider entry. Use HTTPS endpoints and keep URLs environment-specific through config templating where needed.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.baseUrl","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["models","url-secret"],"label":"Model Provider Base URL","help":"Base URL for the provider endpoint used to serve model requests for that provider entry. Use HTTPS endpoints and keep URLs environment-specific through config templating where needed.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.headers","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider Headers","help":"Static HTTP headers merged into provider requests for tenant routing, proxy auth, or custom gateway requirements. Use this sparingly and keep sensitive header values in secrets.","hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*.headers.*","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["models","security"],"hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*.headers.*.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -3953,7 +4065,7 @@
|
||||
{"recordType":"path","path":"models.providers.*.injectNumCtxForOpenAICompat","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider Inject num_ctx (OpenAI Compat)","help":"Controls whether OpenClaw injects `options.num_ctx` for Ollama providers configured with the OpenAI-compatible adapter (`openai-completions`). Default is true. Set false only if your proxy/upstream rejects unknown `options` payload fields.","hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.models","kind":"core","type":"array","required":true,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Model Provider Model List","help":"Declared model list for a provider including identifiers, metadata, and optional compatibility/cost hints. Keep IDs exact to provider catalog values so selection and fallback resolve correctly.","hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*.models.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*.models.*.api","kind":"core","type":"string","required":false,"enumValues":["openai-completions","openai-responses","openai-codex-responses","anthropic-messages","google-generative-ai","github-copilot","bedrock-converse-stream","ollama"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.models.*.api","kind":"core","type":"string","required":false,"enumValues":["openai-completions","openai-responses","openai-codex-responses","anthropic-messages","google-generative-ai","github-copilot","bedrock-converse-stream","ollama","azure-openai-responses"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.models.*.compat","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"models.providers.*.models.*.compat.maxTokensField","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"models.providers.*.models.*.compat.nativeWebSearchTool","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -4023,6 +4135,7 @@
|
||||
{"recordType":"path","path":"plugins.entries.acpx.config.mcpServers.*.env.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.acpx.config.nonInteractivePermissions","kind":"plugin","type":"string","required":false,"enumValues":["deny","fail"],"deprecated":false,"sensitive":false,"tags":["access"],"label":"Non-Interactive Permission Policy","help":"acpx policy when interactive permission prompts are unavailable.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.acpx.config.permissionMode","kind":"plugin","type":"string","required":false,"enumValues":["approve-all","approve-reads","deny-all"],"deprecated":false,"sensitive":false,"tags":["access"],"label":"Permission Mode","help":"Default acpx permission policy for runtime prompts.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.acpx.config.pluginToolsMcpBridge","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Tools MCP Bridge","help":"Default off. When enabled, inject the built-in OpenClaw plugin-tools MCP server into ACPX sessions so ACP agents can call plugin-registered tools.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.acpx.config.queueOwnerTtlSeconds","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["access","advanced"],"label":"Queue Owner TTL Seconds","help":"Idle queue-owner TTL for acpx prompt turns. Keep this short in OpenClaw to avoid delayed completion after each turn.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.acpx.config.strictWindowsCmdWrapper","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Strict Windows cmd Wrapper","help":"Enabled by default. On Windows, reject unresolved .cmd/.bat wrappers instead of shell fallback. Disable only for compatibility with non-standard wrappers.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.acpx.config.timeoutSeconds","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","performance"],"label":"Prompt Timeout Seconds","help":"Optional acpx timeout for each runtime turn.","hasChildren":false}
|
||||
@@ -4043,6 +4156,15 @@
|
||||
{"recordType":"path","path":"plugins.entries.amazon-bedrock.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.amazon-bedrock.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-provider","help":"OpenClaw Anthropic provider plugin (plugin: anthropic)","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-vertex-provider","help":"OpenClaw Anthropic Vertex provider plugin (plugin: anthropic-vertex)","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-vertex-provider Config","help":"Plugin-defined config payload for anthropic-vertex.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/anthropic-vertex-provider","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic-vertex.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/anthropic-provider Config","help":"Plugin-defined config payload for anthropic.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/anthropic-provider","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.anthropic.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
|
||||
@@ -4160,9 +4282,9 @@
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.background","kind":"plugin","type":"boolean","required":false,"defaultValue":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Default Background Highlights","help":"Show added/removed background highlights by default.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.diffIndicators","kind":"plugin","type":"string","required":false,"enumValues":["bars","classic","none"],"defaultValue":"bars","deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Diff Indicator Style","help":"Choose added/removed indicators style.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fileFormat","kind":"plugin","type":"string","required":false,"enumValues":["png","pdf"],"defaultValue":"png","deprecated":false,"sensitive":false,"tags":["storage"],"label":"Default File Format","help":"Rendered file format for file mode (PNG or PDF).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fileMaxWidth","kind":"plugin","type":"number","required":false,"defaultValue":960,"deprecated":false,"sensitive":false,"tags":["performance","storage"],"label":"Default File Max Width","help":"Maximum file render width in CSS pixels.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fileMaxWidth","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance","storage"],"label":"Default File Max Width","help":"Maximum file render width in CSS pixels.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fileQuality","kind":"plugin","type":"string","required":false,"enumValues":["standard","hq","print"],"defaultValue":"standard","deprecated":false,"sensitive":false,"tags":["storage"],"label":"Default File Quality","help":"Quality preset for PNG/PDF rendering.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fileScale","kind":"plugin","type":"number","required":false,"defaultValue":2,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Default File Scale","help":"Device scale factor used while rendering file artifacts.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fileScale","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Default File Scale","help":"Device scale factor used while rendering file artifacts.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fontFamily","kind":"plugin","type":"string","required":false,"defaultValue":"Fira Code","deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Default Font","help":"Preferred font family name for diff content and headers.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.fontSize","kind":"plugin","type":"number","required":false,"defaultValue":15,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Default Font Size","help":"Base diff font size in pixels.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.diffs.config.defaults.format","kind":"plugin","type":"string","required":false,"enumValues":["png","pdf"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -4248,7 +4370,7 @@
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/firecrawl-plugin Config","help":"Plugin-defined config payload for firecrawl.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.config.webSearch","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.config.webSearch.apiKey","kind":"plugin","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"label":"Firecrawl Search API Key","help":"Firecrawl API key for web search (fallback: FIRECRAWL_API_KEY env var).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Firecrawl Search Base URL","help":"Firecrawl Search base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","url-secret"],"label":"Firecrawl Search Base URL","help":"Firecrawl Search base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/firecrawl-plugin","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.firecrawl.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
|
||||
@@ -4418,7 +4540,7 @@
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.config.dbPath","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","storage"],"label":"Database Path","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.config.embedding","kind":"plugin","type":"object","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.config.embedding.apiKey","kind":"plugin","type":"string","required":true,"deprecated":false,"sensitive":true,"tags":["auth","security","storage"],"label":"OpenAI API Key","help":"API key for OpenAI embeddings (or use ${OPENAI_API_KEY})","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.config.embedding.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","storage"],"label":"Base URL","help":"Base URL for compatible providers (e.g. http://localhost:11434/v1)","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.config.embedding.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","storage","url-secret"],"label":"Base URL","help":"Base URL for compatible providers (e.g. http://localhost:11434/v1)","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.config.embedding.dimensions","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","storage"],"label":"Dimensions","help":"Vector dimensions for custom models (required for non-standard models)","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.config.embedding.model","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models","storage"],"label":"Embedding Model","help":"OpenAI embedding model to use","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.memory-lancedb.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Enable @openclaw/memory-lancedb","hasChildren":false}
|
||||
@@ -4477,7 +4599,7 @@
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/moonshot-provider Config","help":"Plugin-defined config payload for moonshot.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.config.webSearch","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.config.webSearch.apiKey","kind":"plugin","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"label":"Kimi Search API Key","help":"Moonshot/Kimi API key (fallback: KIMI_API_KEY or MOONSHOT_API_KEY env var).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Kimi Search Base URL","help":"Kimi base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","url-secret"],"label":"Kimi Search Base URL","help":"Kimi base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.config.webSearch.model","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Kimi Search Model","help":"Kimi model override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/moonshot-provider","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.moonshot.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
|
||||
@@ -4602,7 +4724,7 @@
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/perplexity-plugin Config","help":"Plugin-defined config payload for perplexity.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.config.webSearch","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.config.webSearch.apiKey","kind":"plugin","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"label":"Perplexity API Key","help":"Perplexity or OpenRouter API key for web search.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Perplexity Base URL","help":"Optional Perplexity/OpenRouter chat-completions base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","url-secret"],"label":"Perplexity Base URL","help":"Optional Perplexity/OpenRouter chat-completions base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.config.webSearch.model","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Perplexity Model","help":"Optional Sonar/OpenRouter model override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/perplexity-plugin","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.perplexity.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
|
||||
@@ -4629,6 +4751,35 @@
|
||||
{"recordType":"path","path":"plugins.entries.qianfan.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qianfan.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qianfan.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/qqbot","help":"OpenClaw QQ Bot channel plugin (plugin: qqbot)","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/qqbot Config","help":"Plugin-defined config payload for qqbot.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.accounts","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.accounts.*","kind":"plugin","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.allowFrom","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.allowFrom.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.appId","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.audioFormatPolicy","kind":"plugin","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.clientSecret","kind":"plugin","required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.clientSecretFile","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","storage"],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.defaultAccount","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.markdownSupport","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.name","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.stt","kind":"plugin","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.systemPrompt","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.tts","kind":"plugin","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.upgradeMode","kind":"plugin","type":"string","required":false,"enumValues":["doc","hot-reload"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.upgradeUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.urlDirectUpload","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.voiceDirectUploadFormats","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.config.voiceDirectUploadFormats.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/qqbot","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.qqbot.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.sglang","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/sglang-provider","help":"OpenClaw SGLang provider plugin (plugin: sglang)","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.sglang.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/sglang-provider Config","help":"Plugin-defined config payload for sglang.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.sglang.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/sglang-provider","hasChildren":false}
|
||||
@@ -4687,7 +4838,7 @@
|
||||
{"recordType":"path","path":"plugins.entries.tavily.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/tavily-plugin Config","help":"Plugin-defined config payload for tavily.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.tavily.config.webSearch","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.tavily.config.webSearch.apiKey","kind":"plugin","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"label":"Tavily API Key","help":"Tavily API key for web search and extraction (fallback: TAVILY_API_KEY env var).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.tavily.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Tavily Base URL","help":"Tavily API base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.tavily.config.webSearch.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","url-secret"],"label":"Tavily Base URL","help":"Tavily API base URL override.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.tavily.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/tavily-plugin","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.tavily.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.tavily.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
|
||||
@@ -4826,31 +4977,6 @@
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.transcriptTimeoutMs","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.auto","kind":"plugin","type":"string","required":false,"enumValues":["off","always","inbound","tagged"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.lang","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.outputFormat","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.pitch","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.proxy","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.rate","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.saveSubtitles","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.timeoutMs","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.voice","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.edge.volume","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.apiKey","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["advanced","auth","media","security"],"label":"ElevenLabs API Key","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.applyTextNormalization","kind":"plugin","type":"string","required":false,"enumValues":["auto","on","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media"],"label":"ElevenLabs Base URL","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.languageCode","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.modelId","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media","models"],"label":"ElevenLabs Model ID","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.seed","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.voiceId","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media"],"label":"ElevenLabs Voice ID","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.voiceSettings","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.voiceSettings.similarityBoost","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.voiceSettings.speed","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.voiceSettings.stability","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.voiceSettings.style","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.elevenlabs.voiceSettings.useSpeakerBoost","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.maxTextLength","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.mode","kind":"plugin","type":"string","required":false,"enumValues":["final","all"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -4863,15 +4989,54 @@
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.modelOverrides.allowVoice","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.modelOverrides.allowVoiceSettings","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.modelOverrides.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.openai","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.openai.apiKey","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["advanced","auth","media","security"],"label":"OpenAI API Key","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.openai.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.openai.instructions","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.openai.model","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media","models"],"label":"OpenAI TTS Model","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.openai.speed","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.openai.voice","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media"],"label":"OpenAI TTS Voice","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.prefsPath","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.provider","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media"],"label":"TTS Provider Override","help":"Deep-merges with messages.tts (Microsoft is ignored for calls).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.*","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.*.apiKey","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","media","security"],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.lang","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.outputFormat","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.pitch","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.proxy","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.rate","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.saveSubtitles","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.timeoutMs","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.voice","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.edge.volume","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.apiKey","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["advanced","auth","media","security"],"label":"ElevenLabs API Key","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.applyTextNormalization","kind":"plugin","type":"string","required":false,"enumValues":["auto","on","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media","url-secret"],"label":"ElevenLabs Base URL","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.languageCode","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.modelId","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media","models"],"label":"ElevenLabs Model ID","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.seed","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.voiceId","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media"],"label":"ElevenLabs Voice ID","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.voiceSettings","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.voiceSettings.similarityBoost","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.voiceSettings.speed","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.voiceSettings.stability","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.voiceSettings.style","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.elevenlabs.voiceSettings.useSpeakerBoost","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.lang","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.outputFormat","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.pitch","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.proxy","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.rate","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.saveSubtitles","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.timeoutMs","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.voice","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.microsoft.volume","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.openai","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.openai.apiKey","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["advanced","auth","media","security"],"label":"OpenAI API Key","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.openai.baseUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.openai.instructions","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.openai.model","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media","models"],"label":"OpenAI TTS Model","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.openai.speed","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.providers.openai.voice","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced","media"],"label":"OpenAI TTS Voice","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.summaryModel","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tts.timeoutMs","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.entries.voice-call.config.tunnel","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -4974,7 +5139,7 @@
|
||||
{"recordType":"path","path":"plugins.installs.*.clawhubPackage","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.installs.*.clawhubUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.installs.*.installedAt","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Install Time","help":"ISO timestamp of last install/update.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.installs.*.installPath","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Plugin Install Path","help":"Resolved install directory (usually ~/.openclaw/extensions/<id>).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.installs.*.installPath","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Plugin Install Path","help":"Resolved install directory for the installed plugin bundle.","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.installs.*.integrity","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Resolved Integrity","help":"Resolved npm dist integrity hash for the fetched artifact (if reported by npm).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.installs.*.marketplaceName","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Marketplace Name","help":"Marketplace display name recorded for marketplace-backed plugin installs (if available).","hasChildren":false}
|
||||
{"recordType":"path","path":"plugins.installs.*.marketplacePlugin","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Marketplace Plugin","help":"Plugin entry name inside the source marketplace, used for later updates.","hasChildren":false}
|
||||
@@ -5178,7 +5343,7 @@
|
||||
{"recordType":"path","path":"tools.exec.ask","kind":"core","type":"string","required":false,"enumValues":["off","on-miss","always"],"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Exec Ask","help":"Approval strategy for when exec commands require human confirmation before running. Use stricter ask behavior in shared channels and lower-friction settings in private operator contexts.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.exec.backgroundMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.exec.cleanupMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.exec.host","kind":"core","type":"string","required":false,"enumValues":["sandbox","gateway","node"],"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Exec Host","help":"Selects execution host strategy for shell commands, typically controlling local vs delegated execution environment. Use the safest host mode that still satisfies your automation requirements.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.exec.host","kind":"core","type":"string","required":false,"enumValues":["auto","sandbox","gateway","node"],"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Exec Target","help":"Selects execution target strategy for shell commands. Use \"auto\" for runtime-aware behavior (sandbox when available, otherwise gateway), or pin sandbox/gateway/node explicitly when you need a fixed surface.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.exec.node","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Exec Node Binding","help":"Node binding configuration for exec tooling when command execution is delegated through connected nodes. Use explicit node binding only when multi-node routing is required.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.exec.notifyOnExit","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Exec Notify On Exit","help":"When true (default), backgrounded exec sessions on exit and node exec lifecycle events enqueue a system event and request a heartbeat.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.exec.notifyOnExitEmptySuccess","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Exec Notify On Empty Success","help":"When true, successful backgrounded exec exits with empty output still enqueue a completion system event (default: false).","hasChildren":false}
|
||||
@@ -5238,7 +5403,7 @@
|
||||
{"recordType":"path","path":"tools.media.audio.attachments.maxAttachments","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.attachments.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.attachments.prefer","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","tools","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.deepgram","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.audio.deepgram.detectLanguage","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.deepgram.punctuate","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -5255,7 +5420,7 @@
|
||||
{"recordType":"path","path":"tools.media.audio.models.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.audio.models.*.args","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.audio.models.*.args.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","tools","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.models.*.capabilities","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.audio.models.*.capabilities.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.audio.models.*.command","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -5299,7 +5464,7 @@
|
||||
{"recordType":"path","path":"tools.media.image.attachments.maxAttachments","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.attachments.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.attachments.prefer","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","tools","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.deepgram","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.image.deepgram.detectLanguage","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.deepgram.punctuate","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -5316,7 +5481,7 @@
|
||||
{"recordType":"path","path":"tools.media.image.models.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.image.models.*.args","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.image.models.*.args.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","tools","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.models.*.capabilities","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.image.models.*.capabilities.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.image.models.*.command","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -5358,7 +5523,7 @@
|
||||
{"recordType":"path","path":"tools.media.models.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.models.*.args","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.models.*.args.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","tools","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.models.*.capabilities","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.models.*.capabilities.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.models.*.command","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -5386,7 +5551,7 @@
|
||||
{"recordType":"path","path":"tools.media.video.attachments.maxAttachments","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.attachments.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.attachments.prefer","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","tools","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.deepgram","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.video.deepgram.detectLanguage","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.deepgram.punctuate","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -5403,7 +5568,7 @@
|
||||
{"recordType":"path","path":"tools.media.video.models.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.video.models.*.args","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.video.models.*.args.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.models.*.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["media","tools","url-secret"],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.models.*.capabilities","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.media.video.models.*.capabilities.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.media.video.models.*.command","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -5487,7 +5652,7 @@
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Firecrawl Base URL","help":"Firecrawl base URL (e.g. https://api.firecrawl.dev or custom endpoint).","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools","url-secret"],"label":"Firecrawl Base URL","help":"Firecrawl base URL (e.g. https://api.firecrawl.dev or custom endpoint).","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable Firecrawl Fallback","help":"Enable Firecrawl fallback for web_fetch (if configured).","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.maxAgeMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Firecrawl Cache Max Age (ms)","help":"Firecrawl maxAge (ms) for cached results when supported by the API.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.fetch.firecrawl.onlyMainContent","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Firecrawl Main Content Only","help":"When true, Firecrawl returns only the main content (default: true).","hasChildren":false}
|
||||
@@ -5504,53 +5669,15 @@
|
||||
{"recordType":"path","path":"tools.web.search.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.brave","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.brave.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.brave.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.brave.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.brave.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.brave.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.brave.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.brave.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.cacheTtlMinutes","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance","storage","tools"],"label":"Web Search Cache TTL (min)","help":"Cache TTL in minutes for web_search results.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable Web Search Tool","help":"Enable the web_search tool (requires a provider API key).","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.firecrawl","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.firecrawl.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.firecrawl.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.firecrawl.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.firecrawl.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.firecrawl.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.firecrawl.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.gemini","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.gemini.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.gemini.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.gemini.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.gemini.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.gemini.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.gemini.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.grok","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.grok.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.grok.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.grok.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.grok.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.grok.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.grok.inlineCitations","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.grok.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.kimi","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.kimi.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.kimi.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.kimi.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.kimi.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.kimi.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.kimi.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable Web Search Tool","help":"Enable managed web_search and optional Codex-native search for eligible models.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.maxResults","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Search Max Results","help":"Number of results to return (1-10).","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.perplexity","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.perplexity.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.perplexity.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.perplexity.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.perplexity.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.perplexity.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.perplexity.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.openaiCodex","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"tools.web.search.openaiCodex.allowedDomains","kind":"core","required":false,"deprecated":false,"sensitive":false,"tags":["access","tools"],"label":"Codex Allowed Domains","help":"Optional domain allowlist passed to the native Codex web_search tool.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.openaiCodex.contextSize","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Codex Search Context Size","help":"Native Codex search context size hint: \"low\", \"medium\", or \"high\".","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.openaiCodex.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable Native Codex Web Search","help":"Enable native Codex web search for Codex-capable models.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.openaiCodex.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Codex Web Search Mode","help":"Native Codex web search mode: \"cached\" (default) or \"live\".","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.openaiCodex.userLocation","kind":"core","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Web Search Provider","help":"Search provider id. Auto-detected from available API keys if omitted.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.search.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Search Timeout (sec)","help":"Timeout in seconds for web_search requests.","hasChildren":false}
|
||||
{"recordType":"path","path":"tools.web.x_search","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -44,6 +44,13 @@ Token credentials (`type: "token"`) support inline `token` and/or `tokenRef`.
|
||||
2. For eligible profiles, token material may be resolved from inline value or `tokenRef`.
|
||||
3. Unresolvable refs produce `unresolved_ref` in `models status --probe` output.
|
||||
|
||||
## OAuth SecretRef Policy Guard
|
||||
|
||||
- SecretRef input is for static credentials only.
|
||||
- If a profile credential is `type: "oauth"`, SecretRef objects are not supported for that profile credential material.
|
||||
- If `auth.profiles.<id>.mode` is `"oauth"`, SecretRef-backed `keyRef`/`tokenRef` input for that profile is rejected.
|
||||
- Violations are hard failures in startup/reload auth resolution paths.
|
||||
|
||||
## Legacy-Compatible Messaging
|
||||
|
||||
For script compatibility, probe errors keep this first line unchanged:
|
||||
|
||||
109
docs/automation/clawflow.md
Normal file
109
docs/automation/clawflow.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
summary: "ClawFlow workflow orchestration for background tasks and detached runs"
|
||||
read_when:
|
||||
- You want a flow to own one or more detached tasks
|
||||
- You want to inspect or cancel a background job as a unit
|
||||
- You want to understand how flows relate to tasks and background work
|
||||
title: "ClawFlow"
|
||||
---
|
||||
|
||||
# ClawFlow
|
||||
|
||||
ClawFlow is the flow layer above [Background Tasks](/automation/tasks). Tasks still track detached work. ClawFlow groups those task runs into a single job, keeps the parent owner context, and gives you a flow-level control surface.
|
||||
|
||||
Use ClawFlow when the work is more than a single detached run. A flow can still be one task, but it can also coordinate multiple tasks in a simple linear sequence.
|
||||
|
||||
## TL;DR
|
||||
|
||||
- Tasks are the execution records.
|
||||
- ClawFlow is the job-level wrapper above tasks.
|
||||
- A flow keeps one owner/session context for the whole job.
|
||||
- Use `openclaw flows list`, `openclaw flows show`, and `openclaw flows cancel` to inspect or manage flows.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
openclaw flows list
|
||||
openclaw flows show <flow-id-or-owner-session>
|
||||
openclaw flows cancel <flow-id-or-owner-session>
|
||||
```
|
||||
|
||||
## How it relates to tasks
|
||||
|
||||
Background tasks still do the low-level work:
|
||||
|
||||
- ACP runs
|
||||
- subagent runs
|
||||
- cron executions
|
||||
- CLI-initiated runs
|
||||
|
||||
ClawFlow sits above that ledger:
|
||||
|
||||
- it keeps related task runs under one flow id
|
||||
- it tracks the flow state separately from the individual task state
|
||||
- it makes blocked or multi-step work easier to inspect from one place
|
||||
|
||||
For a single detached run, the flow can be a one-task flow. For more structured work, ClawFlow can keep multiple task runs under the same job.
|
||||
|
||||
## Runtime substrate
|
||||
|
||||
ClawFlow is the runtime substrate, not a workflow language.
|
||||
|
||||
It owns:
|
||||
|
||||
- the flow id
|
||||
- the owner session and return context
|
||||
- waiting state
|
||||
- small persisted outputs
|
||||
- finish, fail, cancel, and blocked state
|
||||
|
||||
It does **not** own branching or business logic. Put that in the authoring layer that sits above it:
|
||||
|
||||
- Lobster
|
||||
- acpx
|
||||
- plain TypeScript helpers
|
||||
- bundled skills
|
||||
|
||||
In practice, authoring layers target a small runtime surface:
|
||||
|
||||
- `createFlow(...)`
|
||||
- `runTaskInFlow(...)`
|
||||
- `setFlowWaiting(...)`
|
||||
- `setFlowOutput(...)`
|
||||
- `appendFlowOutput(...)`
|
||||
- `emitFlowUpdate(...)`
|
||||
- `resumeFlow(...)`
|
||||
- `finishFlow(...)`
|
||||
- `failFlow(...)`
|
||||
|
||||
That keeps flow ownership and return-to-thread behavior in core without forcing a single DSL on top of it.
|
||||
|
||||
## Authoring pattern
|
||||
|
||||
The intended shape is linear:
|
||||
|
||||
1. Create one flow for the job.
|
||||
2. Run one detached task under that flow.
|
||||
3. Wait for the child task or outside event.
|
||||
4. Resume the flow in the caller.
|
||||
5. Spawn the next child task or finish.
|
||||
|
||||
ClawFlow persists the minimal state needed to resume that job: the current step, the task it is waiting on, and a small output bag for handoff between steps.
|
||||
|
||||
## CLI surface
|
||||
|
||||
The flow CLI is intentionally small:
|
||||
|
||||
- `openclaw flows list` shows active and recent flows
|
||||
- `openclaw flows show <lookup>` shows one flow and its linked tasks
|
||||
- `openclaw flows cancel <lookup>` cancels the flow and any active child tasks
|
||||
|
||||
`flows show` also surfaces the current wait target and any stored output keys, which is often enough to answer "what is this job waiting on?" without digging into every child task.
|
||||
|
||||
The lookup token accepts either a flow id or the owner session key.
|
||||
|
||||
## Related
|
||||
|
||||
- [Background Tasks](/automation/tasks) — detached work ledger
|
||||
- [CLI: flows](/cli/flows) — flow inspection and control commands
|
||||
- [Cron Jobs](/automation/cron-jobs) — scheduled jobs that may create tasks
|
||||
@@ -270,7 +270,7 @@ Isolated jobs (`agentTurn`) can set `lightContext: true` to run with lightweight
|
||||
Isolated jobs can deliver output to a channel via the top-level `delivery` config:
|
||||
|
||||
- `delivery.mode`: `announce` (channel delivery), `webhook` (HTTP POST), or `none`.
|
||||
- `delivery.channel`: `whatsapp` / `telegram` / `discord` / `slack` / `signal` / `imessage` / `irc` / `googlechat` / `line` / `last`, plus extension channels like `msteams` / `mattermost` (plugins).
|
||||
- `delivery.channel`: `last` or any deliverable channel id, for example `discord`, `matrix`, `telegram`, or `whatsapp`.
|
||||
- `delivery.to`: channel-specific recipient target.
|
||||
|
||||
`announce` delivery is only valid for isolated jobs (`sessionTarget: "isolated"`).
|
||||
|
||||
@@ -525,6 +525,11 @@ If the gateway is unavailable or does not support plugin approvals, the tool cal
|
||||
|
||||
Runs after the built-in install security scan and before installation continues. OpenClaw fires this hook for interactive skill installs as well as plugin bundle, package, and single-file installs.
|
||||
|
||||
Default behavior differs by target type:
|
||||
|
||||
- Plugin installs fail closed on built-in scan `critical` findings and scan errors unless the operator explicitly uses `openclaw plugins install --dangerously-force-unsafe-install`.
|
||||
- Skill installs still surface built-in scan findings and scan errors as warnings and continue by default.
|
||||
|
||||
Return fields:
|
||||
|
||||
- **`findings`**: Additional scan findings to surface as warnings
|
||||
|
||||
@@ -13,46 +13,34 @@ OpenClaw provides several automation mechanisms, each suited to different use ca
|
||||
|
||||
## Quick decision guide
|
||||
|
||||
```
|
||||
Do you need something to run on a schedule?
|
||||
YES → Is exact timing critical?
|
||||
YES → Cron (isolated)
|
||||
NO → Can it batch with other checks?
|
||||
YES → Heartbeat
|
||||
NO → Cron
|
||||
NO → Continue...
|
||||
|
||||
Do you need to react to an event (message, tool call, session change)?
|
||||
YES → Hooks (or plugin hooks)
|
||||
|
||||
Do you need to receive external HTTP events?
|
||||
YES → Webhooks
|
||||
|
||||
Do you want persistent instructions the agent always follows?
|
||||
YES → Standing Orders
|
||||
|
||||
Do you want to track what background work happened?
|
||||
→ Background Tasks (automatic for cron, ACP, subagents)
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A{Run on a schedule?} -->|Yes| B{Exact timing needed?}
|
||||
A -->|No| C{React to events?}
|
||||
B -->|Yes| D[Cron]
|
||||
B -->|No| E[Heartbeat]
|
||||
C -->|Yes| F[Hooks]
|
||||
C -->|No| G[Standing Orders]
|
||||
```
|
||||
|
||||
## Mechanisms at a glance
|
||||
|
||||
| Mechanism | What it does | Runs in | Creates task record |
|
||||
|---|---|---|---|
|
||||
| [Heartbeat](/gateway/heartbeat) | Periodic main-session turn — batches multiple checks | Main session | No |
|
||||
| [Cron](/automation/cron-jobs) | Scheduled jobs with precise timing | Main or isolated session | Yes (all types) |
|
||||
| [Background Tasks](/automation/tasks) | Tracks detached work (cron, ACP, subagents, CLI) | N/A (ledger) | N/A |
|
||||
| [Hooks](/automation/hooks) | Event-driven scripts triggered by agent lifecycle events | Hook runner | No |
|
||||
| [Standing Orders](/automation/standing-orders) | Persistent instructions injected into the system prompt | Main session | No |
|
||||
| [Webhooks](/automation/webhook) | Receive inbound HTTP events and route to the agent | Gateway HTTP | No |
|
||||
| Mechanism | What it does | Runs in | Creates task record |
|
||||
| ---------------------------------------------- | -------------------------------------------------------- | ------------------------ | ------------------- |
|
||||
| [Heartbeat](/gateway/heartbeat) | Periodic main-session turn — batches multiple checks | Main session | No |
|
||||
| [Cron](/automation/cron-jobs) | Scheduled jobs with precise timing | Main or isolated session | Yes (all types) |
|
||||
| [Background Tasks](/automation/tasks) | Tracks detached work (cron, ACP, subagents, CLI) | N/A (ledger) | N/A |
|
||||
| [Hooks](/automation/hooks) | Event-driven scripts triggered by agent lifecycle events | Hook runner | No |
|
||||
| [Standing Orders](/automation/standing-orders) | Persistent instructions injected into the system prompt | Main session | No |
|
||||
| [Webhooks](/automation/webhook) | Receive inbound HTTP events and route to the agent | Gateway HTTP | No |
|
||||
|
||||
### Specialized automation
|
||||
|
||||
| Mechanism | What it does |
|
||||
|---|---|
|
||||
| [Gmail PubSub](/automation/gmail-pubsub) | Real-time Gmail notifications via Google PubSub |
|
||||
| [Polling](/automation/poll) | Periodic data source checks (RSS, APIs, etc.) |
|
||||
| [Auth Monitoring](/automation/auth-monitoring) | Credential health and expiry alerts |
|
||||
| Mechanism | What it does |
|
||||
| ---------------------------------------------- | ----------------------------------------------- |
|
||||
| [Gmail PubSub](/automation/gmail-pubsub) | Real-time Gmail notifications via Google PubSub |
|
||||
| [Polling](/automation/poll) | Periodic data source checks (RSS, APIs, etc.) |
|
||||
| [Auth Monitoring](/automation/auth-monitoring) | Credential health and expiry alerts |
|
||||
|
||||
## How they work together
|
||||
|
||||
@@ -63,11 +51,19 @@ The most effective setups combine multiple mechanisms:
|
||||
3. **Hooks** react to specific events (tool calls, session resets, compaction) with custom scripts.
|
||||
4. **Standing Orders** give the agent persistent context ("always check the project board before replying").
|
||||
5. **Background Tasks** automatically track all detached work so you can inspect and audit it.
|
||||
6. **ClawFlow** groups related detached tasks into a single flow when the work needs a higher-level job view.
|
||||
|
||||
See [Cron vs Heartbeat](/automation/cron-vs-heartbeat) for a detailed comparison of the two scheduling mechanisms.
|
||||
|
||||
## ClawFlow
|
||||
|
||||
ClawFlow sits above [Background Tasks](/automation/tasks). Tasks still track the detached runs, while ClawFlow groups related task runs into one job that you can inspect or cancel from the CLI.
|
||||
|
||||
See [ClawFlow](/automation/clawflow) for the flow overview and [CLI: flows](/cli/flows) for the command surface.
|
||||
|
||||
## Related
|
||||
|
||||
- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) — detailed comparison guide
|
||||
- [ClawFlow](/automation/clawflow) — flow-level orchestration above tasks
|
||||
- [Troubleshooting](/automation/troubleshooting) — debugging automation issues
|
||||
- [Configuration Reference](/gateway/configuration-reference) — all config keys
|
||||
|
||||
@@ -210,6 +210,12 @@ A sweeper runs every **60 seconds** and handles three things:
|
||||
|
||||
## How tasks relate to other systems
|
||||
|
||||
### Tasks and ClawFlow
|
||||
|
||||
ClawFlow is the flow layer above tasks. A flow groups one or more task runs into a single job, owns the parent session context, and gives you a higher-level control surface for blocked or multi-step work.
|
||||
|
||||
See [ClawFlow](/automation/clawflow) for the flow overview and [CLI: flows](/cli/flows) for the command surface.
|
||||
|
||||
### Tasks and cron
|
||||
|
||||
A cron job **definition** lives in `~/.openclaw/cron/jobs.json`. **Every** cron execution creates a task record — both main-session and isolated. Main-session cron tasks default to `silent` notify policy so they track without generating notifications.
|
||||
@@ -233,7 +239,9 @@ A task's `runId` links to the agent run doing the work. Agent lifecycle events (
|
||||
## Related
|
||||
|
||||
- [Automation Overview](/automation) — all automation mechanisms at a glance
|
||||
- [ClawFlow](/automation/clawflow) — job-level orchestration above tasks
|
||||
- [Cron Jobs](/automation/cron-jobs) — scheduling background work
|
||||
- [Cron vs Heartbeat](/automation/cron-vs-heartbeat) — choosing the right mechanism
|
||||
- [Heartbeat](/gateway/heartbeat) — periodic main-session turns
|
||||
- [CLI: flows](/cli/flows) — flow inspection and control commands
|
||||
- [CLI: Tasks](/cli/index#tasks) — CLI command reference
|
||||
|
||||
@@ -84,7 +84,7 @@ Payload:
|
||||
- `sessionKey` optional (string): The key used to identify the agent's session. By default this field is rejected unless `hooks.allowRequestSessionKey=true`.
|
||||
- `wakeMode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check.
|
||||
- `deliver` optional (boolean): If `true`, the agent's response will be sent to the messaging channel. Defaults to `true`. Responses that are only heartbeat acknowledgments are automatically skipped.
|
||||
- `channel` optional (string): The messaging channel for delivery. Core channels: `last`, `whatsapp`, `telegram`, `discord`, `slack`, `signal`, `imessage`, `irc`, `googlechat`, `line`. Extension channels (plugins): `msteams`, `mattermost`, and others. Defaults to `last`.
|
||||
- `channel` optional (string): The messaging channel for delivery. Use `last` or any configured channel or plugin id, for example `discord`, `matrix`, `telegram`, or `whatsapp`. Defaults to `last`.
|
||||
- `to` optional (string): The recipient identifier for the channel (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack/Mattermost (plugin), conversation ID for Microsoft Teams). Defaults to the last recipient in the main session.
|
||||
- `model` optional (string): Model override (e.g., `anthropic/claude-sonnet-4-6` or an alias). Must be in the allowed model list if restricted.
|
||||
- `thinking` optional (string): Thinking level override (e.g., `low`, `medium`, `high`).
|
||||
|
||||
@@ -25,6 +25,7 @@ Text is supported everywhere; media and reactions vary by channel.
|
||||
- [Microsoft Teams](/channels/msteams) — Bot Framework; enterprise support (plugin, installed separately).
|
||||
- [Nextcloud Talk](/channels/nextcloud-talk) — Self-hosted chat via Nextcloud Talk (plugin, installed separately).
|
||||
- [Nostr](/channels/nostr) — Decentralized DMs via NIP-04 (plugin, installed separately).
|
||||
- [QQ Bot](/channels/qqbot) — QQ Bot API; private chat, group chat, and rich media.
|
||||
- [Signal](/channels/signal) — signal-cli; privacy-focused.
|
||||
- [Slack](/channels/slack) — Bolt SDK; workspace apps.
|
||||
- [Synology Chat](/channels/synology-chat) — Synology NAS Chat via outgoing+incoming webhooks (plugin, installed separately).
|
||||
|
||||
@@ -143,6 +143,7 @@ This is a practical baseline config with DM pairing, room allowlist, and E2EE en
|
||||
|
||||
dm: {
|
||||
policy: "pairing",
|
||||
threadReplies: "off",
|
||||
},
|
||||
|
||||
groupPolicy: "allowlist",
|
||||
@@ -501,9 +502,10 @@ The repair flow does not delete old rooms automatically. It only picks the healt
|
||||
|
||||
Matrix supports native Matrix threads for both automatic replies and message-tool sends.
|
||||
|
||||
- `threadReplies: "off"` keeps replies top-level.
|
||||
- `threadReplies: "off"` keeps replies top-level and keeps inbound threaded messages on the parent session.
|
||||
- `threadReplies: "inbound"` replies inside a thread only when the inbound message was already in that thread.
|
||||
- `threadReplies: "always"` keeps room replies in a thread rooted at the triggering message.
|
||||
- `threadReplies: "always"` keeps room replies in a thread rooted at the triggering message and routes that conversation through the matching thread-scoped session from the first triggering message.
|
||||
- `dm.threadReplies` overrides the top-level setting for DMs only. For example, you can keep room threads isolated while keeping DMs flat.
|
||||
- Inbound threaded messages include the thread root message as extra agent context.
|
||||
- Message-tool sends now auto-inherit the current Matrix thread when the target is the same room, or the same DM user target, unless an explicit `threadId` is provided.
|
||||
- Runtime thread bindings are supported for Matrix. `/focus`, `/unfocus`, `/agents`, `/session idle`, `/session max-age`, and thread-bound `/acp spawn` now work in Matrix rooms and DMs.
|
||||
@@ -577,6 +579,16 @@ Current behavior:
|
||||
- `reactionNotifications: "off"` disables reaction system events.
|
||||
- Reaction removals are still not synthesized into system events because Matrix surfaces those as redactions, not as standalone `m.reaction` removals.
|
||||
|
||||
## History context
|
||||
|
||||
- `channels.matrix.historyLimit` controls how many recent room messages are included as `InboundHistory` when a Matrix room message triggers the agent.
|
||||
- It falls back to `messages.groupChat.historyLimit`. Set `0` to disable.
|
||||
- Matrix room history is room-only. DMs keep using normal session history.
|
||||
- Matrix room history is pending-only: OpenClaw buffers room messages that did not trigger a reply yet, then snapshots that window when a mention or other trigger arrives.
|
||||
- The current trigger message is not included in `InboundHistory`; it stays in the main inbound body for that turn.
|
||||
- Retries of the same Matrix event reuse the original history snapshot instead of drifting forward to newer room messages.
|
||||
- Fetched room context (including reply and thread context lookups) is filtered by sender allowlists (`groupAllowFrom`), so non-allowlisted messages are excluded from agent context.
|
||||
|
||||
## DM and room policy example
|
||||
|
||||
```json5
|
||||
@@ -586,6 +598,7 @@ Current behavior:
|
||||
dm: {
|
||||
policy: "allowlist",
|
||||
allowFrom: ["@admin:example.org"],
|
||||
threadReplies: "off",
|
||||
},
|
||||
groupPolicy: "allowlist",
|
||||
groupAllowFrom: ["@admin:example.org"],
|
||||
@@ -633,6 +646,7 @@ See [Pairing](/channels/pairing) for the shared DM pairing flow and storage layo
|
||||
dm: {
|
||||
policy: "allowlist",
|
||||
allowFrom: ["@ops:example.org"],
|
||||
threadReplies: "off",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -732,6 +746,7 @@ Live directory lookup uses the logged-in Matrix account:
|
||||
- `groupPolicy`: `open`, `allowlist`, or `disabled`.
|
||||
- `groupAllowFrom`: allowlist of user IDs for room traffic.
|
||||
- `groupAllowFrom` entries should be full Matrix user IDs. Unresolved names are ignored at runtime.
|
||||
- `historyLimit`: max room messages to include as group history context. Falls back to `messages.groupChat.historyLimit`. Set `0` to disable.
|
||||
- `replyToMode`: `off`, `first`, or `all`.
|
||||
- `streaming`: `off` (default) or `partial`. `partial` enables single-message draft previews with edit-in-place updates.
|
||||
- `threadReplies`: `off`, `inbound`, or `always`.
|
||||
@@ -747,8 +762,9 @@ Live directory lookup uses the logged-in Matrix account:
|
||||
- `mediaMaxMb`: media size cap in MB for Matrix media handling. It applies to outbound sends and inbound media processing.
|
||||
- `autoJoin`: invite auto-join policy (`always`, `allowlist`, `off`). Default: `off`.
|
||||
- `autoJoinAllowlist`: rooms/aliases allowed when `autoJoin` is `allowlist`. Alias entries are resolved to room IDs during invite handling; OpenClaw does not trust alias state claimed by the invited room.
|
||||
- `dm`: DM policy block (`enabled`, `policy`, `allowFrom`).
|
||||
- `dm`: DM policy block (`enabled`, `policy`, `allowFrom`, `threadReplies`).
|
||||
- `dm.allowFrom` entries should be full Matrix user IDs unless you already resolved them through live directory lookup.
|
||||
- `dm.threadReplies`: DM-only thread policy override (`off`, `inbound`, `always`). It overrides the top-level `threadReplies` setting for both reply placement and session isolation in DMs.
|
||||
- `accounts`: named per-account overrides. Top-level `channels.matrix` values act as defaults for these entries.
|
||||
- `groups`: per-room policy map. Prefer room IDs or aliases; unresolved room names are ignored at runtime. Session/group identity uses the stable room ID after resolution, while human-readable labels still come from room names.
|
||||
- `rooms`: legacy alias for `groups`.
|
||||
|
||||
@@ -286,10 +286,22 @@ This is often easier than hand-editing JSON manifests.
|
||||
6. **Run the gateway**
|
||||
- The Teams channel starts automatically when the plugin is installed and `msteams` config exists with credentials.
|
||||
|
||||
## Member info action
|
||||
|
||||
OpenClaw exposes a Graph-backed `member-info` action for Microsoft Teams so agents and automations can resolve channel member details (display name, email, role) directly from Microsoft Graph.
|
||||
|
||||
Requirements:
|
||||
|
||||
- `Member.Read.Group` RSC permission (already in the recommended manifest)
|
||||
- For cross-team lookups: `User.Read.All` Graph Application permission with admin consent
|
||||
|
||||
The action is gated by `channels.msteams.actions.memberInfo` (default: enabled when Graph credentials are available).
|
||||
|
||||
## History context
|
||||
|
||||
- `channels.msteams.historyLimit` controls how many recent channel/group messages are wrapped into the prompt.
|
||||
- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
|
||||
- Fetched thread history is filtered by sender allowlists (`allowFrom` / `groupAllowFrom`), so thread context seeding only includes messages from allowed senders.
|
||||
- DM history can be limited with `channels.msteams.dmHistoryLimit` (user turns). Per-user overrides: `channels.msteams.dms["<user_id>"].historyLimit`.
|
||||
|
||||
## Current Teams RSC Permissions (Manifest)
|
||||
@@ -476,6 +488,7 @@ Key settings (see `/gateway/configuration` for shared channel patterns):
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender`: per-channel per-sender tool policy overrides (`"*"` wildcard supported).
|
||||
- `toolsBySender` keys should use explicit prefixes:
|
||||
`id:`, `e164:`, `username:`, `name:` (legacy unprefixed keys still map to `id:` only).
|
||||
- `channels.msteams.actions.memberInfo`: enable or disable the Graph-backed member info action (default: enabled when Graph credentials are available).
|
||||
- `channels.msteams.sharePointSiteId`: SharePoint site ID for file uploads in group chats/channels (see [Sending files in group chats](#sending-files-in-group-chats)).
|
||||
|
||||
## Routing & Sessions
|
||||
|
||||
193
docs/channels/qqbot.md
Normal file
193
docs/channels/qqbot.md
Normal file
@@ -0,0 +1,193 @@
|
||||
---
|
||||
summary: "QQ Bot setup, config, and usage"
|
||||
read_when:
|
||||
- You want to connect OpenClaw to QQ
|
||||
- You need QQ Bot credential setup
|
||||
- You want QQ Bot group or private chat support
|
||||
title: QQ Bot
|
||||
---
|
||||
|
||||
# QQ Bot
|
||||
|
||||
QQ Bot connects to OpenClaw via the official QQ Bot API (WebSocket gateway). The
|
||||
plugin supports C2C private chat, group @messages, and guild channel messages with
|
||||
rich media (images, voice, video, files).
|
||||
|
||||
Status: bundled channel plugin. Direct messages, group chats, guild channels, and
|
||||
media are supported. Reactions and threads are not supported.
|
||||
|
||||
## Bundled with OpenClaw
|
||||
|
||||
Current OpenClaw installs bundle QQ Bot. You do not need a separate
|
||||
`openclaw plugins install` step for normal setup.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Go to the [QQ Open Platform](https://q.qq.com/) and scan the QR code with your
|
||||
phone QQ to register / log in.
|
||||
2. Click **Create Bot** to create a new QQ bot.
|
||||
3. Find **AppID** and **AppSecret** on the bot's settings page and copy them.
|
||||
|
||||
> AppSecret is not stored in plaintext — if you leave the page without saving it,
|
||||
> you'll have to regenerate a new one.
|
||||
|
||||
4. Add the channel:
|
||||
|
||||
```bash
|
||||
openclaw channels add --channel qqbot --token "AppID:AppSecret"
|
||||
```
|
||||
|
||||
5. Restart the Gateway.
|
||||
|
||||
Interactive setup paths:
|
||||
|
||||
```bash
|
||||
openclaw channels add
|
||||
openclaw configure --section channels
|
||||
```
|
||||
|
||||
## Configure
|
||||
|
||||
Minimal config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
qqbot: {
|
||||
enabled: true,
|
||||
appId: "YOUR_APP_ID",
|
||||
clientSecret: "YOUR_APP_SECRET",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Default-account env vars:
|
||||
|
||||
- `QQBOT_APP_ID`
|
||||
- `QQBOT_CLIENT_SECRET`
|
||||
|
||||
File-backed AppSecret:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
qqbot: {
|
||||
enabled: true,
|
||||
appId: "YOUR_APP_ID",
|
||||
clientSecretFile: "/path/to/qqbot-secret.txt",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Env fallback applies to the default QQ Bot account only.
|
||||
- `openclaw channels add --channel qqbot --token-file ...` provides the
|
||||
AppSecret only; the AppID must already be set in config or `QQBOT_APP_ID`.
|
||||
- `clientSecret` also accepts SecretRef input, not just a plaintext string.
|
||||
|
||||
### Multi-account setup
|
||||
|
||||
Run multiple QQ bots under a single OpenClaw instance:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
qqbot: {
|
||||
enabled: true,
|
||||
appId: "111111111",
|
||||
clientSecret: "secret-of-bot-1",
|
||||
accounts: {
|
||||
bot2: {
|
||||
enabled: true,
|
||||
appId: "222222222",
|
||||
clientSecret: "secret-of-bot-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Each account launches its own WebSocket connection and maintains an independent
|
||||
token cache (isolated by `appId`).
|
||||
|
||||
Add a second bot via CLI:
|
||||
|
||||
```bash
|
||||
openclaw channels add --channel qqbot --account bot2 --token "222222222:secret-of-bot-2"
|
||||
```
|
||||
|
||||
### Voice (STT / TTS)
|
||||
|
||||
STT and TTS support two-level configuration with priority fallback:
|
||||
|
||||
| Setting | Plugin-specific | Framework fallback |
|
||||
| ------- | -------------------- | ----------------------------- |
|
||||
| STT | `channels.qqbot.stt` | `tools.media.audio.models[0]` |
|
||||
| TTS | `channels.qqbot.tts` | `messages.tts` |
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
qqbot: {
|
||||
stt: {
|
||||
provider: "your-provider",
|
||||
model: "your-stt-model",
|
||||
},
|
||||
tts: {
|
||||
provider: "your-provider",
|
||||
model: "your-tts-model",
|
||||
voice: "your-voice",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Set `enabled: false` on either to disable.
|
||||
|
||||
Outbound audio upload/transcode behavior can also be tuned with
|
||||
`channels.qqbot.audioFormatPolicy`:
|
||||
|
||||
- `sttDirectFormats`
|
||||
- `uploadDirectFormats`
|
||||
- `transcodeEnabled`
|
||||
|
||||
## Target formats
|
||||
|
||||
| Format | Description |
|
||||
| -------------------------- | ------------------ |
|
||||
| `qqbot:c2c:OPENID` | Private chat (C2C) |
|
||||
| `qqbot:group:GROUP_OPENID` | Group chat |
|
||||
| `qqbot:channel:CHANNEL_ID` | Guild channel |
|
||||
|
||||
> Each bot has its own set of user OpenIDs. An OpenID received by Bot A **cannot**
|
||||
> be used to send messages via Bot B.
|
||||
|
||||
## Slash commands
|
||||
|
||||
Built-in commands intercepted before the AI queue:
|
||||
|
||||
| Command | Description |
|
||||
| -------------- | ------------------------------------ |
|
||||
| `/bot-ping` | Latency test |
|
||||
| `/bot-version` | Show the OpenClaw framework version |
|
||||
| `/bot-help` | List all commands |
|
||||
| `/bot-upgrade` | Show the QQBot upgrade guide link |
|
||||
| `/bot-logs` | Export recent gateway logs as a file |
|
||||
|
||||
Append `?` to any command for usage help (for example `/bot-upgrade ?`).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Bot replies "gone to Mars":** credentials not configured or Gateway not started.
|
||||
- **No inbound messages:** verify `appId` and `clientSecret` are correct, and the
|
||||
bot is enabled on the QQ Open Platform.
|
||||
- **Setup with `--token-file` still shows unconfigured:** `--token-file` only sets
|
||||
the AppSecret. You still need `appId` in config or `QQBOT_APP_ID`.
|
||||
- **Proactive messages not arriving:** QQ may intercept bot-initiated messages if
|
||||
the user hasn't interacted recently.
|
||||
- **Voice not transcribed:** ensure STT is configured and the provider is reachable.
|
||||
@@ -100,8 +100,9 @@ Permission model (client debug mode):
|
||||
|
||||
- Auto-approval is allowlist-based and only applies to trusted core tool IDs.
|
||||
- `read` auto-approval is scoped to the current working directory (`--cwd` when set).
|
||||
- Unknown/non-core tool names, out-of-scope reads, and dangerous tools always require explicit prompt approval.
|
||||
- ACP only auto-approves narrow readonly classes: scoped `read` calls under the active cwd plus readonly search tools (`search`, `web_search`, `memory_search`). Unknown/non-core tools, out-of-scope reads, exec-capable tools, control-plane tools, mutating tools, and interactive flows always require explicit prompt approval.
|
||||
- Server-provided `toolCall.kind` is treated as untrusted metadata (not an authorization source).
|
||||
- This ACP bridge policy is separate from ACPX harness permissions. If you run OpenClaw through the `acpx` backend, `plugins.entries.acpx.config.permissionMode=approve-all` is the break-glass “yolo” switch for that harness session.
|
||||
|
||||
## How to use this
|
||||
|
||||
|
||||
@@ -113,6 +113,10 @@ openclaw config set --batch-json '[
|
||||
openclaw config set --batch-file ./config-set.batch.json --dry-run
|
||||
```
|
||||
|
||||
Policy note:
|
||||
|
||||
- SecretRef assignments are rejected on unsupported runtime-mutable surfaces (for example `hooks.token`, `commands.ownerDisplaySecret`, Discord thread-binding webhook tokens, and WhatsApp creds JSON). See [SecretRef Credential Surface](/reference/secretref-credential-surface).
|
||||
|
||||
Batch parsing always uses the batch payload (`--batch-json`/`--batch-file`) as the source of truth.
|
||||
`--strict-json` / `--json` do not change batch parsing behavior.
|
||||
|
||||
@@ -204,6 +208,8 @@ Dry-run behavior:
|
||||
|
||||
- Builder mode: runs SecretRef resolvability checks for changed refs/providers.
|
||||
- JSON mode (`--strict-json`, `--json`, or batch mode): runs schema validation plus SecretRef resolvability checks.
|
||||
- Policy validation also runs for known unsupported SecretRef target surfaces.
|
||||
- Policy checks evaluate the full post-change config, so parent-object writes (for example setting `hooks` as an object) cannot bypass unsupported-surface validation.
|
||||
- Exec SecretRef checks are skipped by default during dry-run to avoid command side effects.
|
||||
- Use `--allow-exec` with `--dry-run` to opt in to exec SecretRef checks (this may execute provider commands).
|
||||
- `--allow-exec` is dry-run only and errors if used without `--dry-run`.
|
||||
@@ -289,6 +295,7 @@ Failure example:
|
||||
If dry-run fails:
|
||||
|
||||
- `config schema validation failed`: your post-change config shape is invalid; fix path/value or provider/ref object shape.
|
||||
- `Config policy validation failed: unsupported SecretRef usage`: move that credential back to plaintext/string input and keep SecretRefs on supported surfaces only.
|
||||
- `SecretRef assignment(s) could not be resolved`: referenced provider/ref currently cannot resolve (missing env var, invalid file pointer, exec provider failure, or provider/source mismatch).
|
||||
- `Dry run note: skipped <n> exec SecretRef resolvability check(s)`: dry-run skipped exec refs; rerun with `--allow-exec` if you need exec resolvability validation.
|
||||
- For batch mode, fix failing entries and rerun `--dry-run` before writing.
|
||||
|
||||
@@ -44,6 +44,8 @@ Notes:
|
||||
- If a required auth SecretRef is unresolved in this command path, `daemon status --json` reports `rpc.authWarning` when probe connectivity/auth fails; pass `--token`/`--password` explicitly or resolve the secret source first.
|
||||
- If the probe succeeds, unresolved auth-ref warnings are suppressed to avoid false positives.
|
||||
- On Linux systemd installs, `status` token-drift checks include both `Environment=` and `EnvironmentFile=` unit sources.
|
||||
- Drift checks resolve `gateway.auth.token` SecretRefs using merged runtime env (service command env first, then process env fallback).
|
||||
- If token auth is not effectively active (explicit `gateway.auth.mode` of `password`/`none`/`trusted-proxy`, or mode unset where password can win and no token candidate can win), token-drift checks skip config token resolution.
|
||||
- When token auth requires a token and `gateway.auth.token` is SecretRef-managed, `install` validates that the SecretRef is resolvable but does not persist the resolved token into service environment metadata.
|
||||
- If token auth requires a token and the configured token SecretRef is unresolved, install fails closed.
|
||||
- If both `gateway.auth.token` and `gateway.auth.password` are configured and `gateway.auth.mode` is unset, install is blocked until mode is set explicitly.
|
||||
|
||||
54
docs/cli/flows.md
Normal file
54
docs/cli/flows.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
summary: "CLI reference for `openclaw flows` (list, inspect, cancel)"
|
||||
read_when:
|
||||
- You want to inspect or cancel a flow
|
||||
- You want to see how background tasks roll up into a higher-level job
|
||||
title: "flows"
|
||||
---
|
||||
|
||||
# `openclaw flows`
|
||||
|
||||
Inspect and manage [ClawFlow](/automation/clawflow) jobs.
|
||||
|
||||
```bash
|
||||
openclaw flows list
|
||||
openclaw flows show <lookup>
|
||||
openclaw flows cancel <lookup>
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### `flows list`
|
||||
|
||||
List tracked flows and their task counts.
|
||||
|
||||
```bash
|
||||
openclaw flows list
|
||||
openclaw flows list --status blocked
|
||||
openclaw flows list --json
|
||||
```
|
||||
|
||||
### `flows show`
|
||||
|
||||
Show one flow by flow id or owner session key.
|
||||
|
||||
```bash
|
||||
openclaw flows show <lookup>
|
||||
openclaw flows show <lookup> --json
|
||||
```
|
||||
|
||||
The output includes the flow status, current step, wait target, blocked summary when present, stored output keys, and linked tasks.
|
||||
|
||||
### `flows cancel`
|
||||
|
||||
Cancel a flow and any active child tasks.
|
||||
|
||||
```bash
|
||||
openclaw flows cancel <lookup>
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [ClawFlow](/automation/clawflow) — job-level orchestration above tasks
|
||||
- [Background Tasks](/automation/tasks) — detached work ledger
|
||||
- [CLI reference](/cli/index) — full command tree
|
||||
@@ -116,6 +116,8 @@ Notes:
|
||||
- If the probe succeeds, unresolved auth-ref warnings are suppressed to avoid false positives.
|
||||
- Use `--require-rpc` in scripts and automation when a listening service is not enough and you need the Gateway RPC itself to be healthy.
|
||||
- On Linux systemd installs, service auth drift checks read both `Environment=` and `EnvironmentFile=` values from the unit (including `%h`, quoted paths, multiple files, and optional `-` files).
|
||||
- Drift checks resolve `gateway.auth.token` SecretRefs using merged runtime env (service command env first, then process env fallback).
|
||||
- If token auth is not effectively active (explicit `gateway.auth.mode` of `password`/`none`/`trusted-proxy`, or mode unset where password can win and no token candidate can win), token-drift checks skip config token resolution.
|
||||
|
||||
### `gateway probe`
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ This page describes the current CLI behavior. If commands change, update this do
|
||||
- [`tui`](/cli/tui)
|
||||
- [`browser`](/cli/browser)
|
||||
- [`cron`](/cli/cron)
|
||||
- [`flows`](/cli/flows)
|
||||
- [`dns`](/cli/dns)
|
||||
- [`docs`](/cli/docs)
|
||||
- [`hooks`](/cli/hooks)
|
||||
@@ -171,6 +172,10 @@ openclaw [--dev] [--profile <name>] <command>
|
||||
show
|
||||
notify
|
||||
cancel
|
||||
flows
|
||||
list
|
||||
show
|
||||
cancel
|
||||
gateway
|
||||
call
|
||||
health
|
||||
@@ -809,6 +814,14 @@ List and manage [background task](/automation/tasks) runs across agents.
|
||||
- `tasks cancel <id>` — cancel a running task
|
||||
- `tasks audit` — surface operational issues (stale, lost, delivery failures)
|
||||
|
||||
### `flows`
|
||||
|
||||
List and manage [ClawFlow](/automation/clawflow) jobs across agents.
|
||||
|
||||
- `flows list` — show active and recent flows
|
||||
- `flows show <id>` — show details for a specific flow
|
||||
- `flows cancel <id>` — cancel a flow and its active child tasks
|
||||
|
||||
## Gateway
|
||||
|
||||
### `gateway`
|
||||
@@ -905,12 +918,14 @@ Subcommands:
|
||||
|
||||
Common RPCs:
|
||||
|
||||
- `config.set` (validate + write full config; use `baseHash` for optimistic concurrency)
|
||||
- `config.apply` (validate + write config + restart + wake)
|
||||
- `config.patch` (merge a partial update + restart + wake)
|
||||
- `update.run` (run update + restart + wake)
|
||||
|
||||
Tip: when calling `config.set`/`config.apply`/`config.patch` directly, pass `baseHash` from
|
||||
`config.get` if a config already exists.
|
||||
Tip: these config write RPCs preflight active SecretRef resolution for refs in the submitted config payload and reject writes when an effectively active submitted ref is unresolved.
|
||||
|
||||
## Models
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ capabilities.
|
||||
openclaw plugins install <package> # ClawHub first, then npm
|
||||
openclaw plugins install clawhub:<package> # ClawHub only
|
||||
openclaw plugins install <package> --pin # pin version
|
||||
openclaw plugins install <package> --dangerously-force-unsafe-install
|
||||
openclaw plugins install <path> # local path
|
||||
openclaw plugins install <plugin>@<marketplace> # marketplace
|
||||
openclaw plugins install <plugin> --marketplace <name> # marketplace (explicit)
|
||||
@@ -57,6 +58,17 @@ openclaw plugins install <plugin> --marketplace <name> # marketplace (explicit)
|
||||
Bare package names are checked against ClawHub first, then npm. Security note:
|
||||
treat plugin installs like running code. Prefer pinned versions.
|
||||
|
||||
`--dangerously-force-unsafe-install` is a break-glass option for false positives
|
||||
in the built-in dangerous-code scanner. It allows the install to continue even
|
||||
when the built-in scanner reports `critical` findings, but it does **not**
|
||||
bypass plugin `before_install` hook policy blocks and does **not** bypass scan
|
||||
failures.
|
||||
|
||||
This CLI flag applies to `openclaw plugins install`. Gateway-backed skill
|
||||
dependency installs use the matching `dangerouslyForceUnsafeInstall` request
|
||||
override, while `openclaw skills install` remains a separate ClawHub skill
|
||||
download/install flow.
|
||||
|
||||
`plugins install` is also the install surface for hook packs that expose
|
||||
`openclaw.hooks` in `package.json`. Use `openclaw hooks` for filtered hook
|
||||
visibility and per-hook enablement, not package installation.
|
||||
|
||||
@@ -34,3 +34,7 @@ openclaw skills check
|
||||
`search`/`install`/`update` use ClawHub directly and install into the active
|
||||
workspace `skills/` directory. `list`/`info`/`check` still inspect the local
|
||||
skills visible to the current workspace and config.
|
||||
|
||||
This CLI `install` command downloads skill folders from ClawHub. Gateway-backed
|
||||
skill dependency installs triggered from onboarding or Skills settings use the
|
||||
separate `skills.install` request path instead.
|
||||
|
||||
@@ -234,3 +234,10 @@ Suggested `.gitignore` starter:
|
||||
[Channel routing](/channels/channel-routing) for routing configuration.
|
||||
- If `agents.defaults.sandbox` is enabled, non-main sessions can use per-session sandbox
|
||||
workspaces under `agents.defaults.sandbox.workspaceRoot`.
|
||||
|
||||
## Related
|
||||
|
||||
- [Standing Orders](/automation/standing-orders) — persistent instructions in workspace files
|
||||
- [Heartbeat](/gateway/heartbeat) — HEARTBEAT.md workspace file
|
||||
- [Session](/concepts/session) — session storage paths
|
||||
- [Sandboxing](/gateway/sandboxing) — workspace access in sandboxed environments
|
||||
|
||||
@@ -135,3 +135,10 @@ Details: [Gateway protocol](/gateway/protocol), [Pairing](/channels/pairing),
|
||||
- Exactly one Gateway controls a single Baileys session per host.
|
||||
- Handshake is mandatory; any non‑JSON or non‑connect first frame is a hard close.
|
||||
- Events are not replayed; clients must refresh on gaps.
|
||||
|
||||
## Related
|
||||
|
||||
- [Agent Loop](/concepts/agent-loop) — detailed agent execution cycle
|
||||
- [Gateway Protocol](/gateway/protocol) — WebSocket protocol contract
|
||||
- [Queue](/concepts/queue) — command queue and concurrency
|
||||
- [Security](/gateway/security) — trust model and hardening
|
||||
|
||||
@@ -84,3 +84,10 @@ survive.
|
||||
For advanced configuration (reserve tokens, identifier preservation, custom
|
||||
context engines, OpenAI server-side compaction), see the
|
||||
[Session Management Deep Dive](/reference/session-management-compaction).
|
||||
|
||||
## Related
|
||||
|
||||
- [Session](/concepts/session) — session management and lifecycle
|
||||
- [Session Pruning](/concepts/session-pruning) — trimming tool results
|
||||
- [Context](/concepts/context) — how context is built for agent turns
|
||||
- [Hooks](/automation/hooks) — compaction lifecycle hooks (before_compaction, after_compaction)
|
||||
|
||||
@@ -266,3 +266,9 @@ OpenClaw resolves when it needs a context engine.
|
||||
|
||||
See also: [Compaction](/concepts/compaction), [Context](/concepts/context),
|
||||
[Plugins](/tools/plugin), [Plugin manifest](/plugins/manifest).
|
||||
|
||||
## Related
|
||||
|
||||
- [Context](/concepts/context) — how context is built for agent turns
|
||||
- [Plugin Architecture](/plugins/architecture) — registering context engine plugins
|
||||
- [Compaction](/concepts/compaction) — summarizing long conversations
|
||||
|
||||
@@ -170,3 +170,10 @@ pluggable interface, lifecycle hooks, and configuration.
|
||||
- `System prompt (estimate)` = computed on the fly when no run report exists (or when running via a CLI backend that doesn’t generate the report).
|
||||
|
||||
Either way, it reports sizes and top contributors; it does **not** dump the full system prompt or tool schemas.
|
||||
|
||||
## Related
|
||||
|
||||
- [Context Engine](/concepts/context-engine) — custom context injection via plugins
|
||||
- [Compaction](/concepts/compaction) — summarizing long conversations
|
||||
- [System Prompt](/concepts/system-prompt) — how the system prompt is built
|
||||
- [Agent Loop](/concepts/agent-loop) — the full agent execution cycle
|
||||
|
||||
@@ -25,7 +25,7 @@ binary, and can index content beyond your workspace memory files.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Install QMD: `bun install -g https://github.com/tobi/qmd`
|
||||
- Install QMD: `bun install -g @tobilu/qmd`
|
||||
- SQLite build that allows extensions (`brew install sqlite` on macOS).
|
||||
- QMD must be on the gateway's `PATH`.
|
||||
- macOS and Linux work out of the box. Windows is best supported via WSL2.
|
||||
@@ -150,6 +150,12 @@ Set to `120000` for slower hardware.
|
||||
**Empty results in group chats?** Check `memory.qmd.scope` -- the default only
|
||||
allows DM sessions.
|
||||
|
||||
**Workspace-visible temp repos causing `ENAMETOOLONG` or broken indexing?**
|
||||
QMD traversal currently follows the underlying QMD scanner behavior rather than
|
||||
OpenClaw's builtin symlink rules. Keep temporary monorepo checkouts under
|
||||
hidden directories like `.tmp/` or outside indexed QMD roots until QMD exposes
|
||||
cycle-safe traversal or explicit exclusion controls.
|
||||
|
||||
## Configuration
|
||||
|
||||
For the full config surface (`memory.qmd.*`), search modes, update intervals,
|
||||
|
||||
@@ -152,3 +152,10 @@ Outbound message formatting is centralized in `messages`:
|
||||
- Reply threading via `replyToMode` and per-channel defaults
|
||||
|
||||
Details: [Configuration](/gateway/configuration-reference#messages) and channel docs.
|
||||
|
||||
## Related
|
||||
|
||||
- [Streaming](/concepts/streaming) — real-time message delivery
|
||||
- [Retry](/concepts/retry) — message delivery retry behavior
|
||||
- [Queue](/concepts/queue) — message processing queue
|
||||
- [Channels](/channels) — messaging platform integrations
|
||||
|
||||
@@ -129,6 +129,8 @@ Defaults:
|
||||
|
||||
- Billing backoff starts at **5 hours**, doubles per billing failure, and caps at **24 hours**.
|
||||
- Backoff counters reset if the profile hasn’t failed for **24 hours** (configurable).
|
||||
- Overloaded retries allow **1 same-provider profile rotation** before model fallback.
|
||||
- Overloaded retries use **0 ms backoff** by default.
|
||||
|
||||
## Model fallback
|
||||
|
||||
@@ -136,6 +138,11 @@ If all profiles for a provider fail, OpenClaw moves to the next model in
|
||||
`agents.defaults.model.fallbacks`. This applies to auth failures, rate limits, and
|
||||
timeouts that exhausted profile rotation (other errors do not advance fallback).
|
||||
|
||||
Overloaded errors are handled more aggressively than billing cooldowns. By default,
|
||||
OpenClaw allows one same-provider auth-profile retry, then switches to the next
|
||||
configured model fallback without waiting. Tune this with
|
||||
`auth.cooldowns.overloadedProfileRotations` and `auth.cooldowns.overloadedBackoffMs`.
|
||||
|
||||
When a run starts with a model override (hooks or CLI), fallbacks still end at
|
||||
`agents.defaults.model.primary` after trying any configured fallbacks.
|
||||
|
||||
@@ -146,6 +153,7 @@ See [Gateway configuration](/gateway/configuration) for:
|
||||
- `auth.profiles` / `auth.order`
|
||||
- `auth.cooldowns.billingBackoffHours` / `auth.cooldowns.billingBackoffHoursByProvider`
|
||||
- `auth.cooldowns.billingMaxHours` / `auth.cooldowns.failureWindowHours`
|
||||
- `auth.cooldowns.overloadedProfileRotations` / `auth.cooldowns.overloadedBackoffMs`
|
||||
- `agents.defaults.model.primary` / `agents.defaults.model.fallbacks`
|
||||
- `agents.defaults.imageModel` routing
|
||||
|
||||
|
||||
@@ -590,3 +590,10 @@ openclaw models list
|
||||
```
|
||||
|
||||
See also: [/gateway/configuration](/gateway/configuration) for full configuration examples.
|
||||
|
||||
## Related
|
||||
|
||||
- [Models](/concepts/models) — model configuration and aliases
|
||||
- [Model Failover](/concepts/model-failover) — fallback chains and retry behavior
|
||||
- [Configuration Reference](/gateway/configuration-reference#agent-defaults) — model config keys
|
||||
- [Providers](/providers) — per-provider setup guides
|
||||
|
||||
@@ -223,3 +223,10 @@ Merge mode precedence for matching provider IDs:
|
||||
|
||||
Marker persistence is source-authoritative: OpenClaw writes markers from the active source config snapshot (pre-resolution), not from resolved runtime secret values.
|
||||
This applies whenever OpenClaw regenerates `models.json`, including command-driven paths like `openclaw agent`.
|
||||
|
||||
## Related
|
||||
|
||||
- [Model Providers](/concepts/model-providers) — provider routing and auth
|
||||
- [Model Failover](/concepts/model-failover) — fallback chains
|
||||
- [Image Generation](/tools/image-generation) — image model configuration
|
||||
- [Configuration Reference](/gateway/configuration-reference#agent-defaults) — model config keys
|
||||
|
||||
@@ -129,6 +129,48 @@ With **multiple agents**, each `agentId` becomes a **fully isolated persona**:
|
||||
|
||||
This lets **multiple people** share one Gateway server while keeping their AI “brains” and data isolated.
|
||||
|
||||
## Cross-agent QMD memory search
|
||||
|
||||
If one agent should search another agent's QMD session transcripts, add
|
||||
extra collections under `agents.list[].memorySearch.qmd.extraCollections`.
|
||||
Use `agents.defaults.memorySearch.qmd.extraCollections` only when every agent
|
||||
should inherit the same shared transcript collections.
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: "~/workspaces/main",
|
||||
memorySearch: {
|
||||
qmd: {
|
||||
extraCollections: [{ path: "~/agents/family/sessions", name: "family-sessions" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
workspace: "~/workspaces/main",
|
||||
memorySearch: {
|
||||
qmd: {
|
||||
extraCollections: [{ path: "notes" }], // resolves inside workspace -> collection named "notes-main"
|
||||
},
|
||||
},
|
||||
},
|
||||
{ id: "family", workspace: "~/workspaces/family" },
|
||||
],
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: { includeDefaultMemory: false },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The extra collection path can be shared across agents, but the collection name
|
||||
stays explicit when the path is outside the agent workspace. Paths inside the
|
||||
workspace remain agent-scoped so each agent keeps its own transcript search set.
|
||||
|
||||
## One WhatsApp number, multiple people (DM split)
|
||||
|
||||
You can route **different WhatsApp DMs** to different agents while staying on **one WhatsApp account**. Match on sender E.164 (like `+15551234567`) with `peer.kind: "direct"`. Replies still come from the same WhatsApp number (no per‑agent sender identity).
|
||||
|
||||
@@ -185,3 +185,9 @@ Related docs:
|
||||
|
||||
- [/concepts/model-failover](/concepts/model-failover) (rotation + cooldown rules)
|
||||
- [/tools/slash-commands](/tools/slash-commands) (command surface)
|
||||
|
||||
## Related
|
||||
|
||||
- [Authentication](/gateway/authentication) — model provider auth overview
|
||||
- [Secrets](/gateway/secrets) — credential storage and SecretRef
|
||||
- [Configuration Reference](/gateway/configuration-reference#auth-storage) — auth config keys
|
||||
|
||||
@@ -153,3 +153,9 @@ Slack:
|
||||
- `partial` can use Slack native streaming (`chat.startStream`/`append`/`stop`) when available.
|
||||
- `block` uses append-style draft previews.
|
||||
- `progress` uses status preview text, then final answer.
|
||||
|
||||
## Related
|
||||
|
||||
- [Messages](/concepts/messages) — message lifecycle and delivery
|
||||
- [Retry](/concepts/retry) — retry behavior on delivery failure
|
||||
- [Channels](/channels) — per-channel streaming support
|
||||
|
||||
@@ -984,6 +984,7 @@
|
||||
"channels/msteams",
|
||||
"channels/nextcloud-talk",
|
||||
"channels/nostr",
|
||||
"channels/qqbot",
|
||||
"channels/signal",
|
||||
"channels/slack",
|
||||
"channels/synology-chat",
|
||||
@@ -1121,6 +1122,7 @@
|
||||
"automation/cron-jobs",
|
||||
"automation/cron-vs-heartbeat",
|
||||
"automation/tasks",
|
||||
"automation/clawflow",
|
||||
"automation/troubleshooting",
|
||||
"automation/webhook",
|
||||
"automation/gmail-pubsub",
|
||||
@@ -1366,11 +1368,7 @@
|
||||
},
|
||||
{
|
||||
"group": "Node features",
|
||||
"pages": [
|
||||
"nodes/talk",
|
||||
"nodes/voicewake",
|
||||
"nodes/location-command"
|
||||
]
|
||||
"pages": ["nodes/talk", "nodes/voicewake", "nodes/location-command"]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1436,6 +1434,7 @@
|
||||
"cli/approvals",
|
||||
"cli/browser",
|
||||
"cli/cron",
|
||||
"cli/flows",
|
||||
"cli/node",
|
||||
"cli/nodes",
|
||||
"cli/sandbox"
|
||||
|
||||
@@ -6,7 +6,11 @@ read_when:
|
||||
title: "Authentication"
|
||||
---
|
||||
|
||||
# Authentication
|
||||
# Authentication (Model Providers)
|
||||
|
||||
<Note>
|
||||
This page covers **model provider** authentication (API keys, OAuth, setup tokens). For **gateway connection** authentication (token, password, trusted-proxy), see [Configuration](/gateway/configuration) and [Trusted Proxy Auth](/gateway/trusted-proxy-auth).
|
||||
</Note>
|
||||
|
||||
OpenClaw supports OAuth and API keys for model providers. For always-on gateway
|
||||
hosts, API keys are usually the most predictable option. Subscription/OAuth
|
||||
@@ -101,6 +105,7 @@ Auth profile refs are also supported for static credentials:
|
||||
|
||||
- `api_key` credentials can use `keyRef: { source, provider, id }`
|
||||
- `token` credentials can use `tokenRef: { source, provider, id }`
|
||||
- OAuth-mode profiles do not support SecretRef credentials; if `auth.profiles.<id>.mode` is set to `"oauth"`, SecretRef-backed `keyRef`/`tokenRef` input for that profile is rejected.
|
||||
|
||||
Automation-friendly check (exit `1` when expired/missing, `2` when expiring):
|
||||
|
||||
|
||||
@@ -9,14 +9,9 @@ title: "Bridge Protocol"
|
||||
|
||||
# Bridge protocol (legacy node transport)
|
||||
|
||||
The Bridge protocol is a **legacy** node transport (TCP JSONL). New node clients
|
||||
should use the unified Gateway WebSocket protocol instead.
|
||||
|
||||
If you are building an operator or node client, use the
|
||||
[Gateway protocol](/gateway/protocol).
|
||||
|
||||
**Note:** Current OpenClaw builds no longer ship the TCP bridge listener; this document is kept for historical reference.
|
||||
Legacy `bridge.*` config keys are no longer part of the config schema.
|
||||
<Warning>
|
||||
The TCP bridge has been **removed**. Current OpenClaw builds do not ship the bridge listener and `bridge.*` config keys are no longer in the schema. This page is kept for historical reference only. Use the [Gateway Protocol](/gateway/protocol) for all node/operator clients.
|
||||
</Warning>
|
||||
|
||||
## Why we have both
|
||||
|
||||
|
||||
@@ -119,7 +119,6 @@ The provider id becomes the left side of your model ref:
|
||||
input: "arg",
|
||||
modelArg: "--model",
|
||||
modelAliases: {
|
||||
"claude-opus-4-6": "opus",
|
||||
"claude-opus-4-6": "opus",
|
||||
"claude-sonnet-4-6": "sonnet",
|
||||
},
|
||||
|
||||
@@ -154,6 +154,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
// Session behavior
|
||||
session: {
|
||||
scope: "per-sender",
|
||||
dmScope: "per-channel-peer", // recommended for multi-user inboxes
|
||||
reset: {
|
||||
mode: "daily",
|
||||
atHour: 4,
|
||||
@@ -288,7 +289,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
},
|
||||
sandbox: {
|
||||
mode: "non-main",
|
||||
perSession: true,
|
||||
scope: "session", // preferred over legacy perSession: true
|
||||
workspaceRoot: "~/.openclaw/sandboxes",
|
||||
docker: {
|
||||
image: "openclaw-sandbox:bookworm-slim",
|
||||
|
||||
@@ -1463,6 +1463,7 @@ scripts/sandbox-browser-setup.sh # optional browser image
|
||||
- `identity` derives defaults: `ackReaction` from `emoji`, `mentionPatterns` from `name`/`emoji`.
|
||||
- `subagents.allowAgents`: allowlist of agent ids for `sessions_spawn` (`["*"]` = any; default: same agent only).
|
||||
- Sandbox inheritance guard: if the requester session is sandboxed, `sessions_spawn` rejects targets that would run unsandboxed.
|
||||
- `subagents.requireAgentId`: when true, block `sessions_spawn` calls that omit `agentId` (forces explicit profile selection; default: false).
|
||||
|
||||
---
|
||||
|
||||
@@ -3011,7 +3012,8 @@ Notes:
|
||||
```
|
||||
|
||||
- Per-agent profiles are stored at `<agentDir>/auth-profiles.json`.
|
||||
- `auth-profiles.json` supports value-level refs (`keyRef` for `api_key`, `tokenRef` for `token`).
|
||||
- `auth-profiles.json` supports value-level refs (`keyRef` for `api_key`, `tokenRef` for `token`) for static credential modes.
|
||||
- OAuth-mode profiles (`auth.profiles.<id>.mode = "oauth"`) do not support SecretRef-backed auth-profile credentials.
|
||||
- Static runtime credentials come from in-memory resolved snapshots; legacy static `auth.json` entries are scrubbed when discovered.
|
||||
- Legacy OAuth imports from `~/.openclaw/credentials/oauth.json`.
|
||||
- See [OAuth](/concepts/oauth).
|
||||
@@ -3027,6 +3029,8 @@ Notes:
|
||||
billingBackoffHoursByProvider: { anthropic: 3, openai: 8 },
|
||||
billingMaxHours: 24,
|
||||
failureWindowHours: 24,
|
||||
overloadedProfileRotations: 1,
|
||||
overloadedBackoffMs: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -3036,6 +3040,8 @@ Notes:
|
||||
- `billingBackoffHoursByProvider`: optional per-provider overrides for billing backoff hours.
|
||||
- `billingMaxHours`: cap in hours for billing backoff exponential growth (default: `24`).
|
||||
- `failureWindowHours`: rolling window in hours used for backoff counters (default: `24`).
|
||||
- `overloadedProfileRotations`: maximum same-provider auth-profile rotations for overloaded errors before switching to model fallback (default: `1`).
|
||||
- `overloadedBackoffMs`: fixed delay before retrying an overloaded provider/profile rotation (default: `0`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -327,7 +327,7 @@ When validation fails:
|
||||
```
|
||||
|
||||
- `every`: duration string (`30m`, `2h`). Set `0m` to disable.
|
||||
- `target`: `last` | `whatsapp` | `telegram` | `discord` | `none`
|
||||
- `target`: `last` | `none` | `<channel-id>` (for example `discord`, `matrix`, `telegram`, or `whatsapp`)
|
||||
- `directPolicy`: `allow` (default) or `block` for DM-style heartbeat targets
|
||||
- See [Heartbeat](/gateway/heartbeat) for the full guide.
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ Troubleshooting and beacon details: [Bonjour](/gateway/bonjour).
|
||||
- `_openclaw-gw._tcp` (gateway transport beacon)
|
||||
- TXT keys (non-secret):
|
||||
- `role=gateway`
|
||||
- `transport=gateway`
|
||||
- `displayName=<friendly name>` (operator-configured display name)
|
||||
- `lanHost=<hostname>.local`
|
||||
- `sshPort=22` (or whatever is advertised)
|
||||
- `gatewayPort=18789` (Gateway WS + HTTP)
|
||||
@@ -91,6 +93,8 @@ For London/Vienna style setups, Bonjour won’t help. The recommended “direct
|
||||
|
||||
If the gateway can detect it is running under Tailscale, it publishes `tailnetDns` as an optional hint for clients (including wide-area beacons).
|
||||
|
||||
The macOS app now prefers MagicDNS names over raw Tailscale IPs for gateway discovery. This improves reliability when tailnet IPs change (for example after node restarts or CGNAT reassignment), because MagicDNS names resolve to the current IP automatically.
|
||||
|
||||
### 3) Manual / SSH target
|
||||
|
||||
When there is no direct route (or direct is disabled), clients can always connect via SSH by forwarding the loopback gateway port.
|
||||
|
||||
@@ -61,18 +61,22 @@ cat ~/.openclaw/openclaw.json
|
||||
- Optional pre-flight update for git installs (interactive only).
|
||||
- UI protocol freshness check (rebuilds Control UI when the protocol schema is newer).
|
||||
- Health check + restart prompt.
|
||||
- Skills status summary (eligible/missing/blocked).
|
||||
- Skills status summary (eligible/missing/blocked) and plugin status.
|
||||
- Config normalization for legacy values.
|
||||
- Browser migration checks for legacy Chrome extension configs and Chrome MCP readiness.
|
||||
- OpenCode provider override warnings (`models.providers.opencode` / `models.providers.opencode-go`).
|
||||
- OAuth TLS prerequisites check for OpenAI Codex OAuth profiles.
|
||||
- Legacy on-disk state migration (sessions/agent dir/WhatsApp auth).
|
||||
- Legacy plugin manifest contract key migration (`speechProviders`, `mediaUnderstandingProviders`, `imageGenerationProviders` → `contracts`).
|
||||
- Legacy cron store migration (`jobId`, `schedule.cron`, top-level delivery/payload fields, payload `provider`, simple `notify: true` webhook fallback jobs).
|
||||
- Session lock file inspection and stale lock cleanup.
|
||||
- State integrity and permissions checks (sessions, transcripts, state dir).
|
||||
- Config file permission checks (chmod 600) when running locally.
|
||||
- Model auth health: checks OAuth expiry, can refresh expiring tokens, and reports auth-profile cooldown/disabled states.
|
||||
- Extra workspace dir detection (`~/openclaw`).
|
||||
- Sandbox image repair when sandboxing is enabled.
|
||||
- Legacy service migration and extra gateway detection.
|
||||
- Matrix channel legacy state migration (in `--fix` / `--repair` mode).
|
||||
- Gateway runtime checks (service installed but not running; cached launchd label).
|
||||
- Channel status warnings (probed from the running gateway).
|
||||
- Supervisor config audit (launchd/systemd/schtasks) with optional repair.
|
||||
@@ -81,6 +85,9 @@ cat ~/.openclaw/openclaw.json
|
||||
- Security warnings for open DM policies.
|
||||
- Gateway auth checks for local token mode (offers token generation when no token source exists; does not overwrite token SecretRef configs).
|
||||
- systemd linger check on Linux.
|
||||
- Workspace bootstrap file size check (truncation/near-limit warnings for context files).
|
||||
- Shell completion status check and auto-install/upgrade.
|
||||
- Memory search embedding provider readiness check (local model, remote API key, or QMD binary).
|
||||
- Source install checks (pnpm workspace mismatch, missing UI assets, missing tsx binary).
|
||||
- Writes updated config + wizard metadata.
|
||||
|
||||
@@ -122,6 +129,10 @@ Current migrations:
|
||||
- `routing.agents`/`routing.defaultAgentId` → `agents.list` + `agents.list[].default`
|
||||
- `routing.agentToAgent` → `tools.agentToAgent`
|
||||
- `routing.transcribeAudio` → `tools.media.audio.models`
|
||||
- `messages.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `messages.tts.providers.<provider>`
|
||||
- `channels.discord.voice.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `channels.discord.voice.tts.providers.<provider>`
|
||||
- `channels.discord.accounts.<id>.voice.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `channels.discord.accounts.<id>.voice.tts.providers.<provider>`
|
||||
- `plugins.entries.voice-call.config.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `plugins.entries.voice-call.config.tts.providers.<provider>`
|
||||
- `bindings[].match.accountID` → `bindings[].match.accountId`
|
||||
- For channels with named `accounts` but missing `accounts.default`, move account-scoped top-level single-account channel values into `channels.<channel>.accounts.default` when present
|
||||
- `identity` → `agents.list[].identity`
|
||||
@@ -173,6 +184,16 @@ still requires:
|
||||
This check does **not** apply to Docker, sandbox, remote-browser, or other
|
||||
headless flows. Those continue to use raw CDP.
|
||||
|
||||
### 2d) OAuth TLS prerequisites
|
||||
|
||||
When an OpenAI Codex OAuth profile is configured, doctor probes the OpenAI
|
||||
authorization endpoint to verify that the local Node/OpenSSL TLS stack can
|
||||
validate the certificate chain. If the probe fails with a certificate error (for
|
||||
example `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`, expired cert, or self-signed cert),
|
||||
doctor prints platform-specific fix guidance. On macOS with a Homebrew Node, the
|
||||
fix is usually `brew postinstall ca-certificates`. With `--deep`, the probe runs
|
||||
even if the gateway is healthy.
|
||||
|
||||
### 3) Legacy state migrations (disk layout)
|
||||
|
||||
Doctor can migrate older on-disk layouts into the current structure:
|
||||
@@ -191,6 +212,14 @@ the legacy sessions + agent dir on startup so history/auth/models land in the
|
||||
per-agent path without a manual doctor run. WhatsApp auth is intentionally only
|
||||
migrated via `openclaw doctor`.
|
||||
|
||||
### 3a) Legacy plugin manifest migrations
|
||||
|
||||
Doctor scans all installed plugin manifests for deprecated top-level capability keys
|
||||
(`speechProviders`, `mediaUnderstandingProviders`, `imageGenerationProviders`).
|
||||
When found, it offers to move them into the `contracts` object and rewrite the manifest
|
||||
file in-place. This migration is idempotent; if the `contracts` key already has the
|
||||
same values, the legacy key is removed without duplicating the data.
|
||||
|
||||
### 3b) Legacy cron store migrations
|
||||
|
||||
Doctor also checks the cron job store (`~/.openclaw/cron/jobs.json` by default,
|
||||
@@ -210,6 +239,15 @@ Doctor only auto-migrates `notify: true` jobs when it can do so without
|
||||
changing behavior. If a job combines legacy notify fallback with an existing
|
||||
non-webhook delivery mode, doctor warns and leaves that job for manual review.
|
||||
|
||||
### 3c) Session lock cleanup
|
||||
|
||||
Doctor scans every agent session directory for stale write-lock files — files left
|
||||
behind when a session exited abnormally. For each lock file found it reports:
|
||||
the path, PID, whether the PID is still alive, lock age, and whether it is
|
||||
considered stale (dead PID or older than 30 minutes). In `--fix` / `--repair`
|
||||
mode it removes stale lock files automatically; otherwise it prints a note and
|
||||
instructs you to rerun with `--fix`.
|
||||
|
||||
### 4) State integrity checks (session persistence, routing, and safety)
|
||||
|
||||
The state directory is the operational brainstem. If it vanishes, you lose
|
||||
@@ -273,6 +311,15 @@ port. It can also scan for extra gateway-like services and print cleanup hints.
|
||||
Profile-named OpenClaw gateway services are considered first-class and are not
|
||||
flagged as "extra."
|
||||
|
||||
### 8b) Startup Matrix migration
|
||||
|
||||
When a Matrix channel account has a pending or actionable legacy state migration,
|
||||
doctor (in `--fix` / `--repair` mode) creates a pre-migration snapshot and then
|
||||
runs the best-effort migration steps: legacy Matrix state migration and legacy
|
||||
encrypted-state preparation. Both steps are non-fatal; errors are logged and
|
||||
startup continues. In read-only mode (`openclaw doctor` without `--fix`) this check
|
||||
is skipped entirely.
|
||||
|
||||
### 9) Security warnings
|
||||
|
||||
Doctor emits warnings when a provider is open to DMs without an allowlist, or
|
||||
@@ -283,10 +330,44 @@ when a policy is configured in a dangerous way.
|
||||
If running as a systemd user service, doctor ensures lingering is enabled so the
|
||||
gateway stays alive after logout.
|
||||
|
||||
### 11) Skills status
|
||||
### 11) Workspace status (skills, plugins, and legacy dirs)
|
||||
|
||||
Doctor prints a quick summary of eligible/missing/blocked skills for the current
|
||||
workspace.
|
||||
Doctor prints a summary of the workspace state for the default agent:
|
||||
|
||||
- **Skills status**: counts eligible, missing-requirements, and allowlist-blocked skills.
|
||||
- **Legacy workspace dirs**: warns when `~/openclaw` or other legacy workspace directories
|
||||
exist alongside the current workspace.
|
||||
- **Plugin status**: counts loaded/disabled/errored plugins; lists plugin IDs for any
|
||||
errors; reports bundle plugin capabilities.
|
||||
- **Plugin compatibility warnings**: flags plugins that have compatibility issues with
|
||||
the current runtime.
|
||||
- **Plugin diagnostics**: surfaces any load-time warnings or errors emitted by the
|
||||
plugin registry.
|
||||
|
||||
### 11b) Bootstrap file size
|
||||
|
||||
Doctor checks whether workspace bootstrap files (for example `AGENTS.md`,
|
||||
`CLAUDE.md`, or other injected context files) are near or over the configured
|
||||
character budget. It reports per-file raw vs. injected character counts, truncation
|
||||
percentage, truncation cause (`max/file` or `max/total`), and total injected
|
||||
characters as a fraction of the total budget. When files are truncated or near
|
||||
the limit, doctor prints tips for tuning `agents.defaults.bootstrapMaxChars`
|
||||
and `agents.defaults.bootstrapTotalMaxChars`.
|
||||
|
||||
### 11c) Shell completion
|
||||
|
||||
Doctor checks whether tab completion is installed for the current shell
|
||||
(zsh, bash, fish, or PowerShell):
|
||||
|
||||
- If the shell profile uses a slow dynamic completion pattern
|
||||
(`source <(openclaw completion ...)`), doctor upgrades it to the faster
|
||||
cached file variant.
|
||||
- If completion is configured in the profile but the cache file is missing,
|
||||
doctor regenerates the cache automatically.
|
||||
- If no completion is configured at all, doctor prompts to install it
|
||||
(interactive mode only; skipped with `--non-interactive`).
|
||||
|
||||
Run `openclaw completion --write-state` to regenerate the cache manually.
|
||||
|
||||
### 12) Gateway auth checks (local token)
|
||||
|
||||
@@ -309,6 +390,26 @@ Some repair flows need to inspect configured credentials without weakening runti
|
||||
Doctor runs a health check and offers to restart the gateway when it looks
|
||||
unhealthy.
|
||||
|
||||
### 13b) Memory search readiness
|
||||
|
||||
Doctor checks whether the configured memory search embedding provider is ready
|
||||
for the default agent. The behavior depends on the configured backend and provider:
|
||||
|
||||
- **QMD backend**: probes whether the `qmd` binary is available and startable.
|
||||
If not, prints fix guidance including the npm package and a manual binary path option.
|
||||
- **Explicit local provider**: checks for a local model file or a recognized
|
||||
remote/downloadable model URL. If missing, suggests switching to a remote provider.
|
||||
- **Explicit remote provider** (`openai`, `voyage`, etc.): verifies an API key is
|
||||
present in the environment or auth store. Prints actionable fix hints if missing.
|
||||
- **Auto provider**: checks local model availability first, then tries each remote
|
||||
provider in auto-selection order.
|
||||
|
||||
When a gateway probe result is available (gateway was healthy at the time of the
|
||||
check), doctor cross-references its result with the CLI-visible config and notes
|
||||
any discrepancy.
|
||||
|
||||
Use `openclaw memory status --deep` to verify embedding readiness at runtime.
|
||||
|
||||
### 14) Channel status warnings
|
||||
|
||||
If the gateway is healthy, doctor runs a channel status probe and reports
|
||||
|
||||
@@ -8,8 +8,6 @@ title: "Gateway Lock"
|
||||
|
||||
# Gateway lock
|
||||
|
||||
Last updated: 2025-12-11
|
||||
|
||||
## Why
|
||||
|
||||
- Ensure only one gateway instance runs per base port on the same host; additional gateways must use isolated profiles and unique ports.
|
||||
@@ -32,3 +30,8 @@ Last updated: 2025-12-11
|
||||
|
||||
- If the port is occupied by _another_ process, the error is the same; free the port or choose another with `openclaw gateway --port <port>`.
|
||||
- The macOS app still maintains its own lightweight PID guard before spawning the gateway; the runtime lock is enforced by the WebSocket bind.
|
||||
|
||||
## Related
|
||||
|
||||
- [Multiple Gateways](/gateway/multiple-gateways) — running multiple instances with unique ports
|
||||
- [Troubleshooting](/gateway/troubleshooting) — diagnosing `EADDRINUSE` and port conflicts
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
summary: "Health check steps for channel connectivity"
|
||||
summary: "Health check commands and gateway health monitoring"
|
||||
read_when:
|
||||
- Diagnosing WhatsApp channel health
|
||||
- Diagnosing channel connectivity or gateway health
|
||||
- Understanding health check CLI commands and options
|
||||
title: "Health Checks"
|
||||
---
|
||||
|
||||
@@ -41,4 +42,12 @@ Short guide to verify channel connectivity without guessing.
|
||||
|
||||
## Dedicated "health" command
|
||||
|
||||
`openclaw health --json` asks the running Gateway for its health snapshot (no direct channel sockets from the CLI). It reports linked creds/auth age when available, per-channel probe summaries, session-store summary, and a probe duration. It exits non-zero if the Gateway is unreachable or the probe fails/timeouts. Use `--timeout <ms>` to override the 10s default.
|
||||
`openclaw health --json` asks the running Gateway for its health snapshot (no direct channel sockets from the CLI). It reports linked creds/auth age when available, per-channel probe summaries, session-store summary, and a probe duration. It exits non-zero if the Gateway is unreachable or the probe fails/timeouts.
|
||||
|
||||
Options:
|
||||
|
||||
- `--json`: machine-readable JSON output
|
||||
- `--timeout <ms>`: override the default 10s probe timeout
|
||||
- `--probe`: force a live probe of all channels instead of returning the cached health snapshot
|
||||
|
||||
The health snapshot includes: `ok` (boolean), `ts` (timestamp), `durationMs` (probe time), per-channel status, agent availability, and session-store summary.
|
||||
|
||||
@@ -227,7 +227,7 @@ Use `accountId` to target a specific account on multi-account channels like Tele
|
||||
- Session key formats: see [Sessions](/concepts/session) and [Groups](/channels/groups).
|
||||
- `target`:
|
||||
- `last`: deliver to the last used external channel.
|
||||
- explicit channel: `whatsapp` / `telegram` / `discord` / `googlechat` / `slack` / `msteams` / `signal` / `imessage`.
|
||||
- explicit channel: any configured channel or plugin id, for example `discord`, `matrix`, `telegram`, or `whatsapp`.
|
||||
- `none` (default): run the heartbeat but **do not deliver** externally.
|
||||
- `directPolicy`: controls direct/DM delivery behavior:
|
||||
- `allow` (default): allow direct/DM heartbeat delivery.
|
||||
|
||||
@@ -7,6 +7,8 @@ title: "Network model"
|
||||
|
||||
# Network Model
|
||||
|
||||
> This content has been merged into [Network](/network#core-model). See that page for the current guide.
|
||||
|
||||
Most operations flow through the Gateway (`openclaw gateway`), a single long-running
|
||||
process that owns channel connections and the WebSocket control plane.
|
||||
|
||||
|
||||
@@ -43,9 +43,23 @@ Treat this endpoint as a **full operator-access** surface for the gateway instan
|
||||
- A valid Gateway token/password for this endpoint should be treated like an owner/operator credential.
|
||||
- Requests run through the same control-plane agent path as trusted operator actions.
|
||||
- There is no separate non-owner/per-user tool boundary on this endpoint; once a caller passes Gateway auth here, OpenClaw treats that caller as a trusted operator for this gateway.
|
||||
- For shared-secret auth modes (`token` and `password`), the endpoint restores the normal full operator defaults even if the caller sends a narrower `x-openclaw-scopes` header.
|
||||
- Trusted identity-bearing HTTP modes (for example trusted proxy auth or `gateway.auth.mode="none"`) still honor the declared operator scopes on the request.
|
||||
- If the target agent policy allows sensitive tools, this endpoint can use them.
|
||||
- Keep this endpoint on loopback/tailnet/private ingress only; do not expose it directly to the public internet.
|
||||
|
||||
Auth matrix:
|
||||
|
||||
- `gateway.auth.mode="token"` or `"password"` + `Authorization: Bearer ...`
|
||||
- proves possession of the shared gateway operator secret
|
||||
- ignores narrower `x-openclaw-scopes`
|
||||
- restores the full default operator scope set
|
||||
- treats chat turns on this endpoint as owner-sender turns
|
||||
- trusted identity-bearing HTTP modes (for example trusted proxy auth, or `gateway.auth.mode="none"` on private ingress)
|
||||
- authenticate some outer trusted identity or deployment boundary
|
||||
- honor the declared `x-openclaw-scopes` header
|
||||
- only get owner semantics when `operator.admin` is actually present in those declared scopes
|
||||
|
||||
See [Security](/gateway/security) and [Remote access](/gateway/remote).
|
||||
|
||||
## Agent-first model contract
|
||||
|
||||
@@ -24,11 +24,24 @@ Operational behavior matches [OpenAI Chat Completions](/gateway/openai-http-api)
|
||||
|
||||
- use `Authorization: Bearer <token>` with the normal Gateway auth config
|
||||
- treat the endpoint as full operator access for the gateway instance
|
||||
- for shared-secret auth modes (`token` and `password`), ignore narrower bearer-declared `x-openclaw-scopes` values and restore the normal full operator defaults
|
||||
- for trusted identity-bearing HTTP modes (for example trusted proxy auth or `gateway.auth.mode="none"`), still honor the declared operator scopes on the request
|
||||
- select agents with `model: "openclaw"`, `model: "openclaw/default"`, `model: "openclaw/<agentId>"`, or `x-openclaw-agent-id`
|
||||
- use `x-openclaw-model` when you want to override the selected agent's backend model
|
||||
- use `x-openclaw-session-key` for explicit session routing
|
||||
- use `x-openclaw-message-channel` when you want a non-default synthetic ingress channel context
|
||||
|
||||
Auth matrix:
|
||||
|
||||
- `gateway.auth.mode="token"` or `"password"` + `Authorization: Bearer ...`
|
||||
- proves possession of the shared gateway operator secret
|
||||
- ignores narrower `x-openclaw-scopes`
|
||||
- restores the full default operator scope set
|
||||
- treats chat turns on this endpoint as owner-sender turns
|
||||
- trusted identity-bearing HTTP modes (for example trusted proxy auth, or `gateway.auth.mode="none"` on private ingress)
|
||||
- honor the declared `x-openclaw-scopes` header
|
||||
- only get owner semantics when `operator.admin` is actually present in those declared scopes
|
||||
|
||||
Enable or disable this endpoint with `gateway.http.endpoints.responses.enabled`.
|
||||
|
||||
The same compatibility surface also includes:
|
||||
|
||||
@@ -4,6 +4,8 @@ read_when: "Connecting the macOS app to a remote gateway over SSH"
|
||||
title: "Remote Gateway Setup"
|
||||
---
|
||||
|
||||
> This content has been merged into [Remote Access](/gateway/remote#macos-persistent-ssh-tunnel-via-launchagent). See that page for the current guide.
|
||||
|
||||
# Running OpenClaw.app with a Remote Gateway
|
||||
|
||||
OpenClaw.app uses SSH tunneling to connect to a remote gateway. This guide shows you how to set it up.
|
||||
|
||||
@@ -151,3 +151,98 @@ Short version: **keep the Gateway loopback-only** unless you’re sure you need
|
||||
- Treat browser control like operator access: tailnet-only + deliberate node pairing.
|
||||
|
||||
Deep dive: [Security](/gateway/security).
|
||||
|
||||
### macOS: persistent SSH tunnel via LaunchAgent
|
||||
|
||||
For macOS clients connecting to a remote gateway, the easiest persistent setup uses an SSH `LocalForward` config entry plus a LaunchAgent to keep the tunnel alive across reboots and crashes.
|
||||
|
||||
#### Step 1: add SSH config
|
||||
|
||||
Edit `~/.ssh/config`:
|
||||
|
||||
```ssh
|
||||
Host remote-gateway
|
||||
HostName <REMOTE_IP>
|
||||
User <REMOTE_USER>
|
||||
LocalForward 18789 127.0.0.1:18789
|
||||
IdentityFile ~/.ssh/id_rsa
|
||||
```
|
||||
|
||||
Replace `<REMOTE_IP>` and `<REMOTE_USER>` with your values.
|
||||
|
||||
#### Step 2: copy SSH key (one-time)
|
||||
|
||||
```bash
|
||||
ssh-copy-id -i ~/.ssh/id_rsa <REMOTE_USER>@<REMOTE_IP>
|
||||
```
|
||||
|
||||
#### Step 3: configure the gateway token
|
||||
|
||||
Store the token in config so it persists across restarts:
|
||||
|
||||
```bash
|
||||
openclaw config set gateway.remote.token "<your-token>"
|
||||
```
|
||||
|
||||
#### Step 4: create the LaunchAgent
|
||||
|
||||
Save this as `~/Library/LaunchAgents/ai.openclaw.ssh-tunnel.plist`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>ai.openclaw.ssh-tunnel</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/bin/ssh</string>
|
||||
<string>-N</string>
|
||||
<string>remote-gateway</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
#### Step 5: load the LaunchAgent
|
||||
|
||||
```bash
|
||||
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/ai.openclaw.ssh-tunnel.plist
|
||||
```
|
||||
|
||||
The tunnel will start automatically at login, restart on crash, and keep the forwarded port live.
|
||||
|
||||
Note: if you have a leftover `com.openclaw.ssh-tunnel` LaunchAgent from an older setup, unload and delete it.
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
Check if the tunnel is running:
|
||||
|
||||
```bash
|
||||
ps aux | grep "ssh -N remote-gateway" | grep -v grep
|
||||
lsof -i :18789
|
||||
```
|
||||
|
||||
Restart the tunnel:
|
||||
|
||||
```bash
|
||||
launchctl kickstart -k gui/$UID/ai.openclaw.ssh-tunnel
|
||||
```
|
||||
|
||||
Stop the tunnel:
|
||||
|
||||
```bash
|
||||
launchctl bootout gui/$UID/ai.openclaw.ssh-tunnel
|
||||
```
|
||||
|
||||
| Config entry | What it does |
|
||||
| ------------------------------------ | ------------------------------------------------------------ |
|
||||
| `LocalForward 18789 127.0.0.1:18789` | Forwards local port 18789 to remote port 18789 |
|
||||
| `ssh -N` | SSH without executing remote commands (port-forwarding only) |
|
||||
| `KeepAlive` | Automatically restarts the tunnel if it crashes |
|
||||
| `RunAtLoad` | Starts the tunnel when the LaunchAgent loads at login |
|
||||
|
||||
@@ -50,8 +50,8 @@ Not sandboxed:
|
||||
|
||||
`agents.defaults.sandbox.scope` controls **how many containers** are created:
|
||||
|
||||
- `"session"` (default): one container per session.
|
||||
- `"agent"`: one container per agent.
|
||||
- `"agent"` (default): one container per agent.
|
||||
- `"session"`: one container per session.
|
||||
- `"shared"`: one container shared by all sandboxed sessions.
|
||||
|
||||
## Backend
|
||||
|
||||
@@ -20,6 +20,7 @@ Secrets are resolved into an in-memory runtime snapshot.
|
||||
- Resolution is eager during activation, not lazy on request paths.
|
||||
- Startup fails fast when an effectively active SecretRef cannot be resolved.
|
||||
- Reload uses atomic swap: full success, or keep the last-known-good snapshot.
|
||||
- SecretRef policy violations (for example OAuth-mode auth profiles combined with SecretRef input) fail activation before runtime swap.
|
||||
- Runtime requests read from the active in-memory snapshot only.
|
||||
- After the first successful config activation/load, runtime code paths keep reading that active in-memory snapshot until a successful reload swaps it.
|
||||
- Outbound delivery paths also read from that active snapshot (for example Discord reply/thread delivery and Telegram action sends); they do not re-resolve SecretRefs on each send.
|
||||
@@ -364,6 +365,7 @@ Runtime-minted or rotating credentials and OAuth refresh material are intentiona
|
||||
- Field without a ref: unchanged.
|
||||
- Field with a ref: required on active surfaces during activation.
|
||||
- If both plaintext and ref are present, ref takes precedence on supported precedence paths.
|
||||
- The redaction sentinel `__OPENCLAW_REDACTED__` is reserved for internal config redaction/restore and is rejected as literal submitted config data.
|
||||
|
||||
Warning and audit signals:
|
||||
|
||||
@@ -383,12 +385,14 @@ Secret activation runs on:
|
||||
- Config reload hot-apply path
|
||||
- Config reload restart-check path
|
||||
- Manual reload via `secrets.reload`
|
||||
- Gateway config write RPC preflight (`config.set` / `config.apply` / `config.patch`) for active-surface SecretRef resolvability within the submitted config payload before persisting edits
|
||||
|
||||
Activation contract:
|
||||
|
||||
- Success swaps the snapshot atomically.
|
||||
- Startup failure aborts gateway startup.
|
||||
- Runtime reload failure keeps the last-known-good snapshot.
|
||||
- Write-RPC preflight failure rejects the submitted config and keeps both disk config and active runtime snapshot unchanged.
|
||||
- Providing an explicit per-call channel token to an outbound helper/tool call does not trigger SecretRef activation; activation points remain startup, reload, and explicit `secrets.reload`.
|
||||
|
||||
## Degraded and recovered signals
|
||||
|
||||
@@ -285,6 +285,7 @@ aggregates:
|
||||
- `hooks.gmail.allowUnsafeExternalContent=true`
|
||||
- `hooks.mappings[<index>].allowUnsafeExternalContent=true`
|
||||
- `tools.exec.applyPatch.workspaceOnly=false`
|
||||
- `plugins.entries.acpx.config.permissionMode=approve-all`
|
||||
|
||||
Complete `dangerous*` / `dangerously*` config keys defined in OpenClaw config
|
||||
schema:
|
||||
@@ -446,8 +447,11 @@ Plugins run **in-process** with the Gateway. Treat them as trusted code:
|
||||
- Restart the Gateway after plugin changes.
|
||||
- If you install plugins (`openclaw plugins install <package>`), treat it like running untrusted code:
|
||||
- The install path is the per-plugin directory under the active plugin install root.
|
||||
- OpenClaw runs a built-in dangerous-code scan before install. `critical` findings block by default.
|
||||
- OpenClaw uses `npm pack` and then runs `npm install --omit=dev` in that directory (npm lifecycle scripts can execute code during install).
|
||||
- Prefer pinned, exact versions (`@scope/pkg@1.2.3`), and inspect the unpacked code on disk before enabling.
|
||||
- `--dangerously-force-unsafe-install` is break-glass only for built-in scan false positives. It does not bypass plugin `before_install` hook policy blocks and does not bypass scan failures.
|
||||
- Gateway-backed skill dependency installs follow the same dangerous/suspicious split: built-in `critical` findings block unless the caller explicitly sets `dangerouslyForceUnsafeInstall`, while suspicious findings still warn only. `openclaw skills install` remains the separate ClawHub skill download/install flow.
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
@@ -802,7 +806,10 @@ still require token/password auth.
|
||||
Important boundary note:
|
||||
|
||||
- Gateway HTTP bearer auth is effectively all-or-nothing operator access.
|
||||
- Treat credentials that can call `/v1/chat/completions`, `/v1/responses`, `/tools/invoke`, or `/api/channels/*` as full-access operator secrets for that gateway.
|
||||
- Treat credentials that can call `/v1/chat/completions`, `/v1/responses`, or `/api/channels/*` as full-access operator secrets for that gateway.
|
||||
- On the OpenAI-compatible HTTP surface, shared-secret bearer auth restores the full default operator scopes and owner semantics for agent turns; narrower `x-openclaw-scopes` values do not reduce that shared-secret path.
|
||||
- Per-request scope semantics on HTTP only apply when the request comes from an identity-bearing mode such as trusted proxy auth or `gateway.auth.mode="none"` on a private ingress.
|
||||
- `/tools/invoke` follows the same shared-secret rule: token/password bearer auth is treated as full operator access there too, while identity-bearing modes still honor declared scopes.
|
||||
- Do not share these credentials with untrusted callers; prefer separate gateways per trust boundary.
|
||||
|
||||
**Trust assumption:** tokenless Serve auth assumes the gateway host is trusted.
|
||||
|
||||
@@ -8,7 +8,7 @@ title: "Tools Invoke API"
|
||||
|
||||
# Tools Invoke (HTTP)
|
||||
|
||||
OpenClaw’s Gateway exposes a simple HTTP endpoint for invoking a single tool directly. It is always enabled and uses Gateway auth plus tool policy, but callers that pass Gateway bearer auth are treated as trusted operators for that gateway.
|
||||
OpenClaw’s Gateway exposes a simple HTTP endpoint for invoking a single tool directly. It is always enabled and uses Gateway auth plus tool policy. Like the OpenAI-compatible `/v1/*` surface, shared-secret bearer auth is treated as trusted operator access for the whole gateway.
|
||||
|
||||
- `POST /tools/invoke`
|
||||
- Same port as the Gateway (WS + HTTP multiplex): `http://<gateway-host>:<port>/tools/invoke`
|
||||
@@ -26,7 +26,29 @@ Notes:
|
||||
- When `gateway.auth.mode="token"`, use `gateway.auth.token` (or `OPENCLAW_GATEWAY_TOKEN`).
|
||||
- When `gateway.auth.mode="password"`, use `gateway.auth.password` (or `OPENCLAW_GATEWAY_PASSWORD`).
|
||||
- If `gateway.auth.rateLimit` is configured and too many auth failures occur, the endpoint returns `429` with `Retry-After`.
|
||||
- Treat this credential as a full-access operator secret for that gateway. It is not a scoped API token for a narrower `/tools/invoke` role.
|
||||
|
||||
## Security boundary (important)
|
||||
|
||||
Treat this endpoint as a **full operator-access** surface for the gateway instance.
|
||||
|
||||
- HTTP bearer auth here is not a narrow per-user scope model.
|
||||
- A valid Gateway token/password for this endpoint should be treated like an owner/operator credential.
|
||||
- For shared-secret auth modes (`token` and `password`), the endpoint restores the normal full operator defaults even if the caller sends a narrower `x-openclaw-scopes` header.
|
||||
- Shared-secret auth also treats direct tool invokes on this endpoint as owner-sender turns.
|
||||
- Trusted identity-bearing HTTP modes (for example trusted proxy auth or `gateway.auth.mode="none"` on a private ingress) still honor the declared operator scopes on the request.
|
||||
- Keep this endpoint on loopback/tailnet/private ingress only; do not expose it directly to the public internet.
|
||||
|
||||
Auth matrix:
|
||||
|
||||
- `gateway.auth.mode="token"` or `"password"` + `Authorization: Bearer ...`
|
||||
- proves possession of the shared gateway operator secret
|
||||
- ignores narrower `x-openclaw-scopes`
|
||||
- restores the full default operator scope set
|
||||
- treats direct tool invokes on this endpoint as owner-sender turns
|
||||
- trusted identity-bearing HTTP modes (for example trusted proxy auth, or `gateway.auth.mode="none"` on private ingress)
|
||||
- authenticate some outer trusted identity or deployment boundary
|
||||
- honor the declared `x-openclaw-scopes` header
|
||||
- only get owner semantics when `operator.admin` is actually present in those declared scopes
|
||||
|
||||
## Request body
|
||||
|
||||
@@ -62,17 +84,24 @@ If a tool is not allowed by policy, the endpoint returns **404**.
|
||||
|
||||
Important boundary notes:
|
||||
|
||||
- `POST /tools/invoke` is in the same trusted-operator bucket as other Gateway HTTP APIs such as `/v1/chat/completions`, `/v1/responses`, and `/api/channels/*`.
|
||||
- Exec approvals are operator guardrails, not a separate authorization boundary for this HTTP endpoint. If a tool is reachable here via Gateway auth + tool policy, `/tools/invoke` does not add an extra per-call approval prompt.
|
||||
- Do not share Gateway bearer credentials with untrusted callers. If you need separation across trust boundaries, run separate gateways (and ideally separate OS users/hosts).
|
||||
|
||||
Gateway HTTP also applies a hard deny list by default (even if session policy allows the tool):
|
||||
|
||||
- `cron`
|
||||
- `sessions_spawn`
|
||||
- `sessions_send`
|
||||
- `gateway`
|
||||
- `whatsapp_login`
|
||||
- `exec` — direct command execution (RCE surface)
|
||||
- `spawn` — arbitrary child process creation (RCE surface)
|
||||
- `shell` — shell command execution (RCE surface)
|
||||
- `fs_write` — arbitrary file mutation on the host
|
||||
- `fs_delete` — arbitrary file deletion on the host
|
||||
- `fs_move` — arbitrary file move/rename on the host
|
||||
- `apply_patch` — patch application can rewrite arbitrary files
|
||||
- `sessions_spawn` — session orchestration; spawning agents remotely is RCE
|
||||
- `sessions_send` — cross-session message injection
|
||||
- `cron` — persistent automation control plane
|
||||
- `gateway` — gateway control plane; prevents reconfiguration via HTTP
|
||||
- `nodes` — node command relay can reach system.run on paired hosts
|
||||
- `whatsapp_login` — interactive setup requiring terminal QR scan; hangs on HTTP
|
||||
|
||||
You can customize this deny list via `gateway.tools`:
|
||||
|
||||
@@ -108,7 +137,7 @@ To help group policies resolve context, you can optionally set:
|
||||
|
||||
```bash
|
||||
curl -sS http://127.0.0.1:18789/tools/invoke \
|
||||
-H 'Authorization: Bearer YOUR_TOKEN' \
|
||||
-H 'Authorization: Bearer secret' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"tool": "sessions_list",
|
||||
|
||||
@@ -145,7 +145,8 @@ If logs show nonce/signature errors, update the connecting client and verify it:
|
||||
Related:
|
||||
|
||||
- [/web/control-ui](/web/control-ui)
|
||||
- [/gateway/authentication](/gateway/authentication)
|
||||
- [/gateway/configuration](/gateway/configuration) (gateway auth modes)
|
||||
- [/gateway/trusted-proxy-auth](/gateway/trusted-proxy-auth)
|
||||
- [/gateway/remote](/gateway/remote)
|
||||
- [/cli/devices](/cli/devices)
|
||||
|
||||
|
||||
@@ -254,6 +254,17 @@ location / {
|
||||
}
|
||||
```
|
||||
|
||||
## Mixed token configuration
|
||||
|
||||
OpenClaw rejects ambiguous configurations where both a `gateway.auth.token` (or `OPENCLAW_GATEWAY_TOKEN`) and `trusted-proxy` mode are active at the same time. Mixed token configs can cause loopback requests to silently authenticate on the wrong auth path.
|
||||
|
||||
If you see a `mixed_trusted_proxy_token` error on startup:
|
||||
|
||||
- Remove the shared token when using trusted-proxy mode, or
|
||||
- Switch `gateway.auth.mode` to `"token"` if you intend token-based auth.
|
||||
|
||||
Loopback trusted-proxy auth also fails closed: same-host callers must supply the configured identity headers through a trusted proxy instead of being silently authenticated.
|
||||
|
||||
## Security Checklist
|
||||
|
||||
Before enabling trusted-proxy auth, verify:
|
||||
@@ -263,6 +274,7 @@ Before enabling trusted-proxy auth, verify:
|
||||
- [ ] **Proxy strips headers**: Your proxy overwrites (not appends) `x-forwarded-*` headers from clients
|
||||
- [ ] **TLS termination**: Your proxy handles TLS; users connect via HTTPS
|
||||
- [ ] **allowUsers is set** (recommended): Restrict to known users rather than allowing anyone authenticated
|
||||
- [ ] **No mixed token config**: Do not set both `gateway.auth.token` and `gateway.auth.mode: "trusted-proxy"`
|
||||
|
||||
## Security Audit
|
||||
|
||||
|
||||
@@ -2784,6 +2784,8 @@ Related: [/concepts/oauth](/concepts/oauth) (OAuth flows, token storage, multi-a
|
||||
|
||||
- The target channel supports outbound media and isn't blocked by allowlists.
|
||||
- The file is within the provider's size limits (images are resized to max 2048px).
|
||||
- `tools.fs.workspaceOnly=true` keeps local-path sends limited to workspace, temp/media-store, and sandbox-validated files.
|
||||
- `tools.fs.workspaceOnly=false` lets `MEDIA:` send host-local files the agent can already read, but only for media plus safe document types (images, audio, video, PDF, and Office docs). Plain text and secret-like files are still blocked.
|
||||
|
||||
See [Images](/nodes/images).
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
||||
- Shared unit, extension, channel, and gateway runs all stay on Vitest `forks`.
|
||||
- The wrapper keeps measured fork-isolated exceptions and heavy singleton lanes explicit in `test/fixtures/test-parallel.behavior.json`.
|
||||
- The wrapper peels the heaviest measured files into dedicated lanes instead of relying on a growing hand-maintained exclusion list.
|
||||
- CLI startup benchmarking now has distinct saved outputs: `pnpm test:startup:bench:smoke` writes the targeted smoke artifact at `.artifacts/cli-startup-bench-smoke.json`, `pnpm test:startup:bench:save` writes the full-suite artifact at `.artifacts/cli-startup-bench-all.json` with `runs=5` and `warmup=1`, and `pnpm test:startup:bench:update` refreshes the checked-in fixture at `test/fixtures/cli-startup-bench.json` with `runs=5` and `warmup=1`.
|
||||
- For surface-only local runs, unit, extension, and channel shared lanes can overlap their isolated hotspots instead of waiting behind one serial prefix.
|
||||
- For multi-surface local runs, the wrapper keeps the shared surface phases ordered, but batches inside the same shared phase now fan out together, deferred isolated work can overlap the next shared phase, and spare `unit-fast` headroom now starts that deferred work earlier instead of leaving those slots idle.
|
||||
- Refresh the timing snapshots with `pnpm test:perf:update-timings` and `pnpm test:perf:update-timings:extensions` after major suite shape changes.
|
||||
@@ -205,7 +206,7 @@ Live tests are split into two layers so we can isolate failures:
|
||||
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
|
||||
- Set `OPENCLAW_LIVE_MODELS=modern` (or `all`, alias for modern) to actually run this suite; otherwise it skips to keep `pnpm test:live` focused on gateway smoke
|
||||
- How to select models:
|
||||
- `OPENCLAW_LIVE_MODELS=modern` to run the modern allowlist (Opus/Sonnet/Haiku 4.5, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4)
|
||||
- `OPENCLAW_LIVE_MODELS=modern` to run the modern allowlist (Opus/Sonnet 4.6+, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4)
|
||||
- `OPENCLAW_LIVE_MODELS=all` is an alias for the modern allowlist
|
||||
- or `OPENCLAW_LIVE_MODELS="openai/gpt-5.2,anthropic/claude-opus-4-6,..."` (comma allowlist)
|
||||
- How to select providers:
|
||||
@@ -236,7 +237,7 @@ Live tests are split into two layers so we can isolate failures:
|
||||
- How to enable:
|
||||
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
|
||||
- How to select models:
|
||||
- Default: modern allowlist (Opus/Sonnet/Haiku 4.5, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4)
|
||||
- Default: modern allowlist (Opus/Sonnet 4.6+, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4)
|
||||
- `OPENCLAW_LIVE_GATEWAY_MODELS=all` is an alias for the modern allowlist
|
||||
- Or set `OPENCLAW_LIVE_GATEWAY_MODELS="provider/model"` (or comma list) to narrow
|
||||
- How to select providers (avoid “OpenRouter everything”):
|
||||
|
||||
@@ -342,3 +342,11 @@ flowchart TD
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related
|
||||
|
||||
- [FAQ](/help/faq) — frequently asked questions
|
||||
- [Gateway Troubleshooting](/gateway/troubleshooting) — gateway-specific issues
|
||||
- [Doctor](/gateway/doctor) — automated health checks and repairs
|
||||
- [Channel Troubleshooting](/channels/troubleshooting) — channel connectivity issues
|
||||
- [Automation Troubleshooting](/automation/troubleshooting) — cron and heartbeat issues
|
||||
|
||||
@@ -48,7 +48,7 @@ update **without** changing your persisted channel:
|
||||
|
||||
```bash
|
||||
# Install a specific version
|
||||
openclaw update --tag 2026.3.30-beta.1
|
||||
openclaw update --tag 2026.3.31-beta.1
|
||||
|
||||
# Install from the beta dist-tag (one-off, does not persist)
|
||||
openclaw update --tag beta
|
||||
@@ -57,7 +57,7 @@ openclaw update --tag beta
|
||||
openclaw update --tag main
|
||||
|
||||
# Install a specific npm package spec
|
||||
openclaw update --tag openclaw@2026.3.30-beta.1
|
||||
openclaw update --tag openclaw@2026.3.31-beta.1
|
||||
```
|
||||
|
||||
Notes:
|
||||
@@ -75,7 +75,7 @@ Preview what `openclaw update` would do without making changes:
|
||||
```bash
|
||||
openclaw update --dry-run
|
||||
openclaw update --channel beta --dry-run
|
||||
openclaw update --tag 2026.3.30-beta.1 --dry-run
|
||||
openclaw update --tag 2026.3.31-beta.1 --dry-run
|
||||
openclaw update --dry-run --json
|
||||
```
|
||||
|
||||
|
||||
@@ -394,3 +394,11 @@ scripts/sandbox-setup.sh
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related
|
||||
|
||||
- [Install Overview](/install) — all installation methods
|
||||
- [Podman](/install/podman) — Podman alternative to Docker
|
||||
- [ClawDock](/install/clawdock) — Docker Compose community setup
|
||||
- [Updating](/install/updating) — keeping OpenClaw up to date
|
||||
- [Configuration](/gateway/configuration) — gateway configuration after install
|
||||
|
||||
@@ -136,3 +136,9 @@ export PATH="$HOME/.npm-global/bin:$PATH"
|
||||
```
|
||||
|
||||
Add the `export PATH=...` line to your `~/.bashrc` or `~/.zshrc` to make it permanent.
|
||||
|
||||
## Related
|
||||
|
||||
- [Install Overview](/install) — all installation methods
|
||||
- [Updating](/install/updating) — keeping OpenClaw up to date
|
||||
- [Getting Started](/start/getting-started) — first steps after install
|
||||
|
||||
@@ -126,3 +126,9 @@ To return to latest: `git checkout main && git pull`.
|
||||
- Run `openclaw doctor` again and read the output carefully.
|
||||
- Check: [Troubleshooting](/gateway/troubleshooting)
|
||||
- Ask in Discord: [https://discord.gg/clawd](https://discord.gg/clawd)
|
||||
|
||||
## Related
|
||||
|
||||
- [Install Overview](/install) — all installation methods
|
||||
- [Doctor](/gateway/doctor) — health checks after updates
|
||||
- [Migrating](/install/migrating) — major version migration guides
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user