mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-10 16:03:47 +08:00
Compare commits
478 Commits
qa
...
codex/refa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af826ae7fc | ||
|
|
f33b5426aa | ||
|
|
df1a008584 | ||
|
|
cc7cbc0580 | ||
|
|
2dc0ea95d7 | ||
|
|
224f195709 | ||
|
|
c422f10aef | ||
|
|
619da391ab | ||
|
|
2d886f03a7 | ||
|
|
6a53001e2f | ||
|
|
0843ad5ad1 | ||
|
|
960a631b25 | ||
|
|
607d341451 | ||
|
|
b6da5443fc | ||
|
|
a84858d315 | ||
|
|
23549694f7 | ||
|
|
4a5fe2e0e7 | ||
|
|
32ae0eba54 | ||
|
|
24f76d04eb | ||
|
|
5b53ddcc5f | ||
|
|
51ff658586 | ||
|
|
be85d9aaec | ||
|
|
fd1b355f84 | ||
|
|
b9a9290dfc | ||
|
|
b141da4ca9 | ||
|
|
4857f9d0c2 | ||
|
|
aa7c67e6a9 | ||
|
|
fdf381f1a7 | ||
|
|
5cff2ff94b | ||
|
|
ac66507ccb | ||
|
|
3dec7f2596 | ||
|
|
83f47a4d0a | ||
|
|
a9dbaa1124 | ||
|
|
367f52f483 | ||
|
|
b371af76a3 | ||
|
|
3584d28141 | ||
|
|
c1b1d14218 | ||
|
|
79348f73c8 | ||
|
|
cef64f0b5a | ||
|
|
e91405ebf9 | ||
|
|
bfa1fa1700 | ||
|
|
54ad458267 | ||
|
|
c6d3ee70e2 | ||
|
|
8a841b531f | ||
|
|
1582bbbfc5 | ||
|
|
4780788bbb | ||
|
|
eb6d0ce2c2 | ||
|
|
b5fc435bd5 | ||
|
|
8e1c81e707 | ||
|
|
17a324b0de | ||
|
|
bb60b53124 | ||
|
|
d7f75ee087 | ||
|
|
b58f9c5258 | ||
|
|
a234157337 | ||
|
|
f30c087fdf | ||
|
|
1a3eb38aaf | ||
|
|
2ed2dbba00 | ||
|
|
48611ec40a | ||
|
|
471d056e2f | ||
|
|
1351bacaa4 | ||
|
|
f7e76e31f3 | ||
|
|
1703bdcaf6 | ||
|
|
a62193c09e | ||
|
|
5e0b58fbc6 | ||
|
|
4ed60d950d | ||
|
|
05f9dd7a01 | ||
|
|
d6d8d1716f | ||
|
|
7f1b159c03 | ||
|
|
6a57f5403d | ||
|
|
53c52124b9 | ||
|
|
0655e173c4 | ||
|
|
dea3ab0aa9 | ||
|
|
94256ea1a0 | ||
|
|
e29d370969 | ||
|
|
06f9677b5b | ||
|
|
beed40e918 | ||
|
|
c73aeed929 | ||
|
|
a4a1cfc8c2 | ||
|
|
39b05c4920 | ||
|
|
08492dfeee | ||
|
|
2f72363984 | ||
|
|
64f889cd4b | ||
|
|
a2a9fa7f6f | ||
|
|
cd564bf5a5 | ||
|
|
c11e7a7420 | ||
|
|
00372508b5 | ||
|
|
ca94f02959 | ||
|
|
a2376462e9 | ||
|
|
d66960206b | ||
|
|
c2a8aac282 | ||
|
|
5a6d80da7f | ||
|
|
afb89b439a | ||
|
|
d624ec3a0b | ||
|
|
9ce4abfe55 | ||
|
|
a213a580d5 | ||
|
|
a78c4de737 | ||
|
|
7b62fcd87d | ||
|
|
d1c7d9af80 | ||
|
|
fbbe2a1675 | ||
|
|
82710f2add | ||
|
|
516a43f9f2 | ||
|
|
57d1685a65 | ||
|
|
b0c7bac9ce | ||
|
|
e7407f8178 | ||
|
|
1033db4d31 | ||
|
|
3a7a67b218 | ||
|
|
2176b68e50 | ||
|
|
b4e5d91941 | ||
|
|
5586b3fd19 | ||
|
|
d7f3af3b06 | ||
|
|
d83dd9b536 | ||
|
|
d3e67a0de7 | ||
|
|
932194b7d5 | ||
|
|
52146f8803 | ||
|
|
aa464f8573 | ||
|
|
8279375bdf | ||
|
|
58f95b8000 | ||
|
|
8a43223014 | ||
|
|
9b7002ee59 | ||
|
|
456ad889c7 | ||
|
|
ce8492f9a0 | ||
|
|
a8e827856a | ||
|
|
9bc43b61bf | ||
|
|
2a4eea58a9 | ||
|
|
a4f16f572c | ||
|
|
e92f55302b | ||
|
|
8a987030c0 | ||
|
|
6b627d4707 | ||
|
|
8206328a94 | ||
|
|
714ba48a6f | ||
|
|
36080283e4 | ||
|
|
97e1437803 | ||
|
|
466e17436d | ||
|
|
d8abb287eb | ||
|
|
791c083c0a | ||
|
|
8806ef804e | ||
|
|
5cadf069e9 | ||
|
|
ce1cd26fbc | ||
|
|
181a50e146 | ||
|
|
604e16c765 | ||
|
|
4da7b453b1 | ||
|
|
6424b08772 | ||
|
|
dcd0cf9f98 | ||
|
|
55192e2d51 | ||
|
|
a9125ec0b0 | ||
|
|
31016c5ed9 | ||
|
|
cac40c01e9 | ||
|
|
bcc0e3de2e | ||
|
|
8cd9007ec1 | ||
|
|
2ff29a33d0 | ||
|
|
0ef9383487 | ||
|
|
84e76f7cce | ||
|
|
b664541158 | ||
|
|
1a47675e6c | ||
|
|
a01c4c3a0e | ||
|
|
318111286f | ||
|
|
98ce1c2902 | ||
|
|
7bef5a7466 | ||
|
|
b8e2e5c251 | ||
|
|
c71ee4d844 | ||
|
|
267ebc3ba5 | ||
|
|
dcfc1f16ed | ||
|
|
b43d73b633 | ||
|
|
05d351c430 | ||
|
|
79d6713d81 | ||
|
|
5790435975 | ||
|
|
b5e87be7f0 | ||
|
|
a1c1598742 | ||
|
|
7fe5dc36f0 | ||
|
|
14dbcd0451 | ||
|
|
24d213e4bc | ||
|
|
33c0627c64 | ||
|
|
4461432e31 | ||
|
|
4d57b69163 | ||
|
|
c02f72be37 | ||
|
|
387f47d19b | ||
|
|
a68935e497 | ||
|
|
b7e86f4d0d | ||
|
|
595adfc2f6 | ||
|
|
8ed5d0cf1e | ||
|
|
881a343f34 | ||
|
|
cdb9f37989 | ||
|
|
bb440b328f | ||
|
|
8ff41a6bc4 | ||
|
|
848cc5e0ce | ||
|
|
550872777e | ||
|
|
db0db3abdb | ||
|
|
962650f879 | ||
|
|
5834ee54e8 | ||
|
|
80b1fb034a | ||
|
|
3a3c52c357 | ||
|
|
85ce5022cc | ||
|
|
d943516838 | ||
|
|
ee4695499a | ||
|
|
3f6840230b | ||
|
|
bb494ea3ed | ||
|
|
7d8190b588 | ||
|
|
5127468494 | ||
|
|
038bfd9652 | ||
|
|
ffc1f7b337 | ||
|
|
c6bf955b0c | ||
|
|
24d5494dbf | ||
|
|
5ad27fa25f | ||
|
|
6008ed6c24 | ||
|
|
3bf92944b2 | ||
|
|
3126809cb0 | ||
|
|
cb76e5c899 | ||
|
|
1a65c3b06d | ||
|
|
6642d4a341 | ||
|
|
218182aaca | ||
|
|
4df05cae48 | ||
|
|
415a7efe8d | ||
|
|
57620654d1 | ||
|
|
c79306ba89 | ||
|
|
6a43205299 | ||
|
|
5ba562d147 | ||
|
|
163c6f5e35 | ||
|
|
5ed02c1097 | ||
|
|
eae0ac333d | ||
|
|
ba69205f6e | ||
|
|
33ff535614 | ||
|
|
8d3e557fc4 | ||
|
|
fe93f29486 | ||
|
|
2d7157b424 | ||
|
|
6243806f7b | ||
|
|
8f9b1ad48d | ||
|
|
a74fb94fa3 | ||
|
|
adbcfbe2bb | ||
|
|
2ce38dfc31 | ||
|
|
7a14967f8e | ||
|
|
043d9d370f | ||
|
|
18d6d5b629 | ||
|
|
846d2734e7 | ||
|
|
9b89fa3937 | ||
|
|
aee1f0b453 | ||
|
|
c3fd7fbbe7 | ||
|
|
198083cde3 | ||
|
|
15aed55470 | ||
|
|
acd78e0c2f | ||
|
|
c3d8a6d270 | ||
|
|
dfae62616f | ||
|
|
17521116db | ||
|
|
9e8151f347 | ||
|
|
de0d6efc6e | ||
|
|
eced1fa905 | ||
|
|
7075da59bd | ||
|
|
0047048179 | ||
|
|
b169b2c977 | ||
|
|
2ade009901 | ||
|
|
a6d0ab1482 | ||
|
|
df38bc2271 | ||
|
|
8405d86a8b | ||
|
|
63fcc52520 | ||
|
|
ff6fd18629 | ||
|
|
a32a3e2331 | ||
|
|
d25609bc06 | ||
|
|
7e4c5294ae | ||
|
|
37b3acad34 | ||
|
|
97878b853a | ||
|
|
7a3443e9ac | ||
|
|
82ce30b789 | ||
|
|
511e6c4189 | ||
|
|
f64a058348 | ||
|
|
6e3155ca84 | ||
|
|
c1bba98e88 | ||
|
|
3a4b96bfbf | ||
|
|
65f18d6e24 | ||
|
|
003f52db98 | ||
|
|
5eb551ccfa | ||
|
|
b723b30def | ||
|
|
9408f682f6 | ||
|
|
f7670bde7e | ||
|
|
40ffada812 | ||
|
|
6f2f840e97 | ||
|
|
eb8f0e1bf2 | ||
|
|
575371b6f7 | ||
|
|
3d3ef6f65f | ||
|
|
33363ab922 | ||
|
|
8e51207626 | ||
|
|
69466cec2f | ||
|
|
c66ff16c59 | ||
|
|
0b38916c5e | ||
|
|
5a5b2b1764 | ||
|
|
1f912482e5 | ||
|
|
fb61986767 | ||
|
|
1a537fcfcf | ||
|
|
7b7d645193 | ||
|
|
692671f377 | ||
|
|
0aaf753148 | ||
|
|
934641df86 | ||
|
|
7b05253bed | ||
|
|
dfd39a81d8 | ||
|
|
71fa5f481d | ||
|
|
de4344a23a | ||
|
|
4399aaebbb | ||
|
|
42abcf9886 | ||
|
|
9f2b760d33 | ||
|
|
5da21bc2f7 | ||
|
|
7ff7a27f61 | ||
|
|
bcd0a492a4 | ||
|
|
b0f4af3bad | ||
|
|
79d722e922 | ||
|
|
8143b9a23e | ||
|
|
9320efd9db | ||
|
|
7cd015b203 | ||
|
|
21270c2586 | ||
|
|
629baf5fa7 | ||
|
|
695c9c887b | ||
|
|
e1142f4197 | ||
|
|
88e9268399 | ||
|
|
c9471f08d5 | ||
|
|
1ef6bada36 | ||
|
|
7e29e84fa4 | ||
|
|
1d2d70a8fd | ||
|
|
d28b02a7b1 | ||
|
|
84eb617a79 | ||
|
|
28955a36e7 | ||
|
|
98bac6a0e4 | ||
|
|
9a0d88a868 | ||
|
|
d842251ef8 | ||
|
|
2780980a28 | ||
|
|
ca1da659e4 | ||
|
|
89e8c8672c | ||
|
|
4fedc5c105 | ||
|
|
01c5dde6d1 | ||
|
|
9efc033434 | ||
|
|
d893ae341c | ||
|
|
e8cbc1ee8a | ||
|
|
9ddc3576d1 | ||
|
|
a705845e18 | ||
|
|
c731c1e61f | ||
|
|
760c4be438 | ||
|
|
1a7c2a9bc8 | ||
|
|
e6f1a59e67 | ||
|
|
70d77f5425 | ||
|
|
ed1734a7c7 | ||
|
|
388f82f22f | ||
|
|
4e550a873e | ||
|
|
79e5101a88 | ||
|
|
1840611fe6 | ||
|
|
fd0ffec4e4 | ||
|
|
e681cc057b | ||
|
|
ac2ca8b2ca | ||
|
|
ee4fe4fb1e | ||
|
|
21d5f7a915 | ||
|
|
8bfbc2ba5d | ||
|
|
75752a862d | ||
|
|
e4206007cc | ||
|
|
d70162864a | ||
|
|
31f5463a1c | ||
|
|
987f4bba80 | ||
|
|
c74b222ec1 | ||
|
|
9f7aaa8ad7 | ||
|
|
074af3f40e | ||
|
|
6f5ba51f74 | ||
|
|
1dc3da6eda | ||
|
|
7343d1b2ad | ||
|
|
aca488d5be | ||
|
|
2f5d6c859d | ||
|
|
c039675054 | ||
|
|
e9bf9fde06 | ||
|
|
a060b89e3f | ||
|
|
fc9648b620 | ||
|
|
35b132c7eb | ||
|
|
227a13bd55 | ||
|
|
88ea0751a9 | ||
|
|
81c095d945 | ||
|
|
2635e07bf0 | ||
|
|
76da484bed | ||
|
|
21ef63d9f2 | ||
|
|
19c081d4a2 | ||
|
|
41d08a6feb | ||
|
|
dbac5fa258 | ||
|
|
deb212d3b0 | ||
|
|
259338565a | ||
|
|
59a243e46b | ||
|
|
d0afdb56ce | ||
|
|
bc7f845714 | ||
|
|
dbcd35f6c2 | ||
|
|
78fe96f2d4 | ||
|
|
5f6ba749ff | ||
|
|
b9d26fd1a4 | ||
|
|
996dccb19c | ||
|
|
3b7e6152d1 | ||
|
|
c9e505f54a | ||
|
|
8ad6dd92d7 | ||
|
|
4175caee6e | ||
|
|
1bf5339b98 | ||
|
|
283a6c65b7 | ||
|
|
7fc3b22f53 | ||
|
|
379f0d78e6 | ||
|
|
25da786c68 | ||
|
|
cfe66c6e02 | ||
|
|
fef155cdbc | ||
|
|
63db3443f1 | ||
|
|
0f58cef75e | ||
|
|
66c047ddc3 | ||
|
|
3a957cfe8b | ||
|
|
c5f69111ce | ||
|
|
440428889e | ||
|
|
dbfd96f4ec | ||
|
|
495ebd28a4 | ||
|
|
064f474ed7 | ||
|
|
fd0cc90427 | ||
|
|
4559ece355 | ||
|
|
8c1ca1f245 | ||
|
|
359be4eb48 | ||
|
|
2d7ec1b641 | ||
|
|
be526d6423 | ||
|
|
0a21eebf56 | ||
|
|
af81ee9fee | ||
|
|
1ad5695aa4 | ||
|
|
f02e435188 | ||
|
|
fd917a471c | ||
|
|
be5a2611b9 | ||
|
|
f2dc241e9d | ||
|
|
3b84884793 | ||
|
|
afca9540bf | ||
|
|
852e8f7a2a | ||
|
|
1d736dcbbc | ||
|
|
e3eb615da8 | ||
|
|
3fa70f3044 | ||
|
|
d609f71c9b | ||
|
|
64cf52ca20 | ||
|
|
e468da1040 | ||
|
|
219afbc2cc | ||
|
|
4954d025e2 | ||
|
|
b3d73b648b | ||
|
|
1fb0b4f557 | ||
|
|
7e724c6140 | ||
|
|
72ba7c8995 | ||
|
|
cd348659ce | ||
|
|
9d315cdf42 | ||
|
|
d4e06d1249 | ||
|
|
d5cde2171b | ||
|
|
ef3a185225 | ||
|
|
84fb62170a | ||
|
|
83d29dae2b | ||
|
|
1030b498de | ||
|
|
cc09171929 | ||
|
|
f1f8fd5970 | ||
|
|
2489913ede | ||
|
|
4a85810091 | ||
|
|
19de5d1b56 | ||
|
|
4613f121ad | ||
|
|
a9c52dd935 | ||
|
|
3d952aa35d | ||
|
|
03be4c2489 | ||
|
|
19e97193d3 | ||
|
|
48653c2031 | ||
|
|
48f0d9aaf7 | ||
|
|
a3f6e58928 | ||
|
|
3a9569ff38 | ||
|
|
de04eeab76 | ||
|
|
647fc7bfec | ||
|
|
f9f44b9b96 | ||
|
|
50ed91a589 | ||
|
|
1afa076cfa | ||
|
|
22db77d2b6 | ||
|
|
0b8336f49d | ||
|
|
2d6e75ccd5 | ||
|
|
bca6faf11d | ||
|
|
455c642acb | ||
|
|
bff55b55cb | ||
|
|
fb77c8ce4e | ||
|
|
5b9cdb8975 | ||
|
|
43fe68f9ef | ||
|
|
8be017fae6 | ||
|
|
a4b767c89b | ||
|
|
0e61a1d0ca | ||
|
|
5ac07b8ef0 | ||
|
|
b5f8cd4fcf | ||
|
|
aa497e9c52 | ||
|
|
92c498cf7b | ||
|
|
90fcc1f551 | ||
|
|
c6e117897f | ||
|
|
41e39eb46f | ||
|
|
a5b6b71468 |
@@ -68,7 +68,9 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
|
||||
- Windows installer/tgz phases now retry once after guest-ready recheck; keep new Windows smoke steps idempotent so a transport-flake retry is safe.
|
||||
- If a Windows retry sees the VM become `suspended` or `stopped`, resume/start it before the next `prlctl exec`; otherwise the second attempt just repeats the same `rc=255`.
|
||||
- Windows global `npm install -g` phases can stay quiet for a minute or more even when healthy; inspect the phase log before calling it hung, and only treat it as a regression once the retry wrapper or timeout trips.
|
||||
- Fresh Windows tgz install phases should also use the background PowerShell runner plus done-file/log-drain pattern; do not rely on one long-lived `prlctl exec ... powershell ... npm install -g` transport for package installs.
|
||||
- Fresh Windows ref-mode onboard should use the same background PowerShell runner plus done-file/log-drain pattern as the npm-update helper, including startup materialization checks, host-side timeouts on short poll `prlctl exec` calls, and retry-on-poll-failure behavior for transient transport flakes.
|
||||
- Fresh Windows daemon-health reachability should use a hello-only gateway probe and a longer per-probe timeout than the default local attach path; full health RPCs are too eager during initial startup on current main.
|
||||
- Fresh Windows ref-mode agent verification should set `OPENAI_API_KEY` in the PowerShell environment before invoking `openclaw.cmd agent`, for the same pairing-required fallback reason as macOS.
|
||||
- The standalone Windows upgrade smoke lane should stop the managed gateway after `upgrade.install-main` and before `upgrade.onboard-ref`. Restarting before onboard can leave the old process alive on the pre-onboard token while onboard rewrites `~/.openclaw/openclaw.json`, which then fails `gateway-health` with `unauthorized: gateway token mismatch`.
|
||||
- If standalone Windows upgrade fails with a gateway token mismatch but `pnpm test:parallels:npm-update` passes, trust the mismatch as a standalone ref-onboard ordering bug first; the npm-update helper does not re-run ref-mode onboard on the same guest.
|
||||
|
||||
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -1,5 +1,7 @@
|
||||
name: CI
|
||||
|
||||
# Keep PR CI synchronized on branch updates.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
@@ -743,6 +745,11 @@ jobs:
|
||||
continue-on-error: true
|
||||
run: pnpm lint:ui:no-raw-window-open
|
||||
|
||||
- name: Check control UI locale sync
|
||||
id: control_ui_i18n
|
||||
continue-on-error: true
|
||||
run: pnpm ui:i18n:check
|
||||
|
||||
- name: Run gateway watch regression harness
|
||||
id: gateway_watch_regression
|
||||
continue-on-error: true
|
||||
@@ -775,6 +782,7 @@ jobs:
|
||||
EXTENSION_PLUGIN_SDK_INTERNAL_BOUNDARY_OUTCOME: ${{ steps.extension_plugin_sdk_internal_boundary.outcome }}
|
||||
EXTENSION_RELATIVE_OUTSIDE_PACKAGE_BOUNDARY_OUTCOME: ${{ steps.extension_relative_outside_package_boundary.outcome }}
|
||||
NO_RAW_WINDOW_OPEN_OUTCOME: ${{ steps.no_raw_window_open.outcome }}
|
||||
CONTROL_UI_I18N_OUTCOME: ${{ steps.control_ui_i18n.outcome }}
|
||||
GATEWAY_WATCH_REGRESSION_OUTCOME: ${{ steps.gateway_watch_regression.outcome }}
|
||||
run: |
|
||||
failures=0
|
||||
@@ -795,6 +803,7 @@ jobs:
|
||||
"extension-plugin-sdk-internal-boundary|$EXTENSION_PLUGIN_SDK_INTERNAL_BOUNDARY_OUTCOME" \
|
||||
"extension-relative-outside-package-boundary|$EXTENSION_RELATIVE_OUTSIDE_PACKAGE_BOUNDARY_OUTCOME" \
|
||||
"lint:ui:no-raw-window-open|$NO_RAW_WINDOW_OPEN_OUTCOME" \
|
||||
"ui:i18n:check|$CONTROL_UI_I18N_OUTCOME" \
|
||||
"gateway-watch-regression|$GATEWAY_WATCH_REGRESSION_OUTCOME"; do
|
||||
name="${result%%|*}"
|
||||
outcome="${result#*|}"
|
||||
|
||||
172
.github/workflows/control-ui-locale-refresh.yml
vendored
Normal file
172
.github/workflows/control-ui-locale-refresh.yml
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
name: Control UI Locale Refresh
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- ui/src/i18n/locales/en.ts
|
||||
- ui/src/i18n/locales/*.ts
|
||||
- ui/src/i18n/.i18n/*
|
||||
- ui/src/i18n/lib/types.ts
|
||||
- ui/src/i18n/lib/registry.ts
|
||||
- scripts/control-ui-i18n.ts
|
||||
- .github/workflows/control-ui-locale-refresh.yml
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
schedule:
|
||||
- cron: "23 4 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: control-ui-locale-refresh
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
plan:
|
||||
if: github.repository == 'openclaw/openclaw' && (github.event_name != 'push' || github.actor != 'github-actions[bot]')
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has_locales: ${{ steps.plan.outputs.has_locales }}
|
||||
locales_json: ${{ steps.plan.outputs.locales_json }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Plan locale matrix
|
||||
id: plan
|
||||
env:
|
||||
BEFORE_SHA: ${{ github.event.before }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
all_locales_json='["zh-CN","zh-TW","pt-BR","de","es","ja-JP","ko","fr","tr","uk","id","pl"]'
|
||||
|
||||
if [ "$EVENT_NAME" != "push" ]; then
|
||||
echo "has_locales=true" >> "$GITHUB_OUTPUT"
|
||||
echo "locales_json=$all_locales_json" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
before_ref="$BEFORE_SHA"
|
||||
if [ -z "$before_ref" ] || [ "$before_ref" = "0000000000000000000000000000000000000000" ]; then
|
||||
before_ref="$(git rev-parse HEAD^)"
|
||||
fi
|
||||
|
||||
changed_files="$(git diff --name-only "$before_ref" HEAD)"
|
||||
echo "changed files:"
|
||||
printf '%s\n' "$changed_files"
|
||||
|
||||
if printf '%s\n' "$changed_files" | grep -Eq '^(ui/src/i18n/locales/en\.ts|ui/src/i18n/lib/types\.ts|ui/src/i18n/lib/registry\.ts|scripts/control-ui-i18n\.ts|\.github/workflows/control-ui-locale-refresh\.yml)$'; then
|
||||
echo "has_locales=true" >> "$GITHUB_OUTPUT"
|
||||
echo "locales_json=$all_locales_json" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
locales_json="$(printf '%s\n' "$changed_files" | node <<'EOF'
|
||||
const fs = require("node:fs");
|
||||
const changed = fs.readFileSync(0, "utf8").split(/\r?\n/).filter(Boolean);
|
||||
const locales = new Set();
|
||||
for (const file of changed) {
|
||||
let match = file.match(/^ui\/src\/i18n\/locales\/(.+)\.ts$/);
|
||||
if (match && match[1] !== "en") {
|
||||
locales.add(match[1]);
|
||||
continue;
|
||||
}
|
||||
match = file.match(/^ui\/src\/i18n\/\.i18n\/(.+)\.(?:meta\.json|tm\.jsonl)$/);
|
||||
if (match) {
|
||||
locales.add(match[1]);
|
||||
}
|
||||
}
|
||||
process.stdout.write(JSON.stringify([...locales]));
|
||||
EOF
|
||||
)"
|
||||
|
||||
if [ "$locales_json" = "[]" ]; then
|
||||
echo "has_locales=false" >> "$GITHUB_OUTPUT"
|
||||
echo "locales_json=[]" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "has_locales=true" >> "$GITHUB_OUTPUT"
|
||||
echo "locales_json=$locales_json" >> "$GITHUB_OUTPUT"
|
||||
|
||||
refresh:
|
||||
needs: plan
|
||||
if: github.repository == 'openclaw/openclaw' && needs.plan.outputs.has_locales == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
locale: ${{ fromJson(needs.plan.outputs.locales_json) }}
|
||||
runs-on: ubuntu-latest
|
||||
name: Refresh ${{ matrix.locale }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: true
|
||||
submodules: false
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
use-sticky-disk: "false"
|
||||
|
||||
- name: Ensure translation provider secrets exist
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_I18N_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${OPENAI_API_KEY:-}" ] && [ -z "${ANTHROPIC_API_KEY:-}" ]; then
|
||||
echo "Missing OPENCLAW_DOCS_I18N_OPENAI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY secret."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Refresh control UI locale files
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_I18N_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
OPENCLAW_CONTROL_UI_I18N_MODEL: gpt-5.4
|
||||
OPENCLAW_CONTROL_UI_I18N_THINKING: low
|
||||
run: node --import tsx scripts/control-ui-i18n.ts sync --locale "${{ matrix.locale }}" --write
|
||||
|
||||
- name: Commit and push locale updates
|
||||
env:
|
||||
LOCALE: ${{ matrix.locale }}
|
||||
TARGET_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if git diff --quiet -- ui/src/i18n; then
|
||||
echo "No control UI locale changes for ${LOCALE}."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add -A ui/src/i18n
|
||||
FAST_COMMIT=1 git commit -m "chore(ui): refresh ${LOCALE} control ui locale"
|
||||
|
||||
for attempt in 1 2 3 4 5; do
|
||||
git fetch origin "${TARGET_BRANCH}"
|
||||
git rebase "origin/${TARGET_BRANCH}"
|
||||
if git push origin HEAD:"${TARGET_BRANCH}"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "Push attempt ${attempt} for ${LOCALE} failed; retrying."
|
||||
sleep $((attempt * 2))
|
||||
done
|
||||
|
||||
echo "Failed to push ${LOCALE} locale update after retries."
|
||||
exit 1
|
||||
70
.github/workflows/docs-sync-publish.yml
vendored
Normal file
70
.github/workflows/docs-sync-publish.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Docs Sync Publish Repo
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- docs/**
|
||||
- scripts/docs-sync-publish.mjs
|
||||
- .github/workflows/docs-sync-publish.yml
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync-publish-repo:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Clone publish repo
|
||||
env:
|
||||
OPENCLAW_DOCS_SYNC_TOKEN: ${{ secrets.OPENCLAW_DOCS_SYNC_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git clone \
|
||||
"https://x-access-token:${OPENCLAW_DOCS_SYNC_TOKEN}@github.com/openclaw/docs.git" \
|
||||
publish
|
||||
|
||||
- name: Sync docs into publish repo
|
||||
run: |
|
||||
node scripts/docs-sync-publish.mjs \
|
||||
--target "$GITHUB_WORKSPACE/publish" \
|
||||
--source-repo "$GITHUB_REPOSITORY" \
|
||||
--source-sha "$GITHUB_SHA"
|
||||
|
||||
- name: Commit publish repo sync
|
||||
working-directory: publish
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if git diff --quiet -- docs .openclaw-sync; then
|
||||
echo "No publish-repo changes."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config user.name "openclaw-docs-sync[bot]"
|
||||
git config user.email "openclaw-docs-sync[bot]@users.noreply.github.com"
|
||||
git add docs .openclaw-sync
|
||||
git commit -m "chore(sync): mirror docs from $GITHUB_REPOSITORY@$GITHUB_SHA"
|
||||
for attempt in 1 2 3 4 5; do
|
||||
git fetch origin main
|
||||
git rebase origin/main
|
||||
if git push origin HEAD:main; then
|
||||
exit 0
|
||||
fi
|
||||
echo "Push attempt ${attempt} failed; retrying."
|
||||
sleep $((attempt * 2))
|
||||
done
|
||||
|
||||
echo "Failed to push publish-repo sync after retries."
|
||||
exit 1
|
||||
42
.github/workflows/docs-translate-trigger-release.yml
vendored
Normal file
42
.github/workflows/docs-translate-trigger-release.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Docs Trigger Locale Translate On Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dispatch-translate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger locale translates in publish repo
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.OPENCLAW_DOCS_SYNC_TOKEN }}
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for event_type in \
|
||||
translate-zh-cn-release \
|
||||
translate-ja-jp-release \
|
||||
translate-es-release \
|
||||
translate-pt-br-release \
|
||||
translate-ko-release \
|
||||
translate-de-release \
|
||||
translate-fr-release \
|
||||
translate-ar-release \
|
||||
translate-it-release \
|
||||
translate-tr-release \
|
||||
translate-uk-release \
|
||||
translate-id-release \
|
||||
translate-pl-release
|
||||
do
|
||||
gh api repos/openclaw/docs/dispatches \
|
||||
--method POST \
|
||||
-f event_type="${event_type}" \
|
||||
-f client_payload[release_tag]="${RELEASE_TAG}" \
|
||||
-f client_payload[source_repository]="${GITHUB_REPOSITORY}" \
|
||||
-f client_payload[source_sha]="${GITHUB_SHA}"
|
||||
done
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -145,6 +145,7 @@ changelog/fragments/
|
||||
|
||||
# Local scratch workspace
|
||||
.tmp/
|
||||
.artifacts/
|
||||
test/fixtures/openclaw-vitest-unit-report.json
|
||||
analysis/
|
||||
.artifacts/qa-e2e/
|
||||
|
||||
23
AGENTS.md
23
AGENTS.md
@@ -38,8 +38,14 @@
|
||||
- Plugin and extension boundary:
|
||||
- Public docs: `docs/plugins/building-plugins.md`, `docs/plugins/architecture.md`, `docs/plugins/sdk-overview.md`, `docs/plugins/sdk-entrypoints.md`, `docs/plugins/sdk-runtime.md`, `docs/plugins/manifest.md`, `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/sdk-provider-plugins.md`
|
||||
- Definition files: `src/plugin-sdk/plugin-entry.ts`, `src/plugin-sdk/core.ts`, `src/plugin-sdk/provider-entry.ts`, `src/plugin-sdk/channel-contract.ts`, `scripts/lib/plugin-sdk-entrypoints.json`, `package.json`
|
||||
- Invariant: core must stay extension-agnostic. Adding a bundled or third-party extension should not require unrelated core edits just to teach core that the extension exists.
|
||||
- Rule: extensions must cross into core only through `openclaw/plugin-sdk/*`, manifest metadata, and documented runtime helpers. Do not import `src/**` from extension production code.
|
||||
- Rule: core code and tests must not deep-import bundled plugin internals such as a plugin's `src/**` files or `onboard.js`. If core needs a bundled plugin helper, expose it through that plugin's `api.ts` and, when it is a real cross-package contract, through `src/plugin-sdk/<id>.ts`.
|
||||
- Rule: do not add hardcoded bundled extension/provider/channel/capability id lists, maps, or named special cases in core when a manifest, capability, registry, or plugin-owned contract can express the same behavior.
|
||||
- Rule: extension-owned compatibility behavior belongs to the owning extension. Core may orchestrate generic doctor/config flows, but extension-specific legacy repairs, detection rules, onboarding, auth detection, and provider defaults should live in plugin-owned contracts.
|
||||
- Rule: for legacy config specifically, prefer doctor-owned repair paths over startup/load-time core migrations. Do not add new plugin-specific legacy migration logic to shared core/runtime surfaces when `openclaw doctor --fix` can own it.
|
||||
- Rule: when a test is asserting extension-specific behavior, keep that coverage in the owning extension when feasible. Core tests should assert generic contracts and registry/capability behavior, not extension internals.
|
||||
- Refactor trigger: if you encounter core code or tests that name a specific extension/provider/channel for extension-owned behavior, refactor toward a generic registry/capability/plugin-owned seam instead of adding another special case.
|
||||
- Compatibility: new plugin seams are allowed, but they must be added as documented, backwards-compatible, versioned contracts. We have third-party plugins in the wild and do not break them casually.
|
||||
- Channel boundary:
|
||||
- Public docs: `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/architecture.md`
|
||||
@@ -67,6 +73,7 @@
|
||||
- Extension test boundary:
|
||||
- Keep extension-owned onboarding/config/provider coverage under the owning bundled plugin package when feasible.
|
||||
- If core tests need bundled plugin behavior, consume it through public `src/plugin-sdk/<id>.ts` facades or the plugin's `api.ts`, not private extension modules.
|
||||
- If a core test is asserting extension-specific behavior instead of a generic contract, move it to the owning extension package.
|
||||
|
||||
## Docs Linking (Mintlify)
|
||||
|
||||
@@ -81,16 +88,24 @@
|
||||
- README (GitHub): keep absolute docs URLs (`https://docs.openclaw.ai/...`) so links work on GitHub.
|
||||
- Docs content must be generic: no personal device names/hostnames/paths; use placeholders like `user@gateway-host` and “gateway host”.
|
||||
|
||||
## Docs i18n (zh-CN)
|
||||
## Docs i18n (generated publish locales)
|
||||
|
||||
- `docs/zh-CN/**` is generated; do not edit unless the user explicitly asks.
|
||||
- Pipeline: update English docs → adjust glossary (`docs/.i18n/glossary.zh-CN.json`) → run `scripts/docs-i18n` → apply targeted fixes only if instructed.
|
||||
- Foreign-language docs are not maintained in this repo. The generated publish output lives in the separate `openclaw/docs` repo (often cloned locally as the sibling `openclaw-docs` directory); do not add or edit localized docs under `docs/<locale>/**` here.
|
||||
- Those localized docs are autogenerated. Treat this repo's English docs plus glossary files as the source of truth, and let the publish/translation pipeline update `openclaw/docs`.
|
||||
- Pipeline: update English docs here → adjust the matching `docs/.i18n/glossary.<locale>.json` entries → let the publish-repo sync + `scripts/docs-i18n` run in `openclaw/docs` / local `openclaw-docs` clone → apply targeted fixes only if instructed.
|
||||
- Before rerunning `scripts/docs-i18n`, add glossary entries for any new technical terms, page titles, or short nav labels that must stay in English or use a fixed translation (for example `Doctor` or `Polls`).
|
||||
- `pnpm docs:check-i18n-glossary` enforces glossary coverage for changed English doc titles and short internal doc labels before translation reruns.
|
||||
- Translation memory: `docs/.i18n/zh-CN.tm.jsonl` (generated).
|
||||
- Translation memory lives in generated `docs/.i18n/*.tm.jsonl` files in the publish repo.
|
||||
- See `docs/.i18n/README.md`.
|
||||
- The pipeline can be slow/inefficient; if it’s dragging, ping @jospalmbier on Discord instead of hacking around it.
|
||||
|
||||
## Control UI i18n (generated in repo)
|
||||
|
||||
- Control UI foreign-language locale bundles are generated in this repo; do not hand-edit `ui/src/i18n/locales/*.ts` for non-English locales or `ui/src/i18n/.i18n/*` unless a targeted generated-output fix is explicitly requested.
|
||||
- Source of truth is `ui/src/i18n/locales/en.ts` plus the generator/runtime wiring in `scripts/control-ui-i18n.ts`, `ui/src/i18n/lib/types.ts`, and `ui/src/i18n/lib/registry.ts`.
|
||||
- Pipeline: update English control UI strings and locale wiring here → run `pnpm ui:i18n:sync` (or let `Control UI Locale Refresh` do it) → commit the regenerated locale bundles and `.i18n` metadata.
|
||||
- If the control UI locale outputs drift, regenerate them; do not manually translate or hand-maintain the generated locale files by default.
|
||||
|
||||
## exe.dev VM ops (general)
|
||||
|
||||
- Access: stable path is `ssh exe.dev` then `ssh vm-name` (assume SSH key already set).
|
||||
|
||||
299
CHANGELOG.md
299
CHANGELOG.md
@@ -10,127 +10,216 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Changes
|
||||
|
||||
- Memory/dreaming (experimental): add weighted short-term recall promotion, managed dreaming modes (`off|core|rem|deep`), a `/dreaming` command, Dreams UI, multilingual conceptual tagging, and doctor/status repair support so durable memory promotion can run in the background with less manual setup. (#60569, #60697)
|
||||
- Channels/context visibility: add configurable `contextVisibility` per channel (`all`, `allowlist`, `allowlist_quote`) so supplemental quote, thread, and fetched history context can be filtered by sender allowlists instead of always passing through as received.
|
||||
- Memory/dreaming (experimental): add weighted short-term recall promotion, a `/dreaming` command, Dreams UI, multilingual conceptual tagging, and doctor/status repair support, while refactoring dreaming from competing modes into three cooperative phases (light, deep, REM) with independent schedules and recovery behavior so durable memory promotion can run in the background with less manual setup. (#60569, #60697) Thanks @vignesh07.
|
||||
- Memory/dreaming: add configurable aging controls (`recencyHalfLifeDays`, `maxAgeDays`) plus optional verbose logging so operators can tune recall decay and inspect promotion decisions more easily.
|
||||
- Memory/dreaming: add REM preview tooling (`openclaw memory rem-harness`, `promote-explain`), surface possible lasting truths during REM staging, and make deep promotion replay-safe so reruns reconcile instead of duplicating `MEMORY.md` entries.
|
||||
- Agents/video generation: add the built-in `video_generate` tool so agents can create videos through configured providers and return the generated media directly in the reply.
|
||||
- Control UI/multilingual: add localized control UI support for Simplified Chinese, Traditional Chinese, Brazilian Portuguese, German, Spanish, Japanese, Korean, French, Turkish, Indonesian, Polish, and Ukrainian. Thanks @vincentkoc.
|
||||
- iOS/exec approvals: add generic APNs approval notifications that open an in-app exec approval modal, fetch command details only after authenticated operator reconnect, and clear stale notification state when the approval resolves. (#60239) Thanks @ngutman.
|
||||
- Matrix/exec approvals: add Matrix-native exec approval prompts with account-scoped approvers, channel-or-DM delivery, and room-thread aware resolution handling. (#58635) Thanks @gumadeiras.
|
||||
- Providers/StepFun: add the bundled StepFun provider plugin with standard and Step Plan endpoints, China/global onboarding choices, `step-3.5-flash` on both catalogs, and `step-3.5-flash-2603` currently exposed on Step Plan. (#60032) Thanks @hengm3467.
|
||||
- Providers/Fireworks: add a bundled Fireworks AI provider plugin with `FIREWORKS_API_KEY` onboarding, Fire Pass Kimi defaults, and dynamic Fireworks model-id support.
|
||||
- Providers/config: add `models.providers.*.request` overrides for headers and auth on model-provider paths, and full request transport overrides for media provider HTTP paths.
|
||||
- MiniMax/TTS: add a bundled MiniMax speech provider backed by the T2A v2 API so speech synthesis can run through MiniMax-native voices and auth. (#55921) Thanks @duncanita.
|
||||
- Control UI/skills: add ClawHub search, detail, and install flows directly in the Skills panel. (#60134) Thanks @samzong.
|
||||
- Providers/Ollama: add a bundled Ollama Web Search provider for key-free `web_search` via your configured Ollama host and `ollama signin`. (#59318) Thanks @BruceMacD.
|
||||
- Plugins/onboarding: add plugin config TUI prompts to onboard and configure wizards so more plugin setup can stay in the guided flow. (#60590)
|
||||
- Plugins/install: add `openclaw plugins install --force` to overwrite existing plugin and hook-pack install targets without using the dangerous-code override flag. (#60544) Thanks @gumadeiras.
|
||||
- Providers/Anthropic: remove setup-token from new onboarding and auth-command setup paths, keep existing configured legacy token profiles runnable, and steer new Anthropic setup to Claude CLI or API keys.
|
||||
- Providers/OpenAI Codex: add forward-compat `openai-codex/gpt-5.4-mini` synthesis across provider runtime, model catalog, and model listing so Codex mini works before bundled Pi catalog updates land.
|
||||
- Tools/web_search: add a bundled MiniMax Search provider backed by the Coding Plan search API, with region reuse from `MINIMAX_API_HOST` and plugin-owned credential config. (#54648) Thanks @fengmk2.
|
||||
- Channels/context visibility: add configurable `contextVisibility` per channel (`all`, `allowlist`, `allowlist_quote`) so quoted, threaded, and fetched history context can be filtered by sender allowlists instead of always passing through as received.
|
||||
- Plugins: add plugin-config TUI prompts to guided onboarding/setup flows, and add `openclaw plugins install --force` so existing plugin and hook-pack targets can be replaced without using the dangerous-code override flag. (#60590, #60544)
|
||||
- Channels/context visibility: add configurable `contextVisibility` per channel (`all`, `allowlist`, `allowlist_quote`) so supplemental quote, thread, and fetched history context can be filtered by sender allowlists instead of always passing through as received.
|
||||
- Providers: add bundled Qwen, Fireworks AI, and StepFun providers, plus MiniMax TTS, Ollama Web Search, and MiniMax Search integrations for chat, speech, and search workflows. (#60032, #55921, #59318, #54648)
|
||||
- Providers/Amazon Bedrock: add bundled Mantle support plus inference-profile discovery and automatic request-region injection so Bedrock-hosted Claude, GPT-OSS, Qwen, Kimi, GLM, and similar routes work with less manual setup. (#61296, #61299) Thanks @wirjo.
|
||||
- Providers/request overrides: add shared model and media request transport overrides across OpenAI-, Anthropic-, Google-, and compatible provider paths, including headers, auth, proxy, and TLS controls. (#60200)
|
||||
- Matrix/exec approvals: add Matrix-native exec approval prompts with account-scoped approvers, channel-or-DM delivery, and room-thread aware resolution handling. (#58635) Thanks @gumadeiras.
|
||||
- Agents/Claude CLI: expose OpenClaw tools to background Claude CLI runs through a loopback MCP bridge that reuses gateway tool policy, honors session/account/channel scoping, and only advertises the bridge when the local runtime is actually live. (#35676) Thanks @mylukin.
|
||||
- Agents/Claude CLI: switch bundled Claude CLI runs to stdin + `stream-json` partial-message streaming so prompts stop riding argv, long replies show live progress, and final session/usage metadata still land cleanly.
|
||||
- Prompt caching: keep prompt prefixes more reusable across transport fallback, deterministic MCP tool ordering, compaction, and embedded image history so follow-up turns hit cache more reliably. (#58036, #58037, #58038, #59054, #60603, #60691) Thanks @bcherny.
|
||||
- Prompt caching: keep prompt prefixes more reusable across transport fallback, deterministic MCP tool ordering, compaction, embedded image history, normalized system-prompt fingerprints, `openclaw status --verbose` cache diagnostics, and the removal of duplicate in-band tool inventories from agent system prompts so follow-up turns hit cache more reliably. (#58036, #58037, #58038, #59054, #60603, #60691) Thanks @bcherny and @vincentkoc.
|
||||
- Providers/OpenAI: add forward-compat `openai-codex/gpt-5.4-mini`, an opt-in GPT personality, and provider-owned GPT-5 prompt contributions so Codex/GPT runs stay cache-stable and compatible with bundled catalog lag.
|
||||
- Providers/Anthropic: remove the Claude CLI backend and setup-token from new onboarding, keep existing configured legacy profiles runnable, and have `openclaw doctor` repair or remove stale `anthropic:claude-cli` state during migration.
|
||||
- Agents/progress: add experimental structured plan updates and structured execution item events so compatible UIs can show clearer step-by-step progress during long-running runs.
|
||||
- Agents/Claude CLI: expose OpenClaw tools to background Claude CLI runs through a loopback MCP bridge and switch bundled runs to stdin + `stream-json` partial-message streaming so prompts stop riding argv, long replies show live progress, and final session/usage metadata still land cleanly. (#35676) Thanks @mylukin.
|
||||
- ACPX/runtime: embed the ACP runtime directly in the bundled `acpx` plugin, remove the extra external ACP CLI hop, harden live ACP session binding and reuse, and add a generic `reply_dispatch` hook so bundled plugins like ACPX can own reply interception without hardcoded ACP paths in core auto-reply routing. (#61319)
|
||||
- Config/schema: enrich the exported `openclaw config schema` JSON Schema with field titles and descriptions so editors, agents, and other schema consumers receive the same config help metadata. (#60067) Thanks @solavrc.
|
||||
- Agents/cache: diagnostics: add prompt-cache break diagnostics, trace live cache scenarios through embedded runner paths, and show cache reuse explicitly in `openclaw status --verbose`. Thanks @vincentkoc.
|
||||
- Agents/cache: stabilize cache-relevant system prompt fingerprints by normalizing equivalent structured prompt whitespace, line endings, hook-added system context, and runtime capability ordering so semantically unchanged prompts reuse KV/cache more reliably. Thanks @vincentkoc.
|
||||
- Config/schema: enrich the exported `openclaw config schema` JSON Schema with field titles and descriptions so editors, agents, and other schema consumers receive the same config help metadata. (#60067) Thanks @solavrc.
|
||||
- Providers/StepFun: add the bundled StepFun provider plugin with standard and Step Plan endpoints, China/global onboarding choices, `step-3.5-flash` on both catalogs, and `step-3.5-flash-2603` currently exposed on Step Plan. (#60032) Thanks @hengm3467.
|
||||
- Agents/tool prompts: remove the duplicate in-band tool inventory from agent system prompts so tool-calling models rely on the structured tool definitions as the single source of truth, improving prompt stability and reducing stale tool guidance.
|
||||
- Tools/video generation: add bundled xAI (`grok-imagine-video`) and Alibaba Model Studio Wan video providers, plus live-test/default model wiring for both.
|
||||
- Providers/CLI: remove bundled CLI text-provider backends and the `agents.defaults.cliBackends` surface, while keeping ACP harness sessions and Gemini media understanding on the native bundled providers.
|
||||
- Matrix/exec approvals: clarify unavailable-approval replies so Matrix no longer claims chat approvals are unsupported when native exec approvals are merely unconfigured. (#61424) Thanks @gumadeiras.
|
||||
- Docs/IRC: replace public IRC hostname examples with `irc.example.com` and recommend private servers for bot coordination while listing common public networks for intentional use.
|
||||
- Memory/dreaming: write dreaming trail content to top-level `DREAMS.md` instead of daily memory notes, update `/dreaming` help text to point there, and keep `DREAMS.md` available for explicit reads without pulling it into default recall. Thanks @davemorin.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Security: preserve restrictive plugin-only tool allowlists, require owner access for `/allowlist add` and `/allowlist remove`, fail closed when `before_tool_call` hooks crash, block browser SSRF redirect bypasses earlier, and keep non-interactive auth-choice inference scoped to bundled and already-trusted plugins. (#58476, #59836, #59822, #58771, #59120) Thanks @eleqtrizit and @pgondhi987.
|
||||
- Providers/OpenAI: make GPT-5 and Codex runs act sooner with lower-verbosity defaults, visible progress during tool work, and a one-shot retry when a turn only narrates the plan instead of taking action.
|
||||
- Providers/OpenAI and reply delivery: preserve native `reasoning.effort: "none"` and strict schemas where supported, add GPT-5.4 assistant `phase` metadata across replay and the Gateway `/v1/responses` layer, and keep commentary buffered until `final_answer` so web chat, session previews, embedded replies, and Telegram partials stop leaking planning text. Fixes #59150, #59643, #61282.
|
||||
- Telegram: fix current-model checks in the model picker, HTML-format non-default `/model` confirmations, explicit topic replies, persisted reaction ownership across restarts, caption-media placeholder and `file_id` preservation on download failure, and upgraded-install inbound image reads. (#60384, #60042, #59634, #59207, #59948, #59971) Thanks @sfuminya, @GitZhangChi, @dashhuang, @samzong, @v1p0r, and @neeravmakwana.
|
||||
- Telegram: restore DM voice-note preflight transcription so direct-message audio stops arriving as raw `<media:audio>` placeholders. (#61008) Thanks @manueltarouca.
|
||||
- Telegram/reasoning: only create a Telegram reasoning preview lane when the session is explicitly `reasoning:stream`, so hidden `<think>` traces from streamed replies stop surfacing as chat previews on normal sessions. Thanks @vincentkoc.
|
||||
- Telegram/native command menu: trim long menu descriptions before dropping commands so sub-100 command sets can still fit Telegram's payload budget and keep more `/` entries visible. (#61129) Thanks @neeravmakwana.
|
||||
- Discord: keep REST, webhook, and monitor traffic on the configured proxy, preserve component-only media sends, honor `@everyone` and `@here` mention gates, keep ACK reactions on the active account, and split voice connect/playback timeouts so auto-join is more reliable. (#57465, #60361, #60345) Thanks @geekhuashan.
|
||||
- Discord/reply tags: strip leaked `[[reply_to_current]]` control tags from preview text and honor explicit reply-tag threading during final delivery, so Discord replies stay attached to the triggering message instead of printing reply metadata into chat.
|
||||
- Discord/replies: replace the unshipped `replyToOnlyWhenBatched` flag with `replyToMode: "batched"` so native reply references only attach on debounced multi-message turns while explicit reply tags still work.
|
||||
- Discord/image generation: include the real generated `MEDIA:` paths in tool output, avoid duplicate plain-output media requeueing, and persist volatile workspace-generated media into durable outbound media before final reply delivery so generated image replies stop pointing at missing local files.
|
||||
- Slack: route live DM replies back to the concrete inbound DM channel while keeping persisted routing metadata user-scoped, so normal assistant replies stop disappearing when pairing and system messages still arrive. (#59030) Thanks @afurm.
|
||||
- WhatsApp: restore `channels.whatsapp.blockStreaming` and reset watchdog timeouts after reconnect so quiet chats stop falling into reconnect loops. (#60007, #60069) Thanks @MonkeyLeeT and @mcaxtr.
|
||||
- Android/Talk Mode: cancel in-flight `talk.speak` playback when speech is explicitly stopped, and restore spoken replies on both node-scoped and gateway-backed sessions by keeping reply routing and embedded transport overrides aligned with the current playback path. (#60306, #61164, #61214)
|
||||
- Voice-call/OpenAI: pass full plugin config into realtime transcription provider resolution so streaming calls can discover the bundled OpenAI realtime transcription provider again. Fixes #60936. Thanks @sliekens and @vincentkoc.
|
||||
- Matrix/exec approvals: anchor seeded approval reactions to the primary Matrix prompt event, resolve them from event metadata instead of prompt text, and clean up chunked approval prompts correctly. (#60931) Thanks @gumadeiras.
|
||||
- Matrix: recover more reliably when secret storage or recovery keys are missing by recreating secret storage during repair and backup reset, hold crypto snapshot locks during persistence, and surface explicit too-large attachment markers. (#59846, #59851, #60599, #60289) Thanks @al3mart, @emonty, and @efe-arv.
|
||||
- Matrix/DM sessions: add `channels.matrix.dm.sessionScope`, shared-session collision notices, and aligned outbound session reuse so separate Matrix DM rooms can keep distinct context when configured. (#61373) Thanks @gumadeiras.
|
||||
- Matrix: move legacy top-level `avatarUrl` into the default account during multi-account promotion and keep env-backed account setup avatar config persisted. (#61437) Thanks @gumadeiras.
|
||||
- MS Teams: download inline DM images via Graph API and preserve channel reply threading in proactive fallback. (#52212, #55198) Thanks @Ted-developer and @hyojin.
|
||||
- MS Teams: replace the deprecated Teams SDK HttpPlugin stub with `httpServerAdapter` so recurring gateway deprecation warnings stop firing and the Express 5 compatibility workaround stays on the supported SDK path. (#60939) Thanks @coolramukaka-sys.
|
||||
- Control UI/chat: add a per-session thinking-level picker in the chat header and mobile chat settings, and keep the browser bundle on UI-local thinking/session-key helpers so Safari no longer crashes on Node-only imports before rendering chat controls.
|
||||
- Sandbox/SSH: reject hardlinked files during cross-device rename fallback so EXDEV file copies preserve the same pinned file-boundary checks as direct reads.
|
||||
- Control UI: keep Stop visible during tool-only execution, preserve pending-send busy state, and clear stale ClawHub search results as soon as the query changes. (#54528, #59800, #60267) Thanks @chziyue and @frankekn.
|
||||
- Control UI/avatar: honor `ui.assistant.avatar` when serving `/avatar/:agentId` so Appearance UI avatar paths stop falling back to initials placeholders. (#60778) Thanks @hannasdev.
|
||||
- Control UI/cron: highlight the Cron refresh button while refresh is in flight so the page's loading state stays visible even when prior data remains on screen. (#60394) Thanks @coder-zhuzm.
|
||||
- Control UI/Overview: prevent gateway access token/password visibility toggle buttons from overlapping their inputs at narrow widths. (#56924) Thanks @bbddbb1.
|
||||
- Auto-reply: unify reply lifecycle ownership across preflight compaction, session rotation, CLI-backed runs, and gateway restart handling so `/stop` and same-session overlap checks target the right active turn and restart-interrupted turns return the restart notice instead of being silently dropped. (#61267) Thanks @dutifulbob.
|
||||
- Reply delivery: prevent duplicate block replies on `text_end` channels so providers that emit explicit text-end boundaries no longer double-send the same final message. (#61530)
|
||||
- Gateway/startup: default `gateway.mode` to `local` when unset, detect PID recycling in gateway lock files on Windows and macOS, and show startup progress so healthy restarts stop getting blocked by stale locks. (#54801, #60085, #59843) Thanks @BradGroux and @TonyDerek-dot.
|
||||
- Gateway/macOS: let launchd `KeepAlive` own in-process gateway restarts again, adding a short supervised-exit delay so rapid restarts avoid launchd crash-loop unloads while `openclaw gateway restart` still reports real LaunchAgent errors synchronously.
|
||||
- Gateway/macOS: re-bootstrap the LaunchAgent if `launchctl kickstart -k` unloads it during restart so failed restarts do not leave the gateway unmanaged until manual repair.
|
||||
- Gateway/macOS: recover installed-but-unloaded LaunchAgents during `openclaw gateway start` and `restart`, while still preferring live unmanaged gateways during restart recovery. (#43766) Thanks @HenryC-3.
|
||||
- Gateway/Windows scheduled tasks: preserve Task Scheduler settings on reinstall, fail loudly when `/Run` does not start, and report fast failed restarts accurately instead of pretending they timed out after 60 seconds. (#59335) Thanks @tmimmanuel.
|
||||
- Windows/restart: fall back to the installed Startup-entry launcher when the scheduled task was never registered, so `/restart` can relaunch the gateway on Windows setups where `schtasks` install fell back during onboarding. (#58943) Thanks @imechZhangLY.
|
||||
- Windows/restart: clean up stale gateway listeners before Windows self-restart and treat listener and argv probe failures as inconclusive, so scheduled-task relaunch no longer falls into an `EADDRINUSE` retry loop. (#60480) Thanks @arifahmedjoy.
|
||||
- Update/npm: prefer the npm binary that owns the installed global OpenClaw prefix so mixed Homebrew-plus-nvm setups update the right install. (#60153) Thanks @jayeshp19.
|
||||
- CLI/skills JSON: route `skills list --json`, `skills info --json`, and `skills check --json` output to stdout instead of stderr so machine-readable consumers receive JSON on the expected stream again. (#60914; fixes #57599; landed from contributor PR #57611 by @Aftabbs) Thanks @Aftabbs.
|
||||
- CLI/Commander: preserve Commander-computed exit codes for argument and help-error paths, and cover the user-argv parse mode in the regression tests so invalid CLI invocations no longer report success when exits are intercepted. (#60923) Thanks @Linux2010.
|
||||
- Cron: replay interrupted recurring jobs on the first gateway restart instead of waiting for a second restart. (#60583) Thanks @joelnishanth.
|
||||
- Cron: send failure notifications through the job's primary delivery channel using the same session context as successful delivery when no explicit `failureDestination` is configured. (#60622) Thanks @artwalker.
|
||||
- Exec/remote skills: stop advertising `exec host=node` when the current exec policy cannot route to a node, and clarify blocked exec-host override errors with both the requested host and allowed config path.
|
||||
- Agents/Claude CLI/security: clear inherited Claude Code config-root and plugin-root env overrides like `CLAUDE_CONFIG_DIR` and `CLAUDE_CODE_PLUGIN_*`, so OpenClaw-launched Claude CLI runs cannot be silently pointed at an alternate Claude config/plugin tree with different hooks, plugins, or auth context. Thanks @vincentkoc.
|
||||
- Agents/Claude CLI/security: clear inherited Claude Code provider-routing and managed-auth env overrides, and mark OpenClaw-launched Claude CLI runs as host-managed, so Claude CLI backdoor sessions cannot be silently redirected to proxy, Bedrock, Vertex, Foundry, or parent-managed token contexts. Thanks @vincentkoc.
|
||||
- Agents/Claude CLI/security: force host-managed Claude CLI backdoor runs to `--setting-sources user`, even under custom backend arg overrides, so repo-local `.claude` project/local settings, hooks, and plugin discovery do not silently execute inside non-interactive OpenClaw sessions. Thanks @vincentkoc.
|
||||
- Agents/Claude CLI: treat malformed bare `--permission-mode` backend overrides as missing and fail safe back to `bypassPermissions`, so custom `cliBackends.claude-cli.args` security config cannot accidentally consume the next flag as a bogus permission mode. Thanks @vincentkoc.
|
||||
- Gateway/device pairing: require non-admin paired-device sessions to manage only their own device for token rotate/revoke and paired-device removal, blocking cross-device token theft inside pairing-scoped sessions. (#50627) Thanks @coygeek.
|
||||
- Gateway/plugin routes: keep gateway-auth plugin runtime routes on write-only fallback scopes unless a trusted-proxy caller explicitly declares narrower `x-openclaw-scopes`, so plugin HTTP handlers no longer mint admin-level runtime scopes on missing or untrusted HTTP scope headers. (#59815) Thanks @pgondhi987.
|
||||
- Build/types: fix the Node `createRequire(...)` helper typing so provider-runtime lazy loads compile cleanly again and `pnpm build` no longer fails in the Pi embedded provider error-pattern path.
|
||||
- Gateway/security: scope loopback browser-origin auth throttling by normalized origin so one localhost Control UI tab cannot lock out a different localhost browser origin after repeated auth failures.
|
||||
- Gateway/auth: serialize async shared-secret auth attempts per client so concurrent Tailscale-capable failures cannot overrun the intended auth rate-limit budget. Thanks @Telecaster2147.
|
||||
- Device pairing/security: keep non-operator device scope checks bound to the requested role prefix so bootstrap verification cannot redeem `operator.*` scopes through `node` auth. (#57258) Thanks @jlapenna.
|
||||
- Device pairing: reject rotating device tokens into roles that were never approved during pairing, and keep reconnect role checks bounded to the paired device's approved role set. (#60462) Thanks @eleqtrizit.
|
||||
- Gateway/device auth: reuse cached device-token scopes only for cached-token reconnects, while keeping explicit `deviceToken` scope requests and empty-cache fallbacks intact so reconnects preserve `operator.read` without breaking explicit auth flows. (#46032) Thanks @caicongyang.
|
||||
- Mobile pairing/security: fail closed for internal `/pair` setup-code issuance, cleanup, and approval paths when gateway pairing scopes are missing, and keep approval-time requested-scope enforcement on the internal command path. (#55996) Thanks @coygeek.
|
||||
- Mobile pairing/bootstrap: keep QR bootstrap handoff tokens bounded to the mobile-safe contract so node handoff stays unscoped and operator handoff drops mixed `node.*`, `operator.admin`, and `operator.pairing` scopes.
|
||||
- Mobile pairing/Android: tighten secure endpoint handling so Tailscale and public remote setup reject cleartext endpoints, private LAN pairing still works, merged-role approvals mint both node and operator device tokens, and bootstrap tokens survive node auto-pair until operator approval finishes. (#60128, #60208, #60221) Thanks @obviyus.
|
||||
- Android/canvas security: require exact normalized A2UI URL matches before forwarding canvas bridge actions, rejecting query mismatches and descendant paths while still allowing fragment-only A2UI navigation.
|
||||
- Synology Chat/security: default low-level HTTPS helper TLS verification to on so helper/API defaults match the shipped safe account default, and only explicit `allowInsecureSsl: true` opts out.
|
||||
- Synology Chat/security: route webhook token comparison through the shared constant-time secret helper for consistency with other bundled plugins.
|
||||
- Plugins/marketplace: block remote marketplace symlink escapes without breaking ordinary local marketplace install paths. (#60556) Thanks @eleqtrizit.
|
||||
- Telegram/local Bot API: honor `channels.telegram.apiRoot` for buffered media downloads, add `channels.telegram.network.dangerouslyAllowPrivateNetwork` for trusted fake-IP setups, and require `channels.telegram.trustedLocalFileRoots` before reading absolute Bot API `file_path` values. (#59544, #60705) Thanks @SARAMALI15792 and @obviyus.
|
||||
- Outbound/sanitizer: strip leaked `<tool_call>`, `<function_calls>`, and model special tokens from shared user-visible assistant text, including truncated tool-call streams, so internal scaffolding no longer bleeds into replies across surfaces. (#60619) Thanks @oliviareid-svg.
|
||||
- Agents/errors: surface an explicit disk-full message when local session or transcript writes fail with `ENOSPC`/`disk full`, so those runs stop degrading into opaque `NO_REPLY`-style failures. Thanks @vincentkoc.
|
||||
- Exec approvals: remove heuristic command-obfuscation gating from host exec so gateway and node runs rely on explicit policy, allowlist, and strict inline-eval rules only.
|
||||
- Agents/tool results: cap live tool-result persistence and overflow-recovery truncation at 40k characters so oversized tool output stays bounded without discarding recent context entirely.
|
||||
- Discord/video replies: split text-plus-video deliveries into a text reply followed by a media-only send, and let live provider auth checks honor manifest-declared API key env vars like `MODELSTUDIO_API_KEY`.
|
||||
- Config/All Settings: keep the raw config view intact when sensitive fields are blank instead of corrupting or dropping the rendered snapshot. (#28214) Thanks @solodmd.
|
||||
- Plugin SDK/facades: back-fill bundled plugin facade sentinels before plugin-id tracking re-enters config loading, so CLI/provider startup no longer crashes with `shouldNormalizeGoogleProviderConfig is not a function` or other empty-facade reads during bundled plugin re-entry. Thanks @adam91holt.
|
||||
- Plugins/facades: back-fill facade sentinels before tracked-plugin resolution re-enters config loading, so facade exports stay defined during circular provider normalization. (#61180) Thanks @adam91holt.
|
||||
- Discord/image generation: include the real generated `MEDIA:` paths in tool output and avoid duplicate plain-output media requeueing so Discord image replies stop pointing at missing local files.
|
||||
- Slack: route live DM replies back to the concrete inbound DM channel while keeping persisted routing metadata user-scoped, so normal assistant replies stop disappearing when pairing and system messages still arrive. (#59030) Thanks @afurm.
|
||||
- Discord/reply tags: strip leaked `[[reply_to_current]]` control tags from preview text and honor explicit reply-tag threading during final delivery, so Discord replies stay attached to the triggering message instead of printing reply metadata into chat.
|
||||
- Telegram: fix current-model checks in the model picker, HTML-format non-default `/model` confirmations, explicit topic replies, persisted reaction ownership across restarts, caption-media placeholder and `file_id` preservation on download failure, and upgraded-install inbound image reads. (#60384, #60042, #59634, #59207, #59948, #59971) Thanks @sfuminya, @GitZhangChi, @dashhuang, @samzong, @v1p0r, and @neeravmakwana.
|
||||
- Telegram: restore DM voice-note preflight transcription so direct-message audio stops arriving as raw `<media:audio>` placeholders. (#61008) Thanks @manueltarouca.
|
||||
- Telegram/reasoning: only create a Telegram reasoning preview lane when the session is explicitly `reasoning:stream`, so hidden `<think>` traces from streamed replies stop surfacing as chat previews on normal sessions. Thanks @vincentkoc.
|
||||
- Telegram/native command menu: trim long menu descriptions before dropping commands so sub-100 command sets can still fit Telegram's payload budget and keep more `/` entries visible. (#61129) Thanks @neeravmakwana.
|
||||
- Feishu/reasoning: only expose streamed reasoning previews when the session is explicitly `reasoning:stream`, so hidden reasoning traces do not surface on normal streaming sessions. Thanks @vincentkoc.
|
||||
- Discord: keep REST, webhook, and monitor traffic on the configured proxy, preserve component-only media sends, honor `@everyone` and `@here` mention gates, keep ACK reactions on the active account, and split voice connect/playback timeouts so auto-join is more reliable. (#57465, #60361, #60345) Thanks @geekhuashan.
|
||||
- WhatsApp: restore `channels.whatsapp.blockStreaming` and reset watchdog timeouts after reconnect so quiet chats stop falling into reconnect loops. (#60007, #60069) Thanks @MonkeyLeeT and @mcaxtr.
|
||||
- Memory: keep `memory-core` builtin embedding registration on the already-registered path so selecting `memory-core` no longer recurses through plugin discovery and crashes during startup. (#61402) Thanks @ngutman.
|
||||
- MS Teams: download inline DM images via Graph API and preserve channel reply threading in proactive fallback. (#52212, #55198) Thanks @Ted-developer and @hyojin.
|
||||
- MS Teams: replace the deprecated Teams SDK HttpPlugin stub with `httpServerAdapter` so recurring gateway deprecation warnings stop firing and the Express 5 compatibility workaround stays on the supported SDK path. (#60939) Thanks @coolramukaka-sys.
|
||||
- Matrix/exec approvals: anchor seeded approval reactions to the primary Matrix prompt event, resolve them from event metadata instead of prompt text, and clean up chunked approval prompts correctly. (#60931) Thanks @gumadeiras.
|
||||
- Matrix: recover more reliably when secret storage or recovery keys are missing by recreating secret storage during repair and backup reset, hold crypto snapshot locks during persistence, and surface explicit too-large attachment markers. (#59846, #59851, #60599, #60289) Thanks @al3mart, @emonty, and @efe-arv.
|
||||
- Android/Talk Mode: cancel in-flight `talk.speak` playback when speech is explicitly stopped, so stale replies stop starting after barge-in or manual stop. (#61164) Thanks @obviyus.
|
||||
- Android/Talk Mode: restore spoken assistant replies on node-scoped sessions by keeping reply routing synced to the resolved node session key and pausing mic capture during reply playback. (#60306) Thanks @MKV21.
|
||||
- Android/Talk Mode: restore voice replies on gateway-backed talk mode sessions by updating embedded runner transport overrides to the current agent transport API. (#61214) Thanks @obviyus.
|
||||
- Voice-call/OpenAI: pass full plugin config into realtime transcription provider resolution so streaming calls can discover the bundled OpenAI realtime transcription provider again. Fixes #60936. Thanks @sliekens and @vincentkoc.
|
||||
- Control UI/chat: add a per-session thinking-level picker in the chat header and mobile chat settings, and keep the browser bundle on UI-local thinking/session-key helpers so Safari no longer crashes on Node-only imports before rendering chat controls.
|
||||
- Control UI: keep Stop visible during tool-only execution, preserve pending-send busy state, and clear stale ClawHub search results as soon as the query changes. (#54528, #59800, #60267) Thanks @chziyue and @frankekn.
|
||||
- Control UI/avatar: honor `ui.assistant.avatar` when serving `/avatar/:agentId` so Appearance UI avatar paths stop falling back to initials placeholders. (#60778) Thanks @hannasdev.
|
||||
- Control UI/cron: highlight the Cron refresh button while refresh is in flight so the page's loading state stays visible even when prior data remains on screen. (#60394) Thanks @coder-zhuzm.
|
||||
- Control UI/Overview: prevent gateway access token/password visibility toggle buttons from overlapping their inputs at narrow widths. (#56924) Thanks @bbddbb1.
|
||||
- CLI/skills JSON: route `skills list --json`, `skills info --json`, and `skills check --json` output to stdout instead of stderr so machine-readable consumers receive JSON on the expected stream again. (#60914; fixes #57599; landed from contributor PR #57611 by @Aftabbs) Thanks @Aftabbs.
|
||||
- CLI/Commander: preserve Commander-computed exit codes for argument and help-error paths, and cover the user-argv parse mode in the regression tests so invalid CLI invocations no longer report success when exits are intercepted. (#60923) Thanks @Linux2010.
|
||||
- Cron: replay interrupted recurring jobs on the first gateway restart instead of waiting for a second restart. (#60583) Thanks @joelnishanth.
|
||||
- Cron: send failure notifications through the job's primary delivery channel using the same session context as successful delivery when no explicit `failureDestination` is configured. (#60622) Thanks @artwalker.
|
||||
- Live model switching: only treat explicit user-driven model changes as pending live switches, so fallback rotation, heartbeat overrides, and compaction no longer trip `LiveSessionModelSwitchError` before making an API call. (#60266) Thanks @kiranvk-2011.
|
||||
- Exec approvals: reuse durable exact-command `allow-always` approvals in allowlist mode so identical reruns stop prompting, and tighten Windows interpreter/path approval handling so wrapper and malformed-path cases fail closed more consistently. (#59880, #59780, #58040, #59182) Thanks @luoyanglang, @SnowSky1, and @pgondhi987.
|
||||
- Node exec approvals: keep node-host `system.run` approvals bound to the prepared execution plan across async forwarding, so mutable script operands still get approval-time binding and drift revalidation instead of dropping back to unbound execution.
|
||||
- Agents/exec approvals: let `exec-approvals.json` agent security override stricter gateway tool defaults so approved subagents can use `security: “full”` without falling back to allowlist enforcement again. (#60310) Thanks @lml2468.
|
||||
- Agents/exec: restore `host=node` routing for node-pinned and `host=auto` sessions, while still blocking sandboxed `auto` sessions from jumping to gateway. (#60788) Thanks @openperf.
|
||||
- Exec/heartbeat: use the canonical `exec-event` wake reason for `notifyOnExit` so background exec completions still trigger follow-up turns when `HEARTBEAT.md` is empty or comments-only. (#41479) Thanks @rstar327.
|
||||
- Heartbeat: skip wake delivery when the target session lane is already busy so the pending event is retried instead of getting drained too early. (#40526) Thanks @lucky7323.
|
||||
- Group chats/agent prompts: tell models to minimize empty lines and use normal chat-style spacing so group replies avoid document-style blank-line formatting.
|
||||
- Providers/OpenAI GPT: treat short approval turns like `ok do it` and `go ahead` as immediate action turns, and trim overly memo-like GPT-5 chat confirmations so OpenAI replies stay shorter and more conversational by default.
|
||||
- Providers/OpenAI Codex: split native `contextWindow` from runtime `contextTokens`, keep the default effective cap at `272000`, and expose a per-model `contextTokens` override on `models.providers.*.models[]`.
|
||||
- Providers/OpenAI-compatible WS: compute fallback token totals from normalized usage when providers omit or zero `total_tokens`, so DashScope-compatible sessions stop storing zero totals after alias normalization. (#54940) Thanks @lyfuci.
|
||||
- Agents/OpenAI: mark Claude-compatible file tool schemas as `additionalProperties: false` so direct OpenAI GPT-5 routes stop rejecting the `read` tool with invalid strict-schema errors.
|
||||
- Agents/OpenAI: fall back to `strict: false` for native OpenAI tool calls when a tool schema is not strict-compatible, and normalize empty-object tool schemas to include `required: []`, so direct GPT-5 routes stop failing with invalid strict-schema errors like missing `path` in `required`.
|
||||
- Agents/GPT: add explicit work-item lifecycle events for embedded runs, use them to surface real progress more reliably, and stop counting tool-started turns as planning-only retries.
|
||||
- Plugins/OpenAI: enable `gpt-image-1` reference-image edits through `/images/edits` multipart uploads, and stop inferring unsupported resolution overrides when no explicit `size` or `resolution` is provided.
|
||||
- Agents/replay: remove the malformed assistant-content canonicalization repair from replay history sanitization instead of extending that legacy repair path into replay validation.
|
||||
- Plugins/OpenAI: tune the OpenAI prompt overlay for live-chat cadence so GPT replies stay shorter, more human, and less wall-of-text by default.
|
||||
- Providers/compat: stop forcing OpenAI-only defaults on proxy and custom OpenAI-compatible routes, preserve native vendor-specific reasoning/tool/streaming behavior across Anthropic-compatible, Moonshot, Mistral, ModelStudio, OpenRouter, xAI, and Z.ai endpoints, and route GitHub Copilot Claude models through Anthropic Messages instead of OpenAI Responses.
|
||||
- Providers/GitHub Copilot: send IDE identity headers on runtime model requests and GitHub token exchange so IDE-authenticated Copilot runs stop failing with missing `Editor-Version`. (#60641) Thanks @VACInc and @vincentkoc.
|
||||
- Providers/OpenRouter failover: classify `403 “Key limit exceeded”` spending-limit responses as billing so model fallback continues instead of stopping on generic auth. (#59892) Thanks @rockcent.
|
||||
- Providers/Anthropic: keep `claude-cli/*` auth on live Claude CLI credentials at runtime, avoid persisting stale bearer-token profiles, and suppress macOS Keychain prompts during non-interactive Claude CLI setup. (#61234) Thanks @darkamenosa.
|
||||
- Providers/Anthropic: when Claude CLI auth becomes the default, write a real `claude-cli` auth profile so local and gateway agent runs can use Claude CLI immediately without missing-API-key failures. Thanks @vincentkoc.
|
||||
- Providers/Anthropic Vertex: honor `cacheRetention: “long”` with the real 1-hour prompt-cache TTL on Vertex AI endpoints, and default `anthropic-vertex` cache retention like direct Anthropic. (#60888) Thanks @affsantos.
|
||||
- Agents/Anthropic: preserve native `toolu_*` replay ids on direct Anthropic and Anthropic Vertex paths so cache-sensitive history stops rewriting known-valid Anthropic tool-use ids. (#52612)
|
||||
- Providers/Google: add model-level `cacheRetention` support for direct Gemini system prompts by creating, reusing, and refreshing `cachedContents` automatically on Google AI Studio runs. (#51372) Thanks @rafaelmariano-glitch.
|
||||
- Google Gemini CLI auth: detect bundled npm installs by scanning packaged bundle files for the Gemini OAuth client config, so `npm install -g @google/gemini-cli` layouts work again. (#60486) Thanks @wzfmini01.
|
||||
- Google Gemini CLI auth: detect personal OAuth mode from local Gemini settings and skip Code Assist project discovery for those logins, so personal Google accounts stop failing with `loadCodeAssist 400 Bad Request`. (#49226) Thanks @bobworrall.
|
||||
- Google Gemini CLI auth: improve OAuth credential discovery across Windows nvm and Homebrew libexec installs, and align Code Assist metadata so Gemini login stops failing on packaged CLI layouts. (#40729) Thanks @hughcube.
|
||||
- Google Gemini CLI models: add forward-compat support for stable `gemini-2.5-*` model ids by letting the bundled CLI provider clone them from Google templates, so `gemini-2.5-flash-lite` and related configured models stop showing up as missing. (#35274) Thanks @mySebbe.
|
||||
- Google image generation: disable pinned DNS for Gemini image requests and honor explicit `pinDns` overrides in shared provider HTTP helpers so proxy-backed image generation works again. (#59873) Thanks @luoyanglang.
|
||||
- Providers/Microsoft Foundry: preserve explicit image capability on normalized Foundry deployments, repair stale GPT/o-series text-only model metadata across gateway and runtime paths, and keep unknown fallback models from borrowing unrelated image support.
|
||||
- Providers/Model Studio: preserve native streaming usage reporting for DashScope-compatible endpoints even when they are configured under a generic provider key, so streamed token totals stop sticking at zero. (#52395) Thanks @IVY-AI-gif.
|
||||
- Providers/Z.AI: preserve explicitly registered `glm-5-*` variants like `glm-5-turbo` instead of intercepting them with the generic GLM-5 forward-compat shim. (#48185) Thanks @haoyu-haoyu.
|
||||
- Amazon Bedrock/aws-sdk auth: stop injecting the fake `AWS_PROFILE` apiKey marker when no AWS auth env vars exist, so instance-role and other default-chain setups keep working without poisoning provider config. (#61194) Thanks @wirjo.
|
||||
- Agents/Kimi tool-call repair: preserve tool arguments that were already present on streamed tool calls when later malformed deltas fail reevaluation, while still dropping stale repair-only state before `toolcall_end`.
|
||||
- Plugins/Kimi Coding: parse tagged tool calls and keep Anthropic-native tool payloads so Kimi coding endpoints execute tools instead of echoing raw markup. (#60051, #60391) Thanks @obviyus and @Eric-Guo.
|
||||
- Media understanding: auto-register image-capable config providers for vision routing, so custom GLM-style provider ids with image models stop failing with “no media-understanding provider registered”. (#51418) Thanks @xydt-610.
|
||||
- Plugins/media understanding: enable bundled Groq and Deepgram providers by default so configured transcription models work without extra plugin activation config. (#59982) Thanks @yxjsxy.
|
||||
- MiniMax/pricing: keep bundled MiniMax highspeed pricing distinct in provider catalogs and preserve the lower M2.5 cache-read pricing when onboarding older MiniMax models. (#54214) Thanks @octo-patch.
|
||||
- MiniMax: advertise image input on bundled `MiniMax-M2.7` and `MiniMax-M2.7-highspeed` model definitions so image-capable flows can route through the M2.7 family correctly. (#54843) Thanks @MerlinMiao88888888.
|
||||
- Models/MiniMax: honor `MINIMAX_API_HOST` for implicit bundled MiniMax provider catalogs so China-hosted API-key setups pick `api.minimaxi.com/anthropic` without manual provider config. (#34524) Thanks @caiqinghua.
|
||||
- Usage/MiniMax: invert remaining-style `usage_percent` fields when MiniMax reports only remaining percentage data, so usage bars stop showing nearly-full remaining quota as nearly-exhausted usage. (#60254) Thanks @jwchmodx.
|
||||
- MiniMax: advertise image input on bundled `MiniMax-M2.7` and `MiniMax-M2.7-highspeed` model definitions so image-capable flows can route through the M2.7 family correctly. (#54843) Thanks @MerlinMiao88888888.
|
||||
- Media understanding: auto-register image-capable config providers for vision routing, so custom GLM-style provider ids with image models stop failing with “no media-understanding provider registered”. (#51418) Thanks @xydt-610.
|
||||
- Providers/OpenAI: preserve native `reasoning.effort: "none"` and strict tool schemas on direct OpenAI-family endpoints, keep compat routes on compat shaping, fix Responses WebSocket warm-up behavior, keep stable session and turn metadata, and fall back more gracefully after early WebSocket failures.
|
||||
- Providers/OpenAI Codex: split native `contextWindow` from runtime `contextTokens`, keep the default effective cap at `272000`, and expose a per-model `contextTokens` override on `models.providers.*.models[]`.
|
||||
- Providers/compat: stop forcing OpenAI-only defaults on proxy and custom OpenAI-compatible routes, preserve native vendor-specific reasoning/tool/streaming behavior across Anthropic-compatible, Moonshot, Mistral, ModelStudio, OpenRouter, xAI, and Z.ai endpoints, and route GitHub Copilot Claude models through Anthropic Messages instead of OpenAI Responses.
|
||||
- Providers/Model Studio: preserve native streaming usage reporting for DashScope-compatible endpoints even when they are configured under a generic provider key, so streamed token totals stop sticking at zero. (#52395) Thanks @IVY-AI-gif.
|
||||
- Providers/OpenAI-compatible WS: compute fallback token totals from normalized usage when providers omit or zero `total_tokens`, so DashScope-compatible sessions stop storing zero totals after alias normalization. (#54940) Thanks @lyfuci.
|
||||
- Status/usage: let `/status` and `session_status` fall back to transcript token totals when the session meta store stayed at zero, so LM Studio, Ollama, DashScope, and similar OpenAI-compatible providers stop showing `Context: 0/...`. (#55041) Thanks @jjjojoj.
|
||||
- Providers/Z.AI: preserve explicitly registered `glm-5-*` variants like `glm-5-turbo` instead of intercepting them with the generic GLM-5 forward-compat shim. (#48185) Thanks @haoyu-haoyu.
|
||||
- Live model switching: only treat explicit user-driven model changes as pending live switches, so fallback rotation, heartbeat overrides, and compaction no longer trip `LiveSessionModelSwitchError` before making an API call. (#60266) Thanks @kiranvk-2011.
|
||||
- Voice-call/OpenAI: pass full plugin config into realtime transcription provider resolution so streaming calls can discover the bundled OpenAI realtime transcription provider again. Fixes #60936. Thanks @sliekens and @vincentkoc.
|
||||
- Plugins/OpenAI: enable `gpt-image-1` reference-image edits through `/images/edits` multipart uploads, and stop inferring unsupported resolution overrides when no explicit `size` or `resolution` is provided.
|
||||
- Gateway/startup: default `gateway.mode` to `local` when unset, detect PID recycling in gateway lock files on Windows and macOS, and show startup progress so healthy restarts stop getting blocked by stale locks. (#54801, #60085, #59843)
|
||||
- Mobile pairing/Android: tighten secure endpoint handling so Tailscale and public remote setup reject cleartext endpoints, private LAN pairing still works, merged-role approvals mint both node and operator device tokens, and bootstrap tokens survive node auto-pair until operator approval finishes. (#60128, #60208, #60221)
|
||||
- Android/Talk Mode: restore spoken assistant replies on node-scoped sessions by keeping reply routing synced to the resolved node session key and pausing mic capture during reply playback. (#60306) Thanks @MKV21.
|
||||
- Telegram: fix current-model checks in the model picker, HTML-format non-default `/model` confirmations, explicit topic replies, persisted reaction ownership across restarts, caption-media placeholder and `file_id` preservation on download failure, and upgraded-install inbound image reads. (#60384, #60042, #59634, #59207, #59948, #59971)
|
||||
- Telegram/local Bot API: honor `channels.telegram.apiRoot` for buffered media downloads, add `channels.telegram.network.dangerouslyAllowPrivateNetwork` for trusted fake-IP setups, and require `channels.telegram.trustedLocalFileRoots` before reading absolute Bot API `file_path` values. (#59544, #60705)
|
||||
- Matrix: recover more reliably when secret storage or recovery keys are missing by recreating secret storage during repair and backup reset, hold crypto snapshot locks during persistence, and surface explicit too-large attachment markers. (#59846, #59851, #60599, #60289)
|
||||
- ACP/agents: inherit the target agent workspace for cross-agent ACP spawns and fall back safely when the inherited workspace no longer exists. (#58438) Thanks @zssggle-rgb.
|
||||
- ACPX/Windows: preserve backslashes and absolute `.exe` paths in Claude CLI parsing, and fail fast on wrapper-script targets with guidance to use `cmd.exe /c`, `powershell.exe -File`, or `node <script>`. (#60689)
|
||||
- Gateway/Windows scheduled tasks: preserve Task Scheduler settings on reinstall, fail loudly when `/Run` does not start, and report fast failed restarts accurately instead of pretending they timed out after 60 seconds. (#59335) Thanks @tmimmanuel.
|
||||
- Discord: keep REST, webhook, and monitor traffic on the configured proxy, preserve component-only media sends, honor `@everyone` and `@here` mention gates, keep ACK reactions on the active account, and split voice connect/playback timeouts so auto-join is more reliable. (#57465, #60361, #60345)
|
||||
- WhatsApp: restore `channels.whatsapp.blockStreaming` and reset watchdog timeouts after reconnect so quiet chats stop falling into reconnect loops. (#60007, #60069)
|
||||
- Control UI: keep Stop visible during tool-only execution, preserve pending-send busy state, and clear stale ClawHub search results as soon as the query changes. (#54528, #59800, #60267)
|
||||
- MS Teams: download inline DM images via Graph API and preserve channel reply threading in proactive fallback. (#52212, #55198)
|
||||
- Agents/Claude CLI: persist explicit `openclaw agent --session-id` runs under a stable session key so follow-ups can reuse the stored CLI binding and resume the same underlying Claude session.
|
||||
- Agents/CLI backends: invalidate stored CLI session reuse when local CLI login state or the selected auth profile credential changes, so relogin and token rotation stop resuming stale sessions.
|
||||
- Gateway/macOS: recover installed-but-unloaded LaunchAgents during `openclaw gateway start` and `restart`, while still preferring live unmanaged gateways during restart recovery. (#43766) Thanks @HenryC-3.
|
||||
- Auth/failover: persist selected fallback overrides before retrying, shorten `auth_permanent` lockouts, and refresh websocket/shared-auth sessions only when real auth changes occur so retries and secret rotations behave predictably. (#60404, #60323, #60387)
|
||||
- Cron: replay interrupted recurring jobs on the first gateway restart instead of waiting for a second restart. (#60583) Thanks @joelnishanth.
|
||||
- Plugins/media understanding: enable bundled Groq and Deepgram providers by default so configured transcription models work without extra plugin activation config. (#59982) Thanks @yxjsxy.
|
||||
- Plugins/Kimi Coding: parse tagged tool calls and keep Anthropic-native tool payloads so Kimi coding endpoints execute tools instead of echoing raw markup. (#60051, #60391)
|
||||
- Tools/web_search (Kimi): when `tools.web.search.kimi.baseUrl` is unset, inherit native Moonshot chat `baseUrl` (`.ai` / `.cn`) so China console keys authenticate on the same host as chat. Fixes #44851. (#56769) Thanks @tonga54.
|
||||
- Plugins/marketplace: block remote marketplace symlink escapes without breaking ordinary local marketplace install paths. (#60556) Thanks @eleqtrizit.
|
||||
- Plugins/install: preserve unsafe override flags across linked plugin and hook-pack probes so local `--link` installs honor the documented override behavior. (#60624) Thanks @JerrettDavis.
|
||||
- Config/All Settings: keep the raw config view intact when sensitive fields are blank instead of corrupting or dropping the rendered snapshot. (#28214) Thanks @solodmd.
|
||||
- Security: preserve restrictive plugin-only tool allowlists, require owner access for `/allowlist add` and `/allowlist remove`, fail closed when `before_tool_call` hooks crash, block browser SSRF redirect bypasses earlier, and keep non-interactive auth-choice inference scoped to bundled and already-trusted plugins. (#58476, #59836, #59822, #58771, #59120)
|
||||
- Exec approvals: reuse durable exact-command `allow-always` approvals in allowlist mode so identical reruns stop prompting, and tighten Windows interpreter/path approval handling so wrapper and malformed-path cases fail closed more consistently. (#59880, #59780, #58040, #59182)
|
||||
- Agents/runtime: make default subagent allowlists, inherited skills/workspaces, and duplicate session-id resolution behave more predictably, and include value-shape hints in missing-parameter tool errors. (#59944, #59992, #59858, #55317)
|
||||
- Update/npm: prefer the npm binary that owns the installed global OpenClaw prefix so mixed Homebrew-plus-nvm setups update the right install. (#60153) Thanks @jayeshp19.
|
||||
- Gateway/plugin routes: keep gateway-auth plugin runtime routes on write-only fallback scopes unless a trusted-proxy caller explicitly declares narrower `x-openclaw-scopes`, so plugin HTTP handlers no longer mint admin-level runtime scopes on missing or untrusted HTTP scope headers. (#59815) Thanks @pgondhi987.
|
||||
- Agents/exec approvals: let `exec-approvals.json` agent security override stricter gateway tool defaults so approved subagents can use `security: "full"` without falling back to allowlist enforcement again. (#60310) Thanks @lml2468.
|
||||
- Tasks/maintenance: reconcile stale cron and chat-backed CLI task rows against live cron-job and agent-run ownership instead of treating any persisted session key as proof that the task is still running. (#60310) Thanks @lml2468.
|
||||
- Providers/GitHub Copilot: send IDE identity headers on runtime model requests and GitHub token exchange so IDE-authenticated Copilot runs stop failing with missing `Editor-Version`. (#60641) Thanks @VACInc and @vincentkoc.
|
||||
- Usage/MiniMax: let usage snapshots treat `minimax-portal` and MiniMax CN aliases as the same MiniMax quota surface, and prefer stored MiniMax OAuth before falling back to Coding Plan keys.
|
||||
- Usage/MiniMax: prefer the chat-model `model_remains` entry and derive Coding Plan window labels from MiniMax interval timestamps so MiniMax usage snapshots stop picking zero-budget media rows and misreporting 4h windows as `5h`. (#52349) Thanks @IVY-AI-gif.
|
||||
- Model picker/providers: treat bundled BytePlus and Volcengine plan aliases as their native providers during setup, and expose their bundled standard/coding catalogs before auth so setup can suggest the right models. (#58819) Thanks @Luckymingxuan.
|
||||
- Prompt caching: route Codex Responses and Anthropic Vertex through boundary-aware cache shaping, and report the actual outbound system prompt in cache traces so cache reuse and misses line up with what providers really receive. Thanks @vincentkoc.
|
||||
- Prompt caching: order stable workspace project-context files before `HEARTBEAT.md` and keep `HEARTBEAT.md` below the system-prompt cache boundary so heartbeat churn does not invalidate the stable project-context prefix. (#58979) Thanks @yozu and @vincentkoc.
|
||||
- Plugins/cache: inherit the active gateway workspace for provider, web-search, and web-fetch snapshot loads when callers omit `workspaceDir`, so compatible plugin registries and snapshot caches stop missing on gateway-owned runtime paths. (#61138) Thanks @jzakirov.
|
||||
- Agents/Kimi tool-call repair: preserve tool arguments that were already present on streamed tool calls when later malformed deltas fail reevaluation, while still dropping stale repair-only state before `toolcall_end`.
|
||||
- MiniMax/pricing: keep bundled MiniMax highspeed pricing distinct in provider catalogs and preserve the lower M2.5 cache-read pricing when onboarding older MiniMax models. (#54214) Thanks @octo-patch.
|
||||
- Agents/cache: preserve the full 3-turn prompt-cache image window across tool loops, keep colliding bundled MCP tool definitions deterministic, and reapply Anthropic Vertex cache shaping after payload hook replacements so KV/cache reuse stays stable. Thanks @vincentkoc.
|
||||
- Device pairing: reject rotating device tokens into roles that were never approved during pairing, and keep reconnect role checks bounded to the paired device's approved role set. (#60462) Thanks @eleqtrizit.
|
||||
- Mobile pairing/security: fail closed for internal `/pair` setup-code issuance, cleanup, and approval paths when gateway pairing scopes are missing, and keep approval-time requested-scope enforcement on the internal command path. (#55996) Thanks @coygeek.
|
||||
- Status/cache: restore `cacheRead` and `cacheWrite` in transcript fallback so `/status` keeps showing cache hit percentages when session logs are the only complete usage source. (#59247) Thanks @stuartsy.
|
||||
- Exec approvals/node host: forward prepared `system.run` approval plans on the async node invoke path so mutable script operands keep their approval-time binding and drift revalidation instead of dropping back to unbound execution.
|
||||
- Synology Chat/security: default low-level HTTPS helper TLS verification to on so helper/API defaults match the shipped safe account default, and only explicit `allowInsecureSsl: true` opts out.
|
||||
- Android/canvas security: require exact normalized A2UI URL matches before forwarding canvas bridge actions, rejecting query mismatches and descendant paths while still allowing fragment-only A2UI navigation.
|
||||
- Cron: send failure notifications through the job's primary delivery channel using the same session context as successful delivery when no explicit `failureDestination` is configured. (#60622) Thanks @artwalker.
|
||||
- Mobile pairing/bootstrap: keep QR bootstrap handoff tokens bounded to the mobile-safe contract so node handoff stays unscoped and operator handoff drops mixed `node.*`, `operator.admin`, and `operator.pairing` scopes.
|
||||
- Gateway/auth: serialize async shared-secret auth attempts per client so concurrent Tailscale-capable failures cannot overrun the intended auth rate-limit budget. Thanks @Telecaster2147.
|
||||
- Doctor/config: compare normalized `talk` configs by deep structural equality instead of key-order-sensitive serialization so `openclaw doctor --fix` stops repeatedly reporting/applying no-op `talk.provider/providers` normalization. (#59911) Thanks @ejames-dev.
|
||||
- Providers/Anthropic Vertex: honor `cacheRetention: "long"` with the real 1-hour prompt-cache TTL on Vertex AI endpoints, and default `anthropic-vertex` cache retention like direct Anthropic. (#60888) Thanks @affsantos.
|
||||
- Gateway/device auth: reuse cached device-token scopes only for cached-token reconnects, while keeping explicit `deviceToken` scope requests and empty-cache fallbacks intact so reconnects preserve `operator.read` without breaking explicit auth flows. (#46032) Thanks @caicongyang.
|
||||
- Agents/scheduling: steer background-now work toward automatic completion wake and treat `process` polling as on-demand inspection or intervention instead of default completion handling. (#60877) Thanks @vincentkoc.
|
||||
- Google Gemini CLI auth: improve OAuth credential discovery across Windows nvm and Homebrew libexec installs, and align Code Assist metadata so Gemini login stops failing on packaged CLI layouts. (#40729) Thanks @hughcube.
|
||||
- Google Gemini CLI auth: detect bundled npm installs by scanning packaged bundle files for the Gemini OAuth client config, so `npm install -g @google/gemini-cli` layouts work again. (#60486) Thanks @wzfmini01.
|
||||
- Mattermost/config schema: accept `groups.*.requireMention` again so existing Mattermost configs no longer fail strict validation after upgrade. (#58271) Thanks @MoerAI.
|
||||
- Agents/failover: scope Anthropic `An unknown error occurred` failover matching by provider so generic internal unknown-error text no longer triggers retryable timeout fallback. (#59325) Thanks @aaron-he-zhu.
|
||||
- Providers/OpenRouter failover: classify `403 "Key limit exceeded"` spending-limit responses as billing so model fallback continues instead of stopping on generic auth. (#59892) Thanks @rockcent.
|
||||
- Device pairing/security: keep non-operator device scope checks bound to the requested role prefix so bootstrap verification cannot redeem `operator.*` scopes through `node` auth. (#57258) Thanks @jlapenna.
|
||||
- Gateway/device pairing: require non-admin paired-device sessions to manage only their own device for token rotate/revoke and paired-device removal, blocking cross-device token theft inside pairing-scoped sessions. (#50627) Thanks @coygeek.
|
||||
- CLI/skills JSON: route `skills list --json`, `skills info --json`, and `skills check --json` output to stdout instead of stderr so machine-readable consumers receive JSON on the expected stream again. (#60914; fixes #57599; landed from contributor PR #57611 by @Aftabbs) Thanks @Aftabbs.
|
||||
- Agents/subagents: honor allowlist validation, auth-profile handoff, and session override state when a subagent retries after `LiveSessionModelSwitchError`. (#58178) Thanks @openperf.
|
||||
- Google image generation: disable pinned DNS for Gemini image requests and honor explicit `pinDns` overrides in shared provider HTTP helpers so proxy-backed image generation works again. (#59873) Thanks @luoyanglang.
|
||||
- Agents/exec: restore `host=node` routing for node-pinned and `host=auto` sessions, while still blocking sandboxed `auto` sessions from jumping to gateway. (#60788) Thanks @openperf.
|
||||
- Agents/compaction: keep assistant tool calls and displaced tool results in the same compaction chunk so strict summarization providers stop rejecting orphaned tool pairs. (#58849) Thanks @openperf.
|
||||
- Outbound/sanitizer: strip leaked `<tool_call>`, `<function_calls>`, and model special tokens from shared user-visible assistant text, including truncated tool-call streams, so internal scaffolding no longer bleeds into replies across surfaces. (#60619) Thanks @oliviareid-svg.
|
||||
- Telegram: restore DM voice-note preflight transcription so direct-message audio stops arriving as raw `<media:audio>` placeholders. (#61008) Thanks @manueltarouca.
|
||||
- Control UI/avatar: honor `ui.assistant.avatar` when serving `/avatar/:agentId` so Appearance UI avatar paths stop falling back to initials placeholders. (#60778) Thanks @hannasdev.
|
||||
- Control UI/Overview: prevent gateway access token/password visibility toggle buttons from overlapping their inputs at narrow widths. (#56924) Thanks @bbddbb1.
|
||||
- Control UI/cron: highlight the Cron refresh button while refresh is in flight so the page's loading state stays visible even when prior data remains on screen. (#60394) Thanks @coder-zhuzm.
|
||||
- MS Teams: replace the deprecated Teams SDK HttpPlugin stub with `httpServerAdapter` so recurring gateway deprecation warnings stop firing and the Express 5 compatibility workaround stays on the supported SDK path. (#60939) Thanks @coolramukaka-sys.
|
||||
- CLI/Commander: preserve Commander-computed exit codes for argument and help-error paths, and cover the user-argv parse mode in the regression tests so invalid CLI invocations no longer report success when exits are intercepted. (#60923) Thanks @Linux2010.
|
||||
- Telegram/native command menu: trim long menu descriptions before dropping commands so sub-100 command sets can still fit Telegram's payload budget and keep more `/` entries visible. (#61129) Thanks @neeravmakwana.
|
||||
- Tools/web_search (Kimi): when `tools.web.search.kimi.baseUrl` is unset, inherit native Moonshot chat `baseUrl` (`.ai` / `.cn`) so China console keys authenticate on the same host as chat. Fixes #44851. (#56769) Thanks @tonga54.
|
||||
- Agents/Claude CLI: keep non-interactive `--permission-mode bypassPermissions` when custom `cliBackends.claude-cli.args` override defaults, including fallback resolution before the runtime plugin registry is active, so cron and heartbeat Claude CLI runs do not regress to interactive approval mode. (#61114) Thanks @cathrynlavery and @thewilloftheshadow.
|
||||
- Agents/Claude CLI: persist explicit `openclaw agent --session-id` runs under a stable session key so follow-ups can reuse the stored CLI binding and resume the same underlying Claude session.
|
||||
- Agents/Claude CLI: persist routed Claude session bindings, rotate them on `/new` and `/reset`, and keep live Claude CLI model switches moving across the configured Claude family so resumed sessions follow the real active thread and model. Thanks @vincentkoc.
|
||||
- Agents/CLI backends: invalidate stored CLI session reuse when local CLI login state or the selected auth profile credential changes, so relogin and token rotation stop resuming stale sessions.
|
||||
- Agents/Claude CLI/images: reuse stable hydrated image file paths and preserve shared media extensions like HEIC when passing image refs to local CLI runs, so Claude CLI image prompts stop thrashing KV cache prefixes and oddball image formats do not fall back to `.bin`. Thanks @vincentkoc.
|
||||
- Agents/compaction: keep assistant tool calls and displaced tool results in the same compaction chunk so strict summarization providers stop rejecting orphaned tool pairs. (#58849) Thanks @openperf.
|
||||
- Agents/failover: scope Anthropic `An unknown error occurred` failover matching by provider so generic internal unknown-error text no longer triggers retryable timeout fallback. (#59325) Thanks @aaron-he-zhu.
|
||||
- Agents/subagents: honor allowlist validation, auth-profile handoff, and session override state when a subagent retries after `LiveSessionModelSwitchError`. (#58178) Thanks @openperf.
|
||||
- Agents/runtime: make default subagent allowlists, inherited skills/workspaces, and duplicate session-id resolution behave more predictably, and include value-shape hints in missing-parameter tool errors. (#59944, #59992, #59858, #55317) Thanks @hclsys, @gumadeiras, @joelnishanth, and @priyansh19.
|
||||
- Agents/pairing: merge completion announce delivery context with the requester session fallback so missing `to` still reaches the original channel, and include `operator.talk.secrets` in CLI default operator scopes for node-role device pairing approvals. (#56481) Thanks @maxpetrusenko.
|
||||
- Agents/scheduling: steer background-now work toward automatic completion wake and treat `process` polling as on-demand inspection or intervention instead of default completion handling. (#60877) Thanks @vincentkoc.
|
||||
- Agents/skills: skip `.git` and `node_modules` when mirroring skills into sandbox workspaces so read-only sandboxes do not copy repo history or dependency trees. (#61090) Thanks @joelnishanth.
|
||||
- Android/Talk Mode: cancel in-flight `talk.speak` playback when speech is explicitly stopped, so stale replies stop starting after barge-in or manual stop. (#61164) Thanks @obviyus.
|
||||
- ACP/agents: inherit the target agent workspace for cross-agent ACP spawns and fall back safely when the inherited workspace no longer exists. (#58438) Thanks @zssggle-rgb.
|
||||
- ACPX/Windows: preserve backslashes and absolute `.exe` paths in Claude CLI parsing, and fail fast on wrapper-script targets with guidance to use `cmd.exe /c`, `powershell.exe -File`, or `node <script>`. (#60689) Thanks @steipete.
|
||||
- Auth/failover: persist selected fallback overrides before retrying, shorten `auth_permanent` lockouts, and refresh websocket/shared-auth sessions only when real auth changes occur so retries and secret rotations behave predictably. (#60404, #60323, #60387) Thanks @extrasmall0 and @mappel-nv.
|
||||
- Gateway/channels: pin the initial startup channel registry before later plugin-registry churn so configured channels stay visible and `channels.status` stops falling back to empty `channelOrder` / `channels` payloads after runtime plugin loads.
|
||||
- Prompt caching: order stable workspace project-context files before `HEARTBEAT.md` and keep `HEARTBEAT.md` below the system-prompt cache boundary so heartbeat churn does not invalidate the stable project-context prefix. (#58979) Thanks @yozu and @vincentkoc.
|
||||
- Prompt caching: route Codex Responses and Anthropic Vertex through boundary-aware cache shaping, and report the actual outbound system prompt in cache traces so cache reuse and misses line up with what providers really receive. Thanks @vincentkoc.
|
||||
- Agents/cache: preserve the full 3-turn prompt-cache image window across tool loops, keep colliding bundled MCP tool definitions deterministic, and reapply Anthropic Vertex cache shaping after payload hook replacements so KV/cache reuse stays stable. Thanks @vincentkoc.
|
||||
- Status/cache: restore `cacheRead` and `cacheWrite` in transcript fallback so `/status` keeps showing cache hit percentages when session logs are the only complete usage source. (#59247) Thanks @stuartsy.
|
||||
- Status/usage: let `/status` and `session_status` fall back to transcript token totals when the session meta store stayed at zero, so LM Studio, Ollama, DashScope, and similar OpenAI-compatible providers stop showing `Context: 0/...`. (#55041) Thanks @jjjojoj.
|
||||
- Mattermost/config schema: accept `groups.*.requireMention` again so existing Mattermost configs no longer fail strict validation after upgrade. (#58271) Thanks @MoerAI.
|
||||
- Doctor/config: compare normalized `talk` configs by deep structural equality instead of key-order-sensitive serialization so `openclaw doctor --fix` stops repeatedly reporting/applying no-op `talk.provider/providers` normalization. (#59911) Thanks @ejames-dev.
|
||||
- Anthropic CLI onboarding: rewrite migrated fallback model refs during non-interactive Claude CLI setup too, so onboarding and scripted setup no longer keep stale `anthropic/*` fallbacks after switching the primary model to `claude-cli/*`. Thanks @vincentkoc.
|
||||
- Models/Anthropic CLI auth: replace migrated `agents.defaults.models` allowlists when `openclaw models auth login --provider anthropic --method cli --set-default` switches to `claude-cli/*`, so stale `anthropic/*` entries do not linger beside the migrated Claude CLI defaults. Thanks @vincentkoc.
|
||||
- Doctor/Claude CLI: add dedicated Claude CLI health checks so `openclaw doctor` can spot missing local installs or broken auth before agent runs fail. Thanks @vincentkoc.
|
||||
- Plugins/auth-choice: apply provider-owned auth config patches without recursively preserving replaced default-model maps, so Anthropic Claude CLI and similar migrations can intentionally swap model allowlists during onboarding and setup instead of accumulating stale entries. Thanks @vincentkoc.
|
||||
- Plugins/onboarding: write dotted plugin uiHint paths like Brave `webSearch.mode` as nested plugin config so `llm-context` setup stops failing validation. (#61159) Thanks @obviyus.
|
||||
- Android/Talk Mode: restore voice replies on gateway-backed talk mode sessions by updating embedded runner transport overrides to the current agent transport API. (#61214) Thanks @obviyus.
|
||||
- Amazon Bedrock/aws-sdk auth: stop injecting the fake `AWS_PROFILE` apiKey marker when no AWS auth env vars exist, so instance-role and other default-chain setups keep working without poisoning provider config. (#61194) Thanks @wirjo.
|
||||
- Plugins/install: preserve unsafe override flags across linked plugin and hook-pack probes so local `--link` installs honor the documented override behavior. (#60624) Thanks @JerrettDavis.
|
||||
- Plugins/cache: inherit the active gateway workspace for provider, web-search, and web-fetch snapshot loads when callers omit `workspaceDir`, so compatible plugin registries and snapshot caches stop missing on gateway-owned runtime paths. (#61138) Thanks @jzakirov.
|
||||
- Plugin SDK/context engines: export the missing context-engine result and subagent lifecycle types from `openclaw/plugin-sdk` so context engine plugins can type `ContextEngine` implementations without local workarounds. (#61251) Thanks @DaevMithran.
|
||||
- Tasks/maintenance: reconcile stale cron and chat-backed CLI task rows against live cron-job and agent-run ownership instead of treating any persisted session key as proof that the task is still running. (#60310) Thanks @lml2468.
|
||||
- Plugins: suppress trust-warning noise during non-activating snapshot and CLI metadata loads. (#61427) Thanks @gumadeiras.
|
||||
- Agents/video generation: accept `agents.defaults.videoGenerationModel` in strict config validation and `openclaw config set/get`, so gateways using `video_generate` no longer fail to boot after enabling a video model.
|
||||
- Matrix/streaming: add a quiet preview mode for streamed Matrix replies, keep legacy `partial` preview-first behavior, and finalize quiet media captions correctly so previews stop notifying early without dropping final text semantics. (#61450) Thanks @gumadeiras.
|
||||
|
||||
## 2026.4.2
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ RUN pnpm build:docker
|
||||
# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
|
||||
ENV OPENCLAW_PREFER_PNPM=1
|
||||
RUN pnpm ui:build
|
||||
RUN pnpm qa:lab:build
|
||||
|
||||
# Prune dev dependencies and strip build-only metadata before copying
|
||||
# runtime assets into the final image.
|
||||
@@ -156,6 +157,7 @@ COPY --from=runtime-assets --chown=node:node /app/openclaw.mjs .
|
||||
COPY --from=runtime-assets --chown=node:node /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} ./${OPENCLAW_BUNDLED_PLUGIN_DIR}
|
||||
COPY --from=runtime-assets --chown=node:node /app/skills ./skills
|
||||
COPY --from=runtime-assets --chown=node:node /app/docs ./docs
|
||||
COPY --from=runtime-assets --chown=node:node /app/qa ./qa
|
||||
|
||||
# In npm-installed Docker images, prefer the copied source extension tree for
|
||||
# bundled discovery so package metadata that points at source entries stays valid.
|
||||
|
||||
@@ -97,6 +97,7 @@ 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.
|
||||
- Direct localhost/loopback Control UI and Gateway WebSocket sessions authenticated with the shared gateway secret (`token` / `password`) are in that same trusted-operator bucket. Local auto-paired device sessions on that path are expected to retain full localhost operator capability; they do not create a separate `operator.write` vs `operator.admin` security boundary.
|
||||
- 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
|
||||
|
||||
@@ -65,8 +65,8 @@ android {
|
||||
applicationId = "ai.openclaw.app"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 2026040401
|
||||
versionName = "2026.4.4"
|
||||
versionCode = 2026040501
|
||||
versionName = "2026.4.5"
|
||||
ndk {
|
||||
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
@@ -239,44 +239,52 @@ tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
val stripReleaseDnsjavaServiceDescriptor =
|
||||
tasks.register("stripReleaseDnsjavaServiceDescriptor") {
|
||||
androidComponents {
|
||||
onVariants(selector().withBuildType("release")) { variant ->
|
||||
val variantName = variant.name
|
||||
val variantNameCapitalized = variantName.replaceFirstChar(Char::titlecase)
|
||||
val stripTaskName = "strip${variantNameCapitalized}DnsjavaServiceDescriptor"
|
||||
val mergeTaskName = "merge${variantNameCapitalized}JavaResource"
|
||||
val minifyTaskName = "minify${variantNameCapitalized}WithR8"
|
||||
val mergedJar =
|
||||
layout.buildDirectory.file(
|
||||
"intermediates/merged_java_res/release/mergeReleaseJavaResource/base.jar",
|
||||
"intermediates/merged_java_res/$variantName/$mergeTaskName/base.jar",
|
||||
)
|
||||
|
||||
inputs.file(mergedJar)
|
||||
outputs.file(mergedJar)
|
||||
val stripTask =
|
||||
tasks.register(stripTaskName) {
|
||||
inputs.file(mergedJar)
|
||||
outputs.file(mergedJar)
|
||||
|
||||
doLast {
|
||||
val jarFile = mergedJar.get().asFile
|
||||
if (!jarFile.exists()) {
|
||||
return@doLast
|
||||
doLast {
|
||||
val jarFile = mergedJar.get().asFile
|
||||
if (!jarFile.exists()) {
|
||||
return@doLast
|
||||
}
|
||||
|
||||
val unpackDir = temporaryDir.resolve("merged-java-res")
|
||||
delete(unpackDir)
|
||||
copy {
|
||||
from(zipTree(jarFile))
|
||||
into(unpackDir)
|
||||
exclude(dnsjavaInetAddressResolverService)
|
||||
}
|
||||
delete(jarFile)
|
||||
ant.invokeMethod(
|
||||
"zip",
|
||||
mapOf(
|
||||
"destfile" to jarFile.absolutePath,
|
||||
"basedir" to unpackDir.absolutePath,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val unpackDir = temporaryDir.resolve("merged-java-res")
|
||||
delete(unpackDir)
|
||||
copy {
|
||||
from(zipTree(jarFile))
|
||||
into(unpackDir)
|
||||
exclude(dnsjavaInetAddressResolverService)
|
||||
}
|
||||
delete(jarFile)
|
||||
ant.invokeMethod(
|
||||
"zip",
|
||||
mapOf(
|
||||
"destfile" to jarFile.absolutePath,
|
||||
"basedir" to unpackDir.absolutePath,
|
||||
),
|
||||
)
|
||||
tasks.matching { it.name == mergeTaskName }.configureEach {
|
||||
finalizedBy(stripTask)
|
||||
}
|
||||
tasks.matching { it.name == minifyTaskName }.configureEach {
|
||||
dependsOn(stripTask)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.matching { it.name == "stripReleaseDnsjavaServiceDescriptor" }.configureEach {
|
||||
dependsOn("mergeReleaseJavaResource")
|
||||
}
|
||||
|
||||
tasks.matching { it.name == "minifyReleaseWithR8" }.configureEach {
|
||||
dependsOn(stripReleaseDnsjavaServiceDescriptor)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<parameter
|
||||
android:name="prompt"
|
||||
android:key="prompt"
|
||||
android:mimeType="text/*"
|
||||
android:mimeType="https://schema.org/Text"
|
||||
android:required="true" />
|
||||
</intent>
|
||||
</capability>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Shared iOS version defaults.
|
||||
// Generated overrides live in build/Version.xcconfig (git-ignored).
|
||||
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.4.4
|
||||
OPENCLAW_MARKETING_VERSION = 2026.4.4
|
||||
OPENCLAW_BUILD_VERSION = 2026040401
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.4.5
|
||||
OPENCLAW_MARKETING_VERSION = 2026.4.5
|
||||
OPENCLAW_BUILD_VERSION = 2026040501
|
||||
|
||||
#include? "../build/Version.xcconfig"
|
||||
|
||||
@@ -92,6 +92,54 @@ If you need to force a specific build number:
|
||||
pnpm ios:beta -- --build-number 7
|
||||
```
|
||||
|
||||
### Maintainer Quick Release Checklist
|
||||
|
||||
Use this when a clone is missing local iOS release setup and you want the shortest path to a TestFlight upload.
|
||||
|
||||
1. Confirm Fastlane auth is set up:
|
||||
|
||||
```bash
|
||||
cd apps/ios
|
||||
fastlane ios auth_check
|
||||
```
|
||||
|
||||
2. If auth is missing, bootstrap it once on this Mac:
|
||||
|
||||
```bash
|
||||
scripts/ios-asc-keychain-setup.sh \
|
||||
--key-path /absolute/path/to/AuthKey_XXXXXXXXXX.p8 \
|
||||
--issuer-id YOUR_ISSUER_ID \
|
||||
--write-env
|
||||
```
|
||||
|
||||
This should create `apps/ios/fastlane/.env` with the non-secret ASC variables while the private key stays in Keychain.
|
||||
|
||||
3. Set the official/TestFlight relay URL for the build:
|
||||
|
||||
```bash
|
||||
export OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com
|
||||
```
|
||||
|
||||
4. Upload the beta:
|
||||
|
||||
```bash
|
||||
pnpm ios:beta
|
||||
```
|
||||
|
||||
5. Expected behavior:
|
||||
- Fastlane reads `package.json.version`
|
||||
- resolves the next TestFlight build number for that short version
|
||||
- generates `apps/ios/build/BetaRelease.xcconfig`
|
||||
- archives `OpenClaw`
|
||||
- uploads the IPA to TestFlight
|
||||
|
||||
6. Expected outputs after a successful run:
|
||||
- `apps/ios/build/beta/OpenClaw-<version>.ipa`
|
||||
- `apps/ios/build/beta/OpenClaw-<version>.app.dSYM.zip`
|
||||
- Fastlane log line like `Uploaded iOS beta: version=<version> short=<short> build=<build>`
|
||||
|
||||
7. If this is a fresh clone on a maintainer machine that already works elsewhere, it is OK to copy the non-secret `apps/ios/fastlane/.env` from another trusted local clone on the same Mac. The Keychain-backed private key remains machine-local and is not stored in the repo.
|
||||
|
||||
## APNs Expectations For Local/Manual Builds
|
||||
|
||||
- The app calls `registerForRemoteNotifications()` at launch.
|
||||
|
||||
196
apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift
Normal file
196
apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift
Normal file
@@ -0,0 +1,196 @@
|
||||
import SwiftUI
|
||||
|
||||
private struct ExecApprovalPromptDialogModifier: ViewModifier {
|
||||
@Environment(NodeAppModel.self) private var appModel: NodeAppModel
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.overlay {
|
||||
if let prompt = self.appModel.pendingExecApprovalPrompt {
|
||||
ZStack {
|
||||
Color.black.opacity(0.38)
|
||||
.ignoresSafeArea()
|
||||
|
||||
ExecApprovalPromptCard(
|
||||
prompt: prompt,
|
||||
isResolving: self.appModel.pendingExecApprovalPromptResolving,
|
||||
errorText: self.appModel.pendingExecApprovalPromptErrorText,
|
||||
brighten: self.colorScheme == .light,
|
||||
onAllowOnce: {
|
||||
Task {
|
||||
await self.appModel.resolvePendingExecApprovalPrompt(decision: "allow-once")
|
||||
}
|
||||
},
|
||||
onAllowAlways: {
|
||||
Task {
|
||||
await self.appModel.resolvePendingExecApprovalPrompt(decision: "allow-always")
|
||||
}
|
||||
},
|
||||
onDeny: {
|
||||
Task {
|
||||
await self.appModel.resolvePendingExecApprovalPrompt(decision: "deny")
|
||||
}
|
||||
},
|
||||
onCancel: {
|
||||
self.appModel.dismissPendingExecApprovalPrompt()
|
||||
})
|
||||
.padding(.horizontal, 20)
|
||||
.frame(maxWidth: 460)
|
||||
.transition(.scale(scale: 0.98).combined(with: .opacity))
|
||||
}
|
||||
.zIndex(1)
|
||||
}
|
||||
}
|
||||
.animation(.easeInOut(duration: 0.18), value: self.appModel.pendingExecApprovalPrompt?.id)
|
||||
}
|
||||
}
|
||||
|
||||
private struct ExecApprovalPromptCard: View {
|
||||
let prompt: NodeAppModel.ExecApprovalPrompt
|
||||
let isResolving: Bool
|
||||
let errorText: String?
|
||||
let brighten: Bool
|
||||
let onAllowOnce: () -> Void
|
||||
let onAllowAlways: () -> Void
|
||||
let onDeny: () -> Void
|
||||
let onCancel: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text("Exec approval required")
|
||||
.font(.headline)
|
||||
Text("OpenClaw opened from a notification. Review this exec request before continuing.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Text(self.prompt.commandText)
|
||||
.font(.system(size: 15, weight: .regular, design: .monospaced))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(10)
|
||||
.background(.black.opacity(0.14), in: RoundedRectangle(cornerRadius: 12, style: .continuous))
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
if let host = self.normalized(self.prompt.host) {
|
||||
ExecApprovalPromptMetadataRow(label: "Host", value: host)
|
||||
}
|
||||
if let nodeId = self.normalized(self.prompt.nodeId) {
|
||||
ExecApprovalPromptMetadataRow(label: "Node", value: nodeId)
|
||||
}
|
||||
if let agentId = self.normalized(self.prompt.agentId) {
|
||||
ExecApprovalPromptMetadataRow(label: "Agent", value: agentId)
|
||||
}
|
||||
if let expiresText = self.expiresText(self.prompt.expiresAtMs) {
|
||||
ExecApprovalPromptMetadataRow(label: "Expires", value: expiresText)
|
||||
}
|
||||
}
|
||||
|
||||
if let errorText = self.normalized(self.errorText) {
|
||||
Text(errorText)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
|
||||
if self.isResolving {
|
||||
HStack(spacing: 8) {
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
Text("Resolving…")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
VStack(spacing: 10) {
|
||||
Button {
|
||||
self.onAllowOnce()
|
||||
} label: {
|
||||
Text("Allow Once")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.disabled(self.isResolving)
|
||||
|
||||
if self.prompt.allowsAllowAlways {
|
||||
Button {
|
||||
self.onAllowAlways()
|
||||
} label: {
|
||||
Text("Allow Always")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.disabled(self.isResolving)
|
||||
}
|
||||
|
||||
HStack(spacing: 10) {
|
||||
Button(role: .destructive) {
|
||||
self.onDeny()
|
||||
} label: {
|
||||
Text("Deny")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.disabled(self.isResolving)
|
||||
|
||||
Button(role: .cancel) {
|
||||
self.onCancel()
|
||||
} label: {
|
||||
Text("Cancel")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.disabled(self.isResolving)
|
||||
}
|
||||
}
|
||||
.controlSize(.large)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.statusGlassCard(brighten: self.brighten, verticalPadding: 18, horizontalPadding: 18)
|
||||
}
|
||||
|
||||
private func normalized(_ value: String?) -> String? {
|
||||
let trimmed = (value ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
}
|
||||
|
||||
private func expiresText(_ expiresAtMs: Int?) -> String? {
|
||||
guard let expiresAtMs else { return nil }
|
||||
let remainingSeconds = Int((Double(expiresAtMs) / 1000.0) - Date().timeIntervalSince1970)
|
||||
if remainingSeconds <= 0 {
|
||||
return "expired"
|
||||
}
|
||||
if remainingSeconds < 60 {
|
||||
return "under a minute"
|
||||
}
|
||||
if remainingSeconds < 3600 {
|
||||
let minutes = Int(ceil(Double(remainingSeconds) / 60.0))
|
||||
return minutes == 1 ? "about 1 minute" : "about \(minutes) minutes"
|
||||
}
|
||||
let hours = Int(ceil(Double(remainingSeconds) / 3600.0))
|
||||
return hours == 1 ? "about 1 hour" : "about \(hours) hours"
|
||||
}
|
||||
}
|
||||
|
||||
private struct ExecApprovalPromptMetadataRow: View {
|
||||
let label: String
|
||||
let value: String
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(self.label)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(self.value)
|
||||
.font(.footnote)
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func execApprovalPromptDialog() -> some View {
|
||||
self.modifier(ExecApprovalPromptDialogModifier())
|
||||
}
|
||||
}
|
||||
@@ -61,11 +61,35 @@ final class NodeAppModel {
|
||||
let request: AgentDeepLink
|
||||
}
|
||||
|
||||
struct ExecApprovalPrompt: Identifiable, Equatable {
|
||||
let id: String
|
||||
let commandText: String
|
||||
let allowedDecisions: [String]
|
||||
let host: String?
|
||||
let nodeId: String?
|
||||
let agentId: String?
|
||||
let expiresAtMs: Int?
|
||||
|
||||
var allowsAllowAlways: Bool {
|
||||
self.allowedDecisions.contains("allow-always")
|
||||
}
|
||||
}
|
||||
|
||||
private enum ExecApprovalResolutionOutcome {
|
||||
case resolved
|
||||
case stale
|
||||
case unavailable
|
||||
case failed(message: String)
|
||||
}
|
||||
|
||||
private let deepLinkLogger = Logger(subsystem: "ai.openclaw.ios", category: "DeepLink")
|
||||
private let pushWakeLogger = Logger(subsystem: "ai.openclaw.ios", category: "PushWake")
|
||||
private let pendingActionLogger = Logger(subsystem: "ai.openclaw.ios", category: "PendingAction")
|
||||
private let locationWakeLogger = Logger(subsystem: "ai.openclaw.ios", category: "LocationWake")
|
||||
private let watchReplyLogger = Logger(subsystem: "ai.openclaw.ios", category: "WatchReply")
|
||||
private let execApprovalNotificationLogger = Logger(
|
||||
subsystem: "ai.openclaw.ios",
|
||||
category: "ExecApprovalNotification")
|
||||
enum CameraHUDKind {
|
||||
case photo
|
||||
case recording
|
||||
@@ -98,6 +122,10 @@ final class NodeAppModel {
|
||||
var lastShareEventText: String = "No share events yet."
|
||||
var openChatRequestID: Int = 0
|
||||
private(set) var pendingAgentDeepLinkPrompt: AgentDeepLinkPrompt?
|
||||
private(set) var pendingExecApprovalPrompt: ExecApprovalPrompt?
|
||||
private(set) var pendingExecApprovalPromptResolving: Bool = false
|
||||
private(set) var pendingExecApprovalPromptErrorText: String?
|
||||
private var pendingExecApprovalPromptRequestGeneration: Int = 0
|
||||
private var queuedAgentDeepLinkPrompt: AgentDeepLinkPrompt?
|
||||
private var lastAgentDeepLinkPromptAt: Date = .distantPast
|
||||
@ObservationIgnored private var queuedAgentDeepLinkPromptTask: Task<Void, Never>?
|
||||
@@ -2607,6 +2635,19 @@ extension NodeAppModel {
|
||||
+ "backgrounded=\(self.isBackgrounded) "
|
||||
+ "autoReconnect=\(self.gatewayAutoReconnectEnabled)"
|
||||
self.pushWakeLogger.info("\(receivedMessage, privacy: .public)")
|
||||
|
||||
if await ExecApprovalNotificationBridge.handleResolvedPushIfNeeded(
|
||||
userInfo: userInfo,
|
||||
notificationCenter: self.notificationCenter)
|
||||
{
|
||||
if let approvalId = ExecApprovalNotificationBridge.approvalID(from: userInfo) {
|
||||
self.clearPendingExecApprovalPromptIfMatches(approvalId)
|
||||
}
|
||||
self.execApprovalNotificationLogger.info(
|
||||
"Handled exec approval cleanup push wakeId=\(wakeId, privacy: .public)")
|
||||
return true
|
||||
}
|
||||
|
||||
let result = await self.reconnectGatewaySessionsForSilentPushIfNeeded(wakeId: wakeId)
|
||||
let outcomeMessage =
|
||||
"Silent push outcome wakeId=\(wakeId) "
|
||||
@@ -2779,6 +2820,216 @@ extension NodeAppModel {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
private struct ExecApprovalGetRequest: Encodable {
|
||||
let id: String
|
||||
}
|
||||
|
||||
private struct ExecApprovalResolveRequest: Encodable {
|
||||
let id: String
|
||||
let decision: String
|
||||
}
|
||||
|
||||
private struct ExecApprovalGetResponse: Decodable {
|
||||
var id: String
|
||||
var commandText: String
|
||||
var allowedDecisions: [String]
|
||||
var host: String?
|
||||
var nodeId: String?
|
||||
var agentId: String?
|
||||
var expiresAtMs: Int?
|
||||
}
|
||||
|
||||
func presentExecApprovalNotificationPrompt(_ prompt: ExecApprovalNotificationPrompt) async {
|
||||
let approvalId = prompt.approvalId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !approvalId.isEmpty else { return }
|
||||
|
||||
self.pendingExecApprovalPromptRequestGeneration &+= 1
|
||||
let requestGeneration = self.pendingExecApprovalPromptRequestGeneration
|
||||
self.pendingExecApprovalPromptResolving = true
|
||||
self.pendingExecApprovalPromptErrorText = nil
|
||||
|
||||
let fetchedPrompt = await self.fetchExecApprovalPrompt(approvalId: approvalId)
|
||||
guard self.pendingExecApprovalPromptRequestGeneration == requestGeneration else {
|
||||
return
|
||||
}
|
||||
self.pendingExecApprovalPromptResolving = false
|
||||
switch fetchedPrompt {
|
||||
case let .loaded(fetchedPrompt):
|
||||
self.presentFetchedExecApprovalPrompt(fetchedPrompt)
|
||||
case .stale:
|
||||
await ExecApprovalNotificationBridge.removeNotifications(
|
||||
forApprovalID: approvalId,
|
||||
notificationCenter: self.notificationCenter)
|
||||
self.clearPendingExecApprovalPromptIfMatches(approvalId)
|
||||
case let .failed(message):
|
||||
self.execApprovalNotificationLogger.error(
|
||||
"Exec approval prompt fetch failed id=\(approvalId, privacy: .public) reason=\(message, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
private enum ExecApprovalPromptFetchOutcome {
|
||||
case loaded(ExecApprovalPrompt)
|
||||
case stale
|
||||
case failed(message: String)
|
||||
}
|
||||
|
||||
private func presentFetchedExecApprovalPrompt(_ prompt: ExecApprovalPrompt) {
|
||||
self.pendingExecApprovalPrompt = prompt
|
||||
self.pendingExecApprovalPromptResolving = false
|
||||
self.pendingExecApprovalPromptErrorText = nil
|
||||
}
|
||||
|
||||
private static func makeExecApprovalPrompt(from details: ExecApprovalGetResponse) -> ExecApprovalPrompt? {
|
||||
let approvalId = details.id.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let commandText = details.commandText.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !approvalId.isEmpty, !commandText.isEmpty else { return nil }
|
||||
return ExecApprovalPrompt(
|
||||
id: approvalId,
|
||||
commandText: commandText,
|
||||
allowedDecisions: details.allowedDecisions.compactMap { decision in
|
||||
let trimmed = decision.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
},
|
||||
host: details.host?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
nodeId: details.nodeId?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
agentId: details.agentId?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
expiresAtMs: details.expiresAtMs)
|
||||
}
|
||||
|
||||
private func fetchExecApprovalPrompt(approvalId: String) async -> ExecApprovalPromptFetchOutcome {
|
||||
let connected = await self.ensureOperatorApprovalConnection(timeoutMs: 12_000)
|
||||
guard connected else {
|
||||
return .failed(message: "operator_not_connected")
|
||||
}
|
||||
|
||||
do {
|
||||
let payloadJSON = try Self.encodePayload(ExecApprovalGetRequest(id: approvalId))
|
||||
let response = try await self.operatorGateway.request(
|
||||
method: "exec.approval.get",
|
||||
paramsJSON: payloadJSON,
|
||||
timeoutSeconds: 12)
|
||||
let details = try JSONDecoder().decode(ExecApprovalGetResponse.self, from: response)
|
||||
guard let prompt = Self.makeExecApprovalPrompt(from: details) else {
|
||||
return .failed(message: "invalid_prompt_payload")
|
||||
}
|
||||
return .loaded(prompt)
|
||||
} catch {
|
||||
if Self.isApprovalNotificationStaleError(error) {
|
||||
return .stale
|
||||
}
|
||||
return .failed(message: error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
func dismissPendingExecApprovalPrompt() {
|
||||
self.pendingExecApprovalPrompt = nil
|
||||
self.pendingExecApprovalPromptResolving = false
|
||||
self.pendingExecApprovalPromptErrorText = nil
|
||||
}
|
||||
|
||||
func dismissPendingExecApprovalPrompt(approvalId: String) {
|
||||
self.clearPendingExecApprovalPromptIfMatches(approvalId)
|
||||
}
|
||||
|
||||
func resolvePendingExecApprovalPrompt(decision: String) async {
|
||||
guard let prompt = self.pendingExecApprovalPrompt else { return }
|
||||
let normalizedDecision = decision.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !normalizedDecision.isEmpty else { return }
|
||||
|
||||
self.pendingExecApprovalPromptResolving = true
|
||||
self.pendingExecApprovalPromptErrorText = nil
|
||||
let outcome = await self.resolveExecApprovalNotificationDecision(
|
||||
approvalId: prompt.id,
|
||||
decision: normalizedDecision)
|
||||
switch outcome {
|
||||
case .resolved, .stale, .unavailable:
|
||||
break
|
||||
case let .failed(message):
|
||||
self.pendingExecApprovalPromptResolving = false
|
||||
self.pendingExecApprovalPromptErrorText = message
|
||||
}
|
||||
}
|
||||
|
||||
private func resolveExecApprovalNotificationDecision(
|
||||
approvalId: String,
|
||||
decision: String
|
||||
) async -> ExecApprovalResolutionOutcome {
|
||||
let normalizedApprovalID = approvalId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let normalizedDecision = decision.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !normalizedApprovalID.isEmpty, !normalizedDecision.isEmpty else {
|
||||
return .failed(message: "Invalid approval request.")
|
||||
}
|
||||
|
||||
let connected = await self.ensureOperatorApprovalConnection(timeoutMs: 12_000)
|
||||
guard connected else {
|
||||
self.execApprovalNotificationLogger.error(
|
||||
"Exec approval action failed id=\(normalizedApprovalID, privacy: .public): operator not connected")
|
||||
return .failed(message: "OpenClaw couldn't connect to the gateway operator session.")
|
||||
}
|
||||
|
||||
do {
|
||||
let payloadJSON = try Self.encodePayload(
|
||||
ExecApprovalResolveRequest(id: normalizedApprovalID, decision: normalizedDecision))
|
||||
_ = try await self.operatorGateway.request(
|
||||
method: "exec.approval.resolve",
|
||||
paramsJSON: payloadJSON,
|
||||
timeoutSeconds: 12)
|
||||
await ExecApprovalNotificationBridge.removeNotifications(
|
||||
forApprovalID: normalizedApprovalID,
|
||||
notificationCenter: self.notificationCenter)
|
||||
self.clearPendingExecApprovalPromptIfMatches(normalizedApprovalID)
|
||||
return .resolved
|
||||
} catch {
|
||||
if Self.isApprovalNotificationStaleError(error) {
|
||||
await ExecApprovalNotificationBridge.removeNotifications(
|
||||
forApprovalID: normalizedApprovalID,
|
||||
notificationCenter: self.notificationCenter)
|
||||
self.clearPendingExecApprovalPromptIfMatches(normalizedApprovalID)
|
||||
return .stale
|
||||
}
|
||||
if Self.isApprovalNotificationUnavailableError(error) {
|
||||
await ExecApprovalNotificationBridge.removeNotifications(
|
||||
forApprovalID: normalizedApprovalID,
|
||||
notificationCenter: self.notificationCenter)
|
||||
self.clearPendingExecApprovalPromptIfMatches(normalizedApprovalID)
|
||||
return .unavailable
|
||||
}
|
||||
let logMessage =
|
||||
"Exec approval action failed id=\(normalizedApprovalID) error=\(error.localizedDescription)"
|
||||
self.execApprovalNotificationLogger.error("\(logMessage, privacy: .public)")
|
||||
return .failed(
|
||||
message: "OpenClaw couldn't resolve this approval right now. Try again.")
|
||||
}
|
||||
}
|
||||
|
||||
private func clearPendingExecApprovalPromptIfMatches(_ approvalId: String) {
|
||||
let normalizedApprovalID = approvalId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard self.pendingExecApprovalPrompt?.id == normalizedApprovalID else { return }
|
||||
self.dismissPendingExecApprovalPrompt()
|
||||
}
|
||||
|
||||
nonisolated private static func isApprovalNotificationStaleError(_ error: Error) -> Bool {
|
||||
guard let gatewayError = error as? GatewayResponseError else { return false }
|
||||
if gatewayError.code != "INVALID_REQUEST" {
|
||||
return false
|
||||
}
|
||||
if gatewayError.detailsReason == "APPROVAL_NOT_FOUND" {
|
||||
return true
|
||||
}
|
||||
return gatewayError.message.lowercased().contains("unknown or expired approval id")
|
||||
}
|
||||
|
||||
nonisolated private static func isApprovalNotificationUnavailableError(_ error: Error) -> Bool {
|
||||
guard let gatewayError = error as? GatewayResponseError else { return false }
|
||||
if gatewayError.code != "INVALID_REQUEST" {
|
||||
return false
|
||||
}
|
||||
if gatewayError.detailsReason == "APPROVAL_ALLOW_ALWAYS_UNAVAILABLE" {
|
||||
return true
|
||||
}
|
||||
return gatewayError.message.lowercased().contains("allow-always is unavailable")
|
||||
}
|
||||
|
||||
private struct SilentPushWakeAttemptResult {
|
||||
var applied: Bool
|
||||
var reason: String
|
||||
@@ -2790,14 +3041,69 @@ extension NodeAppModel {
|
||||
let pollIntervalNs = UInt64(max(50, pollMs)) * 1_000_000
|
||||
let deadline = Date().addingTimeInterval(Double(clampedTimeoutMs) / 1000.0)
|
||||
while Date() < deadline {
|
||||
if Task.isCancelled {
|
||||
return false
|
||||
}
|
||||
if await self.isGatewayConnected() {
|
||||
return true
|
||||
}
|
||||
try? await Task.sleep(nanoseconds: pollIntervalNs)
|
||||
do {
|
||||
try await Task.sleep(nanoseconds: pollIntervalNs)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return await self.isGatewayConnected()
|
||||
}
|
||||
|
||||
private func waitForOperatorConnection(timeoutMs: Int, pollMs: Int) async -> Bool {
|
||||
let clampedTimeoutMs = max(0, timeoutMs)
|
||||
let pollIntervalNs = UInt64(max(50, pollMs)) * 1_000_000
|
||||
let deadline = Date().addingTimeInterval(Double(clampedTimeoutMs) / 1000.0)
|
||||
while Date() < deadline {
|
||||
if Task.isCancelled {
|
||||
return false
|
||||
}
|
||||
if await self.isOperatorConnected() {
|
||||
return true
|
||||
}
|
||||
do {
|
||||
try await Task.sleep(nanoseconds: pollIntervalNs)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return await self.isOperatorConnected()
|
||||
}
|
||||
|
||||
private func ensureOperatorReconnectLoopIfNeeded() {
|
||||
guard let cfg = self.activeGatewayConnectConfig else {
|
||||
return
|
||||
}
|
||||
guard self.operatorGatewayTask == nil else {
|
||||
return
|
||||
}
|
||||
let stableID = cfg.stableID.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let effectiveStableID = stableID.isEmpty ? cfg.url.absoluteString : stableID
|
||||
let sessionBox = cfg.tls.map { WebSocketSessionBox(session: GatewayTLSPinningSession(params: $0)) }
|
||||
self.startOperatorGatewayLoop(
|
||||
url: cfg.url,
|
||||
stableID: effectiveStableID,
|
||||
token: cfg.token,
|
||||
bootstrapToken: cfg.bootstrapToken,
|
||||
password: cfg.password,
|
||||
nodeOptions: cfg.nodeOptions,
|
||||
sessionBox: sessionBox)
|
||||
}
|
||||
|
||||
private func ensureOperatorApprovalConnection(timeoutMs: Int) async -> Bool {
|
||||
if await self.isOperatorConnected() {
|
||||
return true
|
||||
}
|
||||
self.ensureOperatorReconnectLoopIfNeeded()
|
||||
return await self.waitForOperatorConnection(timeoutMs: timeoutMs, pollMs: 250)
|
||||
}
|
||||
|
||||
private func reconnectGatewaySessionsForSilentPushIfNeeded(
|
||||
wakeId: String
|
||||
) async -> SilentPushWakeAttemptResult {
|
||||
@@ -3208,6 +3514,46 @@ extension NodeAppModel {
|
||||
includeApprovalScope: includeApprovalScope)
|
||||
}
|
||||
|
||||
func _test_presentExecApprovalPrompt(_ prompt: ExecApprovalPrompt) {
|
||||
self.presentFetchedExecApprovalPrompt(prompt)
|
||||
}
|
||||
|
||||
func _test_dismissPendingExecApprovalPrompt() {
|
||||
self.dismissPendingExecApprovalPrompt()
|
||||
}
|
||||
|
||||
func _test_pendingExecApprovalPrompt() -> ExecApprovalPrompt? {
|
||||
self.pendingExecApprovalPrompt
|
||||
}
|
||||
|
||||
nonisolated static func _test_isApprovalNotificationStaleError(_ error: Error) -> Bool {
|
||||
self.isApprovalNotificationStaleError(error)
|
||||
}
|
||||
|
||||
nonisolated static func _test_isApprovalNotificationUnavailableError(_ error: Error) -> Bool {
|
||||
self.isApprovalNotificationUnavailableError(error)
|
||||
}
|
||||
|
||||
static func _test_makeExecApprovalPrompt(
|
||||
id: String,
|
||||
commandText: String,
|
||||
allowedDecisions: [String],
|
||||
host: String?,
|
||||
nodeId: String?,
|
||||
agentId: String?,
|
||||
expiresAtMs: Int?
|
||||
) -> ExecApprovalPrompt? {
|
||||
self.makeExecApprovalPrompt(
|
||||
from: ExecApprovalGetResponse(
|
||||
id: id,
|
||||
commandText: commandText,
|
||||
allowedDecisions: allowedDecisions,
|
||||
host: host,
|
||||
nodeId: nodeId,
|
||||
agentId: agentId,
|
||||
expiresAtMs: expiresAtMs))
|
||||
}
|
||||
|
||||
static func _test_currentDeepLinkKey() -> String {
|
||||
self.expectedDeepLinkKey()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ private struct PendingWatchPromptAction {
|
||||
var sessionKey: String?
|
||||
}
|
||||
|
||||
private typealias PendingExecApprovalPrompt = ExecApprovalNotificationPrompt
|
||||
|
||||
@MainActor
|
||||
final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrency UNUserNotificationCenterDelegate {
|
||||
private let logger = Logger(subsystem: "ai.openclaw.ios", category: "Push")
|
||||
@@ -21,6 +23,7 @@ final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrenc
|
||||
private var backgroundWakeTask: Task<Bool, Never>?
|
||||
private var pendingAPNsDeviceToken: Data?
|
||||
private var pendingWatchPromptActions: [PendingWatchPromptAction] = []
|
||||
private var pendingExecApprovalPrompts: [PendingExecApprovalPrompt] = []
|
||||
|
||||
weak var appModel: NodeAppModel? {
|
||||
didSet {
|
||||
@@ -44,6 +47,15 @@ final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrenc
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.pendingExecApprovalPrompts.isEmpty {
|
||||
let pending = self.pendingExecApprovalPrompts
|
||||
self.pendingExecApprovalPrompts.removeAll()
|
||||
Task { @MainActor in
|
||||
for prompt in pending {
|
||||
await model.presentExecApprovalNotificationPrompt(prompt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +92,17 @@ final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrenc
|
||||
{
|
||||
self.logger.info("APNs remote notification received keys=\(userInfo.keys.count, privacy: .public)")
|
||||
Task { @MainActor in
|
||||
let notificationCenter = LiveNotificationCenter()
|
||||
if await ExecApprovalNotificationBridge.handleResolvedPushIfNeeded(
|
||||
userInfo: userInfo,
|
||||
notificationCenter: notificationCenter)
|
||||
{
|
||||
if let approvalId = ExecApprovalNotificationBridge.approvalID(from: userInfo) {
|
||||
self.appModel?.dismissPendingExecApprovalPrompt(approvalId: approvalId)
|
||||
}
|
||||
completionHandler(.newData)
|
||||
return
|
||||
}
|
||||
guard let appModel = self.appModel else {
|
||||
self.logger.info("APNs wake skipped: appModel unavailable")
|
||||
self.scheduleBackgroundWakeRefresh(afterSeconds: 90, reason: "silent_push_no_model")
|
||||
@@ -216,6 +239,14 @@ final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrenc
|
||||
sessionKey: sessionKey)
|
||||
}
|
||||
|
||||
private static func parseExecApprovalPrompt(
|
||||
from response: UNNotificationResponse) -> PendingExecApprovalPrompt?
|
||||
{
|
||||
ExecApprovalNotificationBridge.parsePrompt(
|
||||
actionIdentifier: response.actionIdentifier,
|
||||
userInfo: response.notification.request.content.userInfo)
|
||||
}
|
||||
|
||||
private func routeWatchPromptAction(_ action: PendingWatchPromptAction) async {
|
||||
guard let appModel = self.appModel else {
|
||||
self.pendingWatchPromptActions.append(action)
|
||||
@@ -229,13 +260,25 @@ final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrenc
|
||||
_ = await appModel.handleBackgroundRefreshWake(trigger: "watch_prompt_action")
|
||||
}
|
||||
|
||||
private func routeExecApprovalPrompt(_ prompt: PendingExecApprovalPrompt) {
|
||||
guard let appModel = self.appModel else {
|
||||
self.pendingExecApprovalPrompts.append(prompt)
|
||||
return
|
||||
}
|
||||
Task { @MainActor in
|
||||
await appModel.presentExecApprovalNotificationPrompt(prompt)
|
||||
}
|
||||
}
|
||||
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
willPresent notification: UNNotification,
|
||||
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
|
||||
{
|
||||
let userInfo = notification.request.content.userInfo
|
||||
if Self.isWatchPromptNotification(userInfo) {
|
||||
if Self.isWatchPromptNotification(userInfo)
|
||||
|| ExecApprovalNotificationBridge.shouldPresentNotification(userInfo: userInfo)
|
||||
{
|
||||
completionHandler([.banner, .list, .sound])
|
||||
return
|
||||
}
|
||||
@@ -247,18 +290,29 @@ final class OpenClawAppDelegate: NSObject, UIApplicationDelegate, @preconcurrenc
|
||||
didReceive response: UNNotificationResponse,
|
||||
withCompletionHandler completionHandler: @escaping () -> Void)
|
||||
{
|
||||
guard let action = Self.parseWatchPromptAction(from: response) else {
|
||||
completionHandler()
|
||||
if let action = Self.parseWatchPromptAction(from: response) {
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
await self.routeWatchPromptAction(action)
|
||||
completionHandler()
|
||||
}
|
||||
return
|
||||
}
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
if let prompt = Self.parseExecApprovalPrompt(from: response) {
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else {
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
self.routeExecApprovalPrompt(prompt)
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
await self.routeWatchPromptAction(action)
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
completionHandler()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
92
apps/ios/Sources/Push/ExecApprovalNotificationBridge.swift
Normal file
92
apps/ios/Sources/Push/ExecApprovalNotificationBridge.swift
Normal file
@@ -0,0 +1,92 @@
|
||||
import Foundation
|
||||
import UserNotifications
|
||||
|
||||
struct ExecApprovalNotificationPrompt: Sendable, Equatable {
|
||||
let approvalId: String
|
||||
}
|
||||
|
||||
enum ExecApprovalNotificationBridge {
|
||||
static let requestedKind = "exec.approval.requested"
|
||||
static let resolvedKind = "exec.approval.resolved"
|
||||
|
||||
private static let localRequestPrefix = "exec.approval."
|
||||
|
||||
static func shouldPresentNotification(userInfo: [AnyHashable: Any]) -> Bool {
|
||||
self.payloadKind(userInfo: userInfo) == self.requestedKind
|
||||
}
|
||||
|
||||
static func parsePrompt(
|
||||
actionIdentifier: String,
|
||||
userInfo: [AnyHashable: Any]
|
||||
) -> ExecApprovalNotificationPrompt?
|
||||
{
|
||||
guard actionIdentifier == UNNotificationDefaultActionIdentifier else { return nil }
|
||||
guard self.payloadKind(userInfo: userInfo) == self.requestedKind else { return nil }
|
||||
guard let approvalId = self.approvalID(from: userInfo) else { return nil }
|
||||
return ExecApprovalNotificationPrompt(approvalId: approvalId)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
static func handleResolvedPushIfNeeded(
|
||||
userInfo: [AnyHashable: Any],
|
||||
notificationCenter: NotificationCentering
|
||||
) async -> Bool
|
||||
{
|
||||
guard self.payloadKind(userInfo: userInfo) == self.resolvedKind,
|
||||
let approvalId = self.approvalID(from: userInfo)
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
await self.removeNotifications(forApprovalID: approvalId, notificationCenter: notificationCenter)
|
||||
return true
|
||||
}
|
||||
|
||||
@MainActor
|
||||
static func removeNotifications(
|
||||
forApprovalID approvalId: String,
|
||||
notificationCenter: NotificationCentering
|
||||
) async {
|
||||
let normalizedID = approvalId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !normalizedID.isEmpty else { return }
|
||||
|
||||
await notificationCenter.removePendingNotificationRequests(
|
||||
withIdentifiers: [self.localRequestIdentifier(for: normalizedID)])
|
||||
|
||||
let delivered = await notificationCenter.deliveredNotifications()
|
||||
let identifiers = delivered.compactMap { snapshot -> String? in
|
||||
guard self.approvalID(from: snapshot.userInfo) == normalizedID else { return nil }
|
||||
return snapshot.identifier
|
||||
}
|
||||
await notificationCenter.removeDeliveredNotifications(withIdentifiers: identifiers)
|
||||
}
|
||||
|
||||
static func approvalID(from userInfo: [AnyHashable: Any]) -> String? {
|
||||
let raw = self.openClawPayload(userInfo: userInfo)?["approvalId"] as? String
|
||||
let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
}
|
||||
|
||||
private static func localRequestIdentifier(for approvalId: String) -> String {
|
||||
"\(self.localRequestPrefix)\(approvalId)"
|
||||
}
|
||||
|
||||
private static func payloadKind(userInfo: [AnyHashable: Any]) -> String {
|
||||
let raw = self.openClawPayload(userInfo: userInfo)?["kind"] as? String
|
||||
let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return trimmed.isEmpty ? "unknown" : trimmed
|
||||
}
|
||||
|
||||
private static func openClawPayload(userInfo: [AnyHashable: Any]) -> [String: Any]? {
|
||||
if let payload = userInfo["openclaw"] as? [String: Any] {
|
||||
return payload
|
||||
}
|
||||
if let payload = userInfo["openclaw"] as? [AnyHashable: Any] {
|
||||
return payload.reduce(into: [String: Any]()) { partialResult, pair in
|
||||
guard let key = pair.key as? String else { return }
|
||||
partialResult[key] = pair.value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,7 @@ struct RootCanvas: View {
|
||||
}
|
||||
.gatewayTrustPromptAlert()
|
||||
.deepLinkAgentPromptAlert()
|
||||
.execApprovalPromptDialog()
|
||||
.sheet(item: self.$presentedSheet) { sheet in
|
||||
switch sheet {
|
||||
case .settings:
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import Foundation
|
||||
import UserNotifications
|
||||
|
||||
struct NotificationSnapshot: @unchecked Sendable {
|
||||
let identifier: String
|
||||
let userInfo: [AnyHashable: Any]
|
||||
}
|
||||
|
||||
enum NotificationAuthorizationStatus: Sendable {
|
||||
case notDetermined
|
||||
case denied
|
||||
@@ -13,6 +18,9 @@ protocol NotificationCentering: Sendable {
|
||||
func authorizationStatus() async -> NotificationAuthorizationStatus
|
||||
func requestAuthorization(options: UNAuthorizationOptions) async throws -> Bool
|
||||
func add(_ request: UNNotificationRequest) async throws
|
||||
func removePendingNotificationRequests(withIdentifiers identifiers: [String]) async
|
||||
func removeDeliveredNotifications(withIdentifiers identifiers: [String]) async
|
||||
func deliveredNotifications() async -> [NotificationSnapshot]
|
||||
}
|
||||
|
||||
struct LiveNotificationCenter: NotificationCentering, @unchecked Sendable {
|
||||
@@ -55,4 +63,27 @@ struct LiveNotificationCenter: NotificationCentering, @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removePendingNotificationRequests(withIdentifiers identifiers: [String]) async {
|
||||
guard !identifiers.isEmpty else { return }
|
||||
self.center.removePendingNotificationRequests(withIdentifiers: identifiers)
|
||||
}
|
||||
|
||||
func removeDeliveredNotifications(withIdentifiers identifiers: [String]) async {
|
||||
guard !identifiers.isEmpty else { return }
|
||||
self.center.removeDeliveredNotifications(withIdentifiers: identifiers)
|
||||
}
|
||||
|
||||
func deliveredNotifications() async -> [NotificationSnapshot] {
|
||||
await withCheckedContinuation { continuation in
|
||||
self.center.getDeliveredNotifications { notifications in
|
||||
continuation.resume(
|
||||
returning: notifications.map { notification in
|
||||
NotificationSnapshot(
|
||||
identifier: notification.request.identifier,
|
||||
userInfo: notification.request.content.userInfo)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
86
apps/ios/Tests/ExecApprovalNotificationBridgeTests.swift
Normal file
86
apps/ios/Tests/ExecApprovalNotificationBridgeTests.swift
Normal file
@@ -0,0 +1,86 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
import UserNotifications
|
||||
@testable import OpenClaw
|
||||
|
||||
private final class MockNotificationCenter: NotificationCentering, @unchecked Sendable {
|
||||
var authorization: NotificationAuthorizationStatus = .authorized
|
||||
var addedRequests: [UNNotificationRequest] = []
|
||||
var pendingRemovedIdentifiers: [[String]] = []
|
||||
var deliveredRemovedIdentifiers: [[String]] = []
|
||||
var delivered: [NotificationSnapshot] = []
|
||||
|
||||
func authorizationStatus() async -> NotificationAuthorizationStatus {
|
||||
self.authorization
|
||||
}
|
||||
|
||||
func requestAuthorization(options _: UNAuthorizationOptions) async throws -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func add(_ request: UNNotificationRequest) async throws {
|
||||
self.addedRequests.append(request)
|
||||
}
|
||||
|
||||
func removePendingNotificationRequests(withIdentifiers identifiers: [String]) async {
|
||||
self.pendingRemovedIdentifiers.append(identifiers)
|
||||
}
|
||||
|
||||
func removeDeliveredNotifications(withIdentifiers identifiers: [String]) async {
|
||||
self.deliveredRemovedIdentifiers.append(identifiers)
|
||||
}
|
||||
|
||||
func deliveredNotifications() async -> [NotificationSnapshot] {
|
||||
self.delivered
|
||||
}
|
||||
}
|
||||
|
||||
@Suite(.serialized) struct ExecApprovalNotificationBridgeTests {
|
||||
@Test func parsePromptMapsDefaultNotificationTap() {
|
||||
let prompt = ExecApprovalNotificationBridge.parsePrompt(
|
||||
actionIdentifier: UNNotificationDefaultActionIdentifier,
|
||||
userInfo: [
|
||||
"openclaw": [
|
||||
"kind": ExecApprovalNotificationBridge.requestedKind,
|
||||
"approvalId": "approval-123",
|
||||
],
|
||||
])
|
||||
|
||||
#expect(prompt == ExecApprovalNotificationPrompt(approvalId: "approval-123"))
|
||||
}
|
||||
|
||||
@Test @MainActor func handleResolvedPushRemovesMatchingNotifications() async {
|
||||
let center = MockNotificationCenter()
|
||||
center.delivered = [
|
||||
NotificationSnapshot(
|
||||
identifier: "remote-approval-1",
|
||||
userInfo: [
|
||||
"openclaw": [
|
||||
"kind": ExecApprovalNotificationBridge.requestedKind,
|
||||
"approvalId": "approval-123",
|
||||
],
|
||||
]),
|
||||
NotificationSnapshot(
|
||||
identifier: "remote-other",
|
||||
userInfo: [
|
||||
"openclaw": [
|
||||
"kind": ExecApprovalNotificationBridge.requestedKind,
|
||||
"approvalId": "approval-999",
|
||||
],
|
||||
]),
|
||||
]
|
||||
|
||||
let handled = await ExecApprovalNotificationBridge.handleResolvedPushIfNeeded(
|
||||
userInfo: [
|
||||
"openclaw": [
|
||||
"kind": ExecApprovalNotificationBridge.resolvedKind,
|
||||
"approvalId": "approval-123",
|
||||
],
|
||||
],
|
||||
notificationCenter: center)
|
||||
|
||||
#expect(handled)
|
||||
#expect(center.pendingRemovedIdentifiers == [["exec.approval.approval-123"]])
|
||||
#expect(center.deliveredRemovedIdentifiers == [["remote-approval-1"]])
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,14 @@ private final class MockBootstrapNotificationCenter: NotificationCentering, @unc
|
||||
}
|
||||
|
||||
func add(_: UNNotificationRequest) async throws {}
|
||||
|
||||
func removePendingNotificationRequests(withIdentifiers _: [String]) async {}
|
||||
|
||||
func removeDeliveredNotifications(withIdentifiers _: [String]) async {}
|
||||
|
||||
func deliveredNotifications() async -> [NotificationSnapshot] {
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
||||
@Suite(.serialized) struct NodeAppModelInvokeTests {
|
||||
@@ -119,6 +127,79 @@ private final class MockBootstrapNotificationCenter: NotificationCentering, @unc
|
||||
#expect(appModel.mainSessionKey == "agent:agent-123:main")
|
||||
}
|
||||
|
||||
@Test @MainActor func execApprovalPromptPresentationTracksLatestNotificationTap() throws {
|
||||
let appModel = NodeAppModel()
|
||||
appModel._test_presentExecApprovalPrompt(
|
||||
try #require(
|
||||
NodeAppModel._test_makeExecApprovalPrompt(
|
||||
id: "approval-1",
|
||||
commandText: "echo first",
|
||||
allowedDecisions: ["allow-once", "deny"],
|
||||
host: "gateway",
|
||||
nodeId: nil,
|
||||
agentId: "main",
|
||||
expiresAtMs: 1)))
|
||||
|
||||
let firstPrompt = try #require(appModel._test_pendingExecApprovalPrompt())
|
||||
#expect(firstPrompt.id == "approval-1")
|
||||
#expect(firstPrompt.commandText == "echo first")
|
||||
#expect(firstPrompt.allowsAllowAlways == false)
|
||||
|
||||
appModel._test_presentExecApprovalPrompt(
|
||||
try #require(
|
||||
NodeAppModel._test_makeExecApprovalPrompt(
|
||||
id: "approval-2",
|
||||
commandText: "echo second",
|
||||
allowedDecisions: ["allow-once", "allow-always", "deny"],
|
||||
host: "gateway",
|
||||
nodeId: "node-2",
|
||||
agentId: nil,
|
||||
expiresAtMs: 2)))
|
||||
|
||||
let secondPrompt = try #require(appModel._test_pendingExecApprovalPrompt())
|
||||
#expect(secondPrompt.id == "approval-2")
|
||||
#expect(secondPrompt.commandText == "echo second")
|
||||
#expect(secondPrompt.allowsAllowAlways)
|
||||
|
||||
appModel._test_dismissPendingExecApprovalPrompt()
|
||||
#expect(appModel._test_pendingExecApprovalPrompt() == nil)
|
||||
}
|
||||
|
||||
@Test @MainActor func dismissPendingExecApprovalPromptByIdLeavesDifferentPromptVisible() throws {
|
||||
let appModel = NodeAppModel()
|
||||
appModel._test_presentExecApprovalPrompt(
|
||||
try #require(
|
||||
NodeAppModel._test_makeExecApprovalPrompt(
|
||||
id: "approval-active",
|
||||
commandText: "echo keep",
|
||||
allowedDecisions: ["allow-once", "deny"],
|
||||
host: "gateway",
|
||||
nodeId: nil,
|
||||
agentId: nil,
|
||||
expiresAtMs: 1)))
|
||||
|
||||
appModel.dismissPendingExecApprovalPrompt(approvalId: "approval-stale")
|
||||
|
||||
let prompt = try #require(appModel._test_pendingExecApprovalPrompt())
|
||||
#expect(prompt.id == "approval-active")
|
||||
}
|
||||
|
||||
@Test func approvalNotificationErrorClassificationPrefersStructuredDetails() {
|
||||
let staleError = GatewayResponseError(
|
||||
method: "exec.approval.get",
|
||||
code: "INVALID_REQUEST",
|
||||
message: "gateway error",
|
||||
details: ["reason": AnyCodable("APPROVAL_NOT_FOUND")])
|
||||
let unavailableError = GatewayResponseError(
|
||||
method: "exec.approval.resolve",
|
||||
code: "INVALID_REQUEST",
|
||||
message: "gateway error",
|
||||
details: ["reason": AnyCodable("APPROVAL_ALLOW_ALWAYS_UNAVAILABLE")])
|
||||
|
||||
#expect(NodeAppModel._test_isApprovalNotificationStaleError(staleError))
|
||||
#expect(NodeAppModel._test_isApprovalNotificationUnavailableError(unavailableError))
|
||||
}
|
||||
|
||||
@Test func operatorLoopWaitsForBootstrapHandoffBeforeUsingStoredToken() {
|
||||
#expect(
|
||||
!NodeAppModel._test_shouldStartOperatorGatewayLoop(
|
||||
|
||||
@@ -86,6 +86,43 @@ cd apps/ios
|
||||
fastlane ios beta
|
||||
```
|
||||
|
||||
Maintainer recovery path for a fresh clone on the same Mac:
|
||||
|
||||
1. Reuse the existing Keychain-backed ASC key on that machine.
|
||||
2. Restore or recreate `apps/ios/fastlane/.env` so it contains the non-secret variables:
|
||||
|
||||
```bash
|
||||
ASC_KEY_ID=YOUR_KEY_ID
|
||||
ASC_ISSUER_ID=YOUR_ISSUER_ID
|
||||
ASC_KEYCHAIN_SERVICE=openclaw-asc-key
|
||||
ASC_KEYCHAIN_ACCOUNT=YOUR_MAC_USERNAME
|
||||
```
|
||||
|
||||
3. Re-run auth validation:
|
||||
|
||||
```bash
|
||||
cd apps/ios
|
||||
fastlane ios auth_check
|
||||
```
|
||||
|
||||
4. Set the official/TestFlight relay URL before release:
|
||||
|
||||
```bash
|
||||
export OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com
|
||||
```
|
||||
|
||||
5. Upload:
|
||||
|
||||
```bash
|
||||
pnpm ios:beta
|
||||
```
|
||||
|
||||
Quick verification after upload:
|
||||
|
||||
- confirm `apps/ios/build/beta/OpenClaw-<version>.ipa` exists
|
||||
- confirm Fastlane prints `Uploaded iOS beta: version=<version> short=<short> build=<build>`
|
||||
- remember that TestFlight processing can take a few minutes after the upload succeeds
|
||||
|
||||
Versioning rules:
|
||||
|
||||
- Root `package.json.version` is the single source of truth for iOS
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.4.4</string>
|
||||
<string>2026.4.5</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2026040401</string>
|
||||
<string>2026040501</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -127,6 +127,12 @@ public struct GatewayResponseError: LocalizedError, @unchecked Sendable {
|
||||
self.details = details ?? [:]
|
||||
}
|
||||
|
||||
public var detailsReason: String? {
|
||||
let raw = self.details["reason"]?.value as? String
|
||||
let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
}
|
||||
|
||||
public var errorDescription: String? {
|
||||
if self.code == "GATEWAY_ERROR" { return "\(self.method): \(self.message)" }
|
||||
return "\(self.method): [\(self.code)] \(self.message)"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -130,44 +130,7 @@ public enum ToolDisplayRegistry {
|
||||
"messageId",
|
||||
],
|
||||
actions: nil),
|
||||
tools: [
|
||||
"bash": ToolDisplaySpec(
|
||||
emoji: "🛠️",
|
||||
title: "Bash",
|
||||
label: nil,
|
||||
detailKeys: ["command"],
|
||||
actions: nil),
|
||||
"read": ToolDisplaySpec(
|
||||
emoji: "📖",
|
||||
title: "Read",
|
||||
label: nil,
|
||||
detailKeys: ["path"],
|
||||
actions: nil),
|
||||
"write": ToolDisplaySpec(
|
||||
emoji: "✍️",
|
||||
title: "Write",
|
||||
label: nil,
|
||||
detailKeys: ["path"],
|
||||
actions: nil),
|
||||
"edit": ToolDisplaySpec(
|
||||
emoji: "📝",
|
||||
title: "Edit",
|
||||
label: nil,
|
||||
detailKeys: ["path"],
|
||||
actions: nil),
|
||||
"attach": ToolDisplaySpec(
|
||||
emoji: "📎",
|
||||
title: "Attach",
|
||||
label: nil,
|
||||
detailKeys: ["path", "url", "fileName"],
|
||||
actions: nil),
|
||||
"process": ToolDisplaySpec(
|
||||
emoji: "🧰",
|
||||
title: "Process",
|
||||
label: nil,
|
||||
detailKeys: ["sessionId"],
|
||||
actions: nil),
|
||||
])
|
||||
tools: nil)
|
||||
}
|
||||
|
||||
private static func titleFromName(_ name: String) -> String {
|
||||
|
||||
@@ -9,8 +9,8 @@ import Testing
|
||||
}
|
||||
|
||||
@Test func resolvesKnownToolFromConfig() {
|
||||
let summary = ToolDisplayRegistry.resolve(name: "bash", args: nil)
|
||||
let summary = ToolDisplayRegistry.resolve(name: "exec", args: nil)
|
||||
#expect(summary.emoji == "🛠️")
|
||||
#expect(summary.title == "Bash")
|
||||
#expect(summary.title == "Exec")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
20a882f9991e17310013471756ac7ec62c272e29490daeede9c0901bd51c0e69 config-baseline.json
|
||||
8ba6e5c959d5fc3eee9e6c5d1d8f764f164052f4207c0352bb39e2a7dbad64a8 config-baseline.core.json
|
||||
ca6d1fa8a3507566979ea2da2b88a6a7ae49d650f3ebd3eee14a22ed18e5be89 config-baseline.channel.json
|
||||
17fd37605bf6cb087932ec2ebcfa9dd22e669fa6b8b93081ab2deac9d24821c5 config-baseline.plugin.json
|
||||
0135fa04d71f209a54b076f41a3f6cb9795c9169fa631364fb3561eb5ff89891 config-baseline.json
|
||||
0e93c22a45545e13c74647f4945e9d8540d359640ed8c364b0f2514c9dc7a66c config-baseline.core.json
|
||||
ae67508350baf891b902348d55fada6c17e9c053adf53aaf3a8b92cd364ef3f1 config-baseline.channel.json
|
||||
d972a11d0f86080a722bddfe48990dd1b8fa16eb8e157e83f49bd46a5941c512 config-baseline.plugin.json
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cbffdf76d6a7254d8b2d3a601e1206d7b6c835bc44f170d4038bc711a35ef756 plugin-sdk-api-baseline.json
|
||||
fe026bf3ba1e3b55f6c0b560d76940f3c301d8f593d6f0f6dcc4625745c76d31 plugin-sdk-api-baseline.jsonl
|
||||
97509287d728c8f5d1736f7ea07521451ada4b9d7ef56555dbe860a89e1b6e08 plugin-sdk-api-baseline.json
|
||||
a22b3d427953cc8394b28c87ef7a992d2eb4f2c9f6a76fa58b33079e2306661b plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -1,11 +1,42 @@
|
||||
# OpenClaw docs i18n assets
|
||||
|
||||
This folder stores **generated** and **config** files for documentation translations.
|
||||
This folder stores translation config for the source docs repo.
|
||||
|
||||
## Files
|
||||
Generated locale trees and live translation memory now live in the publish repo:
|
||||
|
||||
- `glossary.<lang>.json` — preferred term mappings (used in prompt guidance).
|
||||
- `<lang>.tm.jsonl` — translation memory (cache) keyed by workflow + model + text hash.
|
||||
- repo: `openclaw/docs`
|
||||
- local checkout: `~/Projects/openclaw-docs`
|
||||
|
||||
## Source of truth
|
||||
|
||||
- English docs are authored in `openclaw/openclaw`.
|
||||
- The source docs tree lives under `docs/`.
|
||||
- The source repo no longer keeps committed generated locale trees such as `docs/zh-CN/**`, `docs/ja-JP/**`, `docs/es/**`, `docs/pt-BR/**`, `docs/ko/**`, `docs/de/**`, `docs/fr/**`, `docs/ar/**`, `docs/it/**`, `docs/tr/**`, `docs/uk/**`, `docs/id/**`, or `docs/pl/**`.
|
||||
|
||||
## End-to-end flow
|
||||
|
||||
1. Edit English docs in `openclaw/openclaw`.
|
||||
2. Push to `main`.
|
||||
3. `openclaw/openclaw/.github/workflows/docs-sync-publish.yml` mirrors the docs tree into `openclaw/docs`.
|
||||
4. The sync script rewrites the publish `docs/docs.json` so the generated locale picker blocks exist there even though they are no longer committed in the source repo.
|
||||
5. `openclaw/docs/.github/workflows/translate-zh-cn.yml` refreshes `docs/zh-CN/**` once a day, on demand, and after source-repo release dispatches.
|
||||
6. `openclaw/docs/.github/workflows/translate-ja-jp.yml` does the same for `docs/ja-JP/**`.
|
||||
7. `openclaw/docs/.github/workflows/translate-es.yml`, `translate-pt-br.yml`, `translate-ko.yml`, `translate-de.yml`, `translate-fr.yml`, `translate-ar.yml`, `translate-it.yml`, `translate-tr.yml`, `translate-uk.yml`, `translate-id.yml`, and `translate-pl.yml` do the same for `docs/es/**`, `docs/pt-BR/**`, `docs/ko/**`, `docs/de/**`, `docs/fr/**`, `docs/ar/**`, `docs/it/**`, `docs/tr/**`, `docs/uk/**`, `docs/id/**`, and `docs/pl/**`.
|
||||
|
||||
## Why the split exists
|
||||
|
||||
- Keep generated locale output out of the main product repo.
|
||||
- Keep Mintlify on a single published docs tree.
|
||||
- Preserve the built-in language switcher by letting the publish repo own generated locale trees.
|
||||
|
||||
## Files in this folder
|
||||
|
||||
- `glossary.<lang>.json` — preferred term mappings used as prompt guidance.
|
||||
- `zh-Hans-navigation.json` — curated zh-Hans Mintlify locale navigation reinserted into the publish repo during sync.
|
||||
- `ar-navigation.json`, `de-navigation.json`, `es-navigation.json`, `fr-navigation.json`, `id-navigation.json`, `it-navigation.json`, `ja-navigation.json`, `ko-navigation.json`, `pl-navigation.json`, `pt-BR-navigation.json`, `tr-navigation.json` — starter locale metadata kept alongside the source repo, but the publish sync now clones the full English nav tree for these locales so translated pages are visible in Mintlify without hand-maintaining per-locale nav JSON.
|
||||
- `<lang>.tm.jsonl` — translation memory keyed by workflow + model + text hash.
|
||||
|
||||
In this repo, generated locale TM files such as `docs/.i18n/zh-CN.tm.jsonl`, `docs/.i18n/ja-JP.tm.jsonl`, `docs/.i18n/es.tm.jsonl`, `docs/.i18n/pt-BR.tm.jsonl`, `docs/.i18n/ko.tm.jsonl`, `docs/.i18n/de.tm.jsonl`, `docs/.i18n/fr.tm.jsonl`, `docs/.i18n/ar.tm.jsonl`, `docs/.i18n/it.tm.jsonl`, `docs/.i18n/tr.tm.jsonl`, `docs/.i18n/uk.tm.jsonl`, `docs/.i18n/id.tm.jsonl`, and `docs/.i18n/pl.tm.jsonl` are intentionally no longer committed.
|
||||
|
||||
## Glossary format
|
||||
|
||||
@@ -14,9 +45,7 @@ This folder stores **generated** and **config** files for documentation translat
|
||||
```json
|
||||
{
|
||||
"source": "troubleshooting",
|
||||
"target": "故障排除",
|
||||
"ignore_case": true,
|
||||
"whole_word": false
|
||||
"target": "故障排除"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -25,7 +54,19 @@ Fields:
|
||||
- `source`: English (or source) phrase to prefer.
|
||||
- `target`: preferred translation output.
|
||||
|
||||
## Notes
|
||||
## Translation mechanics
|
||||
|
||||
- Glossary entries are passed to the model as **prompt guidance** (no deterministic rewrites).
|
||||
- The translation memory is updated by `scripts/docs-i18n`.
|
||||
- `scripts/docs-i18n` still owns translation generation.
|
||||
- Doc mode writes `x-i18n.source_hash` into each translated page.
|
||||
- Each publish workflow precomputes a pending file list by comparing the current English source hash to the stored locale `x-i18n.source_hash`.
|
||||
- If the pending count is `0`, the expensive translation step is skipped entirely.
|
||||
- If there are pending files, the workflow translates only those files.
|
||||
- The publish workflow retries transient model-format failures, but unchanged files stay skipped because the same hash check runs on each retry.
|
||||
- The source repo also dispatches zh-CN, ja-JP, es, pt-BR, ko, de, fr, ar, it, tr, uk, id, and pl refreshes after published GitHub releases so release docs can catch up without waiting for the daily cron.
|
||||
|
||||
## Operational notes
|
||||
|
||||
- Sync metadata is written to `.openclaw-sync/source.json` in the publish repo.
|
||||
- Source repo secret: `OPENCLAW_DOCS_SYNC_TOKEN`
|
||||
- Publish repo secret: `OPENCLAW_DOCS_I18N_OPENAI_API_KEY`
|
||||
- If locale output looks stale, check the matching `Translate <locale>` workflow in `openclaw/docs` first.
|
||||
|
||||
18
docs/.i18n/ar-navigation.json
Normal file
18
docs/.i18n/ar-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "ar",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "ابدأ",
|
||||
"groups": [
|
||||
{
|
||||
"group": "نظرة عامة",
|
||||
"pages": ["ar/index"]
|
||||
},
|
||||
{
|
||||
"group": "الخطوات الأولى",
|
||||
"pages": ["ar/start/getting-started", "ar/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/de-navigation.json
Normal file
18
docs/.i18n/de-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "de",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Loslegen",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Überblick",
|
||||
"pages": ["de/index"]
|
||||
},
|
||||
{
|
||||
"group": "Erste Schritte",
|
||||
"pages": ["de/start/getting-started", "de/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/es-navigation.json
Normal file
18
docs/.i18n/es-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "es",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Comenzar",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Resumen",
|
||||
"pages": ["es/index"]
|
||||
},
|
||||
{
|
||||
"group": "Primeros pasos",
|
||||
"pages": ["es/start/getting-started", "es/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/fr-navigation.json
Normal file
18
docs/.i18n/fr-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "fr",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Commencer",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Vue d'ensemble",
|
||||
"pages": ["fr/index"]
|
||||
},
|
||||
{
|
||||
"group": "Premiers pas",
|
||||
"pages": ["fr/start/getting-started", "fr/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
5
docs/.i18n/glossary.ar.json
Normal file
5
docs/.i18n/glossary.ar.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.de.json
Normal file
5
docs/.i18n/glossary.de.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.es.json
Normal file
5
docs/.i18n/glossary.es.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.fr.json
Normal file
5
docs/.i18n/glossary.fr.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.id.json
Normal file
5
docs/.i18n/glossary.id.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.it.json
Normal file
5
docs/.i18n/glossary.it.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.ko.json
Normal file
5
docs/.i18n/glossary.ko.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.pl.json
Normal file
5
docs/.i18n/glossary.pl.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.pt-BR.json
Normal file
5
docs/.i18n/glossary.pt-BR.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.tr.json
Normal file
5
docs/.i18n/glossary.tr.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
5
docs/.i18n/glossary.uk.json
Normal file
5
docs/.i18n/glossary.uk.json
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ "source": "CLI", "target": "CLI" },
|
||||
{ "source": "Mintlify", "target": "Mintlify" },
|
||||
{ "source": "OpenClaw", "target": "OpenClaw" }
|
||||
]
|
||||
18
docs/.i18n/id-navigation.json
Normal file
18
docs/.i18n/id-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "id",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Mulai",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Ringkasan",
|
||||
"pages": ["id/index"]
|
||||
},
|
||||
{
|
||||
"group": "Langkah pertama",
|
||||
"pages": ["id/start/getting-started", "id/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/it-navigation.json
Normal file
18
docs/.i18n/it-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "it",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Inizia",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Panoramica",
|
||||
"pages": ["it/index"]
|
||||
},
|
||||
{
|
||||
"group": "Primi passi",
|
||||
"pages": ["it/start/getting-started", "it/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/ja-navigation.json
Normal file
18
docs/.i18n/ja-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "ja",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "はじめに",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概要",
|
||||
"pages": ["ja-JP/index"]
|
||||
},
|
||||
{
|
||||
"group": "初回セットアップ",
|
||||
"pages": ["ja-JP/start/getting-started", "ja-JP/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/ko-navigation.json
Normal file
18
docs/.i18n/ko-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "ko",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "시작하기",
|
||||
"groups": [
|
||||
{
|
||||
"group": "개요",
|
||||
"pages": ["ko/index"]
|
||||
},
|
||||
{
|
||||
"group": "첫 단계",
|
||||
"pages": ["ko/start/getting-started", "ko/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/pl-navigation.json
Normal file
18
docs/.i18n/pl-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "pl",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Start",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Przeglad",
|
||||
"pages": ["pl/index"]
|
||||
},
|
||||
{
|
||||
"group": "Pierwsze kroki",
|
||||
"pages": ["pl/start/getting-started", "pl/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/pt-BR-navigation.json
Normal file
18
docs/.i18n/pt-BR-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "pt-BR",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Começar",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Visão geral",
|
||||
"pages": ["pt-BR/index"]
|
||||
},
|
||||
{
|
||||
"group": "Primeiros passos",
|
||||
"pages": ["pt-BR/start/getting-started", "pt-BR/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
docs/.i18n/tr-navigation.json
Normal file
18
docs/.i18n/tr-navigation.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"language": "tr",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Baslangic",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Genel bakis",
|
||||
"pages": ["tr/index"]
|
||||
},
|
||||
{
|
||||
"group": "Ilk adimlar",
|
||||
"pages": ["tr/start/getting-started", "tr/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
544
docs/.i18n/zh-Hans-navigation.json
Normal file
544
docs/.i18n/zh-Hans-navigation.json
Normal file
@@ -0,0 +1,544 @@
|
||||
{
|
||||
"language": "zh-Hans",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "快速开始",
|
||||
"groups": [
|
||||
{
|
||||
"group": "首页",
|
||||
"pages": ["zh-CN/index"]
|
||||
},
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/start/showcase"]
|
||||
},
|
||||
{
|
||||
"group": "核心概念",
|
||||
"pages": ["zh-CN/concepts/features"]
|
||||
},
|
||||
{
|
||||
"group": "第一步",
|
||||
"pages": ["zh-CN/start/getting-started", "zh-CN/start/wizard", "zh-CN/start/onboarding"]
|
||||
},
|
||||
{
|
||||
"group": "指南",
|
||||
"pages": ["zh-CN/start/openclaw"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "安装",
|
||||
"groups": [
|
||||
{
|
||||
"group": "安装概览",
|
||||
"pages": ["zh-CN/install/index", "zh-CN/install/installer"]
|
||||
},
|
||||
{
|
||||
"group": "安装方式",
|
||||
"pages": [
|
||||
"zh-CN/install/docker",
|
||||
"zh-CN/install/nix",
|
||||
"zh-CN/install/ansible",
|
||||
"zh-CN/install/bun"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "维护",
|
||||
"pages": ["zh-CN/install/updating", "zh-CN/install/migrating", "zh-CN/install/uninstall"]
|
||||
},
|
||||
{
|
||||
"group": "托管与部署",
|
||||
"pages": [
|
||||
"zh-CN/vps",
|
||||
"zh-CN/install/fly",
|
||||
"zh-CN/install/hetzner",
|
||||
"zh-CN/install/gcp",
|
||||
"zh-CN/install/macos-vm",
|
||||
"zh-CN/install/exe-dev",
|
||||
"zh-CN/install/railway",
|
||||
"zh-CN/install/render",
|
||||
"zh-CN/install/northflank"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "高级",
|
||||
"pages": ["zh-CN/install/development-channels"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "消息渠道",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/channels/index"]
|
||||
},
|
||||
{
|
||||
"group": "消息平台",
|
||||
"pages": [
|
||||
"zh-CN/channels/bluebubbles",
|
||||
"zh-CN/channels/discord",
|
||||
"zh-CN/channels/feishu",
|
||||
"zh-CN/channels/grammy",
|
||||
"zh-CN/channels/googlechat",
|
||||
"zh-CN/channels/imessage",
|
||||
"zh-CN/channels/line",
|
||||
"zh-CN/channels/matrix",
|
||||
"zh-CN/channels/mattermost",
|
||||
"zh-CN/channels/msteams",
|
||||
"zh-CN/channels/nextcloud-talk",
|
||||
"zh-CN/channels/nostr",
|
||||
"zh-CN/channels/signal",
|
||||
"zh-CN/channels/slack",
|
||||
"zh-CN/channels/telegram",
|
||||
"zh-CN/channels/tlon",
|
||||
"zh-CN/channels/twitch",
|
||||
"zh-CN/channels/whatsapp",
|
||||
"zh-CN/channels/zalo",
|
||||
"zh-CN/channels/zalouser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "配置",
|
||||
"pages": [
|
||||
"zh-CN/channels/pairing",
|
||||
"zh-CN/channels/group-messages",
|
||||
"zh-CN/channels/groups",
|
||||
"zh-CN/channels/broadcast-groups",
|
||||
"zh-CN/channels/channel-routing",
|
||||
"zh-CN/channels/location",
|
||||
"zh-CN/channels/troubleshooting"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "代理",
|
||||
"groups": [
|
||||
{
|
||||
"group": "基础",
|
||||
"pages": [
|
||||
"zh-CN/pi",
|
||||
"zh-CN/concepts/architecture",
|
||||
"zh-CN/concepts/agent",
|
||||
"zh-CN/concepts/agent-loop",
|
||||
"zh-CN/concepts/system-prompt",
|
||||
"zh-CN/concepts/context",
|
||||
"zh-CN/concepts/agent-workspace",
|
||||
"zh-CN/concepts/oauth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "引导",
|
||||
"pages": ["zh-CN/start/bootstrapping"]
|
||||
},
|
||||
{
|
||||
"group": "会话与记忆",
|
||||
"pages": [
|
||||
"zh-CN/concepts/session",
|
||||
"zh-CN/concepts/session-pruning",
|
||||
"zh-CN/concepts/session-tool",
|
||||
"zh-CN/concepts/memory",
|
||||
"zh-CN/concepts/compaction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "多代理",
|
||||
"pages": ["zh-CN/concepts/multi-agent", "zh-CN/concepts/presence"]
|
||||
},
|
||||
{
|
||||
"group": "消息与投递",
|
||||
"pages": [
|
||||
"zh-CN/concepts/messages",
|
||||
"zh-CN/concepts/streaming",
|
||||
"zh-CN/concepts/retry",
|
||||
"zh-CN/concepts/queue"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "工具",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/tools/index"]
|
||||
},
|
||||
{
|
||||
"group": "内置工具",
|
||||
"pages": [
|
||||
"zh-CN/tools/apply-patch",
|
||||
"zh-CN/brave-search",
|
||||
"zh-CN/tools/elevated",
|
||||
"zh-CN/tools/exec",
|
||||
"zh-CN/tools/exec-approvals",
|
||||
"zh-CN/tools/firecrawl",
|
||||
"zh-CN/tools/llm-task",
|
||||
"zh-CN/tools/lobster",
|
||||
"zh-CN/perplexity",
|
||||
"zh-CN/tools/reactions",
|
||||
"zh-CN/tools/thinking",
|
||||
"zh-CN/tools/web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "浏览器",
|
||||
"pages": [
|
||||
"zh-CN/tools/browser",
|
||||
"zh-CN/tools/browser-login",
|
||||
"zh-CN/tools/browser-linux-troubleshooting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "代理协作",
|
||||
"pages": [
|
||||
"zh-CN/tools/agent-send",
|
||||
"zh-CN/tools/subagents",
|
||||
"zh-CN/tools/multi-agent-sandbox-tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "技能",
|
||||
"pages": [
|
||||
"zh-CN/tools/creating-skills",
|
||||
"zh-CN/tools/slash-commands",
|
||||
"zh-CN/tools/skills",
|
||||
"zh-CN/tools/skills-config",
|
||||
"zh-CN/tools/clawhub",
|
||||
"zh-CN/tools/plugin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "扩展",
|
||||
"pages": [
|
||||
"zh-CN/plugins/voice-call",
|
||||
"zh-CN/plugins/zalouser",
|
||||
"zh-CN/plugins/manifest",
|
||||
"zh-CN/plugins/agent-tools",
|
||||
"zh-CN/prose"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "自动化与任务",
|
||||
"pages": ["zh-CN/automation/hooks", "zh-CN/automation/cron-jobs"]
|
||||
},
|
||||
{
|
||||
"group": "媒体与设备",
|
||||
"pages": [
|
||||
"zh-CN/nodes/index",
|
||||
"zh-CN/nodes/troubleshooting",
|
||||
"zh-CN/nodes/media-understanding",
|
||||
"zh-CN/nodes/images",
|
||||
"zh-CN/nodes/audio",
|
||||
"zh-CN/nodes/camera",
|
||||
"zh-CN/nodes/talk",
|
||||
"zh-CN/nodes/voicewake",
|
||||
"zh-CN/nodes/location-command",
|
||||
"zh-CN/tts"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "模型",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/providers/index", "zh-CN/providers/models"]
|
||||
},
|
||||
{
|
||||
"group": "模型概念",
|
||||
"pages": ["zh-CN/concepts/models"]
|
||||
},
|
||||
{
|
||||
"group": "配置",
|
||||
"pages": ["zh-CN/concepts/model-providers", "zh-CN/concepts/model-failover"]
|
||||
},
|
||||
{
|
||||
"group": "提供商",
|
||||
"pages": [
|
||||
"zh-CN/providers/anthropic",
|
||||
"zh-CN/providers/bedrock",
|
||||
"zh-CN/providers/claude-max-api-proxy",
|
||||
"zh-CN/providers/deepgram",
|
||||
"zh-CN/providers/github-copilot",
|
||||
"zh-CN/providers/glm",
|
||||
"zh-CN/providers/moonshot",
|
||||
"zh-CN/providers/minimax",
|
||||
"zh-CN/providers/opencode",
|
||||
"zh-CN/providers/ollama",
|
||||
"zh-CN/providers/openai",
|
||||
"zh-CN/providers/openrouter",
|
||||
"zh-CN/providers/qianfan",
|
||||
"zh-CN/providers/qwen",
|
||||
"zh-CN/providers/synthetic",
|
||||
"zh-CN/providers/venice",
|
||||
"zh-CN/providers/vercel-ai-gateway",
|
||||
"zh-CN/providers/xiaomi",
|
||||
"zh-CN/providers/zai"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "平台",
|
||||
"groups": [
|
||||
{
|
||||
"group": "平台概览",
|
||||
"pages": [
|
||||
"zh-CN/platforms/index",
|
||||
"zh-CN/platforms/macos",
|
||||
"zh-CN/platforms/linux",
|
||||
"zh-CN/platforms/windows",
|
||||
"zh-CN/platforms/android",
|
||||
"zh-CN/platforms/ios",
|
||||
"zh-CN/platforms/digitalocean",
|
||||
"zh-CN/platforms/oracle",
|
||||
"zh-CN/platforms/raspberry-pi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "macOS 配套应用",
|
||||
"pages": [
|
||||
"zh-CN/platforms/mac/dev-setup",
|
||||
"zh-CN/platforms/mac/menu-bar",
|
||||
"zh-CN/platforms/mac/voicewake",
|
||||
"zh-CN/platforms/mac/voice-overlay",
|
||||
"zh-CN/platforms/mac/webchat",
|
||||
"zh-CN/platforms/mac/canvas",
|
||||
"zh-CN/platforms/mac/child-process",
|
||||
"zh-CN/platforms/mac/health",
|
||||
"zh-CN/platforms/mac/icon",
|
||||
"zh-CN/platforms/mac/logging",
|
||||
"zh-CN/platforms/mac/permissions",
|
||||
"zh-CN/platforms/mac/remote",
|
||||
"zh-CN/platforms/mac/signing",
|
||||
"zh-CN/platforms/mac/bundled-gateway",
|
||||
"zh-CN/platforms/mac/xpc",
|
||||
"zh-CN/platforms/mac/skills",
|
||||
"zh-CN/platforms/mac/peekaboo"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "网关与运维",
|
||||
"groups": [
|
||||
{
|
||||
"group": "网关",
|
||||
"pages": [
|
||||
"zh-CN/gateway/index",
|
||||
{
|
||||
"group": "配置与运维",
|
||||
"pages": [
|
||||
"zh-CN/gateway/configuration",
|
||||
"zh-CN/gateway/configuration-examples",
|
||||
"zh-CN/gateway/authentication",
|
||||
"zh-CN/gateway/health",
|
||||
"zh-CN/gateway/heartbeat",
|
||||
"zh-CN/gateway/doctor",
|
||||
"zh-CN/gateway/logging",
|
||||
"zh-CN/gateway/gateway-lock",
|
||||
"zh-CN/gateway/background-process",
|
||||
"zh-CN/gateway/multiple-gateways",
|
||||
"zh-CN/gateway/troubleshooting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "安全与沙箱",
|
||||
"pages": [
|
||||
"zh-CN/gateway/security/index",
|
||||
"zh-CN/gateway/sandboxing",
|
||||
"zh-CN/gateway/sandbox-vs-tool-policy-vs-elevated"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "协议与 API",
|
||||
"pages": [
|
||||
"zh-CN/gateway/protocol",
|
||||
"zh-CN/gateway/bridge-protocol",
|
||||
"zh-CN/gateway/openai-http-api",
|
||||
"zh-CN/gateway/openresponses-http-api",
|
||||
"zh-CN/gateway/tools-invoke-http-api",
|
||||
"zh-CN/gateway/cli-backends",
|
||||
"zh-CN/gateway/local-models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "网络与发现",
|
||||
"pages": [
|
||||
"zh-CN/gateway/network-model",
|
||||
"zh-CN/gateway/pairing",
|
||||
"zh-CN/gateway/discovery",
|
||||
"zh-CN/gateway/bonjour"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "远程访问",
|
||||
"pages": [
|
||||
"zh-CN/gateway/remote",
|
||||
"zh-CN/gateway/remote-gateway-readme",
|
||||
"zh-CN/gateway/tailscale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "运维专题",
|
||||
"pages": ["zh-CN/network", "zh-CN/logging"]
|
||||
},
|
||||
{
|
||||
"group": "安全",
|
||||
"pages": ["zh-CN/security/formal-verification"]
|
||||
},
|
||||
{
|
||||
"group": "Web 界面",
|
||||
"pages": [
|
||||
"zh-CN/web/index",
|
||||
"zh-CN/web/control-ui",
|
||||
"zh-CN/web/dashboard",
|
||||
"zh-CN/web/webchat",
|
||||
"zh-CN/web/tui"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "参考",
|
||||
"groups": [
|
||||
{
|
||||
"group": "CLI 命令",
|
||||
"pages": [
|
||||
"zh-CN/cli/index",
|
||||
"zh-CN/cli/acp",
|
||||
"zh-CN/cli/agent",
|
||||
"zh-CN/cli/agents",
|
||||
"zh-CN/cli/approvals",
|
||||
"zh-CN/cli/browser",
|
||||
"zh-CN/cli/channels",
|
||||
"zh-CN/cli/config",
|
||||
"zh-CN/cli/configure",
|
||||
"zh-CN/cli/cron",
|
||||
"zh-CN/cli/dashboard",
|
||||
"zh-CN/cli/devices",
|
||||
"zh-CN/cli/directory",
|
||||
"zh-CN/cli/dns",
|
||||
"zh-CN/cli/docs",
|
||||
"zh-CN/cli/doctor",
|
||||
"zh-CN/cli/gateway",
|
||||
"zh-CN/cli/health",
|
||||
"zh-CN/cli/hooks",
|
||||
"zh-CN/cli/logs",
|
||||
"zh-CN/cli/memory",
|
||||
"zh-CN/cli/message",
|
||||
"zh-CN/cli/models",
|
||||
"zh-CN/cli/node",
|
||||
"zh-CN/cli/nodes",
|
||||
"zh-CN/cli/onboard",
|
||||
"zh-CN/cli/pairing",
|
||||
"zh-CN/cli/plugins",
|
||||
"zh-CN/cli/reset",
|
||||
"zh-CN/cli/sandbox",
|
||||
"zh-CN/cli/security",
|
||||
"zh-CN/cli/sessions",
|
||||
"zh-CN/cli/setup",
|
||||
"zh-CN/cli/skills",
|
||||
"zh-CN/cli/status",
|
||||
"zh-CN/cli/system",
|
||||
"zh-CN/cli/tui",
|
||||
"zh-CN/cli/uninstall",
|
||||
"zh-CN/cli/update",
|
||||
"zh-CN/cli/voicecall",
|
||||
"zh-CN/cli/webhooks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "RPC 与 API",
|
||||
"pages": ["zh-CN/reference/rpc", "zh-CN/reference/device-models"]
|
||||
},
|
||||
{
|
||||
"group": "模板",
|
||||
"pages": [
|
||||
"zh-CN/reference/AGENTS.default",
|
||||
"zh-CN/reference/templates/AGENTS",
|
||||
"zh-CN/reference/templates/BOOT",
|
||||
"zh-CN/reference/templates/BOOTSTRAP",
|
||||
"zh-CN/reference/templates/HEARTBEAT",
|
||||
"zh-CN/reference/templates/IDENTITY",
|
||||
"zh-CN/reference/templates/SOUL",
|
||||
"zh-CN/reference/templates/TOOLS",
|
||||
"zh-CN/reference/templates/USER"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "技术参考",
|
||||
"pages": [
|
||||
"zh-CN/reference/wizard",
|
||||
"zh-CN/reference/token-use",
|
||||
"zh-CN/reference/api-usage-costs",
|
||||
"zh-CN/reference/transcript-hygiene",
|
||||
"zh-CN/date-time"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "概念内部机制",
|
||||
"pages": [
|
||||
"zh-CN/concepts/typebox",
|
||||
"zh-CN/concepts/markdown-formatting",
|
||||
"zh-CN/concepts/typing-indicators",
|
||||
"zh-CN/concepts/usage-tracking",
|
||||
"zh-CN/concepts/timezone"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "项目",
|
||||
"pages": ["zh-CN/reference/credits"]
|
||||
},
|
||||
{
|
||||
"group": "发布策略",
|
||||
"pages": ["zh-CN/reference/RELEASING", "zh-CN/reference/test"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "帮助",
|
||||
"groups": [
|
||||
{
|
||||
"group": "帮助",
|
||||
"pages": ["zh-CN/help/index", "zh-CN/help/troubleshooting", "zh-CN/help/faq"]
|
||||
},
|
||||
{
|
||||
"group": "社区",
|
||||
"pages": ["zh-CN/start/lore"]
|
||||
},
|
||||
{
|
||||
"group": "环境与调试",
|
||||
"pages": [
|
||||
"zh-CN/help/environment",
|
||||
"zh-CN/help/debugging",
|
||||
"zh-CN/help/testing",
|
||||
"zh-CN/help/scripts",
|
||||
"zh-CN/debug/node-issue",
|
||||
"zh-CN/diagnostics/flags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Node 运行时",
|
||||
"pages": ["zh-CN/install/node"]
|
||||
},
|
||||
{
|
||||
"group": "压缩机制内部参考",
|
||||
"pages": ["zh-CN/reference/session-management-compaction"]
|
||||
},
|
||||
{
|
||||
"group": "开发者设置",
|
||||
"pages": ["zh-CN/start/setup", "zh-CN/pi-dev"]
|
||||
},
|
||||
{
|
||||
"group": "文档元信息",
|
||||
"pages": ["zh-CN/start/hubs", "zh-CN/start/docs-directory", "zh-CN/AGENTS"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -571,8 +571,14 @@ Default slash command settings:
|
||||
- `off` (default)
|
||||
- `first`
|
||||
- `all`
|
||||
- `batched`
|
||||
|
||||
Note: `off` disables implicit reply threading. Explicit `[[reply_to_*]]` tags are still honored.
|
||||
`first` always attaches the implicit native reply reference to the first outbound Discord message for the turn.
|
||||
`batched` only attaches Discord's implicit native reply reference when the
|
||||
inbound turn was a debounced batch of multiple messages. This is useful
|
||||
when you want native replies mainly for ambiguous bursty chats, not every
|
||||
single-message turn.
|
||||
|
||||
Message IDs are surfaced in context/history so agents can target specific messages.
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ Channel specific notes:
|
||||
|
||||
- BlueBubbles can optionally enrich unnamed macOS group participants from the local Contacts database before populating `GroupMembers`. This is off by default and only runs after normal group gating passes.
|
||||
|
||||
The agent system prompt includes a group intro on the first turn of a new group session. It reminds the model to respond like a human, avoid Markdown tables, and avoid typing literal `\n` sequences.
|
||||
The agent system prompt includes a group intro on the first turn of a new group session. It reminds the model to respond like a human, avoid Markdown tables, minimize empty lines and follow normal chat spacing, and avoid typing literal `\n` sequences.
|
||||
|
||||
## iMessage specifics
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ IRC ships as an extension plugin, but it is configured in the main config under
|
||||
channels: {
|
||||
irc: {
|
||||
enabled: true,
|
||||
host: "irc.libera.chat",
|
||||
host: "irc.example.com",
|
||||
port: 6697,
|
||||
tls: true,
|
||||
nick: "openclaw-bot",
|
||||
@@ -31,6 +31,8 @@ IRC ships as an extension plugin, but it is configured in the main config under
|
||||
}
|
||||
```
|
||||
|
||||
Prefer a private IRC server for bot coordination. If you intentionally use a public IRC network, common choices include Libera.Chat, OFTC, and Snoonet. Avoid predictable public channels for bot or swarm backchannel traffic.
|
||||
|
||||
3. Start/restart gateway:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -152,6 +152,7 @@ This is a practical baseline config with DM pairing, room allowlist, and E2EE en
|
||||
|
||||
dm: {
|
||||
policy: "pairing",
|
||||
sessionScope: "per-room",
|
||||
threadReplies: "off",
|
||||
},
|
||||
|
||||
@@ -177,9 +178,9 @@ This is a practical baseline config with DM pairing, room allowlist, and E2EE en
|
||||
|
||||
Matrix reply streaming is opt-in.
|
||||
|
||||
Set `channels.matrix.streaming` to `"partial"` when you want OpenClaw to send a single draft reply,
|
||||
edit that draft in place while the model is generating text, and then finalize it when the reply is
|
||||
done:
|
||||
Set `channels.matrix.streaming` to `"partial"` when you want OpenClaw to send a single live preview
|
||||
reply, edit that preview in place while the model is generating text, and then finalize it when the
|
||||
reply is done:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -192,15 +193,164 @@ done:
|
||||
```
|
||||
|
||||
- `streaming: "off"` is the default. OpenClaw waits for the final reply and sends it once.
|
||||
- `streaming: "partial"` creates one editable preview message for the current assistant block instead of sending multiple partial messages.
|
||||
- `blockStreaming: true` enables separate Matrix progress messages. With `streaming: "partial"`, Matrix keeps the live draft for the current block and preserves completed blocks as separate messages.
|
||||
- When `streaming: "partial"` and `blockStreaming` is off, Matrix only edits the live draft and sends the completed reply once that block or turn finishes.
|
||||
- `streaming: "partial"` creates one editable preview message for the current assistant block using normal Matrix text messages. This preserves Matrix's legacy preview-first notification behavior, so stock clients may notify on the first streamed preview text instead of the finished block.
|
||||
- `streaming: "quiet"` creates one editable quiet preview notice for the current assistant block. Use this only when you also configure recipient push rules for finalized preview edits.
|
||||
- `blockStreaming: true` enables separate Matrix progress messages. With preview streaming enabled, Matrix keeps the live draft for the current block and preserves completed blocks as separate messages.
|
||||
- When preview streaming is on and `blockStreaming` is off, Matrix edits the live draft in place and finalizes that same event when the block or turn finishes.
|
||||
- If the preview no longer fits in one Matrix event, OpenClaw stops preview streaming and falls back to normal final delivery.
|
||||
- Media replies still send attachments normally. If a stale preview can no longer be reused safely, OpenClaw redacts it before sending the final media reply.
|
||||
- Preview edits cost extra Matrix API calls. Leave streaming off if you want the most conservative rate-limit behavior.
|
||||
|
||||
`blockStreaming` does not enable draft previews by itself.
|
||||
Use `streaming: "partial"` for preview edits; then add `blockStreaming: true` only if you also want completed assistant blocks to remain visible as separate progress messages.
|
||||
Use `streaming: "partial"` or `streaming: "quiet"` for preview edits; then add `blockStreaming: true` only if you also want completed assistant blocks to remain visible as separate progress messages.
|
||||
|
||||
If you need stock Matrix notifications without custom push rules, use `streaming: "partial"` for preview-first behavior or leave `streaming` off for final-only delivery. With `streaming: "off"`:
|
||||
|
||||
- `blockStreaming: true` sends each finished block as a normal notifying Matrix message.
|
||||
- `blockStreaming: false` sends only the final completed reply as a normal notifying Matrix message.
|
||||
|
||||
### Self-hosted push rules for quiet finalized previews
|
||||
|
||||
If you run your own Matrix infrastructure and want quiet previews to notify only when a block or
|
||||
final reply is done, set `streaming: "quiet"` and add a per-user push rule for finalized preview edits.
|
||||
|
||||
This is usually a recipient-user setup, not a homeserver-global config change:
|
||||
|
||||
Quick map before you start:
|
||||
|
||||
- recipient user = the person who should receive the notification
|
||||
- bot user = the OpenClaw Matrix account that sends the reply
|
||||
- use the recipient user's access token for the API calls below
|
||||
- match `sender` in the push rule against the bot user's full MXID
|
||||
|
||||
1. Configure OpenClaw to use quiet previews:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
matrix: {
|
||||
streaming: "quiet",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
2. Make sure the recipient account already receives normal Matrix push notifications. Quiet preview
|
||||
rules only work if that user already has working pushers/devices.
|
||||
|
||||
3. Get the recipient user's access token.
|
||||
- Use the receiving user's token, not the bot's token.
|
||||
- Reusing an existing client session token is usually easiest.
|
||||
- If you need to mint a fresh token, you can log in through the standard Matrix Client-Server API:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST \
|
||||
"https://matrix.example.org/_matrix/client/v3/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{
|
||||
"type": "m.login.password",
|
||||
"identifier": {
|
||||
"type": "m.id.user",
|
||||
"user": "@alice:example.org"
|
||||
},
|
||||
"password": "REDACTED"
|
||||
}'
|
||||
```
|
||||
|
||||
4. Verify the recipient account already has pushers:
|
||||
|
||||
```bash
|
||||
curl -sS \
|
||||
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
|
||||
"https://matrix.example.org/_matrix/client/v3/pushers"
|
||||
```
|
||||
|
||||
If this returns no active pushers/devices, fix normal Matrix notifications first before adding the
|
||||
OpenClaw rule below.
|
||||
|
||||
OpenClaw marks finalized text-only preview edits with:
|
||||
|
||||
```json
|
||||
{
|
||||
"com.openclaw.finalized_preview": true
|
||||
}
|
||||
```
|
||||
|
||||
5. Create an override push rule for each recipient account which should receive these notifications:
|
||||
|
||||
```bash
|
||||
curl -sS -X PUT \
|
||||
"https://matrix.example.org/_matrix/client/v3/pushrules/global/override/openclaw-finalized-preview" \
|
||||
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{
|
||||
"conditions": [
|
||||
{ "kind": "event_match", "key": "type", "pattern": "m.room.message" },
|
||||
{
|
||||
"kind": "event_property_is",
|
||||
"key": "content.m\\.relates_to.rel_type",
|
||||
"value": "m.replace"
|
||||
},
|
||||
{
|
||||
"kind": "event_property_is",
|
||||
"key": "content.com\\.openclaw\\.finalized_preview",
|
||||
"value": true
|
||||
},
|
||||
{ "kind": "event_match", "key": "sender", "pattern": "@bot:example.org" }
|
||||
],
|
||||
"actions": [
|
||||
"notify",
|
||||
{ "set_tweak": "sound", "value": "default" },
|
||||
{ "set_tweak": "highlight", "value": false }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
Replace these values before you run the command:
|
||||
|
||||
- `https://matrix.example.org`: your homeserver base URL
|
||||
- `$USER_ACCESS_TOKEN`: the receiving user's access token
|
||||
- `@bot:example.org`: your OpenClaw Matrix bot MXID, not the receiving user's MXID
|
||||
|
||||
The rule is evaluated against the event sender:
|
||||
|
||||
- authenticate with the receiving user's token
|
||||
- match `sender` against the OpenClaw bot MXID
|
||||
|
||||
6. Verify the rule exists:
|
||||
|
||||
```bash
|
||||
curl -sS \
|
||||
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
|
||||
"https://matrix.example.org/_matrix/client/v3/pushrules/global/override/openclaw-finalized-preview"
|
||||
```
|
||||
|
||||
7. Test a streamed reply. In quiet mode, the room should show a quiet draft preview and the final
|
||||
in-place edit should notify once the block or turn finishes.
|
||||
|
||||
Notes:
|
||||
|
||||
- Create the rule with the receiving user's access token, not the bot's.
|
||||
- New user-defined `override` rules are inserted ahead of default suppress rules, so no extra ordering parameter is needed.
|
||||
- This only affects text-only preview edits that OpenClaw can safely finalize in place. Media fallbacks and stale-preview fallbacks still use normal Matrix delivery.
|
||||
- If `GET /_matrix/client/v3/pushers` shows no pushers, the user does not yet have working Matrix push delivery for this account/device.
|
||||
|
||||
#### Synapse
|
||||
|
||||
For Synapse, the setup above is usually enough by itself:
|
||||
|
||||
- No special `homeserver.yaml` change is required for finalized OpenClaw preview notifications.
|
||||
- If your Synapse deployment already sends normal Matrix push notifications, the user token + `pushrules` call above is the main setup step.
|
||||
- If you run Synapse behind a reverse proxy or workers, make sure `/_matrix/client/.../pushrules/` reaches Synapse correctly.
|
||||
- If you run Synapse workers, make sure pushers are healthy. Push delivery is handled by the main process or `synapse.app.pusher` / configured pusher workers.
|
||||
|
||||
#### Tuwunel
|
||||
|
||||
For Tuwunel, use the same setup flow and push-rule API call shown above:
|
||||
|
||||
- No Tuwunel-specific config is required for the finalized preview marker itself.
|
||||
- If normal Matrix notifications already work for that user, the user token + `pushrules` call above is the main setup step.
|
||||
- If notifications seem to disappear while the user is active on another device, check whether `suppress_push_when_active` is enabled. Tuwunel added this option in Tuwunel 1.4.2 on September 12, 2025, and it can intentionally suppress pushes to other devices while one device is active.
|
||||
|
||||
## Encryption and verification
|
||||
|
||||
@@ -522,12 +672,17 @@ 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.
|
||||
|
||||
- `dm.sessionScope: "per-user"` (default) keeps Matrix DM routing sender-scoped, so multiple DM rooms can share one session when they resolve to the same peer.
|
||||
- `dm.sessionScope: "per-room"` isolates each Matrix DM room into its own session key while still using normal DM auth and allowlist checks.
|
||||
- Explicit Matrix conversation bindings still win over `dm.sessionScope`, so bound rooms and threads keep their chosen target session.
|
||||
- `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 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.
|
||||
- Same-session DM user-target reuse only kicks in when the current session metadata proves the same DM peer on the same Matrix account; otherwise OpenClaw falls back to normal user-scoped routing.
|
||||
- When OpenClaw sees a Matrix DM room collide with another DM room on the same shared Matrix DM session, it posts a one-time `m.notice` in that room with the `/focus` escape hatch when thread bindings are enabled and the `dm.sessionScope` hint.
|
||||
- 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.
|
||||
- Top-level Matrix room/DM `/focus` creates a new Matrix thread and binds it to the target session when `threadBindings.spawnSubagentSessions=true`.
|
||||
- Running `/focus` or `/acp spawn --thread here` inside an existing Matrix thread binds that current thread instead.
|
||||
@@ -679,7 +834,13 @@ Delivery rules:
|
||||
- `target: "channel"` sends the prompt back to the originating Matrix room or DM
|
||||
- `target: "both"` sends to approver DMs and the originating Matrix room or DM
|
||||
|
||||
Matrix uses text approval prompts today. Approvers resolve them with `/approve <id> allow-once`, `/approve <id> allow-always`, or `/approve <id> deny`.
|
||||
Matrix approval prompts seed reaction shortcuts on the primary approval message:
|
||||
|
||||
- `✅` = allow once
|
||||
- `❌` = deny
|
||||
- `♾️` = allow always when that decision is allowed by the effective exec policy
|
||||
|
||||
Approvers can react on that message or use the fallback slash commands: `/approve <id> allow-once`, `/approve <id> allow-always`, or `/approve <id> deny`.
|
||||
|
||||
Only resolved approvers can approve or deny. Channel delivery includes the command text, so only enable `channel` or `both` in trusted rooms.
|
||||
|
||||
@@ -821,7 +982,7 @@ Live directory lookup uses the logged-in Matrix account:
|
||||
- `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`.
|
||||
- `markdown`: optional Markdown rendering configuration for outbound Matrix text.
|
||||
- `streaming`: `off` (default), `partial`, `true`, or `false`. `partial` and `true` enable single-message draft previews with edit-in-place updates.
|
||||
- `streaming`: `off` (default), `partial`, `quiet`, `true`, or `false`. `partial` and `true` enable preview-first draft updates with normal Matrix text messages. `quiet` uses non-notifying preview notices for self-hosted push-rule setups.
|
||||
- `blockStreaming`: `true` enables separate progress messages for completed assistant blocks while draft preview streaming is active.
|
||||
- `threadReplies`: `off`, `inbound`, or `always`.
|
||||
- `threadBindings`: per-channel overrides for thread-bound session routing and lifecycle.
|
||||
@@ -836,8 +997,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`, `threadReplies`).
|
||||
- `dm`: DM policy block (`enabled`, `policy`, `allowFrom`, `sessionScope`, `threadReplies`).
|
||||
- `dm.allowFrom` entries should be full Matrix user IDs unless you already resolved them through live directory lookup.
|
||||
- `dm.sessionScope`: `per-user` (default) or `per-room`. Use `per-room` when you want each Matrix DM room to keep separate context even if the peer is the same.
|
||||
- `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.
|
||||
- `execApprovals`: Matrix-native exec approval delivery (`enabled`, `approvers`, `target`, `agentFilter`, `sessionFilter`).
|
||||
- `execApprovals.approvers`: Matrix user IDs allowed to approve exec requests. Optional when `dm.allowFrom` already identifies the approvers.
|
||||
|
||||
@@ -79,6 +79,12 @@ pnpm qa:lab:build
|
||||
pnpm openclaw qa ui
|
||||
```
|
||||
|
||||
Full repo-backed QA suite:
|
||||
|
||||
```bash
|
||||
pnpm openclaw qa suite
|
||||
```
|
||||
|
||||
That launches the private QA debugger at a local URL, separate from the
|
||||
shipped Control UI bundle.
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ Current Slack message actions include `send`, `upload-file`, `download-file`, `r
|
||||
|
||||
Reply threading controls:
|
||||
|
||||
- `channels.slack.replyToMode`: `off|first|all` (default `off`)
|
||||
- `channels.slack.replyToMode`: `off|first|all|batched` (default `off`)
|
||||
- `channels.slack.replyToModeByChatType`: per `direct|group|channel`
|
||||
- legacy fallback for direct chats: `channels.slack.dm.replyToMode`
|
||||
|
||||
|
||||
@@ -57,8 +57,6 @@ Notes:
|
||||
- `--reset`: reset dev config + credentials + sessions + workspace (requires `--dev`).
|
||||
- `--force`: kill any existing listener on the selected port before starting.
|
||||
- `--verbose`: verbose logs.
|
||||
- `--cli-backend-logs`: only show CLI backend logs in the console (and enable stdout/stderr).
|
||||
- `--claude-cli-logs`: deprecated alias for `--cli-backend-logs`.
|
||||
- `--ws-log <auto|full|compact>`: websocket log style (default `auto`).
|
||||
- `--compact`: alias for `--ws-log compact`.
|
||||
- `--raw-stream`: log raw model stream events to jsonl.
|
||||
|
||||
@@ -501,7 +501,7 @@ Options:
|
||||
`openrouter-api-key`, `kilocode-api-key`, `litellm-api-key`, `ai-gateway-api-key`,
|
||||
`cloudflare-ai-gateway-api-key`, `moonshot-api-key`, `moonshot-api-key-cn`,
|
||||
`kimi-code-api-key`, `synthetic-api-key`, `venice-api-key`, `together-api-key`,
|
||||
`huggingface-api-key`, `apiKey`, `gemini-api-key`, `google-gemini-cli`, `zai-api-key`,
|
||||
`huggingface-api-key`, `apiKey`, `gemini-api-key`, `zai-api-key`,
|
||||
`zai-coding-global`, `zai-coding-cn`, `zai-global`, `zai-cn`, `xiaomi-api-key`,
|
||||
`minimax-global-oauth`, `minimax-global-api`, `minimax-cn-oauth`, `minimax-cn-api`,
|
||||
`opencode-zen`, `opencode-go`, `github-copilot`, `copilot-proxy`, `xai-api-key`,
|
||||
@@ -1319,7 +1319,7 @@ List and manage [background task](/automation/tasks) runs across agents.
|
||||
- `tasks notify <id>` — change notification policy for a task run
|
||||
- `tasks cancel <id>` — cancel a running task
|
||||
- `tasks audit` — surface operational issues (stale, lost, delivery failures)
|
||||
- `tasks maintenance` — preview or apply tasks and TaskFlow cleanup/reconciliation (ACP/subagent child sessions, active cron jobs, live CLI runs)
|
||||
- `tasks maintenance [--apply] [--json]` — preview or apply tasks and TaskFlow cleanup/reconciliation (ACP/subagent child sessions, active cron jobs, live CLI runs)
|
||||
- `tasks flow list` — list active and recent Task Flow flows
|
||||
- `tasks flow show <lookup>` — inspect a flow by id or lookup key
|
||||
- `tasks flow cancel <lookup>` — cancel a running flow and its active tasks
|
||||
@@ -1353,8 +1353,6 @@ Options:
|
||||
- `--reset` (reset dev config + credentials + sessions + workspace)
|
||||
- `--force` (kill existing listener on port)
|
||||
- `--verbose`
|
||||
- `--cli-backend-logs`
|
||||
- `--claude-cli-logs` (deprecated alias)
|
||||
- `--ws-log <auto|full|compact>`
|
||||
- `--compact` (alias for `--ws-log compact`)
|
||||
- `--raw-stream`
|
||||
@@ -1479,29 +1477,20 @@ Tip: the owner-only `gateway` runtime tool still refuses to rewrite `tools.exec.
|
||||
|
||||
See [/concepts/models](/concepts/models) for fallback behavior and scanning strategy.
|
||||
|
||||
Billing note: Anthropic's public Claude Code docs still include direct Claude
|
||||
Code terminal usage in Claude plan limits. Separately, Anthropic notified
|
||||
OpenClaw users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the
|
||||
**OpenClaw** Claude-login path counts as third-party harness usage and
|
||||
requires **Extra Usage** billed separately from the subscription. For
|
||||
production, prefer an Anthropic API key or another supported
|
||||
subscription-style provider such as OpenAI Codex, Alibaba Cloud Model Studio
|
||||
Coding Plan, MiniMax Coding Plan, or Z.AI / GLM Coding Plan.
|
||||
Billing note: for Anthropic in OpenClaw, the practical split is **API key** or
|
||||
**Claude subscription with Extra Usage**. Anthropic notified OpenClaw users on
|
||||
**April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the **OpenClaw**
|
||||
Claude-login path counts as third-party harness usage and requires
|
||||
**Extra Usage** billed separately from the subscription. Our local repros also
|
||||
show the OpenClaw-identifying prompt string does not reproduce on the
|
||||
Anthropic SDK + API-key path. For production, prefer an Anthropic API key or
|
||||
another supported subscription-style provider such as OpenAI Codex, Alibaba
|
||||
Cloud Model Studio Coding Plan, MiniMax Coding Plan, or Z.AI / GLM Coding
|
||||
Plan.
|
||||
|
||||
Anthropic Claude CLI migration:
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider anthropic --method cli --set-default
|
||||
```
|
||||
|
||||
Onboarding shortcut: `openclaw onboard --auth-choice anthropic-cli`
|
||||
|
||||
Anthropic setup-token is also available again as a legacy/manual auth path.
|
||||
Anthropic setup-token is available again as a legacy/manual auth path.
|
||||
Use it only with the expectation that Anthropic told OpenClaw users the
|
||||
OpenClaw Claude-login path requires **Extra Usage**.
|
||||
|
||||
Legacy alias note: `claude-cli` is the deprecated onboarding auth-choice alias.
|
||||
Use `anthropic-cli` for onboarding, or use `models auth login` directly.
|
||||
OpenClaw-managed Anthropic subscription path requires **Extra Usage**.
|
||||
|
||||
### `models` (root)
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ Full options:
|
||||
|
||||
- Ranks short-term candidates from `memory/YYYY-MM-DD.md` using weighted recall signals (`frequency`, `relevance`, `query diversity`, `recency`).
|
||||
- Uses recall events captured when `memory_search` returns daily-memory hits.
|
||||
- Optional auto-dreaming mode: when `plugins.entries.memory-core.config.dreaming.mode` is `core`, `deep`, or `rem`, `memory-core` auto-manages a cron job that triggers promotion in the background (no manual `openclaw cron add` required).
|
||||
- When dreaming is enabled, `memory-core` auto-manages a cron job for the deep phase that triggers promotion in the background (no manual `openclaw cron add` required).
|
||||
- `--agent <id>`: scope to a single agent (default: the default agent).
|
||||
- `--limit <n>`: max candidates to return/apply.
|
||||
- `--min-score <n>`: minimum weighted promotion score.
|
||||
@@ -92,21 +92,25 @@ Full options:
|
||||
|
||||
## Dreaming (experimental)
|
||||
|
||||
Dreaming is the overnight reflection pass for memory. It is called "dreaming" because the system revisits what was recalled during the day and decides what is worth keeping long-term.
|
||||
Dreaming is the background memory consolidation system with three cooperative
|
||||
phases: **light** (organize into `DREAMS.md` in inline mode), **deep**
|
||||
(promote into `MEMORY.md`), and **REM** (reflect and find patterns in
|
||||
`DREAMS.md` in inline mode).
|
||||
|
||||
- It is opt-in and disabled by default.
|
||||
- Enable it with `plugins.entries.memory-core.config.dreaming.mode`.
|
||||
- You can toggle modes from chat with `/dreaming off|core|rem|deep`. Run `/dreaming` (or `/dreaming options`) to see what each mode does.
|
||||
- When enabled, `memory-core` automatically creates and maintains a managed cron job.
|
||||
- Set `dreaming.limit` to `0` if you want dreaming enabled but automatic promotion effectively paused.
|
||||
- Ranking uses weighted signals: recall frequency, retrieval relevance, query diversity, and temporal recency (recent recalls decay over time).
|
||||
- Promotion into `MEMORY.md` only happens when quality thresholds are met, so long-term memory stays high signal instead of collecting one-off details.
|
||||
- Enable with `plugins.entries.memory-core.config.dreaming.enabled: true`.
|
||||
- Toggle from chat with `/dreaming on|off` or `/dreaming enable|disable light|deep|rem`.
|
||||
- Each phase runs on its own cron schedule, managed automatically by `memory-core`.
|
||||
- Only the deep phase writes durable memory to `MEMORY.md`. With default inline storage, Light and REM write to `DREAMS.md`.
|
||||
- Ranking uses weighted signals: recall frequency, retrieval relevance, query diversity, temporal recency, cross-day consolidation, and derived concept richness.
|
||||
- Promotion re-reads the live daily note before writing to `MEMORY.md`, so edited or deleted short-term snippets do not get promoted from stale recall-store snapshots.
|
||||
- Scheduled and manual `memory promote` runs share the same deep phase defaults unless you pass CLI threshold overrides.
|
||||
- Automatic runs fan out across configured memory workspaces.
|
||||
|
||||
Default mode presets:
|
||||
Default phase schedules:
|
||||
|
||||
- `core`: daily at `0 3 * * *`, `minScore=0.75`, `minRecallCount=3`, `minUniqueQueries=2`
|
||||
- `deep`: every 12 hours (`0 */12 * * *`), `minScore=0.8`, `minRecallCount=3`, `minUniqueQueries=3`
|
||||
- `rem`: every 6 hours (`0 */6 * * *`), `minScore=0.85`, `minRecallCount=4`, `minUniqueQueries=3`
|
||||
- **Light**: every 6 hours (`0 */6 * * *`), `lookbackDays=2`, `limit=100`
|
||||
- **Deep**: daily at 3 AM (`0 3 * * *`), `limit=10`, `minScore=0.8`, `minRecallCount=3`, `minUniqueQueries=3`, `recencyHalfLifeDays=14`
|
||||
- **REM**: weekly, Sunday 5 AM (`0 5 * * 0`), `lookbackDays=7`, `limit=10`
|
||||
|
||||
Example:
|
||||
|
||||
@@ -117,7 +121,7 @@ Example:
|
||||
"memory-core": {
|
||||
"config": {
|
||||
"dreaming": {
|
||||
"mode": "core"
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,4 +136,6 @@ Notes:
|
||||
- `memory status` includes any extra paths configured via `memorySearch.extraPaths`.
|
||||
- If effectively active memory remote API key fields are configured as SecretRefs, the command resolves those values from the active gateway snapshot. If gateway is unavailable, the command fails fast.
|
||||
- Gateway version skew note: this command path requires a gateway that supports `secrets.resolve`; older gateways return an unknown-method error.
|
||||
- Dreaming cadence defaults to each mode's preset schedule. Override cadence with `plugins.entries.memory-core.config.dreaming.frequency` as a cron expression (for example `0 3 * * *`) and fine-tune with `timezone`, `limit`, `minScore`, `minRecallCount`, and `minUniqueQueries`.
|
||||
- Override each phase schedule with `phases.<phase>.cron` and fine-tune deep promotion with `phases.deep.minScore`, `phases.deep.minRecallCount`, `phases.deep.minUniqueQueries`, `phases.deep.recencyHalfLifeDays`, and `phases.deep.maxAgeDays`.
|
||||
- Set `plugins.entries.memory-core.config.dreaming.verboseLogging` to `true` to emit per-run candidate and apply details into the normal gateway logs while tuning the feature.
|
||||
- See [Dreaming](/concepts/dreaming) for full phase descriptions and configuration reference.
|
||||
|
||||
@@ -31,6 +31,8 @@ Current usage-window providers: Anthropic, GitHub Copilot, Gemini CLI, OpenAI
|
||||
Codex, MiniMax, Xiaomi, and z.ai. Usage auth comes from provider-specific hooks
|
||||
when available; otherwise OpenClaw falls back to matching OAuth/API-key
|
||||
credentials from auth profiles, env, or config.
|
||||
In `--json` output, `auth.providers` is the env/config/store-aware provider
|
||||
overview, while `auth.oauth` is auth-store profile health only.
|
||||
Add `--probe` to run live auth probes against each configured provider profile.
|
||||
Probes are real requests (may consume tokens and trigger rate limits).
|
||||
Use `--agent <id>` to inspect a configured agent’s model/auth state. When omitted,
|
||||
@@ -112,15 +114,11 @@ provider you choose.
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider anthropic --method cli --set-default
|
||||
openclaw models auth login --provider openai-codex --set-default
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `login --provider anthropic --method cli --set-default` reuses a local Claude
|
||||
CLI login and rewrites the main Anthropic default-model path to a canonical
|
||||
`claude-cli/claude-*` ref.
|
||||
- `setup-token` and `paste-token` remain generic token commands for providers
|
||||
that expose token auth methods.
|
||||
- `setup-token` requires an interactive TTY and runs the provider's token-auth
|
||||
@@ -132,5 +130,5 @@ Notes:
|
||||
`--profile-id`.
|
||||
- `paste-token --expires-in <duration>` stores an absolute token expiry from a
|
||||
relative duration such as `365d` or `12h`.
|
||||
- Anthropic billing note: Anthropic's public Claude Code docs still include direct Claude Code terminal usage in Claude plan limits. Separately, Anthropic notified OpenClaw users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the **OpenClaw** Claude-login path counts as third-party harness usage and requires **Extra Usage** billed separately from the subscription.
|
||||
- Anthropic billing note: for Anthropic in OpenClaw, the practical split is **API key** or **Claude subscription with Extra Usage**. Anthropic notified OpenClaw users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the **OpenClaw** Claude-login path counts as third-party harness usage and requires **Extra Usage** billed separately from the subscription. Our local repros also show the OpenClaw-identifying prompt string does not reproduce on the Anthropic SDK + API-key path.
|
||||
- Anthropic `setup-token` / `paste-token` are available again as a legacy/manual OpenClaw path. Use them with the expectation that Anthropic told OpenClaw users this path requires **Extra Usage**.
|
||||
|
||||
@@ -167,7 +167,7 @@ pluggable interface, lifecycle hooks, and configuration.
|
||||
`/context` prefers the latest **run-built** system prompt report when available:
|
||||
|
||||
- `System prompt (run)` = captured from the last embedded (tool-capable) run and persisted in the session store.
|
||||
- `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).
|
||||
- `System prompt (estimate)` = computed on the fly when no run report exists yet.
|
||||
|
||||
Either way, it reports sizes and top contributors; it does **not** dump the full system prompt or tool schemas.
|
||||
|
||||
|
||||
289
docs/concepts/dreaming.md
Normal file
289
docs/concepts/dreaming.md
Normal file
@@ -0,0 +1,289 @@
|
||||
---
|
||||
title: "Dreaming (experimental)"
|
||||
summary: "Background memory consolidation with three cooperative phases: light, deep, and REM"
|
||||
read_when:
|
||||
- You want memory promotion to run automatically
|
||||
- You want to understand the three dreaming phases
|
||||
- You want to tune consolidation without polluting MEMORY.md
|
||||
---
|
||||
|
||||
# Dreaming (experimental)
|
||||
|
||||
Dreaming is the background memory consolidation system in `memory-core`. It
|
||||
revisits what came up during conversations and decides what is worth keeping as
|
||||
durable context.
|
||||
|
||||
Dreaming uses three cooperative **phases**, not competing modes. Each phase has
|
||||
a distinct job, writes to a distinct target, and runs on its own schedule.
|
||||
|
||||
## The three phases
|
||||
|
||||
### Light
|
||||
|
||||
Light dreaming sorts the recent mess. It scans recent memory traces, dedupes
|
||||
them by Jaccard similarity, clusters related entries, and stages candidate
|
||||
memories into the shared dreaming trail file (`DREAMS.md`) when inline storage
|
||||
is enabled.
|
||||
|
||||
Light does **not** write anything into `MEMORY.md`. It only organizes and
|
||||
stages. Think: "what from today might matter later?"
|
||||
|
||||
### Deep
|
||||
|
||||
Deep dreaming decides what becomes durable memory. It runs the real promotion
|
||||
logic: weighted scoring across six signals, threshold gates, recall count,
|
||||
unique query diversity, recency decay, and max age filtering.
|
||||
|
||||
Deep is the **only** phase allowed to write durable facts into `MEMORY.md`.
|
||||
It also owns recovery when memory is thin (health drops below a configured
|
||||
threshold). Think: "what is true enough to keep?"
|
||||
|
||||
### REM
|
||||
|
||||
REM dreaming looks for patterns and reflection. It examines recent material,
|
||||
identifies recurring themes through concept tag clustering, and writes
|
||||
higher-order notes and reflections into `DREAMS.md` when inline storage is
|
||||
enabled.
|
||||
|
||||
REM writes to `DREAMS.md` in inline mode, **not** `MEMORY.md`.
|
||||
Its output is interpretive, not canonical. Think: "what pattern am I noticing?"
|
||||
|
||||
## Hard boundaries
|
||||
|
||||
| Phase | Job | Writes to | Does NOT write to |
|
||||
| ----- | --------- | ------------------------- | ----------------- |
|
||||
| Light | Organize | `DREAMS.md` (inline mode) | MEMORY.md |
|
||||
| Deep | Preserve | MEMORY.md | -- |
|
||||
| REM | Interpret | `DREAMS.md` (inline mode) | MEMORY.md |
|
||||
|
||||
## Quick start
|
||||
|
||||
Enable all three phases (recommended):
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"entries": {
|
||||
"memory-core": {
|
||||
"config": {
|
||||
"dreaming": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Enable only deep promotion:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"entries": {
|
||||
"memory-core": {
|
||||
"config": {
|
||||
"dreaming": {
|
||||
"enabled": true,
|
||||
"phases": {
|
||||
"light": { "enabled": false },
|
||||
"deep": { "enabled": true },
|
||||
"rem": { "enabled": false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
All dreaming settings live under `plugins.entries.memory-core.config.dreaming`
|
||||
in `openclaw.json`. See [Memory configuration reference](/reference/memory-config#dreaming-experimental)
|
||||
for the full key list.
|
||||
|
||||
### Global settings
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ---------------- | --------- | ---------- | ------------------------------------------------------------ |
|
||||
| `enabled` | `boolean` | `true` | Master switch for all phases |
|
||||
| `timezone` | `string` | unset | Timezone for schedule evaluation and dreaming date bucketing |
|
||||
| `verboseLogging` | `boolean` | `false` | Emit detailed per-run dreaming logs |
|
||||
| `storage.mode` | `string` | `"inline"` | Inline `DREAMS.md`, separate reports, or both |
|
||||
|
||||
### Light phase config
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------ | ---------- | ------------------------------- | --------------------------------- |
|
||||
| `enabled` | `boolean` | `true` | Enable light phase |
|
||||
| `cron` | `string` | `0 */6 * * *` | Schedule (default: every 6 hours) |
|
||||
| `lookbackDays` | `number` | `2` | How many days of traces to scan |
|
||||
| `limit` | `number` | `100` | Max candidates to stage per run |
|
||||
| `dedupeSimilarity` | `number` | `0.9` | Jaccard threshold for dedup |
|
||||
| `sources` | `string[]` | `["daily","sessions","recall"]` | Data sources to scan |
|
||||
|
||||
### Deep phase config
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| --------------------- | ---------- | ----------------------------------------------- | ------------------------------------ |
|
||||
| `enabled` | `boolean` | `true` | Enable deep phase |
|
||||
| `cron` | `string` | `0 3 * * *` | Schedule (default: daily at 3 AM) |
|
||||
| `limit` | `number` | `10` | Max candidates to promote per cycle |
|
||||
| `minScore` | `number` | `0.8` | Minimum weighted score for promotion |
|
||||
| `minRecallCount` | `number` | `3` | Minimum recall count threshold |
|
||||
| `minUniqueQueries` | `number` | `3` | Minimum distinct query count |
|
||||
| `recencyHalfLifeDays` | `number` | `14` | Days for recency score to halve |
|
||||
| `maxAgeDays` | `number` | `30` | Max daily-note age for promotion |
|
||||
| `sources` | `string[]` | `["daily","memory","sessions","logs","recall"]` | Data sources |
|
||||
|
||||
### Deep recovery config
|
||||
|
||||
Recovery kicks in when long-term memory health drops below a threshold.
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| --------------------------------- | --------- | ------- | ------------------------------------------ |
|
||||
| `recovery.enabled` | `boolean` | `true` | Enable automatic recovery |
|
||||
| `recovery.triggerBelowHealth` | `number` | `0.35` | Health score threshold to trigger recovery |
|
||||
| `recovery.lookbackDays` | `number` | `30` | How far back to look for recovery material |
|
||||
| `recovery.maxRecoveredCandidates` | `number` | `20` | Max candidates to recover per run |
|
||||
| `recovery.minRecoveryConfidence` | `number` | `0.9` | Minimum confidence for recovery candidates |
|
||||
| `recovery.autoWriteMinConfidence` | `number` | `0.97` | Auto-write threshold (skip manual review) |
|
||||
|
||||
### REM phase config
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| -------------------- | ---------- | --------------------------- | --------------------------------------- |
|
||||
| `enabled` | `boolean` | `true` | Enable REM phase |
|
||||
| `cron` | `string` | `0 5 * * 0` | Schedule (default: weekly, Sunday 5 AM) |
|
||||
| `lookbackDays` | `number` | `7` | How many days of material to reflect on |
|
||||
| `limit` | `number` | `10` | Max patterns or themes to write |
|
||||
| `minPatternStrength` | `number` | `0.75` | Minimum tag co-occurrence strength |
|
||||
| `sources` | `string[]` | `["memory","daily","deep"]` | Data sources for reflection |
|
||||
|
||||
### Execution overrides
|
||||
|
||||
Each phase accepts an `execution` block to override global defaults:
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ----------------- | -------- | ------------ | ------------------------------ |
|
||||
| `speed` | `string` | `"balanced"` | `fast`, `balanced`, or `slow` |
|
||||
| `thinking` | `string` | `"medium"` | `low`, `medium`, or `high` |
|
||||
| `budget` | `string` | `"medium"` | `cheap`, `medium`, `expensive` |
|
||||
| `model` | `string` | unset | Override model for this phase |
|
||||
| `maxOutputTokens` | `number` | unset | Cap output tokens |
|
||||
| `temperature` | `number` | unset | Sampling temperature (0-2) |
|
||||
| `timeoutMs` | `number` | unset | Phase timeout in milliseconds |
|
||||
|
||||
## Promotion signals (deep phase)
|
||||
|
||||
Deep dreaming combines six weighted signals. Promotion requires all configured
|
||||
threshold gates to pass simultaneously.
|
||||
|
||||
| Signal | Weight | Description |
|
||||
| ------------------- | ------ | -------------------------------------------------- |
|
||||
| Frequency | 0.24 | How often the same entry was recalled |
|
||||
| Relevance | 0.30 | Average recall scores when retrieved |
|
||||
| Query diversity | 0.15 | Count of distinct query intents that surfaced it |
|
||||
| Recency | 0.15 | Temporal decay (`recencyHalfLifeDays`, default 14) |
|
||||
| Consolidation | 0.10 | Reward recalls repeated across multiple days |
|
||||
| Conceptual richness | 0.06 | Reward entries with richer derived concept tags |
|
||||
|
||||
## Chat commands
|
||||
|
||||
```
|
||||
/dreaming status # Show phase config and cadence
|
||||
/dreaming on # Enable all phases
|
||||
/dreaming off # Disable all phases
|
||||
/dreaming enable light|deep|rem # Enable a specific phase
|
||||
/dreaming disable light|deep|rem # Disable a specific phase
|
||||
/dreaming help # Show usage guide
|
||||
```
|
||||
|
||||
## CLI commands
|
||||
|
||||
Preview and apply deep promotions from the command line:
|
||||
|
||||
```bash
|
||||
# Preview promotion candidates
|
||||
openclaw memory promote
|
||||
|
||||
# Apply promotions to MEMORY.md
|
||||
openclaw memory promote --apply
|
||||
|
||||
# Limit preview count
|
||||
openclaw memory promote --limit 5
|
||||
|
||||
# Include already-promoted entries
|
||||
openclaw memory promote --include-promoted
|
||||
|
||||
# Check dreaming status
|
||||
openclaw memory status --deep
|
||||
```
|
||||
|
||||
See [memory CLI](/cli/memory) for the full flag reference.
|
||||
|
||||
## How it works
|
||||
|
||||
### Light phase pipeline
|
||||
|
||||
1. Read short-term recall entries from `memory/.dreams/short-term-recall.json`.
|
||||
2. Filter entries within `lookbackDays` of the current time.
|
||||
3. Deduplicate by Jaccard similarity (configurable threshold).
|
||||
4. Sort by average recall score, take up to `limit` entries.
|
||||
5. Write staged candidates into `DREAMS.md` under a `## Light Sleep` block when
|
||||
inline storage is enabled.
|
||||
|
||||
### Deep phase pipeline
|
||||
|
||||
1. Read and rank short-term recall candidates using weighted signals.
|
||||
2. Apply threshold gates: `minScore`, `minRecallCount`, `minUniqueQueries`.
|
||||
3. Filter by `maxAgeDays` and apply recency decay.
|
||||
4. Fan out across configured memory workspaces.
|
||||
5. Re-read the live daily note before writing (skip stale or deleted snippets).
|
||||
6. Append qualifying entries to `MEMORY.md` with promoted timestamps.
|
||||
7. Mark promoted entries to exclude them from future cycles.
|
||||
8. If health is below `recovery.triggerBelowHealth`, run the recovery pass.
|
||||
|
||||
### REM phase pipeline
|
||||
|
||||
1. Read recent memory traces within `lookbackDays`.
|
||||
2. Cluster concept tags by co-occurrence.
|
||||
3. Filter patterns by `minPatternStrength`.
|
||||
4. Write themes and reflections into `DREAMS.md` under a `## REM Sleep` block
|
||||
when inline storage is enabled.
|
||||
|
||||
## Scheduling
|
||||
|
||||
Each phase manages its own cron job automatically. When dreaming is enabled,
|
||||
`memory-core` reconciles managed cron jobs on gateway startup. You do not need
|
||||
to manually create cron entries.
|
||||
|
||||
| Phase | Default schedule | Description |
|
||||
| ----- | ---------------- | ------------------- |
|
||||
| Light | `0 */6 * * *` | Every 6 hours |
|
||||
| Deep | `0 3 * * *` | Daily at 3 AM |
|
||||
| REM | `0 5 * * 0` | Weekly, Sunday 5 AM |
|
||||
|
||||
Override any schedule with the phase `cron` key. All schedules honor the global
|
||||
`timezone` setting.
|
||||
|
||||
## Dreams UI
|
||||
|
||||
When dreaming is enabled, the Gateway sidebar shows a **Dreams** tab with
|
||||
memory stats (short-term count, long-term count, promoted count) and the next
|
||||
scheduled cycle time. Daily counters honor `dreaming.timezone` when set and
|
||||
otherwise fall back to the configured user timezone.
|
||||
|
||||
Manual `openclaw memory promote` runs use the same deep phase thresholds by
|
||||
default, so scheduled and on-demand promotion stay aligned unless you pass CLI
|
||||
overrides.
|
||||
|
||||
## Related
|
||||
|
||||
- [Memory](/concepts/memory)
|
||||
- [Memory Search](/concepts/memory-search)
|
||||
- [Memory configuration reference](/reference/memory-config)
|
||||
- [memory CLI](/cli/memory)
|
||||
@@ -1,151 +0,0 @@
|
||||
---
|
||||
title: "Dreaming (experimental)"
|
||||
summary: "Background promotion from short-term recall into long-term memory"
|
||||
read_when:
|
||||
- You want memory promotion to run automatically
|
||||
- You want to understand dreaming modes and thresholds
|
||||
- You want to tune consolidation without polluting MEMORY.md
|
||||
---
|
||||
|
||||
# Dreaming (experimental)
|
||||
|
||||
Dreaming is the background memory consolidation pass in `memory-core`.
|
||||
|
||||
It is called "dreaming" because the system revisits what came up during the day
|
||||
and decides what is worth keeping as durable context.
|
||||
|
||||
Dreaming is **experimental**, **opt-in**, and **off by default**.
|
||||
|
||||
## What dreaming does
|
||||
|
||||
1. Tracks short-term recall events from `memory_search` hits in
|
||||
`memory/YYYY-MM-DD.md`.
|
||||
2. Scores those recall candidates with weighted signals.
|
||||
3. Promotes only qualified candidates into `MEMORY.md`.
|
||||
|
||||
This keeps long-term memory focused on durable, repeated context instead of
|
||||
one-off details.
|
||||
|
||||
## Promotion signals
|
||||
|
||||
Dreaming combines four signals:
|
||||
|
||||
- **Frequency**: how often the same candidate was recalled.
|
||||
- **Relevance**: how strong recall scores were when it was retrieved.
|
||||
- **Query diversity**: how many distinct query intents surfaced it.
|
||||
- **Recency**: temporal weighting over recent recalls.
|
||||
|
||||
Promotion requires all configured threshold gates to pass, not just one signal.
|
||||
|
||||
### Signal weights
|
||||
|
||||
| Signal | Weight | Description |
|
||||
| --------- | ------ | ------------------------------------------------ |
|
||||
| Frequency | 0.35 | How often the same entry was recalled |
|
||||
| Relevance | 0.35 | Average recall scores when retrieved |
|
||||
| Diversity | 0.15 | Count of distinct query intents that surfaced it |
|
||||
| Recency | 0.15 | Temporal decay (14-day half-life) |
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Recall tracking** -- Every `memory_search` hit is recorded to
|
||||
`memory/.dreams/short-term-recall.json` with recall count, scores, and query
|
||||
hash.
|
||||
2. **Scheduled scoring** -- On the configured cadence, candidates are ranked
|
||||
using weighted signals. All threshold gates must pass simultaneously.
|
||||
3. **Promotion** -- Qualifying entries are appended to `MEMORY.md` with a
|
||||
promoted timestamp.
|
||||
4. **Cleanup** -- Already-promoted entries are filtered from future cycles. A
|
||||
file lock prevents concurrent runs.
|
||||
|
||||
## Modes
|
||||
|
||||
`dreaming.mode` controls cadence and default thresholds:
|
||||
|
||||
| Mode | Cadence | minScore | minRecallCount | minUniqueQueries |
|
||||
| ------ | -------------- | -------- | -------------- | ---------------- |
|
||||
| `off` | Disabled | -- | -- | -- |
|
||||
| `core` | Daily 3 AM | 0.75 | 3 | 2 |
|
||||
| `rem` | Every 6 hours | 0.85 | 4 | 3 |
|
||||
| `deep` | Every 12 hours | 0.80 | 3 | 3 |
|
||||
|
||||
## Scheduling model
|
||||
|
||||
When dreaming is enabled, `memory-core` manages the recurring schedule
|
||||
automatically. You do not need to manually create a cron job for this feature.
|
||||
|
||||
You can still tune behavior with explicit overrides such as:
|
||||
|
||||
- `dreaming.frequency` (cron expression)
|
||||
- `dreaming.timezone`
|
||||
- `dreaming.limit`
|
||||
- `dreaming.minScore`
|
||||
- `dreaming.minRecallCount`
|
||||
- `dreaming.minUniqueQueries`
|
||||
|
||||
## Configure
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"entries": {
|
||||
"memory-core": {
|
||||
"config": {
|
||||
"dreaming": {
|
||||
"mode": "core"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Chat commands
|
||||
|
||||
Switch modes and check status from chat:
|
||||
|
||||
```
|
||||
/dreaming core # Switch to core mode (nightly)
|
||||
/dreaming rem # Switch to rem mode (every 6h)
|
||||
/dreaming deep # Switch to deep mode (every 12h)
|
||||
/dreaming off # Disable dreaming
|
||||
/dreaming status # Show current config and cadence
|
||||
/dreaming help # Show mode guide
|
||||
```
|
||||
|
||||
## CLI commands
|
||||
|
||||
Preview and apply promotions from the command line:
|
||||
|
||||
```bash
|
||||
# Preview promotion candidates
|
||||
openclaw memory promote
|
||||
|
||||
# Apply promotions to MEMORY.md
|
||||
openclaw memory promote --apply
|
||||
|
||||
# Limit preview count
|
||||
openclaw memory promote --limit 5
|
||||
|
||||
# Include already-promoted entries
|
||||
openclaw memory promote --include-promoted
|
||||
|
||||
# Check dreaming status
|
||||
openclaw memory status --deep
|
||||
```
|
||||
|
||||
See [memory CLI](/cli/memory) for the full flag reference.
|
||||
|
||||
## Dreams UI
|
||||
|
||||
When dreaming is enabled, the Gateway sidebar shows a **Dreams** tab with
|
||||
memory stats (short-term count, long-term count, promoted count) and the next
|
||||
scheduled cycle time.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Memory](/concepts/memory)
|
||||
- [Memory Search](/concepts/memory-search)
|
||||
- [memory CLI](/cli/memory)
|
||||
- [Memory configuration reference](/reference/memory-config)
|
||||
@@ -98,7 +98,7 @@ It is designed to keep long-term memory high signal:
|
||||
diversity gates.
|
||||
|
||||
For mode behavior (`off`, `core`, `rem`, `deep`), scoring signals, and tuning
|
||||
knobs, see [Dreaming (experimental)](/concepts/memory-dreaming).
|
||||
knobs, see [Dreaming (experimental)](/concepts/dreaming).
|
||||
|
||||
## CLI
|
||||
|
||||
@@ -115,7 +115,7 @@ openclaw memory index --force # Rebuild the index
|
||||
- [Honcho Memory](/concepts/memory-honcho) -- AI-native cross-session memory
|
||||
- [Memory Search](/concepts/memory-search) -- search pipeline, providers, and
|
||||
tuning
|
||||
- [Dreaming (experimental)](/concepts/memory-dreaming) -- background promotion
|
||||
- [Dreaming (experimental)](/concepts/dreaming) -- background promotion
|
||||
from short-term recall to long-term memory
|
||||
- [Memory configuration reference](/reference/memory-config) -- all config knobs
|
||||
- [Compaction](/concepts/compaction) -- how compaction interacts with memory
|
||||
|
||||
@@ -162,12 +162,14 @@ Current bundled examples:
|
||||
OpenAI/Codex catalog rows, thinking/live-model policy, usage-token alias
|
||||
normalization (`input` / `output` and `prompt` / `completion` families), the
|
||||
shared `openai-responses-defaults` stream family for native OpenAI/Codex
|
||||
wrappers, and provider-family metadata
|
||||
- `google` and `google-gemini-cli`: Gemini 3.1 forward-compat fallback,
|
||||
native Gemini replay validation, bootstrap replay sanitation, tagged
|
||||
reasoning-output mode, and modern-model matching; Gemini CLI OAuth also owns
|
||||
auth-profile token formatting, usage-token parsing, and quota endpoint
|
||||
fetching for usage surfaces
|
||||
wrappers, provider-family metadata, bundled image-generation provider
|
||||
registration for `gpt-image-1`, and bundled video-generation provider
|
||||
registration for `sora-2`
|
||||
- `google`: Gemini 3.1 forward-compat fallback, native Gemini replay
|
||||
validation, bootstrap replay sanitation, tagged reasoning-output mode,
|
||||
modern-model matching, bundled image-generation provider registration for
|
||||
Gemini image-preview models, and bundled video-generation provider
|
||||
registration for Veo models
|
||||
- `moonshot`: shared transport, plugin-owned thinking payload normalization
|
||||
- `kilocode`: shared transport, plugin-owned request headers, reasoning payload
|
||||
normalization, proxy-Gemini thought-signature sanitation, and cache-TTL
|
||||
@@ -176,20 +178,32 @@ Current bundled examples:
|
||||
policy, binary-thinking/live-model policy, and usage auth + quota fetching;
|
||||
unknown `glm-5*` ids synthesize from the bundled `glm-4.7` template
|
||||
- `xai`: native Responses transport normalization, `/fast` alias rewrites for
|
||||
Grok fast variants, default `tool_stream`, and xAI-specific tool-schema /
|
||||
reasoning-payload cleanup
|
||||
Grok fast variants, default `tool_stream`, xAI-specific tool-schema /
|
||||
reasoning-payload cleanup, and bundled video-generation provider
|
||||
registration for `grok-imagine-video`
|
||||
- `mistral`: plugin-owned capability metadata
|
||||
- `opencode` and `opencode-go`: plugin-owned capability metadata plus
|
||||
proxy-Gemini thought-signature sanitation
|
||||
- `byteplus`, `cloudflare-ai-gateway`, `huggingface`, `kimi`,
|
||||
`nvidia`, `qianfan`, `stepfun`, `synthetic`, `together`, `venice`,
|
||||
`vercel-ai-gateway`, and `volcengine`: plugin-owned catalogs only
|
||||
- `alibaba`: plugin-owned video-generation catalog for direct Wan model refs
|
||||
such as `alibaba/wan2.6-t2v`
|
||||
- `byteplus`: plugin-owned catalogs plus bundled video-generation provider
|
||||
registration for Seedance text-to-video/image-to-video models
|
||||
- `fal`: bundled video-generation provider registration for hosted third-party
|
||||
image-generation provider registration for FLUX image models plus bundled
|
||||
video-generation provider registration for hosted third-party video models
|
||||
- `cloudflare-ai-gateway`, `huggingface`, `kimi`, `nvidia`, `qianfan`,
|
||||
`stepfun`, `synthetic`, `venice`, `vercel-ai-gateway`, and `volcengine`:
|
||||
plugin-owned catalogs only
|
||||
- `qwen`: plugin-owned catalogs for text models plus shared
|
||||
media-understanding and video-generation provider registrations for its
|
||||
multimodal surfaces; Qwen video generation uses the Standard DashScope video
|
||||
endpoints with bundled Wan models such as `wan2.6-t2v` and `wan2.7-r2v`
|
||||
- `minimax`: plugin-owned catalogs, hybrid Anthropic/OpenAI replay-policy
|
||||
- `minimax`: plugin-owned catalogs, bundled video-generation provider
|
||||
registration for Hailuo video models, bundled image-generation provider
|
||||
registration for `image-01`, hybrid Anthropic/OpenAI replay-policy
|
||||
selection, and usage auth/snapshot logic
|
||||
- `together`: plugin-owned catalogs plus bundled video-generation provider
|
||||
registration for Wan video models
|
||||
- `xiaomi`: plugin-owned catalogs plus usage auth/snapshot logic
|
||||
|
||||
The bundled `openai` plugin now owns both provider ids: `openai` and
|
||||
@@ -253,9 +267,9 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no**
|
||||
- Auth: `ANTHROPIC_API_KEY`
|
||||
- Optional rotation: `ANTHROPIC_API_KEYS`, `ANTHROPIC_API_KEY_1`, `ANTHROPIC_API_KEY_2`, plus `OPENCLAW_LIVE_ANTHROPIC_KEY` (single override)
|
||||
- Example model: `anthropic/claude-opus-4-6`
|
||||
- CLI: `openclaw onboard --auth-choice apiKey` or `openclaw onboard --auth-choice anthropic-cli`
|
||||
- CLI: `openclaw onboard --auth-choice apiKey`
|
||||
- Direct public Anthropic requests support the shared `/fast` toggle and `params.fastMode`, including API-key and OAuth-authenticated traffic sent to `api.anthropic.com`; OpenClaw maps that to Anthropic `service_tier` (`auto` vs `standard_only`)
|
||||
- Billing note: Anthropic's public Claude Code docs still include direct Claude Code terminal usage in Claude plan limits. Separately, Anthropic notified OpenClaw users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the **OpenClaw** Claude-login path counts as third-party harness usage and requires **Extra Usage** billed separately from the subscription.
|
||||
- Billing note: for Anthropic in OpenClaw, the practical split is **API key** or **Claude subscription with Extra Usage**. Anthropic notified OpenClaw users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the **OpenClaw** Claude-login path counts as third-party harness usage and requires **Extra Usage** billed separately from the subscription. Our local repros also show the OpenClaw-identifying prompt string does not reproduce on the Anthropic SDK + API-key path.
|
||||
- Anthropic setup-token is available again as a legacy/manual OpenClaw path. Use it with the expectation that Anthropic told OpenClaw users this path requires **Extra Usage**.
|
||||
|
||||
```json5
|
||||
@@ -331,21 +345,10 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no**
|
||||
(or legacy `cached_content`) to forward a provider-native
|
||||
`cachedContents/...` handle; Gemini cache hits surface as OpenClaw `cacheRead`
|
||||
|
||||
### Google Vertex and Gemini CLI
|
||||
### Google Vertex
|
||||
|
||||
- Providers: `google-vertex`, `google-gemini-cli`
|
||||
- Auth: Vertex uses gcloud ADC; Gemini CLI uses its OAuth flow
|
||||
- Caution: Gemini CLI OAuth in OpenClaw is an unofficial integration. Some users have reported Google account restrictions after using third-party clients. Review Google terms and use a non-critical account if you choose to proceed.
|
||||
- Gemini CLI OAuth is shipped as part of the bundled `google` plugin.
|
||||
- Install Gemini CLI first:
|
||||
- `brew install gemini-cli`
|
||||
- or `npm install -g @google/gemini-cli`
|
||||
- Enable: `openclaw plugins enable google`
|
||||
- Login: `openclaw models auth login --provider google-gemini-cli --set-default`
|
||||
- Default model: `google-gemini-cli/gemini-3.1-pro-preview`
|
||||
- Note: you do **not** paste a client id or secret into `openclaw.json`. The CLI login flow stores
|
||||
tokens in auth profiles on the gateway host.
|
||||
- If requests fail after login, set `GOOGLE_CLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT_ID` on the gateway host.
|
||||
- Provider: `google-vertex`
|
||||
- Auth: gcloud ADC
|
||||
- Gemini CLI JSON replies are parsed from `response`; usage falls back to
|
||||
`stats`, with `stats.cached` normalized into OpenClaw `cacheRead`.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Related:
|
||||
falls back to `agents.defaults.imageModel`, then the resolved session/default
|
||||
model.
|
||||
- `agents.defaults.imageGenerationModel` is used by the shared image-generation capability. If omitted, `image_generate` can still infer an auth-backed provider default. It tries the current default provider first, then the remaining registered image-generation providers in provider-id order. If you set a specific provider/model, also configure that provider's auth/API key.
|
||||
- `agents.defaults.videoGenerationModel` is used by the shared video-generation capability. Unlike image generation, this does not infer a provider default today. Set an explicit `provider/model` such as `qwen/wan2.6-t2v`, and configure that provider's auth/API key too.
|
||||
- `agents.defaults.videoGenerationModel` is used by the shared video-generation capability. If omitted, `video_generate` can still infer an auth-backed provider default. It tries the current default provider first, then the remaining registered video-generation providers in provider-id order. If you set a specific provider/model, also configure that provider's auth/API key.
|
||||
- Per-agent defaults can override `agents.defaults.model` via `agents.list[].model` plus bindings (see [/concepts/multi-agent](/concepts/multi-agent)).
|
||||
|
||||
## Quick model policy
|
||||
@@ -175,7 +175,8 @@ resolved primary model.
|
||||
OAuth status is always shown (and included in `--json` output). If a configured
|
||||
provider has no credentials, `models status` prints a **Missing auth** section.
|
||||
JSON includes `auth.oauth` (warn window + profiles) and `auth.providers`
|
||||
(effective auth per provider).
|
||||
(effective auth per provider, including env-backed credentials). `auth.oauth`
|
||||
is auth-store profile health only; env-only providers do not appear there.
|
||||
Use `--check` for automation (exit `1` when missing/expired, `2` when expiring).
|
||||
Use `--probe` for live auth checks; probe rows can come from auth profiles, env
|
||||
credentials, or `models.json`.
|
||||
@@ -252,4 +253,5 @@ This applies whenever OpenClaw regenerates `models.json`, including command-driv
|
||||
- [Model Providers](/concepts/model-providers) — provider routing and auth
|
||||
- [Model Failover](/concepts/model-failover) — fallback chains
|
||||
- [Image Generation](/tools/image-generation) — image model configuration
|
||||
- [Video Generation](/tools/video-generation) — video model configuration
|
||||
- [Configuration Reference](/gateway/configuration-reference#agent-defaults) — model config keys
|
||||
|
||||
@@ -11,13 +11,14 @@ title: "OAuth"
|
||||
# OAuth
|
||||
|
||||
OpenClaw supports “subscription auth” via OAuth for providers that offer it
|
||||
(notably **OpenAI Codex (ChatGPT OAuth)**). For Anthropic subscriptions, new
|
||||
setup should use the local **Claude CLI** login path on the gateway host, but
|
||||
Anthropic distinguishes between direct Claude Code usage and OpenClaw's reuse
|
||||
path. Anthropic's public Claude Code docs say direct Claude Code use stays
|
||||
inside Claude subscription limits. Separately, Anthropic notified OpenClaw
|
||||
users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that OpenClaw counts as
|
||||
a third-party harness and now requires **Extra Usage** for that traffic.
|
||||
(notably **OpenAI Codex (ChatGPT OAuth)**). For Anthropic, the practical split
|
||||
is now:
|
||||
|
||||
- **Anthropic API key**: normal Anthropic API billing
|
||||
- **Anthropic subscription auth inside OpenClaw**: Anthropic notified OpenClaw
|
||||
users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that this now
|
||||
requires **Extra Usage**
|
||||
|
||||
OpenAI Codex OAuth is explicitly supported for use in external tools like
|
||||
OpenClaw. This page explains:
|
||||
|
||||
@@ -96,62 +97,23 @@ OpenClaw-driven Claude-login traffic.
|
||||
|
||||
## Anthropic Claude CLI migration
|
||||
|
||||
If Claude CLI is already installed and signed in on the gateway host, you can
|
||||
switch Anthropic model selection over to the local CLI backend. This is a
|
||||
supported OpenClaw path when you want to reuse a local Claude CLI login on the
|
||||
same host.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- the `claude` binary is installed on the gateway host
|
||||
- Claude CLI is already authenticated there via `claude auth login`
|
||||
|
||||
Migration command:
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider anthropic --method cli --set-default
|
||||
```
|
||||
|
||||
Onboarding shortcut:
|
||||
|
||||
```bash
|
||||
openclaw onboard --auth-choice anthropic-cli
|
||||
```
|
||||
|
||||
This keeps existing Anthropic auth profiles for rollback, but rewrites the main
|
||||
default-model path from `anthropic/...` to `claude-cli/...`, rewrites matching
|
||||
Anthropic Claude fallbacks, and adds matching `claude-cli/...` allowlist
|
||||
entries under `agents.defaults.models`.
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
openclaw models status
|
||||
```
|
||||
Anthropic no longer has a supported local Claude CLI migration path in
|
||||
OpenClaw. Use Anthropic API keys for Anthropic traffic, or keep legacy
|
||||
token-based auth only where it is already configured and with the expectation
|
||||
that Anthropic treats that OpenClaw path as **Extra Usage**.
|
||||
|
||||
## OAuth exchange (how login works)
|
||||
|
||||
OpenClaw’s interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands.
|
||||
|
||||
### Anthropic Claude CLI
|
||||
### Anthropic setup-token
|
||||
|
||||
Flow shape:
|
||||
|
||||
Claude CLI path:
|
||||
|
||||
1. sign in with `claude auth login` on the gateway host
|
||||
2. run `openclaw models auth login --provider anthropic --method cli --set-default`
|
||||
3. store no new auth profile; switch model selection to `claude-cli/...`
|
||||
4. keep existing Anthropic auth profiles for rollback
|
||||
|
||||
Anthropic's public Claude Code docs describe this direct Claude subscription
|
||||
login flow for `claude` itself. OpenClaw can reuse that local login, but
|
||||
Anthropic separately classifies the OpenClaw-controlled path as third-party
|
||||
harness usage for billing purposes.
|
||||
|
||||
Interactive assistant path:
|
||||
|
||||
- `openclaw onboard` / `openclaw configure` → auth choice `anthropic-cli`
|
||||
1. start Anthropic setup-token or paste-token from OpenClaw
|
||||
2. OpenClaw stores the resulting Anthropic credential in an auth profile
|
||||
3. model selection stays on `anthropic/...`
|
||||
4. existing Anthropic auth profiles remain available for rollback/order control
|
||||
|
||||
### OpenAI Codex (ChatGPT OAuth)
|
||||
|
||||
|
||||
@@ -1,865 +1,66 @@
|
||||
---
|
||||
title: "QA E2E Automation"
|
||||
summary: "Design note for a full end-to-end QA system built on a synthetic message-channel plugin, Dockerized OpenClaw, and subagent-driven scenario execution"
|
||||
summary: "Private QA automation shape for qa-lab, qa-channel, seeded scenarios, and protocol reports"
|
||||
read_when:
|
||||
- You are designing a true end-to-end QA harness for OpenClaw
|
||||
- You want a synthetic message channel for automated feature verification
|
||||
- You want subagents to discover features, run scenarios, and propose fixes
|
||||
- Extending qa-lab or qa-channel
|
||||
- Adding repo-backed QA scenarios
|
||||
- Building higher-realism QA automation around the Gateway dashboard
|
||||
title: "QA E2E Automation"
|
||||
---
|
||||
|
||||
# QA E2E Automation
|
||||
|
||||
This note proposes a true end-to-end QA system for OpenClaw built around a
|
||||
real channel plugin dedicated to testing.
|
||||
The private QA stack is meant to exercise OpenClaw in a more realistic,
|
||||
channel-shaped way than a single unit test can.
|
||||
|
||||
The core idea:
|
||||
Current pieces:
|
||||
|
||||
- run OpenClaw inside Docker in a realistic gateway configuration
|
||||
- expose a synthetic but full-featured message channel as a normal plugin
|
||||
- let a QA harness inject inbound traffic and inspect outbound state
|
||||
- let OpenClaw agents and subagents explore, verify, and report on behavior
|
||||
- optionally escalate failing scenarios into host-side fix workflows that open PRs
|
||||
- `extensions/qa-channel`: synthetic message channel with DM, channel, thread,
|
||||
reaction, edit, and delete surfaces.
|
||||
- `extensions/qa-lab`: debugger UI and QA bus for observing the transcript,
|
||||
injecting inbound messages, and exporting a Markdown report.
|
||||
- `qa/`: repo-backed seed assets for the kickoff task and baseline QA
|
||||
scenarios.
|
||||
|
||||
This is not a unit-test replacement. It is a product-level system test layer.
|
||||
The long-term goal is a two-pane QA site:
|
||||
|
||||
## Chosen direction
|
||||
- Left: Gateway dashboard (Control UI) with the agent.
|
||||
- Right: QA Lab, showing the Slack-ish transcript and scenario plan.
|
||||
|
||||
The initial direction for this project is:
|
||||
That lets an operator or automation loop give the agent a QA mission, observe
|
||||
real channel behavior, and record what worked, failed, or stayed blocked.
|
||||
|
||||
- build the full system inside this repo
|
||||
- test against a matrix, not a single model/provider pair
|
||||
- use Markdown reports as the first output artifact
|
||||
- defer auto-PR and auto-fix work until later
|
||||
- treat Slack-class semantics as the MVP transport target
|
||||
- keep orchestration simple in v1, with a host-side controller that exercises
|
||||
the moving parts directly
|
||||
- evolve toward OpenClaw becoming the orchestration layer later, once the
|
||||
transport, scenario, and reporting model are proven
|
||||
## Repo-backed seeds
|
||||
|
||||
## Goals
|
||||
Seed assets live in `qa/`:
|
||||
|
||||
- Test OpenClaw through a real messaging-channel boundary, not only `chat.send`
|
||||
or embedded mocks.
|
||||
- Verify channel semantics that matter for real use:
|
||||
- DMs
|
||||
- channels/groups
|
||||
- threads
|
||||
- edits
|
||||
- deletes
|
||||
- reactions
|
||||
- polls
|
||||
- attachments
|
||||
- Verify agent behavior across realistic user flows:
|
||||
- memory
|
||||
- thread binding
|
||||
- model switching
|
||||
- cron jobs
|
||||
- subagents
|
||||
- approvals
|
||||
- routing
|
||||
- channel-specific `message` actions
|
||||
- Make the QA runner capable of feature discovery:
|
||||
- read docs
|
||||
- inspect plugin capability discovery
|
||||
- inspect code and config
|
||||
- generate a scenario protocol
|
||||
- Support deterministic protocol tests and best-effort real-model tests as
|
||||
separate lanes.
|
||||
- Allow automated bug triage artifacts that can feed a host-side fix worker.
|
||||
- `qa/QA_KICKOFF_TASK.md`
|
||||
- `qa/seed-scenarios.json`
|
||||
|
||||
## Non-goals
|
||||
These are intentionally in git so the QA plan is visible to both humans and the
|
||||
agent. The baseline list should stay broad enough to cover:
|
||||
|
||||
- Not a replacement for existing unit, contract, or live tests.
|
||||
- Not a production channel.
|
||||
- Not a requirement that all bug fixing happen from inside the Dockerized
|
||||
OpenClaw runtime.
|
||||
- Not a reason to add test-only core branches for one channel.
|
||||
- DM and channel chat
|
||||
- thread behavior
|
||||
- message action lifecycle
|
||||
- cron callbacks
|
||||
- memory recall
|
||||
- model switching
|
||||
- subagent handoff
|
||||
- repo-reading and docs-reading
|
||||
- one small build task such as Lobster Invaders
|
||||
|
||||
## Why a channel plugin
|
||||
## Reporting
|
||||
|
||||
OpenClaw already has the right boundary:
|
||||
`qa-lab` exports a Markdown protocol report from the observed bus timeline.
|
||||
The report should answer:
|
||||
|
||||
- core owns the shared `message` tool, prompt wiring, outer session
|
||||
bookkeeping, and dispatch
|
||||
- channel plugins own:
|
||||
- config
|
||||
- pairing
|
||||
- security
|
||||
- session grammar
|
||||
- threading
|
||||
- outbound delivery
|
||||
- channel-owned actions and capability discovery
|
||||
- What worked
|
||||
- What failed
|
||||
- What stayed blocked
|
||||
- What follow-up scenarios are worth adding
|
||||
|
||||
That means the cleanest design is:
|
||||
## Related docs
|
||||
|
||||
- a real channel plugin for QA transport semantics
|
||||
- a separate QA control plane for injection and inspection
|
||||
|
||||
This keeps the test transport inside the same architecture used by Slack,
|
||||
Discord, Teams, and similar channels.
|
||||
|
||||
## System overview
|
||||
|
||||
The system has six pieces.
|
||||
|
||||
1. `qa-channel` plugin
|
||||
|
||||
- Bundled extension under `extensions/qa-channel`
|
||||
- Normal `ChannelPlugin`
|
||||
- Behaves like a Slack/Discord/Teams-class channel
|
||||
- Registers channel-owned message actions through the shared `message` tool
|
||||
|
||||
2. `qa-bus` sidecar
|
||||
|
||||
- Small HTTP and/or WS service
|
||||
- Canonical state store for synthetic conversations, messages, threads,
|
||||
reactions, edits, and event history
|
||||
- Accepts inbound events from the harness
|
||||
- Exposes inspection and wait APIs for assertions
|
||||
|
||||
3. Dockerized OpenClaw gateway
|
||||
|
||||
- Runs as close to real deployment as practical
|
||||
- Loads `qa-channel`
|
||||
- Uses normal config, routing, session, cron, and plugin loading
|
||||
|
||||
4. QA orchestrator
|
||||
|
||||
- Host-side runner or dedicated OpenClaw-driven controller
|
||||
- Provisions scenario environments
|
||||
- Seeds config
|
||||
- Resets state
|
||||
- Executes test matrix
|
||||
- Collects structured outcomes
|
||||
|
||||
5. Auto-fix worker
|
||||
|
||||
- Host-side workflow
|
||||
- Creates a worktree
|
||||
- launches a coding agent
|
||||
- runs scoped verification
|
||||
- opens a PR
|
||||
|
||||
The auto-fix worker should start outside the container. It needs direct repo
|
||||
and GitHub access, clean worktree control, and better isolation from the
|
||||
runtime under test.
|
||||
|
||||
6. `qa-lab` extension
|
||||
|
||||
- Bundled extension under `extensions/qa-lab`
|
||||
- Owns the QA harness, Markdown report flow, and private debugger UI
|
||||
- Registers hidden CLI entrypoints such as `openclaw qa run` and
|
||||
`openclaw qa ui`
|
||||
- Stays separate from the shipped Control UI bundle
|
||||
|
||||
## High-level flow
|
||||
|
||||
1. Start `qa-bus`.
|
||||
2. Start OpenClaw in Docker with `qa-channel` enabled.
|
||||
3. QA orchestrator injects inbound messages into `qa-bus`.
|
||||
4. `qa-channel` receives them as normal inbound traffic.
|
||||
5. OpenClaw runs the agent loop normally.
|
||||
6. Outbound replies and channel actions flow back through `qa-channel` into
|
||||
`qa-bus`.
|
||||
7. QA orchestrator inspects state or waits on events.
|
||||
8. Orchestrator records pass/fail/flaky/unknown plus artifacts.
|
||||
9. Severe failures optionally emit a bug packet for the host-side fix worker.
|
||||
|
||||
## Lanes
|
||||
|
||||
The system should have two distinct lanes.
|
||||
|
||||
### Lane A: deterministic protocol lane
|
||||
|
||||
Use a deterministic or tightly controlled model setup.
|
||||
|
||||
Preferred options:
|
||||
|
||||
- a canned provider fixture
|
||||
- the bundled `synthetic` provider when useful
|
||||
- fixed prompts with exact assertions
|
||||
|
||||
Purpose:
|
||||
|
||||
- verify transport and product semantics
|
||||
- keep flakiness low
|
||||
- catch regressions in routing, memory plumbing, thread binding, cron, and tool
|
||||
invocation
|
||||
|
||||
### Lane B: quality lane
|
||||
|
||||
Use real providers and real models in a matrix.
|
||||
|
||||
Purpose:
|
||||
|
||||
- verify that the agent can still do good work end to end
|
||||
- evaluate feature discoverability and instruction following
|
||||
- surface model-specific breakage or degraded behavior
|
||||
|
||||
Expected result type:
|
||||
|
||||
- best-effort
|
||||
- rubric-based
|
||||
- more tolerant of wording variation
|
||||
|
||||
Matrix guidance for v1:
|
||||
|
||||
- start with a small curated matrix, not "everything configured"
|
||||
- keep deterministic protocol runs separate from quality runs
|
||||
- report matrix cells independently so one provider/model failure does not hide
|
||||
transport correctness
|
||||
|
||||
Do not mix these lanes. Protocol correctness and model quality should fail
|
||||
independently.
|
||||
|
||||
## Use existing bootstrap seam first
|
||||
|
||||
Before the custom channel exists, OpenClaw already has a useful bootstrap path:
|
||||
|
||||
- admin-scoped synthetic originating-route fields on `chat.send`
|
||||
- synthetic message-channel headers for HTTP flows
|
||||
|
||||
That is enough to build a first QA controller for:
|
||||
|
||||
- thread/session routing
|
||||
- ACP bind flows
|
||||
- subagent delivery
|
||||
- cron wake paths
|
||||
- memory persistence checks
|
||||
|
||||
This should be Phase 0 because it de-risks the scenario protocol before the
|
||||
full channel lands.
|
||||
|
||||
## `qa-lab` extension design
|
||||
|
||||
`qa-lab` is the private operator-facing half of this system.
|
||||
|
||||
Suggested package:
|
||||
|
||||
- `extensions/qa-lab/`
|
||||
|
||||
Suggested responsibilities:
|
||||
|
||||
- host the synthetic bus state machine
|
||||
- host the scenario runner
|
||||
- write Markdown reports
|
||||
- serve a private debugger UI on a separate local server
|
||||
- keep that UI entirely outside the shipped Control UI bundle
|
||||
|
||||
Suggested UI shape:
|
||||
|
||||
- left rail for conversations and threads
|
||||
- center transcript pane
|
||||
- right rail for event stream and report inspection
|
||||
- bottom inject-composer for inbound QA traffic
|
||||
|
||||
## `qa-channel` plugin design
|
||||
|
||||
## Package layout
|
||||
|
||||
Suggested package:
|
||||
|
||||
- `extensions/qa-channel/`
|
||||
|
||||
Suggested file layout:
|
||||
|
||||
- `package.json`
|
||||
- `openclaw.plugin.json`
|
||||
- `index.ts`
|
||||
- `setup-entry.ts`
|
||||
- `api.ts`
|
||||
- `runtime-api.ts`
|
||||
- `src/channel.ts`
|
||||
- `src/channel-api.ts`
|
||||
- `src/config-schema.ts`
|
||||
- `src/setup-core.ts`
|
||||
- `src/setup-surface.ts`
|
||||
- `src/runtime.ts`
|
||||
- `src/channel.runtime.ts`
|
||||
- `src/inbound.ts`
|
||||
- `src/outbound.ts`
|
||||
- `src/state-client.ts`
|
||||
- `src/targets.ts`
|
||||
- `src/threading.ts`
|
||||
- `src/message-actions.ts`
|
||||
- `src/probe.ts`
|
||||
- `src/doctor.ts`
|
||||
- `src/*.test.ts`
|
||||
|
||||
Model it after Slack, Discord, Teams, or Google Chat packaging, not as a one-off
|
||||
test helper.
|
||||
|
||||
## Capabilities
|
||||
|
||||
MVP capabilities:
|
||||
|
||||
- one account
|
||||
- DMs
|
||||
- channels
|
||||
- threads
|
||||
- send text
|
||||
- reply in thread
|
||||
- read
|
||||
- edit
|
||||
- delete
|
||||
- react
|
||||
- search
|
||||
- upload-file
|
||||
- download-file
|
||||
|
||||
Phase 2 capabilities:
|
||||
|
||||
- polls
|
||||
- member-info
|
||||
- channel-info
|
||||
- channel-list
|
||||
- pin and unpin
|
||||
- permissions
|
||||
- topic create and edit
|
||||
|
||||
These map naturally onto the shared `message` tool action model already used by
|
||||
channel plugins.
|
||||
|
||||
## Conversation model
|
||||
|
||||
Use a stable synthetic grammar that supports both simplicity and realistic
|
||||
coverage.
|
||||
|
||||
Suggested ids:
|
||||
|
||||
- DM conversation: `dm:<user-id>`
|
||||
- channel: `chan:<space-id>`
|
||||
- thread: `thread:<space-id>:<thread-id>`
|
||||
- message id: `msg:<ulid>`
|
||||
|
||||
Suggested target forms:
|
||||
|
||||
- `qa:dm:<user-id>`
|
||||
- `qa:chan:<space-id>`
|
||||
- `qa:thread:<space-id>:<thread-id>`
|
||||
|
||||
The plugin should own translation between external target strings and canonical
|
||||
conversation ids.
|
||||
|
||||
## Pairing and security
|
||||
|
||||
Even though this is a QA channel, it should still implement real policy
|
||||
surfaces:
|
||||
|
||||
- DM allowlist / pairing flow
|
||||
- group policy
|
||||
- mention gating where relevant
|
||||
- trusted sender ids
|
||||
|
||||
Reason:
|
||||
|
||||
- these are product features and should be testable through the QA transport
|
||||
- the QA lane should be able to verify policy failures, not only happy paths
|
||||
|
||||
## Threading model
|
||||
|
||||
Threading is one of the main reasons to build this channel.
|
||||
|
||||
Required semantics:
|
||||
|
||||
- create thread from a top-level message
|
||||
- reply inside an existing thread
|
||||
- list thread messages
|
||||
- preserve parent message linkage
|
||||
- let OpenClaw thread binding attach a session to a thread
|
||||
|
||||
The QA bus must preserve:
|
||||
|
||||
- conversation id
|
||||
- thread id
|
||||
- parent message id
|
||||
- sender id
|
||||
- timestamps
|
||||
|
||||
## Channel-owned message actions
|
||||
|
||||
The plugin should implement `actions.describeMessageTool(...)` and
|
||||
`actions.handleAction(...)`.
|
||||
|
||||
MVP action list:
|
||||
|
||||
- `send`
|
||||
- `read`
|
||||
- `reply`
|
||||
- `react`
|
||||
- `edit`
|
||||
- `delete`
|
||||
- `thread-create`
|
||||
- `thread-reply`
|
||||
- `search`
|
||||
- `upload-file`
|
||||
- `download-file`
|
||||
|
||||
This is enough to test the shared `message` tool end to end with real channel
|
||||
semantics.
|
||||
|
||||
## `qa-bus` design
|
||||
|
||||
`qa-bus` is the transport simulator and assertion backend.
|
||||
|
||||
It should not know OpenClaw internals. It should know channel state.
|
||||
|
||||
For v1, keep `qa-bus` in this repo so:
|
||||
|
||||
- fixtures and scenarios evolve with product code
|
||||
- the transport contract can change in lock-step with the plugin
|
||||
- CI and local dev do not need another repo checkout
|
||||
|
||||
## Responsibilities
|
||||
|
||||
- accept inbound user/platform events
|
||||
- persist canonical conversation state
|
||||
- persist append-only event log
|
||||
- expose inspection APIs
|
||||
- expose blocking wait APIs
|
||||
- support reset per scenario or per suite
|
||||
|
||||
## Transport
|
||||
|
||||
HTTP is enough for MVP.
|
||||
|
||||
Suggested endpoints:
|
||||
|
||||
- `POST /reset`
|
||||
- `POST /inbound/message`
|
||||
- `POST /inbound/edit`
|
||||
- `POST /inbound/delete`
|
||||
- `POST /inbound/reaction`
|
||||
- `POST /inbound/thread/create`
|
||||
- `GET /state/conversations`
|
||||
- `GET /state/messages`
|
||||
- `GET /state/threads`
|
||||
- `GET /events`
|
||||
- `POST /wait`
|
||||
|
||||
Optional WS stream:
|
||||
|
||||
- `/stream`
|
||||
|
||||
Useful for live event taps and debugging.
|
||||
|
||||
## State model
|
||||
|
||||
Persist three layers.
|
||||
|
||||
1. Conversation snapshot
|
||||
|
||||
- participants
|
||||
- type
|
||||
- thread topology
|
||||
- latest message pointers
|
||||
|
||||
2. Message snapshot
|
||||
|
||||
- sender
|
||||
- content
|
||||
- attachments
|
||||
- edit history
|
||||
- reactions
|
||||
- parent and thread linkage
|
||||
|
||||
3. Append-only event log
|
||||
|
||||
- canonical timestamp
|
||||
- causal ordering
|
||||
- source: inbound, outbound, action, system
|
||||
- payload
|
||||
|
||||
The append-only log matters because many QA assertions are event-oriented, not
|
||||
just state-oriented.
|
||||
|
||||
## Assertion API
|
||||
|
||||
The harness needs waiters, not just snapshots.
|
||||
|
||||
Suggested `POST /wait` contract:
|
||||
|
||||
- `kind`
|
||||
- `match`
|
||||
- `timeoutMs`
|
||||
|
||||
Examples:
|
||||
|
||||
- wait for outbound message matching text regex
|
||||
- wait for thread creation
|
||||
- wait for reaction added
|
||||
- wait for message edit
|
||||
- wait for no event of type X within Y ms
|
||||
|
||||
This gives stable tests without custom polling code in every scenario.
|
||||
|
||||
## QA orchestrator design
|
||||
|
||||
The orchestrator should own scenario planning and artifact collection.
|
||||
|
||||
Start host-side. Later, OpenClaw can orchestrate parts of it.
|
||||
|
||||
This is the chosen v1 direction.
|
||||
|
||||
Why:
|
||||
|
||||
- simpler to iterate while the transport and scenario protocol are still moving
|
||||
- easier access to the repo, logs, Docker, and test fixtures
|
||||
- easier artifact collection and report generation
|
||||
- avoids over-coupling the first version to subagent behavior before the QA
|
||||
protocol itself is stable
|
||||
|
||||
## Inputs
|
||||
|
||||
- docs pages
|
||||
- channel capability discovery
|
||||
- configured provider/model lane
|
||||
- scenario catalog
|
||||
- repo/test metadata
|
||||
|
||||
## Outputs
|
||||
|
||||
- structured protocol report
|
||||
- scenario transcript
|
||||
- captured channel state
|
||||
- gateway logs
|
||||
- failure packets
|
||||
|
||||
For v1, the primary output is a Markdown report.
|
||||
|
||||
Suggested report sections:
|
||||
|
||||
- suite summary
|
||||
- environment
|
||||
- provider/model matrix
|
||||
- scenarios passed
|
||||
- scenarios failed
|
||||
- flaky or inconclusive scenarios
|
||||
- captured evidence links or inline excerpts
|
||||
- suspected ownership or file hints
|
||||
- follow-up recommendations
|
||||
|
||||
## Scenario format
|
||||
|
||||
Use a data-driven scenario spec.
|
||||
|
||||
Suggested shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "thread-memory-recall",
|
||||
"lane": "deterministic",
|
||||
"preconditions": ["qa-channel", "memory-enabled"],
|
||||
"steps": [
|
||||
{
|
||||
"type": "injectMessage",
|
||||
"to": "qa:dm:user-a",
|
||||
"text": "Remember that the deploy key is kiwi."
|
||||
},
|
||||
{ "type": "waitForOutbound", "match": { "textIncludes": "kiwi" } },
|
||||
{ "type": "injectMessage", "to": "qa:dm:user-a", "text": "What was the deploy key?" },
|
||||
{ "type": "waitForOutbound", "match": { "textIncludes": "kiwi" } }
|
||||
],
|
||||
"assertions": [{ "type": "outboundTextIncludes", "value": "kiwi" }]
|
||||
}
|
||||
```
|
||||
|
||||
Keep the execution engine generic and the scenario catalog declarative.
|
||||
|
||||
## Feature discovery
|
||||
|
||||
The orchestrator can discover candidate scenarios from three sources.
|
||||
|
||||
1. Docs
|
||||
|
||||
- channel docs
|
||||
- testing docs
|
||||
- gateway docs
|
||||
- subagents docs
|
||||
- cron docs
|
||||
|
||||
2. Runtime capability discovery
|
||||
|
||||
- channel `message` action discovery
|
||||
- plugin status and channel capabilities
|
||||
- configured providers/models
|
||||
|
||||
3. Code hints
|
||||
|
||||
- known action names
|
||||
- channel-specific feature flags
|
||||
- config schema
|
||||
|
||||
This should produce a proposed protocol with:
|
||||
|
||||
- must-test
|
||||
- can-test
|
||||
- blocked
|
||||
- unsupported
|
||||
|
||||
## Scenario classes
|
||||
|
||||
Recommended catalog:
|
||||
|
||||
- transport basics
|
||||
- DM send and reply
|
||||
- channel send
|
||||
- thread create and reply
|
||||
- reaction add and read
|
||||
- edit and delete
|
||||
- policy
|
||||
- allowlist
|
||||
- pairing
|
||||
- group mention gating
|
||||
- shared `message` tool
|
||||
- read
|
||||
- search
|
||||
- reply
|
||||
- react
|
||||
- upload and download
|
||||
- agent quality
|
||||
- follows channel context
|
||||
- obeys thread semantics
|
||||
- uses memory across turns
|
||||
- switches model when instructed
|
||||
- automation
|
||||
- cron add and run
|
||||
- cron delivery into channel
|
||||
- scheduled reminders
|
||||
- subagents
|
||||
- spawn
|
||||
- announce
|
||||
- threaded follow-up
|
||||
- nested orchestration when enabled
|
||||
- failure handling
|
||||
- unsupported action
|
||||
- timeout
|
||||
- malformed target
|
||||
- policy denial
|
||||
|
||||
## OpenClaw as orchestrator
|
||||
|
||||
Longer-term, OpenClaw itself can coordinate the QA run.
|
||||
|
||||
Suggested architecture:
|
||||
|
||||
- one controller session
|
||||
- N worker subagents
|
||||
- each worker owns one scenario or scenario shard
|
||||
- workers report structured results back to controller
|
||||
|
||||
Good fits for existing OpenClaw primitives:
|
||||
|
||||
- `sessions_spawn`
|
||||
- `subagents`
|
||||
- cron-based wakeups for long-running suites
|
||||
- thread-bound sessions for scenario-local follow-up
|
||||
|
||||
Best near-term use:
|
||||
|
||||
- controller generates the plan
|
||||
- workers execute scenarios in parallel
|
||||
- controller synthesizes report
|
||||
|
||||
Avoid making the controller also own host Git operations in the first version.
|
||||
|
||||
Chosen direction:
|
||||
|
||||
- v1: host-side controller
|
||||
- v2+: OpenClaw-native orchestration once the scenario protocol and transport
|
||||
model are stable
|
||||
|
||||
## Auto-fix workflow
|
||||
|
||||
The system should emit a structured bug packet when a scenario fails.
|
||||
|
||||
Suggested bug packet:
|
||||
|
||||
- scenario id
|
||||
- lane
|
||||
- failure kind
|
||||
- minimal repro steps
|
||||
- channel event transcript
|
||||
- gateway transcript
|
||||
- logs
|
||||
- suspected files
|
||||
- confidence
|
||||
|
||||
Host-side fix worker flow:
|
||||
|
||||
1. receive bug packet
|
||||
2. create detached worktree
|
||||
3. launch coding agent in worktree
|
||||
4. write failing regression first when practical
|
||||
5. implement fix
|
||||
6. run scoped verification
|
||||
7. open PR
|
||||
|
||||
This should remain host-side at first because it needs:
|
||||
|
||||
- repo write access
|
||||
- worktree hygiene
|
||||
- git credentials
|
||||
- GitHub auth
|
||||
|
||||
Chosen direction:
|
||||
|
||||
- do not auto-open PRs in v1
|
||||
- emit Markdown reports and structured failure packets first
|
||||
- add host-side worktree + PR automation later
|
||||
|
||||
## Rollout plan
|
||||
|
||||
## Phase 0: bootstrap on existing synthetic ingress
|
||||
|
||||
Build a first QA runner without a new channel:
|
||||
|
||||
- use `chat.send` with admin-scoped synthetic originating-route fields
|
||||
- run deterministic scenarios against routing, memory, cron, subagents, and ACP
|
||||
- validate protocol format and artifact collection
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- scenario runner exists
|
||||
- structured protocol report exists
|
||||
- failure artifacts exist
|
||||
|
||||
## Phase 1: MVP `qa-channel`
|
||||
|
||||
Build the plugin and bus with:
|
||||
|
||||
- DM
|
||||
- channels
|
||||
- threads
|
||||
- read
|
||||
- reply
|
||||
- react
|
||||
- edit
|
||||
- delete
|
||||
- search
|
||||
|
||||
Target semantics:
|
||||
|
||||
- Slack-class transport behavior
|
||||
- not full Teams-class parity yet
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- OpenClaw in Docker can talk to `qa-bus`
|
||||
- harness can inject + inspect
|
||||
- one green end-to-end suite across message transport and agent behavior
|
||||
|
||||
## Phase 2: protocol expansion
|
||||
|
||||
Add:
|
||||
|
||||
- attachments
|
||||
- polls
|
||||
- pins
|
||||
- richer policy tests
|
||||
- quality lane with real provider/model matrix
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- scenario matrix covers major built-in features
|
||||
- deterministic and quality lanes are separated
|
||||
|
||||
## Phase 3: subagent-driven QA
|
||||
|
||||
Add:
|
||||
|
||||
- controller agent
|
||||
- worker subagents
|
||||
- scenario discovery from docs + capability discovery
|
||||
- parallel execution
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- one controller can fan out and synthesize a suite report
|
||||
|
||||
## Phase 4: auto-fix loop
|
||||
|
||||
Add:
|
||||
|
||||
- bug packet emission
|
||||
- host-side worktree runner
|
||||
- PR creation
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- selected failures can auto-produce draft PRs
|
||||
|
||||
## Risks
|
||||
|
||||
## Risk: too much magic in one layer
|
||||
|
||||
If the QA channel, bus, and orchestrator all become smart at once, debugging
|
||||
will be painful.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- keep `qa-channel` transport-focused
|
||||
- keep `qa-bus` state-focused
|
||||
- keep orchestrator separate
|
||||
|
||||
## Risk: flaky assertions from model variance
|
||||
|
||||
Mitigation:
|
||||
|
||||
- deterministic lane
|
||||
- quality lane
|
||||
- different pass criteria
|
||||
|
||||
## Risk: test-only branches leaking into core
|
||||
|
||||
Mitigation:
|
||||
|
||||
- no core special cases for `qa-channel`
|
||||
- use normal plugin seams
|
||||
- use admin synthetic ingress only as bootstrap
|
||||
|
||||
## Risk: auto-fix overreach
|
||||
|
||||
Mitigation:
|
||||
|
||||
- keep fix worker host-side
|
||||
- require explicit policy for when PRs can open automatically
|
||||
- gate with scoped tests
|
||||
|
||||
## Risk: building a fake platform nobody uses
|
||||
|
||||
Mitigation:
|
||||
|
||||
- emulate Slack/Discord/Teams semantics, not an abstract transport
|
||||
- prioritize features that stress shared OpenClaw boundaries
|
||||
|
||||
## MVP recommendation
|
||||
|
||||
If building this now, start with this exact order.
|
||||
|
||||
1. Host-side scenario runner using existing synthetic originating-route support.
|
||||
2. `qa-bus` sidecar with state, events, reset, and wait APIs.
|
||||
3. `extensions/qa-channel` MVP with DMs, channels, threads, reply, read, react,
|
||||
edit, delete, and search.
|
||||
4. Markdown report generator for suite + matrix output.
|
||||
5. One deterministic end-to-end suite:
|
||||
- inject inbound DM
|
||||
- verify reply
|
||||
- create thread
|
||||
- verify follow-up in thread
|
||||
- verify memory recall on later turn
|
||||
6. Add curated real-model matrix quality lane.
|
||||
7. Add controller subagent orchestration.
|
||||
8. Add host-side auto-fix worktree runner.
|
||||
|
||||
This order gets real value quickly without requiring the full grand design to
|
||||
land before the first useful signal appears.
|
||||
|
||||
## Current product decisions
|
||||
|
||||
- `qa-bus` lives inside this repo
|
||||
- the first controller is host-side
|
||||
- Slack-class behavior is the MVP target
|
||||
- the quality lane uses a curated matrix
|
||||
- first version produces Markdown reports, not PRs
|
||||
- OpenClaw-native orchestration is a later phase, not a v1 requirement
|
||||
- [Testing](/help/testing)
|
||||
- [QA Channel](/channels/qa-channel)
|
||||
- [Dashboard](/web/dashboard)
|
||||
|
||||
@@ -12,11 +12,23 @@ OpenClaw builds a custom system prompt for every agent run. The prompt is **Open
|
||||
|
||||
The prompt is assembled by OpenClaw and injected into each agent run.
|
||||
|
||||
Provider plugins can contribute cache-aware prompt guidance without replacing
|
||||
the full OpenClaw-owned prompt. The provider runtime can:
|
||||
|
||||
- replace a small set of named core sections (`interaction_style`,
|
||||
`tool_call_style`, `execution_bias`)
|
||||
- inject a **stable prefix** above the prompt cache boundary
|
||||
- inject a **dynamic suffix** below the prompt cache boundary
|
||||
|
||||
Use provider-owned contributions for model-family-specific tuning. Keep legacy
|
||||
`before_prompt_build` prompt mutation for compatibility or truly global prompt
|
||||
changes, not normal provider behavior.
|
||||
|
||||
## Structure
|
||||
|
||||
The prompt is intentionally compact and uses fixed sections:
|
||||
|
||||
- **Tooling**: current tool list + short descriptions.
|
||||
- **Tooling**: structured-tool source-of-truth reminder plus runtime tool-use guidance.
|
||||
- **Safety**: short guardrail reminder to avoid power-seeking behavior or bypassing oversight.
|
||||
- **Skills** (when available): tells the model how to load skill instructions on demand.
|
||||
- **OpenClaw Self-Update**: how to inspect config safely with
|
||||
@@ -51,6 +63,10 @@ The Tooling section also includes runtime guidance for long-running work:
|
||||
- do not poll `subagents list` / `sessions_list` in a loop just to wait for
|
||||
completion
|
||||
|
||||
When the experimental `update_plan` tool is enabled, Tooling also tells the
|
||||
model to use it only for non-trivial multi-step work, keep exactly one
|
||||
`in_progress` step, and avoid repeating the whole plan after each update.
|
||||
|
||||
Safety guardrails in the system prompt are advisory. They guide model behavior but do not enforce policy. Use tool policy, exec approvals, sandboxing, and channel allowlists for hard enforcement; operators can disable these by design.
|
||||
|
||||
On channels with native approval cards/buttons, the runtime prompt now tells the
|
||||
|
||||
583
docs/docs.json
583
docs/docs.json
@@ -124,6 +124,14 @@
|
||||
"source": "/context",
|
||||
"destination": "/concepts/context"
|
||||
},
|
||||
{
|
||||
"source": "/zh-CN",
|
||||
"destination": "/zh-CN/index"
|
||||
},
|
||||
{
|
||||
"source": "/zh-CN/",
|
||||
"destination": "/zh-CN/index"
|
||||
},
|
||||
{
|
||||
"source": "/compaction",
|
||||
"destination": "/concepts/compaction"
|
||||
@@ -1070,7 +1078,7 @@
|
||||
"concepts/memory-qmd",
|
||||
"concepts/memory-honcho",
|
||||
"concepts/memory-search",
|
||||
"concepts/memory-dreaming"
|
||||
"concepts/dreaming"
|
||||
]
|
||||
},
|
||||
"concepts/compaction"
|
||||
@@ -1227,6 +1235,7 @@
|
||||
"pages": [
|
||||
"providers/anthropic",
|
||||
"providers/bedrock",
|
||||
"providers/bedrock-mantle",
|
||||
"providers/chutes",
|
||||
"providers/claude-max-api-proxy",
|
||||
"providers/cloudflare-ai-gateway",
|
||||
@@ -1250,7 +1259,6 @@
|
||||
"providers/openrouter",
|
||||
"providers/perplexity-provider",
|
||||
"providers/qianfan",
|
||||
"providers/qwen_modelstudio",
|
||||
"providers/qwen",
|
||||
"providers/sglang",
|
||||
"providers/stepfun",
|
||||
@@ -1351,7 +1359,6 @@
|
||||
"gateway/openai-http-api",
|
||||
"gateway/openresponses-http-api",
|
||||
"gateway/tools-invoke-http-api",
|
||||
"gateway/cli-backends",
|
||||
"gateway/local-models"
|
||||
]
|
||||
},
|
||||
@@ -1586,576 +1593,6 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"language": "zh-Hans",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "快速开始",
|
||||
"groups": [
|
||||
{
|
||||
"group": "首页",
|
||||
"pages": ["zh-CN/index"]
|
||||
},
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/start/showcase"]
|
||||
},
|
||||
{
|
||||
"group": "核心概念",
|
||||
"pages": ["zh-CN/concepts/features"]
|
||||
},
|
||||
{
|
||||
"group": "第一步",
|
||||
"pages": [
|
||||
"zh-CN/start/getting-started",
|
||||
"zh-CN/start/wizard",
|
||||
"zh-CN/start/onboarding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "指南",
|
||||
"pages": ["zh-CN/start/openclaw"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "安装",
|
||||
"groups": [
|
||||
{
|
||||
"group": "安装概览",
|
||||
"pages": ["zh-CN/install/index", "zh-CN/install/installer"]
|
||||
},
|
||||
{
|
||||
"group": "安装方式",
|
||||
"pages": [
|
||||
"zh-CN/install/docker",
|
||||
"zh-CN/install/nix",
|
||||
"zh-CN/install/ansible",
|
||||
"zh-CN/install/bun"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "维护",
|
||||
"pages": [
|
||||
"zh-CN/install/updating",
|
||||
"zh-CN/install/migrating",
|
||||
"zh-CN/install/uninstall"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "托管与部署",
|
||||
"pages": [
|
||||
"zh-CN/vps",
|
||||
"zh-CN/install/fly",
|
||||
"zh-CN/install/hetzner",
|
||||
"zh-CN/install/gcp",
|
||||
"zh-CN/install/macos-vm",
|
||||
"zh-CN/install/exe-dev",
|
||||
"zh-CN/install/railway",
|
||||
"zh-CN/install/render",
|
||||
"zh-CN/install/northflank"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "高级",
|
||||
"pages": ["zh-CN/install/development-channels"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "消息渠道",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/channels/index"]
|
||||
},
|
||||
{
|
||||
"group": "消息平台",
|
||||
"pages": [
|
||||
"zh-CN/channels/bluebubbles",
|
||||
"zh-CN/channels/discord",
|
||||
"zh-CN/channels/feishu",
|
||||
"zh-CN/channels/grammy",
|
||||
"zh-CN/channels/googlechat",
|
||||
"zh-CN/channels/imessage",
|
||||
"zh-CN/channels/line",
|
||||
"zh-CN/channels/matrix",
|
||||
"zh-CN/channels/mattermost",
|
||||
"zh-CN/channels/msteams",
|
||||
"zh-CN/channels/nextcloud-talk",
|
||||
"zh-CN/channels/nostr",
|
||||
"zh-CN/channels/signal",
|
||||
"zh-CN/channels/slack",
|
||||
"zh-CN/channels/telegram",
|
||||
"zh-CN/channels/tlon",
|
||||
"zh-CN/channels/twitch",
|
||||
"zh-CN/channels/whatsapp",
|
||||
"zh-CN/channels/zalo",
|
||||
"zh-CN/channels/zalouser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "配置",
|
||||
"pages": [
|
||||
"zh-CN/channels/pairing",
|
||||
"zh-CN/channels/group-messages",
|
||||
"zh-CN/channels/groups",
|
||||
"zh-CN/channels/broadcast-groups",
|
||||
"zh-CN/channels/channel-routing",
|
||||
"zh-CN/channels/location",
|
||||
"zh-CN/channels/troubleshooting"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "代理",
|
||||
"groups": [
|
||||
{
|
||||
"group": "基础",
|
||||
"pages": [
|
||||
"zh-CN/pi",
|
||||
"zh-CN/concepts/architecture",
|
||||
"zh-CN/concepts/agent",
|
||||
"zh-CN/concepts/agent-loop",
|
||||
"zh-CN/concepts/system-prompt",
|
||||
"zh-CN/concepts/context",
|
||||
"zh-CN/concepts/agent-workspace",
|
||||
"zh-CN/concepts/oauth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "引导",
|
||||
"pages": ["zh-CN/start/bootstrapping"]
|
||||
},
|
||||
{
|
||||
"group": "会话与记忆",
|
||||
"pages": [
|
||||
"zh-CN/concepts/session",
|
||||
"zh-CN/concepts/session-pruning",
|
||||
"zh-CN/concepts/session-tool",
|
||||
"zh-CN/concepts/memory",
|
||||
"zh-CN/concepts/compaction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "多代理",
|
||||
"pages": ["zh-CN/concepts/multi-agent", "zh-CN/concepts/presence"]
|
||||
},
|
||||
{
|
||||
"group": "消息与投递",
|
||||
"pages": [
|
||||
"zh-CN/concepts/messages",
|
||||
"zh-CN/concepts/streaming",
|
||||
"zh-CN/concepts/retry",
|
||||
"zh-CN/concepts/queue"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "工具",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/tools/index"]
|
||||
},
|
||||
{
|
||||
"group": "内置工具",
|
||||
"pages": [
|
||||
"zh-CN/tools/apply-patch",
|
||||
"zh-CN/brave-search",
|
||||
"zh-CN/tools/elevated",
|
||||
"zh-CN/tools/exec",
|
||||
"zh-CN/tools/exec-approvals",
|
||||
"zh-CN/tools/firecrawl",
|
||||
"zh-CN/tools/llm-task",
|
||||
"zh-CN/tools/lobster",
|
||||
"zh-CN/perplexity",
|
||||
"zh-CN/tools/reactions",
|
||||
"zh-CN/tools/thinking",
|
||||
"zh-CN/tools/web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "浏览器",
|
||||
"pages": [
|
||||
"zh-CN/tools/browser",
|
||||
"zh-CN/tools/browser-login",
|
||||
"zh-CN/tools/browser-linux-troubleshooting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "代理协作",
|
||||
"pages": [
|
||||
"zh-CN/tools/agent-send",
|
||||
"zh-CN/tools/subagents",
|
||||
"zh-CN/tools/multi-agent-sandbox-tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "技能",
|
||||
"pages": [
|
||||
"zh-CN/tools/creating-skills",
|
||||
"zh-CN/tools/slash-commands",
|
||||
"zh-CN/tools/skills",
|
||||
"zh-CN/tools/skills-config",
|
||||
"zh-CN/tools/clawhub",
|
||||
"zh-CN/tools/plugin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "扩展",
|
||||
"pages": [
|
||||
"zh-CN/plugins/voice-call",
|
||||
"zh-CN/plugins/zalouser",
|
||||
"zh-CN/plugins/manifest",
|
||||
"zh-CN/plugins/agent-tools",
|
||||
"zh-CN/prose"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "自动化与任务",
|
||||
"pages": ["zh-CN/automation/hooks", "zh-CN/automation/cron-jobs"]
|
||||
},
|
||||
{
|
||||
"group": "媒体与设备",
|
||||
"pages": [
|
||||
"zh-CN/nodes/index",
|
||||
"zh-CN/nodes/troubleshooting",
|
||||
"zh-CN/nodes/media-understanding",
|
||||
"zh-CN/nodes/images",
|
||||
"zh-CN/nodes/audio",
|
||||
"zh-CN/nodes/camera",
|
||||
"zh-CN/nodes/talk",
|
||||
"zh-CN/nodes/voicewake",
|
||||
"zh-CN/nodes/location-command",
|
||||
"zh-CN/tts"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "模型",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概览",
|
||||
"pages": ["zh-CN/providers/index", "zh-CN/providers/models"]
|
||||
},
|
||||
{
|
||||
"group": "模型概念",
|
||||
"pages": ["zh-CN/concepts/models"]
|
||||
},
|
||||
{
|
||||
"group": "配置",
|
||||
"pages": ["zh-CN/concepts/model-providers", "zh-CN/concepts/model-failover"]
|
||||
},
|
||||
{
|
||||
"group": "提供商",
|
||||
"pages": [
|
||||
"zh-CN/providers/anthropic",
|
||||
"zh-CN/providers/bedrock",
|
||||
"zh-CN/providers/claude-max-api-proxy",
|
||||
"zh-CN/providers/deepgram",
|
||||
"zh-CN/providers/github-copilot",
|
||||
"zh-CN/providers/glm",
|
||||
"zh-CN/providers/moonshot",
|
||||
"zh-CN/providers/minimax",
|
||||
"zh-CN/providers/opencode",
|
||||
"zh-CN/providers/ollama",
|
||||
"zh-CN/providers/openai",
|
||||
"zh-CN/providers/openrouter",
|
||||
"zh-CN/providers/qianfan",
|
||||
"zh-CN/providers/qwen",
|
||||
"zh-CN/providers/synthetic",
|
||||
"zh-CN/providers/venice",
|
||||
"zh-CN/providers/vercel-ai-gateway",
|
||||
"zh-CN/providers/xiaomi",
|
||||
"zh-CN/providers/zai"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "平台",
|
||||
"groups": [
|
||||
{
|
||||
"group": "平台概览",
|
||||
"pages": [
|
||||
"zh-CN/platforms/index",
|
||||
"zh-CN/platforms/macos",
|
||||
"zh-CN/platforms/linux",
|
||||
"zh-CN/platforms/windows",
|
||||
"zh-CN/platforms/android",
|
||||
"zh-CN/platforms/ios",
|
||||
"zh-CN/platforms/digitalocean",
|
||||
"zh-CN/platforms/oracle",
|
||||
"zh-CN/platforms/raspberry-pi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "macOS 配套应用",
|
||||
"pages": [
|
||||
"zh-CN/platforms/mac/dev-setup",
|
||||
"zh-CN/platforms/mac/menu-bar",
|
||||
"zh-CN/platforms/mac/voicewake",
|
||||
"zh-CN/platforms/mac/voice-overlay",
|
||||
"zh-CN/platforms/mac/webchat",
|
||||
"zh-CN/platforms/mac/canvas",
|
||||
"zh-CN/platforms/mac/child-process",
|
||||
"zh-CN/platforms/mac/health",
|
||||
"zh-CN/platforms/mac/icon",
|
||||
"zh-CN/platforms/mac/logging",
|
||||
"zh-CN/platforms/mac/permissions",
|
||||
"zh-CN/platforms/mac/remote",
|
||||
"zh-CN/platforms/mac/signing",
|
||||
"zh-CN/platforms/mac/bundled-gateway",
|
||||
"zh-CN/platforms/mac/xpc",
|
||||
"zh-CN/platforms/mac/skills",
|
||||
"zh-CN/platforms/mac/peekaboo"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "网关与运维",
|
||||
"groups": [
|
||||
{
|
||||
"group": "网关",
|
||||
"pages": [
|
||||
"zh-CN/gateway/index",
|
||||
{
|
||||
"group": "配置与运维",
|
||||
"pages": [
|
||||
"zh-CN/gateway/configuration",
|
||||
"zh-CN/gateway/configuration-examples",
|
||||
"zh-CN/gateway/authentication",
|
||||
"zh-CN/gateway/health",
|
||||
"zh-CN/gateway/heartbeat",
|
||||
"zh-CN/gateway/doctor",
|
||||
"zh-CN/gateway/logging",
|
||||
"zh-CN/gateway/gateway-lock",
|
||||
"zh-CN/gateway/background-process",
|
||||
"zh-CN/gateway/multiple-gateways",
|
||||
"zh-CN/gateway/troubleshooting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "安全与沙箱",
|
||||
"pages": [
|
||||
"zh-CN/gateway/security/index",
|
||||
"zh-CN/gateway/sandboxing",
|
||||
"zh-CN/gateway/sandbox-vs-tool-policy-vs-elevated"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "协议与 API",
|
||||
"pages": [
|
||||
"zh-CN/gateway/protocol",
|
||||
"zh-CN/gateway/bridge-protocol",
|
||||
"zh-CN/gateway/openai-http-api",
|
||||
"zh-CN/gateway/openresponses-http-api",
|
||||
"zh-CN/gateway/tools-invoke-http-api",
|
||||
"zh-CN/gateway/cli-backends",
|
||||
"zh-CN/gateway/local-models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "网络与发现",
|
||||
"pages": [
|
||||
"zh-CN/gateway/network-model",
|
||||
"zh-CN/gateway/pairing",
|
||||
"zh-CN/gateway/discovery",
|
||||
"zh-CN/gateway/bonjour"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "远程访问",
|
||||
"pages": [
|
||||
"zh-CN/gateway/remote",
|
||||
"zh-CN/gateway/remote-gateway-readme",
|
||||
"zh-CN/gateway/tailscale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "运维专题",
|
||||
"pages": ["zh-CN/network", "zh-CN/logging"]
|
||||
},
|
||||
{
|
||||
"group": "安全",
|
||||
"pages": ["zh-CN/security/formal-verification"]
|
||||
},
|
||||
{
|
||||
"group": "Web 界面",
|
||||
"pages": [
|
||||
"zh-CN/web/index",
|
||||
"zh-CN/web/control-ui",
|
||||
"zh-CN/web/dashboard",
|
||||
"zh-CN/web/webchat",
|
||||
"zh-CN/web/tui"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "参考",
|
||||
"groups": [
|
||||
{
|
||||
"group": "CLI 命令",
|
||||
"pages": [
|
||||
"zh-CN/cli/index",
|
||||
"zh-CN/cli/acp",
|
||||
"zh-CN/cli/agent",
|
||||
"zh-CN/cli/agents",
|
||||
"zh-CN/cli/approvals",
|
||||
"zh-CN/cli/browser",
|
||||
"zh-CN/cli/channels",
|
||||
"zh-CN/cli/config",
|
||||
"zh-CN/cli/configure",
|
||||
"zh-CN/cli/cron",
|
||||
"zh-CN/cli/dashboard",
|
||||
"zh-CN/cli/devices",
|
||||
"zh-CN/cli/directory",
|
||||
"zh-CN/cli/dns",
|
||||
"zh-CN/cli/docs",
|
||||
"zh-CN/cli/doctor",
|
||||
"zh-CN/cli/gateway",
|
||||
"zh-CN/cli/health",
|
||||
"zh-CN/cli/hooks",
|
||||
"zh-CN/cli/logs",
|
||||
"zh-CN/cli/memory",
|
||||
"zh-CN/cli/message",
|
||||
"zh-CN/cli/models",
|
||||
"zh-CN/cli/node",
|
||||
"zh-CN/cli/nodes",
|
||||
"zh-CN/cli/onboard",
|
||||
"zh-CN/cli/pairing",
|
||||
"zh-CN/cli/plugins",
|
||||
"zh-CN/cli/reset",
|
||||
"zh-CN/cli/sandbox",
|
||||
"zh-CN/cli/security",
|
||||
"zh-CN/cli/sessions",
|
||||
"zh-CN/cli/setup",
|
||||
"zh-CN/cli/skills",
|
||||
"zh-CN/cli/status",
|
||||
"zh-CN/cli/system",
|
||||
"zh-CN/cli/tui",
|
||||
"zh-CN/cli/uninstall",
|
||||
"zh-CN/cli/update",
|
||||
"zh-CN/cli/voicecall",
|
||||
"zh-CN/cli/webhooks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "RPC 与 API",
|
||||
"pages": ["zh-CN/reference/rpc", "zh-CN/reference/device-models"]
|
||||
},
|
||||
{
|
||||
"group": "模板",
|
||||
"pages": [
|
||||
"zh-CN/reference/AGENTS.default",
|
||||
"zh-CN/reference/templates/AGENTS",
|
||||
"zh-CN/reference/templates/BOOT",
|
||||
"zh-CN/reference/templates/BOOTSTRAP",
|
||||
"zh-CN/reference/templates/HEARTBEAT",
|
||||
"zh-CN/reference/templates/IDENTITY",
|
||||
"zh-CN/reference/templates/SOUL",
|
||||
"zh-CN/reference/templates/TOOLS",
|
||||
"zh-CN/reference/templates/USER"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "技术参考",
|
||||
"pages": [
|
||||
"zh-CN/reference/wizard",
|
||||
"zh-CN/reference/token-use",
|
||||
"zh-CN/reference/api-usage-costs",
|
||||
"zh-CN/reference/transcript-hygiene",
|
||||
"zh-CN/date-time"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "概念内部机制",
|
||||
"pages": [
|
||||
"zh-CN/concepts/typebox",
|
||||
"zh-CN/concepts/markdown-formatting",
|
||||
"zh-CN/concepts/typing-indicators",
|
||||
"zh-CN/concepts/usage-tracking",
|
||||
"zh-CN/concepts/timezone"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "项目",
|
||||
"pages": ["zh-CN/reference/credits"]
|
||||
},
|
||||
{
|
||||
"group": "发布策略",
|
||||
"pages": ["zh-CN/reference/RELEASING", "zh-CN/reference/test"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "帮助",
|
||||
"groups": [
|
||||
{
|
||||
"group": "帮助",
|
||||
"pages": ["zh-CN/help/index", "zh-CN/help/troubleshooting", "zh-CN/help/faq"]
|
||||
},
|
||||
{
|
||||
"group": "社区",
|
||||
"pages": ["zh-CN/start/lore"]
|
||||
},
|
||||
{
|
||||
"group": "环境与调试",
|
||||
"pages": [
|
||||
"zh-CN/help/environment",
|
||||
"zh-CN/help/debugging",
|
||||
"zh-CN/help/testing",
|
||||
"zh-CN/help/scripts",
|
||||
"zh-CN/debug/node-issue",
|
||||
"zh-CN/diagnostics/flags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Node 运行时",
|
||||
"pages": ["zh-CN/install/node"]
|
||||
},
|
||||
{
|
||||
"group": "压缩机制内部参考",
|
||||
"pages": ["zh-CN/reference/session-management-compaction"]
|
||||
},
|
||||
{
|
||||
"group": "开发者设置",
|
||||
"pages": ["zh-CN/start/setup", "zh-CN/pi-dev"]
|
||||
},
|
||||
{
|
||||
"group": "文档元信息",
|
||||
"pages": ["zh-CN/start/hubs", "zh-CN/start/docs-directory", "zh-CN/AGENTS"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"language": "ja",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "はじめに",
|
||||
"groups": [
|
||||
{
|
||||
"group": "概要",
|
||||
"pages": ["ja-JP/index"]
|
||||
},
|
||||
{
|
||||
"group": "初回セットアップ",
|
||||
"pages": ["ja-JP/start/getting-started", "ja-JP/start/wizard"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Model authentication: OAuth, API keys, and Claude CLI reuse"
|
||||
summary: "Model authentication: OAuth, API keys, and legacy Anthropic setup-token"
|
||||
read_when:
|
||||
- Debugging model auth or OAuth expiry
|
||||
- Documenting authentication or credential storage
|
||||
@@ -9,7 +9,7 @@ title: "Authentication"
|
||||
# Authentication (Model Providers)
|
||||
|
||||
<Note>
|
||||
This page covers **model provider** authentication (API keys, OAuth, Claude CLI reuse). For **gateway connection** authentication (token, password, trusted-proxy), see [Configuration](/gateway/configuration) and [Trusted Proxy Auth](/gateway/trusted-proxy-auth).
|
||||
This page covers **model provider** authentication (API keys, OAuth, and legacy Anthropic setup-token). 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
|
||||
@@ -26,8 +26,9 @@ For credential eligibility/reason-code rules used by `models status --probe`, se
|
||||
|
||||
If you’re running a long-lived gateway, start with an API key for your chosen
|
||||
provider.
|
||||
For Anthropic specifically, API key auth is the safe path. Claude CLI reuse is
|
||||
the other supported subscription-style setup path.
|
||||
For Anthropic specifically, API key auth is the safe path. Anthropic
|
||||
subscription-style auth inside OpenClaw is the legacy setup-token path and
|
||||
should be treated as an **Extra Usage** path, not a plan-limits path.
|
||||
|
||||
1. Create an API key in your provider console.
|
||||
2. Put it on the **gateway host** (the machine running `openclaw gateway`).
|
||||
@@ -68,8 +69,9 @@ OpenClaw users that the **OpenClaw** Claude-login path counts as third-party
|
||||
harness usage and requires **Extra Usage** billed separately from the
|
||||
subscription.
|
||||
|
||||
For the clearest setup path, use an Anthropic API key or migrate to Claude CLI
|
||||
on the gateway host.
|
||||
For the clearest setup path, use an Anthropic API key. If you must keep a
|
||||
subscription-style Anthropic path in OpenClaw, use the legacy setup-token path
|
||||
with the expectation that Anthropic treats it as **Extra Usage**.
|
||||
|
||||
Manual token entry (any provider; writes `auth-profiles.json` + updates config):
|
||||
|
||||
@@ -108,41 +110,17 @@ Notes:
|
||||
Optional ops scripts (systemd/Termux) are documented here:
|
||||
[Auth monitoring scripts](/help/scripts#auth-monitoring-scripts)
|
||||
|
||||
## Anthropic: Claude CLI migration
|
||||
## Anthropic note
|
||||
|
||||
If Claude CLI is already installed and signed in on the gateway host, you can
|
||||
switch an existing Anthropic setup over to the CLI backend. This is a
|
||||
supported OpenClaw migration path for reusing a local Claude CLI login on that
|
||||
host.
|
||||
The Anthropic `claude-cli` backend was removed.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- `claude` installed on the gateway host
|
||||
- Claude CLI already signed in there with `claude auth login`
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider anthropic --method cli --set-default
|
||||
```
|
||||
|
||||
This keeps your existing Anthropic auth profiles for rollback, but changes the
|
||||
default model selection to `claude-cli/...` and adds matching Claude CLI
|
||||
allowlist entries under `agents.defaults.models`.
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
Onboarding shortcut:
|
||||
|
||||
```bash
|
||||
openclaw onboard --auth-choice anthropic-cli
|
||||
```
|
||||
|
||||
Interactive `openclaw onboard` and `openclaw configure` still prefer Claude CLI
|
||||
for Anthropic, but Anthropic setup-token is available again as a
|
||||
legacy/manual path and should be used with the Extra Usage billing expectation.
|
||||
- Use Anthropic API keys for Anthropic traffic in OpenClaw.
|
||||
- Anthropic setup-token remains a legacy/manual path and should be used with
|
||||
the Extra Usage billing expectation Anthropic communicated to OpenClaw users.
|
||||
- `openclaw doctor` now detects stale removed Anthropic Claude CLI state. If
|
||||
stored credential bytes still exist, doctor converts them back into
|
||||
Anthropic token/OAuth profiles. If not, doctor removes the stale Claude CLI
|
||||
config and points you to API key or setup-token recovery.
|
||||
|
||||
## Checking model auth status
|
||||
|
||||
@@ -198,8 +176,8 @@ to one model id rather than the whole provider profile.
|
||||
|
||||
### "No credentials found"
|
||||
|
||||
If the Anthropic profile is missing, migrate that setup to Claude CLI or an API
|
||||
key on the **gateway host**, then re-check:
|
||||
If the Anthropic profile is missing, configure an Anthropic API key on the
|
||||
**gateway host** or set up the legacy Anthropic setup-token path, then re-check:
|
||||
|
||||
```bash
|
||||
openclaw models status
|
||||
@@ -208,11 +186,16 @@ openclaw models status
|
||||
### Token expiring/expired
|
||||
|
||||
Run `openclaw models status` to confirm which profile is expiring. If a legacy
|
||||
Anthropic token profile is missing or expired, migrate that setup to Claude CLI
|
||||
or an API key.
|
||||
Anthropic token profile is missing or expired, refresh that setup via
|
||||
setup-token or migrate to an Anthropic API key.
|
||||
|
||||
## Claude CLI requirements
|
||||
If the machine still has stale removed Anthropic Claude CLI state from older
|
||||
builds, run:
|
||||
|
||||
Only needed for the Anthropic Claude CLI reuse path:
|
||||
```bash
|
||||
openclaw doctor --yes
|
||||
```
|
||||
|
||||
- Claude Code CLI installed (`claude` command available)
|
||||
Doctor converts `anthropic:claude-cli` back to Anthropic token/OAuth when the
|
||||
stored credential bytes still exist. Otherwise it removes stale Claude CLI
|
||||
profile/config/model refs and leaves the next-step guidance.
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
---
|
||||
summary: "CLI backends: text-only fallback via local AI CLIs"
|
||||
read_when:
|
||||
- You want a reliable fallback when API providers fail
|
||||
- You are running Claude CLI or other local AI CLIs and want to reuse them
|
||||
- You need a text-only, tool-free path that still supports sessions and images
|
||||
title: "CLI Backends"
|
||||
---
|
||||
|
||||
# CLI backends (fallback runtime)
|
||||
|
||||
OpenClaw can run **local AI CLIs** as a **text-only fallback** when API providers are down,
|
||||
rate-limited, or temporarily misbehaving. This is intentionally conservative:
|
||||
|
||||
- **Tools are disabled** (no tool calls).
|
||||
- **Text in → text out** (reliable, with Claude CLI partial text streaming when enabled).
|
||||
- **Sessions are supported** (so follow-up turns stay coherent).
|
||||
- **Images can be passed through** if the CLI accepts image paths.
|
||||
|
||||
This is designed as a **safety net** rather than a primary path. Use it when you
|
||||
want “always works” text responses without relying on external APIs.
|
||||
|
||||
If you want a full harness runtime with ACP session controls, background tasks,
|
||||
thread/conversation binding, and persistent external coding sessions, use
|
||||
[ACP Agents](/tools/acp-agents) instead. CLI backends are not ACP.
|
||||
|
||||
## Beginner-friendly quick start
|
||||
|
||||
You can use Claude CLI **without any config** (the bundled Anthropic plugin
|
||||
registers a default backend):
|
||||
|
||||
```bash
|
||||
openclaw agent --message "hi" --model claude-cli/claude-sonnet-4-6
|
||||
```
|
||||
|
||||
Codex CLI also works out of the box (via the bundled OpenAI plugin):
|
||||
|
||||
```bash
|
||||
openclaw agent --message "hi" --model codex-cli/gpt-5.4
|
||||
```
|
||||
|
||||
If your gateway runs under launchd/systemd and PATH is minimal, add just the
|
||||
command path:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
cliBackends: {
|
||||
"claude-cli": {
|
||||
command: "/opt/homebrew/bin/claude",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
That’s it. No keys, no extra auth config needed beyond the CLI itself.
|
||||
|
||||
If you use a bundled CLI backend as the **primary message provider** on a
|
||||
gateway host, OpenClaw now auto-loads the owning bundled plugin when your config
|
||||
explicitly references that backend in a model ref or under
|
||||
`agents.defaults.cliBackends`.
|
||||
|
||||
## Using it as a fallback
|
||||
|
||||
Add a CLI backend to your fallback list so it only runs when primary models fail:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "anthropic/claude-opus-4-6",
|
||||
fallbacks: ["claude-cli/claude-sonnet-4-6", "claude-cli/claude-opus-4-6"],
|
||||
},
|
||||
models: {
|
||||
"anthropic/claude-opus-4-6": { alias: "Opus" },
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
"claude-cli/claude-opus-4-6": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- If you use `agents.defaults.models` (allowlist), you must include `claude-cli/...`.
|
||||
- If the primary provider fails (auth, rate limits, timeouts), OpenClaw will
|
||||
try the CLI backend next.
|
||||
- The bundled Claude CLI backend still accepts shorter aliases like
|
||||
`claude-cli/opus`, `claude-cli/opus-4.6`, or `claude-cli/sonnet`, but docs
|
||||
and config examples use the canonical `claude-cli/claude-*` refs.
|
||||
|
||||
## Configuration overview
|
||||
|
||||
All CLI backends live under:
|
||||
|
||||
```
|
||||
agents.defaults.cliBackends
|
||||
```
|
||||
|
||||
Each entry is keyed by a **provider id** (e.g. `claude-cli`, `my-cli`).
|
||||
The provider id becomes the left side of your model ref:
|
||||
|
||||
```
|
||||
<provider>/<model>
|
||||
```
|
||||
|
||||
### Example configuration
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
cliBackends: {
|
||||
"claude-cli": {
|
||||
command: "/opt/homebrew/bin/claude",
|
||||
},
|
||||
"my-cli": {
|
||||
command: "my-cli",
|
||||
args: ["--json"],
|
||||
output: "json",
|
||||
input: "arg",
|
||||
modelArg: "--model",
|
||||
modelAliases: {
|
||||
"claude-opus-4-6": "opus",
|
||||
"claude-sonnet-4-6": "sonnet",
|
||||
},
|
||||
sessionArg: "--session",
|
||||
sessionMode: "existing",
|
||||
sessionIdFields: ["session_id", "conversation_id"],
|
||||
systemPromptArg: "--system",
|
||||
systemPromptWhen: "first",
|
||||
imageArg: "--image",
|
||||
imageMode: "repeat",
|
||||
serialize: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Selects a backend** based on the provider prefix (`claude-cli/...`).
|
||||
2. **Builds a system prompt** using the same OpenClaw prompt + workspace context.
|
||||
3. **Executes the CLI** with a session id (if supported) so history stays consistent.
|
||||
4. **Parses output** (JSON or plain text) and returns the final text.
|
||||
5. **Persists session ids** per backend, so follow-ups reuse the same CLI session.
|
||||
|
||||
## Sessions
|
||||
|
||||
- If the CLI supports sessions, set `sessionArg` (e.g. `--session-id`) or
|
||||
`sessionArgs` (placeholder `{sessionId}`) when the ID needs to be inserted
|
||||
into multiple flags.
|
||||
- If the CLI uses a **resume subcommand** with different flags, set
|
||||
`resumeArgs` (replaces `args` when resuming) and optionally `resumeOutput`
|
||||
(for non-JSON resumes).
|
||||
- `sessionMode`:
|
||||
- `always`: always send a session id (new UUID if none stored).
|
||||
- `existing`: only send a session id if one was stored before.
|
||||
- `none`: never send a session id.
|
||||
|
||||
Serialization notes:
|
||||
|
||||
- `serialize: true` keeps same-lane runs ordered.
|
||||
- Most CLIs serialize on one provider lane.
|
||||
- `claude-cli` is narrower: resumed runs serialize per Claude session id, and fresh runs serialize per workspace path. Independent workspaces can run in parallel.
|
||||
- OpenClaw drops stored CLI session reuse when the backend auth state changes, including relogin, token rotation, or a changed auth profile credential.
|
||||
|
||||
## Images (pass-through)
|
||||
|
||||
If your CLI accepts image paths, set `imageArg`:
|
||||
|
||||
```json5
|
||||
imageArg: "--image",
|
||||
imageMode: "repeat"
|
||||
```
|
||||
|
||||
OpenClaw will write base64 images to temp files. If `imageArg` is set, those
|
||||
paths are passed as CLI args. If `imageArg` is missing, OpenClaw appends the
|
||||
file paths to the prompt (path injection), which is enough for CLIs that auto-
|
||||
load local files from plain paths (Claude CLI behavior).
|
||||
|
||||
## Inputs / outputs
|
||||
|
||||
- `output: "json"` (default) tries to parse JSON and extract text + session id.
|
||||
- For Gemini CLI JSON output, OpenClaw reads reply text from `response` and
|
||||
usage from `stats` when `usage` is missing or empty.
|
||||
- `output: "jsonl"` parses JSONL streams (for example Claude CLI `stream-json`
|
||||
and Codex CLI `--json`) and extracts the final agent message plus session
|
||||
identifiers when present.
|
||||
- `output: "text"` treats stdout as the final response.
|
||||
|
||||
Input modes:
|
||||
|
||||
- `input: "arg"` (default) passes the prompt as the last CLI arg.
|
||||
- `input: "stdin"` sends the prompt via stdin.
|
||||
- If the prompt is very long and `maxPromptArgChars` is set, stdin is used.
|
||||
|
||||
## Defaults (plugin-owned)
|
||||
|
||||
The bundled Anthropic plugin registers a default for `claude-cli`:
|
||||
|
||||
- `command: "claude"`
|
||||
- `args: ["-p", "--output-format", "stream-json", "--include-partial-messages", "--verbose", "--permission-mode", "bypassPermissions"]`
|
||||
- `resumeArgs: ["-p", "--output-format", "stream-json", "--include-partial-messages", "--verbose", "--permission-mode", "bypassPermissions", "--resume", "{sessionId}"]`
|
||||
- `output: "jsonl"`
|
||||
- `input: "stdin"`
|
||||
- `modelArg: "--model"`
|
||||
- `systemPromptArg: "--append-system-prompt"`
|
||||
- `sessionArg: "--session-id"`
|
||||
- `systemPromptWhen: "first"`
|
||||
- `sessionMode: "always"`
|
||||
|
||||
The bundled OpenAI plugin also registers a default for `codex-cli`:
|
||||
|
||||
- `command: "codex"`
|
||||
- `args: ["exec","--json","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]`
|
||||
- `resumeArgs: ["exec","resume","{sessionId}","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]`
|
||||
- `output: "jsonl"`
|
||||
- `resumeOutput: "text"`
|
||||
- `modelArg: "--model"`
|
||||
- `imageArg: "--image"`
|
||||
- `sessionMode: "existing"`
|
||||
|
||||
The bundled Google plugin also registers a default for `google-gemini-cli`:
|
||||
|
||||
- `command: "gemini"`
|
||||
- `args: ["--prompt", "--output-format", "json"]`
|
||||
- `resumeArgs: ["--resume", "{sessionId}", "--prompt", "--output-format", "json"]`
|
||||
- `modelArg: "--model"`
|
||||
- `sessionMode: "existing"`
|
||||
- `sessionIdFields: ["session_id", "sessionId"]`
|
||||
|
||||
Prerequisite: the local Gemini CLI must be installed and available as
|
||||
`gemini` on `PATH` (`brew install gemini-cli` or
|
||||
`npm install -g @google/gemini-cli`).
|
||||
|
||||
Gemini CLI JSON notes:
|
||||
|
||||
- Reply text is read from the JSON `response` field.
|
||||
- Usage falls back to `stats` when `usage` is absent or empty.
|
||||
- `stats.cached` is normalized into OpenClaw `cacheRead`.
|
||||
- If `stats.input` is missing, OpenClaw derives input tokens from
|
||||
`stats.input_tokens - stats.cached`.
|
||||
|
||||
Override only if needed (common: absolute `command` path).
|
||||
|
||||
## Plugin-owned defaults
|
||||
|
||||
CLI backend defaults are now part of the plugin surface:
|
||||
|
||||
- Plugins register them with `api.registerCliBackend(...)`.
|
||||
- The backend `id` becomes the provider prefix in model refs.
|
||||
- User config in `agents.defaults.cliBackends.<id>` still overrides the plugin default.
|
||||
- Backend-specific config cleanup stays plugin-owned through the optional
|
||||
`normalizeConfig` hook.
|
||||
|
||||
## Bundle MCP overlays
|
||||
|
||||
CLI backends still do **not** receive OpenClaw tool calls, but a backend can opt
|
||||
into a generated MCP config overlay with `bundleMcp: true`.
|
||||
|
||||
Current bundled behavior:
|
||||
|
||||
- `claude-cli`: `bundleMcp: true`
|
||||
- `codex-cli`: no bundle MCP overlay
|
||||
- `google-gemini-cli`: no bundle MCP overlay
|
||||
|
||||
When bundle MCP is enabled, OpenClaw:
|
||||
|
||||
- loads enabled bundle-MCP servers for the current workspace
|
||||
- merges them with any existing backend `--mcp-config`
|
||||
- rewrites the CLI args to pass `--strict-mcp-config --mcp-config <generated-file>`
|
||||
|
||||
If no MCP servers are enabled, OpenClaw still injects a strict empty config.
|
||||
That prevents background Claude CLI runs from inheriting ambient user/global MCP
|
||||
servers unexpectedly.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **No OpenClaw tools** (the CLI backend never receives tool calls). Some CLIs
|
||||
may still run their own agent tooling. Backends with `bundleMcp: true`
|
||||
can still receive a generated MCP config overlay for their own CLI-native MCP
|
||||
support.
|
||||
- **Streaming is backend-specific**. Claude CLI forwards partial text from
|
||||
`stream-json`; other CLI backends may still be buffered until exit.
|
||||
- **Structured outputs** depend on the CLI’s JSON format.
|
||||
- **Codex CLI sessions** resume via text output (no JSONL), which is less
|
||||
structured than the initial `--json` run. OpenClaw sessions still work
|
||||
normally.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **CLI not found**: set `command` to a full path.
|
||||
- **Wrong model name**: use `modelAliases` to map `provider/model` → CLI model.
|
||||
- **No session continuity**: ensure `sessionArg` is set and `sessionMode` is not
|
||||
`none` (Codex CLI currently cannot resume with JSON output).
|
||||
- **Images ignored**: set `imageArg` (and verify CLI supports file paths).
|
||||
@@ -73,6 +73,7 @@ Use `channels.defaults` for shared group-policy and heartbeat behavior across pr
|
||||
channels: {
|
||||
defaults: {
|
||||
groupPolicy: "allowlist", // open | allowlist | disabled
|
||||
contextVisibility: "all", // all | allowlist | allowlist_quote
|
||||
heartbeat: {
|
||||
showOk: false,
|
||||
showAlerts: true,
|
||||
@@ -84,6 +85,7 @@ Use `channels.defaults` for shared group-policy and heartbeat behavior across pr
|
||||
```
|
||||
|
||||
- `channels.defaults.groupPolicy`: fallback group policy when a provider-level `groupPolicy` is unset.
|
||||
- `channels.defaults.contextVisibility`: default supplemental context visibility mode for all channels. Values: `all` (default, include all quoted/thread/history context), `allowlist` (only include context from allowlisted senders), `allowlist_quote` (same as allowlist but keep explicit quote/reply context). Per-channel override: `channels.<channel>.contextVisibility`.
|
||||
- `channels.defaults.heartbeat.showOk`: include healthy channel statuses in heartbeat output.
|
||||
- `channels.defaults.heartbeat.showAlerts`: include degraded/error statuses in heartbeat output.
|
||||
- `channels.defaults.heartbeat.useIndicator`: render compact indicator-style heartbeat output.
|
||||
@@ -177,7 +179,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
{ command: "generate", description: "Create an image" },
|
||||
],
|
||||
historyLimit: 50,
|
||||
replyToMode: "first", // off | first | all
|
||||
replyToMode: "first", // off | first | all | batched
|
||||
linkPreview: true,
|
||||
streaming: "partial", // off | partial | block | progress (default: off; opt in explicitly to avoid preview-edit rate limits)
|
||||
actions: { reactions: true, sendMessage: true },
|
||||
@@ -237,7 +239,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
events: true,
|
||||
moderation: false,
|
||||
},
|
||||
replyToMode: "off", // off | first | all
|
||||
replyToMode: "off", // off | first | all | batched
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: ["1234567890", "123456789012345678"],
|
||||
dm: { enabled: true, groupEnabled: false, groupChannels: ["openclaw-dm"] },
|
||||
@@ -291,6 +293,14 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
openai: { voice: "alloy" },
|
||||
},
|
||||
},
|
||||
execApprovals: {
|
||||
enabled: "auto", // true | false | "auto"
|
||||
approvers: ["987654321098765432"],
|
||||
agentFilter: ["default"],
|
||||
sessionFilter: ["discord:"],
|
||||
target: "dm", // dm | channel | both
|
||||
cleanupAfterResolve: false,
|
||||
},
|
||||
retry: {
|
||||
attempts: 3,
|
||||
minDelayMs: 500,
|
||||
@@ -323,6 +333,13 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
- `channels.discord.streaming` is the canonical stream mode key. Legacy `streamMode` and boolean `streaming` values are auto-migrated.
|
||||
- `channels.discord.autoPresence` maps runtime availability to bot presence (healthy => online, degraded => idle, exhausted => dnd) and allows optional status text overrides.
|
||||
- `channels.discord.dangerouslyAllowNameMatching` re-enables mutable name/tag matching (break-glass compatibility mode).
|
||||
- `channels.discord.execApprovals`: Discord-native exec approval delivery and approver authorization.
|
||||
- `enabled`: `true`, `false`, or `"auto"` (default). In auto mode, exec approvals activate when approvers can be resolved from `approvers` or `commands.ownerAllowFrom`.
|
||||
- `approvers`: Discord user IDs allowed to approve exec requests. Falls back to `commands.ownerAllowFrom` when omitted.
|
||||
- `agentFilter`: optional agent ID allowlist. Omit to forward approvals for all agents.
|
||||
- `sessionFilter`: optional session key patterns (substring or regex).
|
||||
- `target`: where to send approval prompts. `"dm"` (default) sends to approver DMs, `"channel"` sends to the originating channel, `"both"` sends to both. When target includes `"channel"`, buttons are only usable by resolved approvers.
|
||||
- `cleanupAfterResolve`: when `true`, deletes approval DMs after approval, denial, or timeout.
|
||||
|
||||
**Reaction notification modes:** `off` (none), `own` (bot's messages, default), `all` (all messages), `allowlist` (from `guilds.<id>.users` on all messages).
|
||||
|
||||
@@ -388,7 +405,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
allowBots: false,
|
||||
reactionNotifications: "own",
|
||||
reactionAllowlist: ["U123"],
|
||||
replyToMode: "off", // off | first | all
|
||||
replyToMode: "off", // off | first | all | batched
|
||||
thread: {
|
||||
historyScope: "thread", // thread | channel
|
||||
inheritParent: false,
|
||||
@@ -412,6 +429,13 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
streaming: "partial", // off | partial | block | progress (preview mode)
|
||||
nativeStreaming: true, // use Slack native streaming API when streaming=partial
|
||||
mediaMaxMb: 20,
|
||||
execApprovals: {
|
||||
enabled: "auto", // true | false | "auto"
|
||||
approvers: ["U123"],
|
||||
agentFilter: ["default"],
|
||||
sessionFilter: ["slack:"],
|
||||
target: "dm", // dm | channel | both
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -436,6 +460,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
**Thread session isolation:** `thread.historyScope` is per-thread (default) or shared across channel. `thread.inheritParent` copies parent channel transcript to new threads.
|
||||
|
||||
- `typingReaction` adds a temporary reaction to the inbound Slack message while a reply is running, then removes it on completion. Use a Slack emoji shortcode such as `"hourglass_flowing_sand"`.
|
||||
- `channels.slack.execApprovals`: Slack-native exec approval delivery and approver authorization. Same schema as Discord: `enabled` (`true`/`false`/`"auto"`), `approvers` (Slack user IDs), `agentFilter`, `sessionFilter`, and `target` (`"dm"`, `"channel"`, or `"both"`).
|
||||
|
||||
| Action group | Default | Notes |
|
||||
| ------------ | ------- | ---------------------- |
|
||||
@@ -623,6 +648,14 @@ Matrix is extension-backed and configured under `channels.matrix`.
|
||||
- `channels.matrix.proxy` routes Matrix HTTP traffic through an explicit HTTP(S) proxy. Named accounts can override it with `channels.matrix.accounts.<id>.proxy`.
|
||||
- `channels.matrix.allowPrivateNetwork` allows private/internal homeservers. `proxy` and `allowPrivateNetwork` are independent controls.
|
||||
- `channels.matrix.defaultAccount` selects the preferred account in multi-account setups.
|
||||
- `channels.matrix.execApprovals`: Matrix-native exec approval delivery and approver authorization.
|
||||
- `enabled`: `true`, `false`, or `"auto"` (default). In auto mode, exec approvals activate when approvers can be resolved from `approvers` or `commands.ownerAllowFrom`.
|
||||
- `approvers`: Matrix user IDs (e.g. `@owner:example.org`) allowed to approve exec requests.
|
||||
- `agentFilter`: optional agent ID allowlist. Omit to forward approvals for all agents.
|
||||
- `sessionFilter`: optional session key patterns (substring or regex).
|
||||
- `target`: where to send approval prompts. `"dm"` (default), `"channel"` (originating room), or `"both"`.
|
||||
- Per-account overrides: `channels.matrix.accounts.<id>.execApprovals`.
|
||||
- `channels.matrix.dm.sessionScope` controls how Matrix DMs group into sessions: `per-user` (default) shares by routed peer, while `per-room` isolates each DM room.
|
||||
- Matrix status probes and live directory lookups use the same proxy policy as runtime traffic.
|
||||
- Full Matrix configuration, targeting rules, and setup examples are documented in [Matrix](/channels/matrix).
|
||||
|
||||
@@ -994,9 +1027,9 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- If you select a provider/model directly, configure the matching provider auth/API key too (for example `GEMINI_API_KEY` or `GOOGLE_API_KEY` for `google/*`, `OPENAI_API_KEY` for `openai/*`, `FAL_KEY` for `fal/*`).
|
||||
- If omitted, `image_generate` can still infer an auth-backed provider default. It tries the current default provider first, then the remaining registered image-generation providers in provider-id order.
|
||||
- `videoGenerationModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
- Used by the shared video-generation capability.
|
||||
- Used by the shared video-generation capability and the built-in `video_generate` tool.
|
||||
- Typical values: `qwen/wan2.6-t2v`, `qwen/wan2.6-i2v`, `qwen/wan2.6-r2v`, `qwen/wan2.6-r2v-flash`, or `qwen/wan2.7-r2v`.
|
||||
- Set this explicitly before using shared video generation. Unlike `imageGenerationModel`, the video-generation runtime does not infer a provider default yet.
|
||||
- If omitted, `video_generate` can still infer an auth-backed provider default. It tries the current default provider first, then the remaining registered video-generation providers in provider-id order.
|
||||
- If you select a provider/model directly, configure the matching provider auth/API key too.
|
||||
- The bundled Qwen video-generation provider currently supports up to 1 output video, 1 input image, 4 input videos, 10 seconds duration, and provider-level `size`, `aspectRatio`, `resolution`, `audio`, and `watermark` options.
|
||||
- `pdfModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
@@ -1032,37 +1065,6 @@ Z.AI GLM-4.x models automatically enable thinking mode unless you set `--thinkin
|
||||
Z.AI models enable `tool_stream` by default for tool call streaming. Set `agents.defaults.models["zai/<model>"].params.tool_stream` to `false` to disable it.
|
||||
Anthropic Claude 4.6 models default to `adaptive` thinking when no explicit thinking level is set.
|
||||
|
||||
### `agents.defaults.cliBackends`
|
||||
|
||||
Optional CLI backends for text-only fallback runs (no tool calls). Useful as a backup when API providers fail.
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
cliBackends: {
|
||||
"claude-cli": {
|
||||
command: "/opt/homebrew/bin/claude",
|
||||
},
|
||||
"my-cli": {
|
||||
command: "my-cli",
|
||||
args: ["--json"],
|
||||
output: "json",
|
||||
modelArg: "--model",
|
||||
sessionArg: "--session",
|
||||
sessionMode: "existing",
|
||||
systemPromptArg: "--system",
|
||||
systemPromptWhen: "first",
|
||||
imageArg: "--image",
|
||||
imageMode: "repeat",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- CLI backends are text-first; tools are always disabled.
|
||||
- Sessions supported when `sessionArg` is set.
|
||||
- Image pass-through supported when `imageArg` accepts file paths.
|
||||
|
||||
@@ -1904,12 +1906,12 @@ Defaults for Talk mode (macOS/iOS/Android).
|
||||
|
||||
Local onboarding defaults new local configs to `tools.profile: "coding"` when unset (existing explicit profiles are preserved).
|
||||
|
||||
| Profile | Includes |
|
||||
| ----------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| `minimal` | `session_status` only |
|
||||
| `coding` | `group:fs`, `group:runtime`, `group:web`, `group:sessions`, `group:memory`, `cron`, `image`, `image_generate` |
|
||||
| `messaging` | `group:messaging`, `sessions_list`, `sessions_history`, `sessions_send`, `session_status` |
|
||||
| `full` | No restriction (same as unset) |
|
||||
| Profile | Includes |
|
||||
| ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `minimal` | `session_status` only |
|
||||
| `coding` | `group:fs`, `group:runtime`, `group:web`, `group:sessions`, `group:memory`, `cron`, `image`, `image_generate`, `video_generate` |
|
||||
| `messaging` | `group:messaging`, `sessions_list`, `sessions_history`, `sessions_send`, `session_status` |
|
||||
| `full` | No restriction (same as unset) |
|
||||
|
||||
### Tool groups
|
||||
|
||||
@@ -1925,7 +1927,7 @@ Local onboarding defaults new local configs to `tools.profile: "coding"` when un
|
||||
| `group:messaging` | `message` |
|
||||
| `group:nodes` | `nodes` |
|
||||
| `group:agents` | `agents_list` |
|
||||
| `group:media` | `image`, `image_generate`, `tts` |
|
||||
| `group:media` | `image`, `image_generate`, `video_generate`, `tts` |
|
||||
| `group:openclaw` | All built-in tools (excludes provider plugins) |
|
||||
|
||||
### `tools.allow` / `tools.deny`
|
||||
@@ -2180,6 +2182,26 @@ Notes:
|
||||
- File permissions are `0700` for directories and `0600` for files.
|
||||
- Cleanup follows the `cleanup` policy: `delete` always removes attachments; `keep` retains them only when `retainOnSessionKeep: true`.
|
||||
|
||||
### `tools.experimental`
|
||||
|
||||
Experimental built-in tool flags. Default off unless a runtime-specific auto-enable rule applies.
|
||||
|
||||
```json5
|
||||
{
|
||||
tools: {
|
||||
experimental: {
|
||||
planTool: true, // enable experimental update_plan
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `planTool`: enables the structured `update_plan` tool for non-trivial multi-step work tracking.
|
||||
- Default: `false` for non-OpenAI providers. OpenAI and OpenAI Codex runs auto-enable it.
|
||||
- When enabled, the system prompt also adds usage guidance so the model only uses it for substantial work and keeps at most one step `in_progress`.
|
||||
|
||||
### `agents.defaults.subagents`
|
||||
|
||||
```json5
|
||||
@@ -2260,17 +2282,22 @@ OpenClaw uses the built-in model catalog. Add custom providers via `models.provi
|
||||
- `models.providers.*.authHeader`: force credential transport in the `Authorization` header when required.
|
||||
- `models.providers.*.baseUrl`: upstream API base URL.
|
||||
- `models.providers.*.headers`: extra static headers for proxy/tenant routing.
|
||||
- `models.providers.*.request`: transport overrides for model-provider HTTP requests.
|
||||
- `request.headers`: extra headers (merged with provider defaults). Values accept SecretRef.
|
||||
- `request.auth`: auth strategy override. Modes: `"provider-default"` (use provider's built-in auth), `"authorization-bearer"` (with `token`), `"header"` (with `headerName`, `value`, optional `prefix`).
|
||||
- `request.proxy`: HTTP proxy override. Modes: `"env-proxy"` (use `HTTP_PROXY`/`HTTPS_PROXY` env vars), `"explicit-proxy"` (with `url`). Both modes accept an optional `tls` sub-object.
|
||||
- `request.tls`: TLS override for direct connections. Fields: `ca`, `cert`, `key`, `passphrase` (all accept SecretRef), `serverName`, `insecureSkipVerify`.
|
||||
- `models.providers.*.models`: explicit provider model catalog entries.
|
||||
- `models.providers.*.models.*.contextWindow`: native model context window metadata.
|
||||
- `models.providers.*.models.*.contextTokens`: optional runtime context cap. Use this when you want a smaller effective context budget than the model's native `contextWindow`.
|
||||
- `models.providers.*.models.*.compat.supportsDeveloperRole`: optional compatibility hint. For `api: "openai-completions"` with a non-empty non-native `baseUrl` (host not `api.openai.com`), OpenClaw forces this to `false` at runtime. Empty/omitted `baseUrl` keeps default OpenAI behavior.
|
||||
- `models.bedrockDiscovery`: Bedrock auto-discovery settings root.
|
||||
- `models.bedrockDiscovery.enabled`: turn discovery polling on/off.
|
||||
- `models.bedrockDiscovery.region`: AWS region for discovery.
|
||||
- `models.bedrockDiscovery.providerFilter`: optional provider-id filter for targeted discovery.
|
||||
- `models.bedrockDiscovery.refreshInterval`: polling interval for discovery refresh.
|
||||
- `models.bedrockDiscovery.defaultContextWindow`: fallback context window for discovered models.
|
||||
- `models.bedrockDiscovery.defaultMaxTokens`: fallback max output tokens for discovered models.
|
||||
- `plugins.entries.amazon-bedrock.config.discovery`: Bedrock auto-discovery settings root.
|
||||
- `plugins.entries.amazon-bedrock.config.discovery.enabled`: turn implicit discovery on/off.
|
||||
- `plugins.entries.amazon-bedrock.config.discovery.region`: AWS region for discovery.
|
||||
- `plugins.entries.amazon-bedrock.config.discovery.providerFilter`: optional provider-id filter for targeted discovery.
|
||||
- `plugins.entries.amazon-bedrock.config.discovery.refreshInterval`: polling interval for discovery refresh.
|
||||
- `plugins.entries.amazon-bedrock.config.discovery.defaultContextWindow`: fallback context window for discovered models.
|
||||
- `plugins.entries.amazon-bedrock.config.discovery.defaultMaxTokens`: fallback max output tokens for discovered models.
|
||||
|
||||
### Provider examples
|
||||
|
||||
@@ -2575,6 +2602,26 @@ See [Local Models](/gateway/local-models). TL;DR: run a large local model via LM
|
||||
- `plugins.entries.<id>.subagent.allowModelOverride`: explicitly trust this plugin to request per-run `provider` and `model` overrides for background subagent runs.
|
||||
- `plugins.entries.<id>.subagent.allowedModels`: optional allowlist of canonical `provider/model` targets for trusted subagent overrides. Use `"*"` only when you intentionally want to allow any model.
|
||||
- `plugins.entries.<id>.config`: plugin-defined config object (validated by native OpenClaw plugin schema when available).
|
||||
- `plugins.entries.firecrawl.config.webFetch`: Firecrawl web-fetch provider settings.
|
||||
- `apiKey`: Firecrawl API key (accepts SecretRef). Falls back to `plugins.entries.firecrawl.config.webSearch.apiKey`, legacy `tools.web.fetch.firecrawl.apiKey`, or `FIRECRAWL_API_KEY` env var.
|
||||
- `baseUrl`: Firecrawl API base URL (default: `https://api.firecrawl.dev`).
|
||||
- `onlyMainContent`: extract only the main content from pages (default: `true`).
|
||||
- `maxAgeMs`: maximum cache age in milliseconds (default: `172800000` / 2 days).
|
||||
- `timeoutSeconds`: scrape request timeout in seconds (default: `60`).
|
||||
- `plugins.entries.xai.config.xSearch`: xAI X Search (Grok web search) settings.
|
||||
- `enabled`: enable the X Search provider.
|
||||
- `model`: Grok model to use for search (e.g. `"grok-4-1-fast"`).
|
||||
- `plugins.entries.memory-core.config.dreaming`: memory dreaming (experimental) settings. See [Dreaming](/concepts/dreaming) for modes and thresholds.
|
||||
- `mode`: dreaming cadence preset (`"off"`, `"core"`, `"rem"`, `"deep"`). Default: `"off"`.
|
||||
- `cron`: optional cron expression override for the dreaming schedule.
|
||||
- `timezone`: timezone for schedule evaluation (falls back to `agents.defaults.userTimezone`).
|
||||
- `limit`: maximum candidates to promote per cycle.
|
||||
- `minScore`: minimum weighted score threshold for promotion.
|
||||
- `minRecallCount`: minimum recall count threshold.
|
||||
- `minUniqueQueries`: minimum distinct query count threshold.
|
||||
- `recencyHalfLifeDays`: days for the recency score to decay by half. Default: `14`.
|
||||
- `maxAgeDays`: optional maximum daily-note age in days allowed for promotion.
|
||||
- `verboseLogging`: emit detailed per-run dreaming logs into the normal gateway log stream.
|
||||
- Enabled Claude bundle plugins can also contribute embedded Pi defaults from `settings.json`; OpenClaw applies those as sanitized agent settings, not as raw OpenClaw config patches.
|
||||
- `plugins.slots.memory`: pick the active memory plugin id, or `"none"` to disable memory plugins.
|
||||
- `plugins.slots.contextEngine`: pick the active context engine plugin id; defaults to `"legacy"` unless you install and select another engine.
|
||||
@@ -3220,6 +3267,7 @@ Notes:
|
||||
|
||||
cacheTrace: {
|
||||
enabled: false,
|
||||
filePath: "~/.openclaw/logs/cache-trace.jsonl",
|
||||
includeMessages: true,
|
||||
includePrompt: true,
|
||||
includeSystem: true,
|
||||
@@ -3240,6 +3288,7 @@ Notes:
|
||||
- `otel.sampleRate`: trace sampling rate `0`–`1`.
|
||||
- `otel.flushIntervalMs`: periodic telemetry flush interval in ms.
|
||||
- `cacheTrace.enabled`: log cache trace snapshots for embedded runs (default: `false`).
|
||||
- `cacheTrace.filePath`: output path for cache trace JSONL (default: `$OPENCLAW_STATE_DIR/logs/cache-trace.jsonl`).
|
||||
- `cacheTrace.includeMessages` / `includePrompt` / `includeSystem`: control what is included in cache trace output (all default: `true`).
|
||||
|
||||
---
|
||||
@@ -3313,7 +3362,9 @@ Notes:
|
||||
- `stream.hiddenBoundarySeparator`: separator before visible text after hidden tool events (default: `"paragraph"`).
|
||||
- `stream.maxOutputChars`: maximum assistant output characters projected per ACP turn.
|
||||
- `stream.maxSessionUpdateChars`: maximum characters for projected ACP status/update lines.
|
||||
- `stream.tagVisibility`: record of tag names to boolean visibility overrides for streamed events.
|
||||
- `runtime.ttlMinutes`: idle TTL in minutes for ACP session workers before eligible cleanup.
|
||||
- `runtime.installCommand`: optional install command to run when bootstrapping an ACP runtime environment.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -307,11 +307,18 @@ Doctor checks:
|
||||
|
||||
Doctor inspects OAuth profiles in the auth store, warns when tokens are
|
||||
expiring/expired, and can refresh them when safe. If the Anthropic
|
||||
OAuth/token profile is stale, it suggests migrating to Claude CLI or an
|
||||
Anthropic API key.
|
||||
OAuth/token profile is stale, it suggests an Anthropic API key or the legacy
|
||||
Anthropic setup-token path.
|
||||
Refresh prompts only appear when running interactively (TTY); `--non-interactive`
|
||||
skips refresh attempts.
|
||||
|
||||
Doctor also detects stale removed Anthropic Claude CLI state. If old
|
||||
`anthropic:claude-cli` credential bytes still exist in `auth-profiles.json`,
|
||||
doctor converts them back into Anthropic token/OAuth profiles and rewrites
|
||||
stale `claude-cli/...` model refs.
|
||||
If the bytes are gone, doctor removes the stale config and prints recovery
|
||||
commands instead.
|
||||
|
||||
Doctor also reports auth profiles that are temporarily unusable due to:
|
||||
|
||||
- short cooldowns (rate limits/timeouts/auth failures)
|
||||
|
||||
@@ -98,7 +98,7 @@ Available groups:
|
||||
- `group:messaging`: `message`
|
||||
- `group:nodes`: `nodes`
|
||||
- `group:agents`: `agents_list`
|
||||
- `group:media`: `image`, `image_generate`, `tts`
|
||||
- `group:media`: `image`, `image_generate`, `video_generate`, `tts`
|
||||
- `group:openclaw`: all built-in OpenClaw tools (excludes provider plugins)
|
||||
|
||||
## Elevated: exec-only "run on host"
|
||||
|
||||
@@ -50,7 +50,8 @@ gateway without forcing a `tsdown` rebuild; source and config changes still
|
||||
rebuild `dist` first.
|
||||
|
||||
Add any gateway CLI flags after `gateway:watch` and they will be passed through on
|
||||
each restart.
|
||||
each restart. Re-running the same watch command for the same repo/flag set now
|
||||
replaces the older watcher instead of leaving duplicate watcher parents behind.
|
||||
|
||||
## Dev profile + dev gateway (--dev)
|
||||
|
||||
|
||||
@@ -565,7 +565,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
<Accordion title="What does onboarding actually do?">
|
||||
`openclaw onboard` is the recommended setup path. In **local mode** it walks you through:
|
||||
|
||||
- **Model/auth setup** (provider OAuth, Claude CLI reuse, and API keys supported, plus local model options such as LM Studio)
|
||||
- **Model/auth setup** (provider OAuth, API keys, Anthropic legacy setup-token, plus local model options such as LM Studio)
|
||||
- **Workspace** location + bootstrap files
|
||||
- **Gateway settings** (bind/port/auth/tailscale)
|
||||
- **Channels** (WhatsApp, Telegram, Discord, Mattermost, Signal, iMessage, plus bundled channel plugins like QQ Bot)
|
||||
@@ -581,12 +581,18 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
**local-only models** so your data stays on your device. Subscriptions (Claude
|
||||
Pro/Max or OpenAI Codex) are optional ways to authenticate those providers.
|
||||
|
||||
Anthropic's public Claude Code docs still include direct Claude Code terminal
|
||||
use in Claude plan limits. Separately, Anthropic notified OpenClaw users on
|
||||
**April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the **OpenClaw**
|
||||
Claude-login path counts as third-party harness usage and now requires
|
||||
**Extra Usage** billed separately from the subscription. OpenAI Codex OAuth
|
||||
is explicitly supported for external tools like OpenClaw.
|
||||
For Anthropic in OpenClaw, the practical split is:
|
||||
|
||||
- **Anthropic API key**: normal Anthropic API billing
|
||||
- **Claude subscription auth in OpenClaw**: Anthropic told OpenClaw users on
|
||||
**April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that this requires
|
||||
**Extra Usage** billed separately from the subscription
|
||||
|
||||
Our local repros also show that `claude -p --append-system-prompt ...` can
|
||||
hit the same Extra Usage guard when the appended prompt identifies
|
||||
OpenClaw, while the same prompt string does **not** reproduce that block on
|
||||
the Anthropic SDK + API-key path. OpenAI Codex OAuth is explicitly
|
||||
supported for external tools like OpenClaw.
|
||||
|
||||
OpenClaw also supports other hosted subscription-style options including
|
||||
**Qwen Cloud Coding Plan**, **MiniMax Coding Plan**, and
|
||||
@@ -600,29 +606,27 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I use Claude Max subscription without an API key?">
|
||||
Yes, via a local **Claude CLI** login on the gateway host.
|
||||
Yes, but treat it as **Claude subscription auth with Extra Usage**.
|
||||
|
||||
Claude Pro/Max subscriptions **do not include an API key**, so Claude CLI
|
||||
reuse is the supported subscription-style path in OpenClaw. Anthropic's
|
||||
public Claude Code docs still cover direct Claude Code terminal use under
|
||||
your plan. Separately, Anthropic told OpenClaw users on **April 4, 2026 at
|
||||
12:00 PM PT / 8:00 PM BST** that the **OpenClaw** Claude-login path
|
||||
requires **Extra Usage** billed separately from the subscription. If you
|
||||
want the clearest and safest supported path for production, use an
|
||||
Anthropic API key.
|
||||
Claude Pro/Max subscriptions do not include an API key. In OpenClaw, that
|
||||
means Anthropic's OpenClaw-specific billing notice applies: subscription
|
||||
traffic requires **Extra Usage**. If you want Anthropic traffic without
|
||||
that Extra Usage path, use an Anthropic API key instead.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do you support Claude subscription auth (Claude Pro or Max)?">
|
||||
Yes. Reuse a local **Claude CLI** login on the gateway host with `openclaw models auth login --provider anthropic --method cli --set-default`.
|
||||
Yes, but the supported interpretation is now:
|
||||
|
||||
Anthropic setup-token is also available again as a legacy/manual OpenClaw path. Anthropic's OpenClaw-specific billing notice still applies there, so use it with the expectation that Anthropic requires **Extra Usage**. See [Anthropic](/providers/anthropic) and [OAuth](/concepts/oauth).
|
||||
- Anthropic in OpenClaw with a subscription means **Extra Usage**
|
||||
- Anthropic in OpenClaw without that path means **API key**
|
||||
|
||||
Important: Anthropic's public Claude Code docs still cover direct Claude
|
||||
Code terminal use under Claude plans. Separately, Anthropic told OpenClaw
|
||||
users on **April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that the
|
||||
**OpenClaw** Claude-login path requires **Extra Usage** billed separately
|
||||
from the subscription.
|
||||
Anthropic setup-token is still available as a legacy/manual OpenClaw path,
|
||||
and Anthropic's OpenClaw-specific billing notice still applies there. We
|
||||
also reproduced the same billing guard locally with direct
|
||||
`claude -p --append-system-prompt ...` usage when the appended prompt
|
||||
identifies OpenClaw, while the same prompt string did **not** reproduce on
|
||||
the Anthropic SDK + API-key path.
|
||||
|
||||
For production or multi-user workloads, Anthropic API key auth is the
|
||||
safer, recommended choice. If you want other subscription-style hosted
|
||||
@@ -652,7 +656,7 @@ for usage/billing and raise limits as needed.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is AWS Bedrock supported?">
|
||||
Yes. OpenClaw has a bundled **Amazon Bedrock (Converse)** provider. With AWS env markers present, OpenClaw can auto-discover the streaming/text Bedrock catalog and merge it as an implicit `amazon-bedrock` provider; otherwise you can explicitly enable `models.bedrockDiscovery.enabled` or add a manual provider entry. See [Amazon Bedrock](/providers/bedrock) and [Model providers](/providers/models). If you prefer a managed key flow, an OpenAI-compatible proxy in front of Bedrock is still a valid option.
|
||||
Yes. OpenClaw has a bundled **Amazon Bedrock (Converse)** provider. With AWS env markers present, OpenClaw can auto-discover the streaming/text Bedrock catalog and merge it as an implicit `amazon-bedrock` provider; otherwise you can explicitly enable `plugins.entries.amazon-bedrock.config.discovery.enabled` or add a manual provider entry. See [Amazon Bedrock](/providers/bedrock) and [Model providers](/providers/models). If you prefer a managed key flow, an OpenAI-compatible proxy in front of Bedrock is still a valid option.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How does Codex auth work?">
|
||||
@@ -671,17 +675,11 @@ for usage/billing and raise limits as needed.
|
||||
<Accordion title="How do I set up Gemini CLI OAuth?">
|
||||
Gemini CLI uses a **plugin auth flow**, not a client id or secret in `openclaw.json`.
|
||||
|
||||
Steps:
|
||||
Use the Gemini API provider instead:
|
||||
|
||||
1. Install Gemini CLI locally so `gemini` is on `PATH`
|
||||
- Homebrew: `brew install gemini-cli`
|
||||
- npm: `npm install -g @google/gemini-cli`
|
||||
2. Enable the plugin: `openclaw plugins enable google`
|
||||
3. Login: `openclaw models auth login --provider google-gemini-cli --set-default`
|
||||
4. Default model after login: `google-gemini-cli/gemini-3.1-pro-preview`
|
||||
5. If requests fail, set `GOOGLE_CLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT_ID` on the gateway host
|
||||
|
||||
This stores OAuth tokens in auth profiles on the gateway host. Details: [Model providers](/concepts/model-providers).
|
||||
1. Enable the plugin: `openclaw plugins enable google`
|
||||
2. Run `openclaw onboard --auth-choice gemini-api-key`
|
||||
3. Set a Google model such as `google/gemini-3.1-pro-preview`
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
||||
- The shared Vitest config also fixes `isolate: false` and uses the non-isolated runner across the root projects, e2e, and live configs.
|
||||
- The root UI lane keeps its `jsdom` setup and optimizer, but now runs on the shared non-isolated runner too.
|
||||
- `pnpm test` inherits the same `threads` + `isolate: false` defaults from the root `vitest.config.ts` projects config.
|
||||
- The shared `scripts/run-vitest.mjs` launcher now also adds `--no-maglev` for Vitest child Node processes by default to reduce V8 compile churn during big local runs. Set `OPENCLAW_VITEST_ENABLE_MAGLEV=1` if you need to compare against stock V8 behavior.
|
||||
- Fast-local iteration note:
|
||||
- `pnpm test:changed` runs the native projects config with `--changed origin/main`.
|
||||
- `pnpm test:max` and `pnpm test:changed:max` keep the same native projects config, just with a higher worker cap.
|
||||
@@ -195,7 +196,7 @@ Live tests are split into two layers so we can isolate failures:
|
||||
- `OPENCLAW_LIVE_MODELS=all` is an alias for the modern allowlist
|
||||
- or `OPENCLAW_LIVE_MODELS="openai/gpt-5.4,anthropic/claude-opus-4-6,..."` (comma allowlist)
|
||||
- How to select providers:
|
||||
- `OPENCLAW_LIVE_PROVIDERS="google,google-antigravity,google-gemini-cli"` (comma allowlist)
|
||||
- `OPENCLAW_LIVE_PROVIDERS="google,google-antigravity"` (comma allowlist)
|
||||
- Where keys come from:
|
||||
- By default: profile store and env fallbacks
|
||||
- Set `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` to enforce **profile store** only
|
||||
@@ -226,7 +227,7 @@ Live tests are split into two layers so we can isolate failures:
|
||||
- `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”):
|
||||
- `OPENCLAW_LIVE_GATEWAY_PROVIDERS="google,google-antigravity,google-gemini-cli,openai,anthropic,zai,minimax"` (comma allowlist)
|
||||
- `OPENCLAW_LIVE_GATEWAY_PROVIDERS="google,google-antigravity,openai,anthropic,zai,minimax"` (comma allowlist)
|
||||
- Tool + image probes are always on in this live test:
|
||||
- `read` probe + `exec+read` probe (tool stress)
|
||||
- image probe runs when the model advertises image input support
|
||||
@@ -244,51 +245,6 @@ openclaw models list
|
||||
openclaw models list --json
|
||||
```
|
||||
|
||||
## Live: CLI backend smoke (Claude CLI or other local CLIs)
|
||||
|
||||
- Test: `src/gateway/gateway-cli-backend.live.test.ts`
|
||||
- Goal: validate the Gateway + agent pipeline using a local CLI backend, without touching your default config.
|
||||
- Enable:
|
||||
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND=1`
|
||||
- Defaults:
|
||||
- Model: `claude-cli/claude-sonnet-4-6`
|
||||
- Command: `claude`
|
||||
- Args: `["-p","--output-format","stream-json","--include-partial-messages","--verbose","--permission-mode","bypassPermissions"]`
|
||||
- Overrides (optional):
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_MODEL="claude-cli/claude-opus-4-6"`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_MODEL="codex-cli/gpt-5.4"`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_COMMAND="/full/path/to/claude"`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_ARGS='["-p","--output-format","stream-json","--include-partial-messages","--verbose","--permission-mode","bypassPermissions"]'`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_CLEAR_ENV='["ANTHROPIC_API_KEY","ANTHROPIC_API_KEY_OLD"]'`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_PROBE=1` to send a real image attachment (paths are injected into the prompt).
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_ARG="--image"` to pass image file paths as CLI args instead of prompt injection.
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_MODE="repeat"` (or `"list"`) to control how image args are passed when `IMAGE_ARG` is set.
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_RESUME_PROBE=1` to send a second turn and validate resume flow.
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_DISABLE_MCP_CONFIG=0` to keep Claude CLI MCP config enabled (default injects a temporary strict empty `--mcp-config` so ambient/global MCP servers stay disabled during the smoke).
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
OPENCLAW_LIVE_CLI_BACKEND=1 \
|
||||
OPENCLAW_LIVE_CLI_BACKEND_MODEL="claude-cli/claude-sonnet-4-6" \
|
||||
pnpm test:live src/gateway/gateway-cli-backend.live.test.ts
|
||||
```
|
||||
|
||||
Docker recipe:
|
||||
|
||||
```bash
|
||||
pnpm test:docker:live-cli-backend
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- The Docker runner lives at `scripts/test-live-cli-backend-docker.sh`.
|
||||
- It runs the live CLI-backend smoke inside the repo Docker image as the non-root `node` user, because Claude CLI rejects `bypassPermissions` when invoked as root.
|
||||
- For `claude-cli`, it installs the Linux `@anthropic-ai/claude-code` package into a cached writable prefix at `OPENCLAW_DOCKER_CLI_TOOLS_DIR` (default: `~/.cache/openclaw/docker-cli-tools`).
|
||||
- For `claude-cli`, the live smoke injects a strict empty MCP config unless you set `OPENCLAW_LIVE_CLI_BACKEND_DISABLE_MCP_CONFIG=0`.
|
||||
- It copies `~/.claude` into the container when available, but on machines where Claude auth is backed by `ANTHROPIC_API_KEY`, it also preserves `ANTHROPIC_API_KEY` / `ANTHROPIC_API_KEY_OLD` for the child Claude CLI via `OPENCLAW_LIVE_CLI_BACKEND_PRESERVE_ENV`.
|
||||
|
||||
## Live: ACP bind smoke (`/acp spawn ... --bind here`)
|
||||
|
||||
- Test: `src/gateway/gateway-acp-bind.live.test.ts`
|
||||
@@ -307,10 +263,10 @@ Notes:
|
||||
- Overrides:
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT=claude`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT=codex`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_ACPX_COMMAND=/full/path/to/acpx`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND='npx -y @agentclientprotocol/claude-agent-acp@<version>'`
|
||||
- Notes:
|
||||
- This lane uses the gateway `chat.send` surface with admin-only synthetic originating-route fields so tests can attach message-channel context without pretending to deliver externally.
|
||||
- When `OPENCLAW_LIVE_ACP_BIND_ACPX_COMMAND` is unset, the test uses the configured/bundled acpx command. If your harness auth depends on env vars from `~/.profile`, prefer a custom `acpx` command that preserves provider env.
|
||||
- When `OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND` is unset, the test uses the embedded `acpx` plugin's built-in agent registry for the selected ACP harness agent.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -329,7 +285,7 @@ pnpm test:docker:live-acp-bind
|
||||
Docker notes:
|
||||
|
||||
- The Docker runner lives at `scripts/test-live-acp-bind-docker.sh`.
|
||||
- It sources `~/.profile`, copies the matching CLI auth home (`~/.claude` or `~/.codex`) into the container, installs `acpx` into a writable npm prefix, then installs the requested live CLI (`@anthropic-ai/claude-code` or `@openai/codex`) if missing.
|
||||
- It sources `~/.profile`, stages the matching CLI auth material into the container, installs `acpx` into a writable npm prefix, then installs the requested live CLI (`@anthropic-ai/claude-code` or `@openai/codex`) if missing.
|
||||
- Inside Docker, the runner sets `OPENCLAW_LIVE_ACP_BIND_ACPX_COMMAND=$HOME/.npm-global/bin/acpx` so acpx keeps provider env vars from the sourced profile available to the child harness CLI.
|
||||
|
||||
### Recommended live recipes
|
||||
@@ -353,10 +309,6 @@ Notes:
|
||||
|
||||
- `google/...` uses the Gemini API (API key).
|
||||
- `google-antigravity/...` uses the Antigravity OAuth bridge (Cloud Code Assist-style agent endpoint).
|
||||
- `google-gemini-cli/...` uses the local Gemini CLI on your machine (separate auth + tooling quirks).
|
||||
- Gemini API vs Gemini CLI:
|
||||
- API: OpenClaw calls Google’s hosted Gemini API over HTTP (API key / profile auth); this is what most users mean by “Gemini”.
|
||||
- CLI: OpenClaw shells out to a local `gemini` binary; it has its own auth and can behave differently (streaming/tool support/version skew).
|
||||
|
||||
## Live: model matrix (what we cover)
|
||||
|
||||
@@ -407,7 +359,7 @@ If you have keys enabled, we also support testing via:
|
||||
|
||||
More providers you can include in the live matrix (if you have creds/config):
|
||||
|
||||
- Built-in: `openai`, `openai-codex`, `anthropic`, `google`, `google-vertex`, `google-antigravity`, `google-gemini-cli`, `zai`, `openrouter`, `opencode`, `opencode-go`, `xai`, `groq`, `cerebras`, `mistral`, `github-copilot`
|
||||
- Built-in: `openai`, `openai-codex`, `anthropic`, `google`, `google-vertex`, `google-antigravity`, `zai`, `openrouter`, `opencode`, `opencode-go`, `xai`, `groq`, `cerebras`, `mistral`, `github-copilot`
|
||||
- Via `models.providers` (custom endpoints): `minimax` (cloud/API), plus any OpenAI/Anthropic-compatible proxy (LM Studio, vLLM, LiteLLM, etc.)
|
||||
|
||||
Tip: don’t try to hardcode “all models” in docs. The authoritative list is whatever `discoverModels(...)` returns on your machine + whatever keys are available.
|
||||
@@ -480,7 +432,6 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
|
||||
|
||||
- Direct models: `pnpm test:docker:live-models` (script: `scripts/test-live-models-docker.sh`)
|
||||
- ACP bind smoke: `pnpm test:docker:live-acp-bind` (script: `scripts/test-live-acp-bind-docker.sh`)
|
||||
- CLI backend smoke: `pnpm test:docker:live-cli-backend` (script: `scripts/test-live-cli-backend-docker.sh`)
|
||||
- Gateway + dev agent: `pnpm test:docker:live-gateway` (script: `scripts/test-live-gateway-models-docker.sh`)
|
||||
- Open WebUI live smoke: `pnpm test:docker:openwebui` (script: `scripts/e2e/openwebui-docker.sh`)
|
||||
- Onboarding wizard (TTY, full scaffolding): `pnpm test:docker:onboard` (script: `scripts/e2e/onboard-docker.sh`)
|
||||
@@ -491,6 +442,10 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
|
||||
The live-model Docker runners also bind-mount the current checkout read-only and
|
||||
stage it into a temporary workdir inside the container. This keeps the runtime
|
||||
image slim while still running Vitest against your exact local source/config.
|
||||
The staging step skips large local-only caches and app build outputs such as
|
||||
`.pnpm-store`, `.worktrees`, `__openclaw_vitest__`, and app-local `.build` or
|
||||
Gradle output directories so Docker live runs do not spend minutes copying
|
||||
machine-specific artifacts.
|
||||
They also set `OPENCLAW_SKIP_CHANNELS=1` so gateway live probes do not start
|
||||
real Telegram/Discord/etc. channel workers inside the container.
|
||||
`test:docker:live-models` still runs `pnpm test:live`, so pass through
|
||||
@@ -527,9 +482,10 @@ Useful env vars:
|
||||
- `OPENCLAW_WORKSPACE_DIR=...` (default: `~/.openclaw/workspace`) mounted to `/home/node/.openclaw/workspace`
|
||||
- `OPENCLAW_PROFILE_FILE=...` (default: `~/.profile`) mounted to `/home/node/.profile` and sourced before running tests
|
||||
- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...` (default: `~/.cache/openclaw/docker-cli-tools`) mounted to `/home/node/.npm-global` for cached CLI installs inside Docker
|
||||
- External CLI auth dirs under `$HOME` are mounted read-only under `/host-auth/...`, then copied into `/home/node/...` before tests start
|
||||
- Default: mount all supported dirs (`.codex`, `.claude`, `.minimax`)
|
||||
- Narrowed provider runs mount only the needed dirs inferred from `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS`
|
||||
- External CLI auth dirs/files under `$HOME` are mounted read-only under `/host-auth...`, then copied into `/home/node/...` before tests start
|
||||
- Default dirs: `.minimax`
|
||||
- Default files: `~/.codex/auth.json`, `~/.codex/config.toml`, `.claude.json`, `~/.claude/.credentials.json`, `~/.claude/settings.json`, `~/.claude/settings.local.json`
|
||||
- Narrowed provider runs mount only the needed dirs/files inferred from `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS`
|
||||
- Override manually with `OPENCLAW_DOCKER_AUTH_DIRS=all`, `OPENCLAW_DOCKER_AUTH_DIRS=none`, or a comma list like `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex`
|
||||
- `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...` to narrow the run
|
||||
- `OPENCLAW_LIVE_GATEWAY_PROVIDERS=...` / `OPENCLAW_LIVE_PROVIDERS=...` to filter providers in-container
|
||||
|
||||
@@ -103,12 +103,7 @@ docker build -t openclaw:local -f Dockerfile .
|
||||
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
|
||||
dist/index.js onboard --mode local --no-install-daemon
|
||||
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
|
||||
dist/index.js config set gateway.mode local
|
||||
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
|
||||
dist/index.js config set gateway.bind lan
|
||||
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
|
||||
dist/index.js config set gateway.controlUi.allowedOrigins \
|
||||
'["http://localhost:18789","http://127.0.0.1:18789"]' --strict-json
|
||||
dist/index.js config set --batch-json '[{"path":"gateway.mode","value":"local"},{"path":"gateway.bind","value":"lan"},{"path":"gateway.controlUi.allowedOrigins","value":["http://localhost:18789","http://127.0.0.1:18789"]}]'
|
||||
docker compose up -d openclaw-gateway
|
||||
```
|
||||
|
||||
@@ -395,8 +390,7 @@ scripts/sandbox-setup.sh
|
||||
Reset gateway mode and bind:
|
||||
|
||||
```bash
|
||||
docker compose run --rm openclaw-cli config set gateway.mode local
|
||||
docker compose run --rm openclaw-cli config set gateway.bind lan
|
||||
docker compose run --rm openclaw-cli config set --batch-json '[{"path":"gateway.mode","value":"local"},{"path":"gateway.bind","value":"lan"}]'
|
||||
docker compose run --rm openclaw-cli devices list --url ws://127.0.0.1:18789
|
||||
```
|
||||
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
---
|
||||
read_when:
|
||||
- 新規ユーザーにOpenClawを紹介するとき
|
||||
summary: OpenClawは、あらゆるOSで動作するAIエージェント向けのマルチチャネルgatewayです。
|
||||
title: OpenClaw
|
||||
x-i18n:
|
||||
generated_at: "2026-02-08T17:15:47Z"
|
||||
model: claude-opus-4-6
|
||||
provider: pi
|
||||
source_hash: fc8babf7885ef91d526795051376d928599c4cf8aff75400138a0d7d9fa3b75f
|
||||
source_path: index.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# OpenClaw 🦞
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
src="/assets/openclaw-logo-text-dark.png"
|
||||
alt="OpenClaw"
|
||||
width="500"
|
||||
class="dark:hidden"
|
||||
/>
|
||||
<img
|
||||
src="/assets/openclaw-logo-text.png"
|
||||
alt="OpenClaw"
|
||||
width="500"
|
||||
class="hidden dark:block"
|
||||
/>
|
||||
</p>
|
||||
|
||||
> _「EXFOLIATE! EXFOLIATE!」_ — たぶん宇宙ロブスター
|
||||
|
||||
<p align="center">
|
||||
<strong>WhatsApp、Telegram、Discord、iMessageなどに対応した、あらゆるOS向けのAIエージェントgateway。</strong><br />
|
||||
メッセージを送信すれば、ポケットからエージェントの応答を受け取れます。プラグインでMattermostなどを追加できます。
|
||||
</p>
|
||||
|
||||
<Columns>
|
||||
<Card title="はじめに" href="/start/getting-started" icon="rocket">
|
||||
OpenClawをインストールし、数分でGatewayを起動できます。
|
||||
</Card>
|
||||
<Card title="ウィザードを実行" href="/start/wizard" icon="sparkles">
|
||||
`openclaw onboard`とペアリングフローによるガイド付きセットアップ。
|
||||
</Card>
|
||||
<Card title="Control UIを開く" href="/web/control-ui" icon="layout-dashboard">
|
||||
チャット、設定、セッション用のブラウザダッシュボードを起動します。
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
OpenClawは、単一のGatewayプロセスを通じてチャットアプリをPiのようなコーディングエージェントに接続します。OpenClawアシスタントを駆動し、ローカルまたはリモートのセットアップをサポートします。
|
||||
|
||||
## 仕組み
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["チャットアプリ + プラグイン"] --> B["Gateway"]
|
||||
B --> C["Piエージェント"]
|
||||
B --> D["CLI"]
|
||||
B --> E["Web Control UI"]
|
||||
B --> F["macOSアプリ"]
|
||||
B --> G["iOSおよびAndroidノード"]
|
||||
```
|
||||
|
||||
Gatewayは、セッション、ルーティング、チャネル接続の信頼できる唯一の情報源です。
|
||||
|
||||
## 主な機能
|
||||
|
||||
<Columns>
|
||||
<Card title="マルチチャネルgateway" icon="network">
|
||||
単一のGatewayプロセスでWhatsApp、Telegram、Discord、iMessageに対応。
|
||||
</Card>
|
||||
<Card title="プラグインチャネル" icon="plug">
|
||||
拡張パッケージでMattermostなどを追加。
|
||||
</Card>
|
||||
<Card title="マルチエージェントルーティング" icon="route">
|
||||
エージェント、ワークスペース、送信者ごとに分離されたセッション。
|
||||
</Card>
|
||||
<Card title="メディアサポート" icon="image">
|
||||
画像、音声、ドキュメントの送受信。
|
||||
</Card>
|
||||
<Card title="Web Control UI" icon="monitor">
|
||||
チャット、設定、セッション、ノード用のブラウザダッシュボード。
|
||||
</Card>
|
||||
<Card title="モバイルノード" icon="smartphone">
|
||||
Canvas対応のiOSおよびAndroidノードをペアリング。
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
## クイックスタート
|
||||
|
||||
<Steps>
|
||||
<Step title="OpenClawをインストール">
|
||||
```bash
|
||||
npm install -g openclaw@latest
|
||||
```
|
||||
</Step>
|
||||
<Step title="オンボーディングとサービスのインストール">
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
</Step>
|
||||
<Step title="WhatsAppをペアリングしてGatewayを起動">
|
||||
```bash
|
||||
openclaw channels login
|
||||
openclaw gateway --port 18789
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
完全なインストールと開発セットアップが必要ですか?[クイックスタート](/start/quickstart)をご覧ください。
|
||||
|
||||
## ダッシュボード
|
||||
|
||||
Gatewayの起動後、ブラウザでControl UIを開きます。
|
||||
|
||||
- ローカルデフォルト: [http://127.0.0.1:18789/](http://127.0.0.1:18789/)
|
||||
- リモートアクセス: [Webサーフェス](/web)および[Tailscale](/gateway/tailscale)
|
||||
|
||||
<p align="center">
|
||||
<img src="/whatsapp-openclaw.jpg" alt="OpenClaw" width="420" />
|
||||
</p>
|
||||
|
||||
## 設定(オプション)
|
||||
|
||||
設定は`~/.openclaw/openclaw.json`にあります。
|
||||
|
||||
- **何もしなければ**、OpenClawはバンドルされたPiバイナリをRPCモードで使用し、送信者ごとのセッションを作成します。
|
||||
- 制限を設けたい場合は、`channels.whatsapp.allowFrom`と(グループの場合)メンションルールから始めてください。
|
||||
|
||||
例:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
whatsapp: {
|
||||
allowFrom: ["+15555550123"],
|
||||
groups: { "*": { requireMention: true } },
|
||||
},
|
||||
},
|
||||
messages: { groupChat: { mentionPatterns: ["@openclaw"] } },
|
||||
}
|
||||
```
|
||||
|
||||
## ここから始める
|
||||
|
||||
<Columns>
|
||||
<Card title="ドキュメントハブ" href="/start/hubs" icon="book-open">
|
||||
ユースケース別に整理されたすべてのドキュメントとガイド。
|
||||
</Card>
|
||||
<Card title="設定" href="/gateway/configuration" icon="settings">
|
||||
Gatewayのコア設定、トークン、プロバイダー設定。
|
||||
</Card>
|
||||
<Card title="リモートアクセス" href="/gateway/remote" icon="globe">
|
||||
SSHおよびtailnetアクセスパターン。
|
||||
</Card>
|
||||
<Card title="チャネル" href="/channels/telegram" icon="message-square">
|
||||
WhatsApp、Telegram、Discordなどのチャネル固有のセットアップ。
|
||||
</Card>
|
||||
<Card title="ノード" href="/nodes" icon="smartphone">
|
||||
ペアリングとCanvas対応のiOSおよびAndroidノード。
|
||||
</Card>
|
||||
<Card title="ヘルプ" href="/help" icon="life-buoy">
|
||||
一般的な修正とトラブルシューティングのエントリーポイント。
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
## 詳細
|
||||
|
||||
<Columns>
|
||||
<Card title="全機能リスト" href="/concepts/features" icon="list">
|
||||
チャネル、ルーティング、メディア機能の完全な一覧。
|
||||
</Card>
|
||||
<Card title="マルチエージェントルーティング" href="/concepts/multi-agent" icon="route">
|
||||
ワークスペースの分離とエージェントごとのセッション。
|
||||
</Card>
|
||||
<Card title="セキュリティ" href="/gateway/security" icon="shield">
|
||||
トークン、許可リスト、安全制御。
|
||||
</Card>
|
||||
<Card title="トラブルシューティング" href="/gateway/troubleshooting" icon="wrench">
|
||||
Gatewayの診断と一般的なエラー。
|
||||
</Card>
|
||||
<Card title="概要とクレジット" href="/reference/credits" icon="info">
|
||||
プロジェクトの起源、貢献者、ライセンス。
|
||||
</Card>
|
||||
</Columns>
|
||||
@@ -1,125 +0,0 @@
|
||||
---
|
||||
read_when:
|
||||
- ゼロからの初回セットアップ
|
||||
- 動作するチャットへの最短ルートを知りたい
|
||||
summary: OpenClawをインストールし、数分で最初のチャットを実行しましょう。
|
||||
title: はじめに
|
||||
x-i18n:
|
||||
generated_at: "2026-02-08T17:15:16Z"
|
||||
model: claude-opus-4-6
|
||||
provider: pi
|
||||
source_hash: 27aeeb3d18c495380e94e6b011b0df3def518535c9f1eee504f04871d8a32269
|
||||
source_path: start/getting-started.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# はじめに
|
||||
|
||||
目標:ゼロから最小限のセットアップで最初の動作するチャットを実現する。
|
||||
|
||||
<Info>
|
||||
最速のチャット方法:Control UIを開く(チャンネル設定は不要)。`openclaw dashboard`を実行してブラウザでチャットするか、<Tooltip headline="Gatewayホスト" tip="OpenClaw Gatewayサービスを実行しているマシン。">Gatewayホスト</Tooltip>で`http://127.0.0.1:18789/`を開きます。
|
||||
ドキュメント:[Dashboard](/web/dashboard)と[Control UI](/web/control-ui)。
|
||||
</Info>
|
||||
|
||||
## 前提条件
|
||||
|
||||
- Node 22以降
|
||||
|
||||
<Tip>
|
||||
不明な場合は`node --version`でNodeのバージョンを確認してください。
|
||||
</Tip>
|
||||
|
||||
## クイックセットアップ(CLI)
|
||||
|
||||
<Steps>
|
||||
<Step title="OpenClawをインストール(推奨)">
|
||||
<Tabs>
|
||||
<Tab title="macOS/Linux">
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Windows (PowerShell)">
|
||||
```powershell
|
||||
iwr -useb https://openclaw.ai/install.ps1 | iex
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
その他のインストール方法と要件:[インストール](/install)。
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
<Step title="オンボーディングウィザードを実行">
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
ウィザードは認証、Gateway設定、およびオプションのチャンネルを構成します。
|
||||
詳細は[オンボーディングウィザード](/start/wizard)を参照してください。
|
||||
|
||||
</Step>
|
||||
<Step title="Gatewayを確認">
|
||||
サービスをインストールした場合、すでに実行されているはずです:
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Control UIを開く">
|
||||
```bash
|
||||
openclaw dashboard
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Check>
|
||||
Control UIが読み込まれれば、Gatewayは使用可能な状態です。
|
||||
</Check>
|
||||
|
||||
## オプションの確認と追加機能
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Gatewayをフォアグラウンドで実行">
|
||||
クイックテストやトラブルシューティングに便利です。
|
||||
|
||||
```bash
|
||||
openclaw gateway --port 18789
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="テストメッセージを送信">
|
||||
構成済みのチャンネルが必要です。
|
||||
|
||||
```bash
|
||||
openclaw message send --target +15555550123 --message "Hello from OpenClaw"
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## さらに詳しく
|
||||
|
||||
<Columns>
|
||||
<Card title="オンボーディングウィザード(詳細)" href="/start/wizard">
|
||||
完全なCLIウィザードリファレンスと高度なオプション。
|
||||
</Card>
|
||||
<Card title="macOSアプリのオンボーディング" href="/start/onboarding">
|
||||
macOSアプリの初回実行フロー。
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
## 完了後の状態
|
||||
|
||||
- 実行中のGateway
|
||||
- 構成済みの認証
|
||||
- Control UIアクセスまたは接続済みのチャンネル
|
||||
|
||||
## 次のステップ
|
||||
|
||||
- DMの安全性と承認:[ペアリング](/channels/pairing)
|
||||
- さらにチャンネルを接続:[チャンネル](/channels)
|
||||
- 高度なワークフローとソースからのビルド:[セットアップ](/start/setup)
|
||||
@@ -1,77 +0,0 @@
|
||||
---
|
||||
read_when:
|
||||
- オンボーディングウィザードの実行または設定時
|
||||
- 新しいマシンのセットアップ時
|
||||
sidebarTitle: Wizard (CLI)
|
||||
summary: CLIオンボーディングウィザード:Gateway、ワークスペース、チャンネル、Skillsの対話式セットアップ
|
||||
title: オンボーディングウィザード(CLI)
|
||||
x-i18n:
|
||||
generated_at: "2026-02-08T17:15:18Z"
|
||||
model: claude-opus-4-6
|
||||
provider: pi
|
||||
source_hash: 9a650d46044a930aa4aaec30b35f1273ca3969bf676ab67bf4e1575b5c46db4c
|
||||
source_path: start/wizard.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# オンボーディングウィザード(CLI)
|
||||
|
||||
CLIオンボーディングウィザードは、macOS、Linux、Windows(WSL2経由)でOpenClawをセットアップする際の推奨パスです。ローカルGatewayまたはリモートGateway接続に加えて、ワークスペースのデフォルト設定、チャンネル、Skillsを構成します。
|
||||
|
||||
```bash
|
||||
openclaw onboard
|
||||
```
|
||||
|
||||
<Info>
|
||||
最速で初回チャットを開始する方法:Control UI を開きます(チャンネル設定は不要)。`openclaw dashboard` を実行してブラウザでチャットできます。ドキュメント:[Dashboard](/web/dashboard)。
|
||||
</Info>
|
||||
|
||||
## クイックスタート vs 詳細設定
|
||||
|
||||
ウィザードは**クイックスタート**(デフォルト設定)と**詳細設定**(完全な制御)のどちらかを選択して開始します。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="クイックスタート(デフォルト設定)">
|
||||
- loopback上のローカルGateway
|
||||
- 既存のワークスペースまたはデフォルトワークスペース
|
||||
- Gatewayポート `18789`
|
||||
- Gateway認証トークンは自動生成(loopback上でも生成されます)
|
||||
- Tailscale公開はオフ
|
||||
- TelegramとWhatsAppのDMはデフォルトで許可リスト(電話番号の入力を求められる場合があります)
|
||||
</Tab>
|
||||
<Tab title="詳細設定(完全な制御)">
|
||||
- モード、ワークスペース、Gateway、チャンネル、デーモン、Skillsの完全なプロンプトフローを表示
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## CLIオンボーディングの詳細
|
||||
|
||||
<Columns>
|
||||
<Card title="CLIリファレンス" href="/start/wizard-cli-reference">
|
||||
ローカルおよびリモートフローの完全な説明、認証とモデルマトリックス、設定出力、ウィザードRPC、signal-cliの動作。
|
||||
</Card>
|
||||
<Card title="自動化とスクリプト" href="/start/wizard-cli-automation">
|
||||
非対話式オンボーディングのレシピと自動化された `agents add` の例。
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
## よく使うフォローアップコマンド
|
||||
|
||||
```bash
|
||||
openclaw configure
|
||||
openclaw agents add <name>
|
||||
```
|
||||
|
||||
<Note>
|
||||
`--json` は非対話モードを意味しません。スクリプトでは `--non-interactive` を使用してください。
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
推奨:エージェントが `web_search` を使用できるように、Brave Search APIキーを設定してください(`web_fetch` はキーなしで動作します)。最も簡単な方法:`openclaw configure --section web` を実行すると `plugins.entries.brave.config.webSearch.apiKey` に保存されます。旧 `tools.web.search.apiKey` パスは互換用に引き続き読み込まれますが、新しい設定では使用しないでください。ドキュメント:[Webツール](/tools/web)。
|
||||
</Tip>
|
||||
|
||||
## 関連ドキュメント
|
||||
|
||||
- CLIコマンドリファレンス:[`openclaw onboard`](/cli/onboard)
|
||||
- macOSアプリのオンボーディング:[オンボーディング](/start/onboarding)
|
||||
- エージェント初回起動の手順:[エージェントブートストラップ](/start/bootstrapping)
|
||||
@@ -503,7 +503,7 @@ if (sandboxRoot) {
|
||||
|
||||
- Refusal magic string scrubbing
|
||||
- Turn validation for consecutive roles
|
||||
- Claude Code parameter compatibility
|
||||
- Strict upstream Pi tool parameter validation
|
||||
|
||||
### Google/Gemini
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ native OpenClaw plugin registers against one or more capability types:
|
||||
| Capability | Registration method | Example plugins |
|
||||
| ---------------------- | ------------------------------------------------ | ------------------------------------ |
|
||||
| Text inference | `api.registerProvider(...)` | `openai`, `anthropic` |
|
||||
| CLI inference backend | `api.registerCliBackend(...)` | `openai`, `anthropic` |
|
||||
| Speech | `api.registerSpeechProvider(...)` | `elevenlabs`, `microsoft` |
|
||||
| Realtime transcription | `api.registerRealtimeTranscriptionProvider(...)` | `openai` |
|
||||
| Realtime voice | `api.registerRealtimeVoiceProvider(...)` | `openai` |
|
||||
|
||||
@@ -151,7 +151,6 @@ A single plugin can register any number of capabilities via the `api` object:
|
||||
| Capability | Registration method | Detailed guide |
|
||||
| ---------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------- |
|
||||
| Text inference (LLM) | `api.registerProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins) |
|
||||
| CLI inference backend | `api.registerCliBackend(...)` | [CLI Backends](/gateway/cli-backends) |
|
||||
| Channel / messaging | `api.registerChannel(...)` | [Channel Plugins](/plugins/sdk-channel-plugins) |
|
||||
| Speech (TTS/STT) | `api.registerSpeechProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
|
||||
| Realtime transcription | `api.registerRealtimeTranscriptionProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
|
||||
@@ -181,6 +180,7 @@ Hook guard semantics to keep in mind:
|
||||
- `before_tool_call`: `{ requireApproval: true }` pauses agent execution and prompts the user for approval via the exec approval overlay, Telegram buttons, Discord interactions, or the `/approve` command on any channel.
|
||||
- `before_install`: `{ block: true }` is terminal and stops lower-priority handlers.
|
||||
- `before_install`: `{ block: false }` is treated as no decision.
|
||||
- `tool_result_persist`: must stay synchronous because it runs in the transcript persistence path; return an updated tool result payload or `undefined` to keep the original.
|
||||
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
|
||||
- `message_sending`: `{ cancel: false }` is treated as no decision.
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ channels, tools, providers, or other capabilities. They are built and maintained
|
||||
by the community, published on [ClawHub](/tools/clawhub) or npm, and
|
||||
installable with a single command.
|
||||
|
||||
ClawHub is the canonical discovery surface for community plugins. Do not open
|
||||
docs-only PRs just to add your plugin here for discoverability; publish it on
|
||||
ClawHub instead.
|
||||
|
||||
```bash
|
||||
openclaw plugins install <package-name>
|
||||
```
|
||||
@@ -116,14 +120,13 @@ We welcome community plugins that are useful, documented, and safe to operate.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Open a PR">
|
||||
Add your plugin to this page with:
|
||||
<Step title="Use docs PRs only for source-doc changes">
|
||||
You do not need a docs PR just to make your plugin discoverable. Publish it
|
||||
on ClawHub instead.
|
||||
|
||||
- Plugin name
|
||||
- npm package name
|
||||
- GitHub repository URL
|
||||
- One-line description
|
||||
- Install command
|
||||
Open a docs PR only when OpenClaw's source docs need an actual content
|
||||
change, such as correcting install guidance or adding cross-repo
|
||||
documentation that belongs in the main docs set.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -89,7 +89,6 @@ Those belong in your plugin code and `package.json`.
|
||||
"modelSupport": {
|
||||
"modelPrefixes": ["router-"]
|
||||
},
|
||||
"cliBackends": ["openrouter-cli"],
|
||||
"providerAuthEnvVars": {
|
||||
"openrouter": ["OPENROUTER_API_KEY"]
|
||||
},
|
||||
@@ -140,7 +139,6 @@ Those belong in your plugin code and `package.json`.
|
||||
| `channels` | No | `string[]` | Channel ids owned by this plugin. Used for discovery and config validation. |
|
||||
| `providers` | No | `string[]` | Provider ids owned by this plugin. |
|
||||
| `modelSupport` | No | `object` | Manifest-owned shorthand model-family metadata used to auto-load the plugin before runtime. |
|
||||
| `cliBackends` | No | `string[]` | CLI inference backend ids owned by this plugin. Used for startup auto-activation from explicit config refs. |
|
||||
| `providerAuthEnvVars` | No | `Record<string, string[]>` | Cheap provider-auth env metadata that OpenClaw can inspect without loading plugin code. |
|
||||
| `providerAuthChoices` | No | `object[]` | Cheap auth-choice metadata for onboarding pickers, preferred-provider resolution, and simple CLI flag wiring. |
|
||||
| `contracts` | No | `object` | Static bundled capability snapshot for speech, realtime transcription, realtime voice, media-understanding, image-generation, video-generation, web-fetch, web search, and tool ownership. |
|
||||
@@ -399,7 +397,7 @@ See [Configuration reference](/gateway/configuration) for the full `plugins.*` s
|
||||
- `kind: "memory"` is selected by `plugins.slots.memory`.
|
||||
- `kind: "context-engine"` is selected by `plugins.slots.contextEngine`
|
||||
(default: built-in `legacy`).
|
||||
- `channels`, `providers`, `cliBackends`, and `skills` can be omitted when a
|
||||
- `channels`, `providers`, and `skills` can be omitted when a
|
||||
plugin does not need them.
|
||||
- If your plugin depends on native modules, document the build steps and any
|
||||
package-manager allowlist requirements (for example, pnpm `allow-build-scripts`
|
||||
|
||||
@@ -61,9 +61,11 @@ Most channel plugins do not need approval-specific code.
|
||||
- Core owns same-chat `/approve`, shared approval button payloads, and generic fallback delivery.
|
||||
- Prefer one `approvalCapability` object on the channel plugin when the channel needs approval-specific behavior.
|
||||
- `approvalCapability.authorizeActorAction` and `approvalCapability.getActionAvailabilityState` are the canonical approval-auth seam.
|
||||
- If your channel exposes native exec approvals, implement `approvalCapability.getActionAvailabilityState` even when the native transport lives entirely under `approvalCapability.native`. Core uses that availability hook to distinguish `enabled` vs `disabled`, decide whether the initiating channel supports native approvals, and include the channel in native-client fallback guidance.
|
||||
- Use `outbound.shouldSuppressLocalPayloadPrompt` or `outbound.beforeDeliverPayload` for channel-specific payload lifecycle behavior such as hiding duplicate local approval prompts or sending typing indicators before delivery.
|
||||
- Use `approvalCapability.delivery` only for native approval routing or fallback suppression.
|
||||
- Use `approvalCapability.render` only when a channel truly needs custom approval payloads instead of the shared renderer.
|
||||
- Use `approvalCapability.describeExecApprovalSetup` when the channel wants the disabled-path reply to explain the exact config knobs needed to enable native exec approvals. The hook receives `{ channel, channelLabel, accountId }`; named-account channels should render account-scoped paths such as `channels.<channel>.accounts.<id>.execApprovals.*` instead of top-level defaults.
|
||||
- If a channel can infer stable owner-like DM identities from existing config, use `createResolvedApproverActionAuthAdapter` from `openclaw/plugin-sdk/approval-runtime` to restrict same-chat `/approve` without adding approval-specific core logic.
|
||||
- If a channel needs native approval delivery, keep channel code focused on target normalization and transport hooks. Use `createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`, `createChannelApproverDmTargetResolver`, `createApproverRestrictedNativeApprovalCapability`, and `createChannelNativeApprovalRuntime` from `openclaw/plugin-sdk/approval-runtime` so core owns request filtering, routing, dedupe, expiry, and gateway subscription.
|
||||
- Native approval channels must route both `accountId` and `approvalKind` through those helpers. `accountId` keeps multi-account approval policy scoped to the right bot account, and `approvalKind` keeps exec vs plugin approval behavior available to the channel without hardcoded branches in core.
|
||||
|
||||
@@ -296,19 +296,19 @@ Current bundled provider examples:
|
||||
</Accordion>
|
||||
|
||||
This table is intentionally the common migration subset, not the full SDK
|
||||
surface. The generated full list of 200+ entrypoints lives in
|
||||
surface. The full list of 200+ entrypoints lives in
|
||||
`scripts/lib/plugin-sdk-entrypoints.json`.
|
||||
|
||||
That generated list still includes some bundled-plugin helper seams such as
|
||||
That list still includes some bundled-plugin helper seams such as
|
||||
`plugin-sdk/feishu`, `plugin-sdk/feishu-setup`, `plugin-sdk/zalo`,
|
||||
`plugin-sdk/zalo-setup`, and `plugin-sdk/matrix*`. Those remain exported for
|
||||
bundled-plugin maintenance and compatibility, but they are intentionally
|
||||
omitted from the common migration table and are not the recommended target for
|
||||
new plugin code.
|
||||
|
||||
The same rule applies to other generated bundled-helper families such as:
|
||||
The same rule applies to other bundled-helper families such as:
|
||||
|
||||
- browser: `plugin-sdk/browser*`
|
||||
- browser support helpers: `plugin-sdk/browser-cdp`, `plugin-sdk/browser-config-runtime`, `plugin-sdk/browser-config-support`, `plugin-sdk/browser-control-auth`, `plugin-sdk/browser-node-runtime`, `plugin-sdk/browser-profiles`, `plugin-sdk/browser-security-runtime`, `plugin-sdk/browser-setup-tools`, `plugin-sdk/browser-support`
|
||||
- Matrix: `plugin-sdk/matrix*`
|
||||
- LINE: `plugin-sdk/line*`
|
||||
- IRC: `plugin-sdk/irc*`
|
||||
|
||||
@@ -122,7 +122,6 @@ explicitly promotes one as public.
|
||||
| `plugin-sdk/provider-entry` | `defineSingleProviderPluginEntry` |
|
||||
| `plugin-sdk/provider-setup` | Curated local/self-hosted provider setup helpers |
|
||||
| `plugin-sdk/self-hosted-provider-setup` | Focused OpenAI-compatible self-hosted provider setup helpers |
|
||||
| `plugin-sdk/cli-backend` | CLI backend defaults + watchdog constants |
|
||||
| `plugin-sdk/provider-auth-runtime` | Runtime API-key resolution helpers for provider plugins |
|
||||
| `plugin-sdk/provider-auth-api-key` | API-key onboarding/profile-write helpers |
|
||||
| `plugin-sdk/provider-auth-result` | Standard OAuth auth-result builder |
|
||||
@@ -198,7 +197,7 @@ explicitly promotes one as public.
|
||||
| `plugin-sdk/json-store` | Small JSON state read/write helpers |
|
||||
| `plugin-sdk/file-lock` | Re-entrant file-lock helpers |
|
||||
| `plugin-sdk/persistent-dedupe` | Disk-backed dedupe cache helpers |
|
||||
| `plugin-sdk/acp-runtime` | ACP runtime/session helpers |
|
||||
| `plugin-sdk/acp-runtime` | ACP runtime/session and reply-dispatch helpers |
|
||||
| `plugin-sdk/agent-config-primitives` | Narrow agent runtime config-schema primitives |
|
||||
| `plugin-sdk/boolean-param` | Loose boolean param reader |
|
||||
| `plugin-sdk/dangerous-name-runtime` | Dangerous-name matching resolution helpers |
|
||||
@@ -262,9 +261,9 @@ explicitly promotes one as public.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Reserved bundled-helper subpaths">
|
||||
| Family | Current generated subpaths | Intended use |
|
||||
| Family | Current subpaths | Intended use |
|
||||
| --- | --- | --- |
|
||||
| Browser | `plugin-sdk/browser`, `plugin-sdk/browser-config-support`, `plugin-sdk/browser-support` | Bundled browser plugin maintenance and compatibility |
|
||||
| Browser | `plugin-sdk/browser-cdp`, `plugin-sdk/browser-config-runtime`, `plugin-sdk/browser-config-support`, `plugin-sdk/browser-control-auth`, `plugin-sdk/browser-node-runtime`, `plugin-sdk/browser-profiles`, `plugin-sdk/browser-security-runtime`, `plugin-sdk/browser-setup-tools`, `plugin-sdk/browser-support` | Bundled browser plugin support helpers (`browser-support` remains the compatibility barrel) |
|
||||
| Matrix | `plugin-sdk/matrix`, `plugin-sdk/matrix-helper`, `plugin-sdk/matrix-runtime-heavy`, `plugin-sdk/matrix-runtime-shared`, `plugin-sdk/matrix-runtime-surface`, `plugin-sdk/matrix-surface`, `plugin-sdk/matrix-thread-bindings` | Bundled Matrix helper/runtime surface |
|
||||
| Line | `plugin-sdk/line`, `plugin-sdk/line-core`, `plugin-sdk/line-runtime`, `plugin-sdk/line-surface` | Bundled LINE helper/runtime surface |
|
||||
| IRC | `plugin-sdk/irc`, `plugin-sdk/irc-surface` | Bundled IRC helper surface |
|
||||
@@ -283,7 +282,6 @@ methods:
|
||||
| Method | What it registers |
|
||||
| ------------------------------------------------ | -------------------------------- |
|
||||
| `api.registerProvider(...)` | Text inference (LLM) |
|
||||
| `api.registerCliBackend(...)` | Local CLI inference backend |
|
||||
| `api.registerChannel(...)` | Messaging channel |
|
||||
| `api.registerSpeechProvider(...)` | Text-to-speech / STT synthesis |
|
||||
| `api.registerRealtimeTranscriptionProvider(...)` | Streaming realtime transcription |
|
||||
@@ -351,18 +349,6 @@ Use `commands` by itself only when you do not need lazy root CLI registration.
|
||||
That eager compatibility path remains supported, but it does not install
|
||||
descriptor-backed placeholders for parse-time lazy loading.
|
||||
|
||||
### CLI backend registration
|
||||
|
||||
`api.registerCliBackend(...)` lets a plugin own the default config for a local
|
||||
AI CLI backend such as `claude-cli` or `codex-cli`.
|
||||
|
||||
- The backend `id` becomes the provider prefix in model refs like `claude-cli/opus`.
|
||||
- The backend `config` uses the same shape as `agents.defaults.cliBackends.<id>`.
|
||||
- User config still wins. OpenClaw merges `agents.defaults.cliBackends.<id>` over the
|
||||
plugin default before running the CLI.
|
||||
- Use `normalizeConfig` when a backend needs compatibility rewrites after merge
|
||||
(for example normalizing old flag shapes).
|
||||
|
||||
### Exclusive slots
|
||||
|
||||
| Method | What it registers |
|
||||
@@ -400,6 +386,7 @@ AI CLI backend such as `claude-cli` or `codex-cli`.
|
||||
- `before_tool_call`: returning `{ block: false }` is treated as no decision (same as omitting `block`), not as an override.
|
||||
- `before_install`: returning `{ block: true }` is terminal. Once any handler sets it, lower-priority handlers are skipped.
|
||||
- `before_install`: returning `{ block: false }` is treated as no decision (same as omitting `block`), not as an override.
|
||||
- `reply_dispatch`: returning `{ handled: true, ... }` is terminal. Once any handler claims dispatch, lower-priority handlers and the default model dispatch path are skipped.
|
||||
- `message_sending`: returning `{ cancel: true }` is terminal. Once any handler sets it, lower-priority handlers are skipped.
|
||||
- `message_sending`: returning `{ cancel: false }` is treated as no decision (same as omitting `cancel`), not as an override.
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ API key auth, and dynamic model resolution.
|
||||
|
||||
Real bundled examples:
|
||||
|
||||
- `google` and `google-gemini-cli`: `google-gemini`
|
||||
- `google`: `google-gemini`
|
||||
- `openrouter`, `kilocode`, `opencode`, and `opencode-go`: `passthrough-gemini`
|
||||
- `amazon-bedrock` and `anthropic-vertex`: `anthropic-by-model`
|
||||
- `minimax`: `hybrid-anthropic-openai`
|
||||
@@ -303,7 +303,7 @@ API key auth, and dynamic model resolution.
|
||||
|
||||
Real bundled examples:
|
||||
|
||||
- `google` and `google-gemini-cli`: `google-thinking`
|
||||
- `google`: `google-thinking`
|
||||
- `kilocode`: `kilocode-thinking`
|
||||
- `moonshot`: `moonshot-thinking`
|
||||
- `minimax` and `minimax-portal`: `minimax-fast-mode`
|
||||
@@ -513,6 +513,13 @@ API key auth, and dynamic model resolution.
|
||||
| 42 | `validateReplayTurns` | Strict replay-turn validation before the embedded runner |
|
||||
| 43 | `onModelSelected` | Post-selection callback (e.g. telemetry) |
|
||||
|
||||
Prompt tuning note:
|
||||
|
||||
- `resolveSystemPromptContribution` lets a provider inject cache-aware
|
||||
system-prompt guidance for a model family. Prefer it over
|
||||
`before_prompt_build` when the behavior belongs to one provider/model
|
||||
family and should preserve the stable/dynamic cache split.
|
||||
|
||||
For detailed descriptions and real-world examples, see
|
||||
[Internals: Provider Runtime Hooks](/plugins/architecture#provider-runtime-hooks).
|
||||
</Accordion>
|
||||
|
||||
72
docs/providers/alibaba.md
Normal file
72
docs/providers/alibaba.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: "Alibaba Model Studio"
|
||||
summary: "Alibaba Model Studio Wan video generation in OpenClaw"
|
||||
read_when:
|
||||
- You want to use Alibaba Wan video generation in OpenClaw
|
||||
- You need Model Studio or DashScope API key setup for video generation
|
||||
---
|
||||
|
||||
# Alibaba Model Studio
|
||||
|
||||
OpenClaw ships a bundled `alibaba` video-generation provider for Wan models on
|
||||
Alibaba Model Studio / DashScope.
|
||||
|
||||
- Provider: `alibaba`
|
||||
- Preferred auth: `MODELSTUDIO_API_KEY`
|
||||
- Also accepted: `DASHSCOPE_API_KEY`, `QWEN_API_KEY`
|
||||
- API: DashScope / Model Studio async video generation
|
||||
|
||||
## Quick start
|
||||
|
||||
1. Set an API key:
|
||||
|
||||
```bash
|
||||
openclaw onboard --auth-choice qwen-standard-api-key
|
||||
```
|
||||
|
||||
2. Set a default video model:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
videoGenerationModel: {
|
||||
primary: "alibaba/wan2.6-t2v",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Built-in Wan models
|
||||
|
||||
The bundled `alibaba` provider currently registers:
|
||||
|
||||
- `alibaba/wan2.6-t2v`
|
||||
- `alibaba/wan2.6-i2v`
|
||||
- `alibaba/wan2.6-r2v`
|
||||
- `alibaba/wan2.6-r2v-flash`
|
||||
- `alibaba/wan2.7-r2v`
|
||||
|
||||
## Current limits
|
||||
|
||||
- Up to **1** output video per request
|
||||
- Up to **1** input image
|
||||
- Up to **4** input videos
|
||||
- Up to **10 seconds** duration
|
||||
- Supports `size`, `aspectRatio`, `resolution`, `audio`, and `watermark`
|
||||
- Reference image/video mode currently requires **remote http(s) URLs**
|
||||
|
||||
## Relationship to Qwen
|
||||
|
||||
The bundled `qwen` provider also uses Alibaba-hosted DashScope endpoints for
|
||||
Wan video generation. Use:
|
||||
|
||||
- `qwen/...` when you want the canonical Qwen provider surface
|
||||
- `alibaba/...` when you want the direct vendor-owned Wan video surface
|
||||
|
||||
## Related
|
||||
|
||||
- [Video Generation](/tools/video-generation)
|
||||
- [Qwen](/providers/qwen)
|
||||
- [Configuration Reference](/gateway/configuration-reference#agent-defaults)
|
||||
@@ -1,40 +1,51 @@
|
||||
---
|
||||
summary: "Use Anthropic Claude via API keys or Claude CLI in OpenClaw"
|
||||
summary: "Use Anthropic Claude via API keys in OpenClaw"
|
||||
read_when:
|
||||
- You want to use Anthropic models in OpenClaw
|
||||
- You want to reuse Claude CLI subscription auth on the gateway host
|
||||
title: "Anthropic"
|
||||
---
|
||||
|
||||
# Anthropic (Claude)
|
||||
|
||||
Anthropic builds the **Claude** model family and provides access via an API.
|
||||
In OpenClaw, new Anthropic setup should use an API key or the local Claude CLI
|
||||
backend. Existing legacy Anthropic token profiles are still honored at runtime
|
||||
if they are already configured.
|
||||
In OpenClaw, new Anthropic setup should use an API key. Existing legacy
|
||||
Anthropic token profiles are still honored at runtime if they are already
|
||||
configured.
|
||||
|
||||
<Warning>
|
||||
Anthropic's public Claude Code docs say direct Claude Code usage is included
|
||||
with Claude subscriptions. Separately, Anthropic notified OpenClaw users on
|
||||
**April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that **OpenClaw counts as a
|
||||
third-party harness**. Their stated policy is that OpenClaw-driven Claude-login
|
||||
traffic no longer uses the included Claude subscription pool and instead
|
||||
requires **Extra Usage** (pay-as-you-go, billed separately from the
|
||||
subscription).
|
||||
For Anthropic in OpenClaw, the billing split is:
|
||||
|
||||
That policy distinction is about **OpenClaw-driven Claude CLI reuse**, not
|
||||
about running `claude` directly in your own terminal.
|
||||
- **Anthropic API key**: normal Anthropic API billing.
|
||||
- **Claude subscription auth inside OpenClaw**: Anthropic told OpenClaw users on
|
||||
**April 4, 2026 at 12:00 PM PT / 8:00 PM BST** that this counts as
|
||||
third-party harness usage and requires **Extra Usage** (pay-as-you-go,
|
||||
billed separately from the subscription).
|
||||
|
||||
Anthropic's current direct-Claude-Code plan docs:
|
||||
Our local repros match that split:
|
||||
|
||||
- direct `claude -p` may still work
|
||||
- `claude -p --append-system-prompt ...` can trip the Extra Usage guard when
|
||||
the prompt identifies OpenClaw
|
||||
- the same OpenClaw-like system prompt does **not** reproduce the block on the
|
||||
Anthropic SDK + `ANTHROPIC_API_KEY` path
|
||||
|
||||
So the practical rule is: **Anthropic API key, or Claude subscription with
|
||||
Extra Usage**. If you want the clearest production path, use an Anthropic API
|
||||
key.
|
||||
|
||||
Anthropic's current public docs:
|
||||
|
||||
- [Claude Code CLI reference](https://code.claude.com/docs/en/cli-reference)
|
||||
- [Claude Agent SDK overview](https://platform.claude.com/docs/en/agent-sdk/overview)
|
||||
|
||||
- [Using Claude Code with your Pro or Max plan](https://support.claude.com/en/articles/11145838-using-claude-code-with-your-pro-or-max-plan)
|
||||
- [Using Claude Code with your Team or Enterprise plan](https://support.anthropic.com/en/articles/11845131-using-claude-code-with-your-team-or-enterprise-plan/)
|
||||
|
||||
If you want a clearer billing path, use an Anthropic API key instead. OpenClaw
|
||||
also supports other subscription-style options, including [OpenAI
|
||||
Codex](/providers/openai), [Qwen Cloud Coding
|
||||
Plan](/providers/qwen), [MiniMax Coding Plan](/providers/minimax),
|
||||
and [Z.AI / GLM Coding Plan](/providers/glm).
|
||||
If you want the clearest billing path, use an Anthropic API key instead.
|
||||
OpenClaw also supports other subscription-style options, including [OpenAI
|
||||
Codex](/providers/openai), [Qwen Cloud Coding Plan](/providers/qwen),
|
||||
[MiniMax Coding Plan](/providers/minimax), and [Z.AI / GLM Coding
|
||||
Plan](/providers/glm).
|
||||
</Warning>
|
||||
|
||||
## Option A: Anthropic API key
|
||||
@@ -52,7 +63,7 @@ openclaw onboard
|
||||
openclaw onboard --anthropic-api-key "$ANTHROPIC_API_KEY"
|
||||
```
|
||||
|
||||
### Claude CLI config snippet
|
||||
### Anthropic config snippet
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -202,154 +213,30 @@ legacy Anthropic token auth (`sk-ant-oat-*`). If you configure
|
||||
falls back to the standard context window by skipping the context1m beta
|
||||
header while keeping the required OAuth betas.
|
||||
|
||||
## Option B: Claude CLI as the message provider
|
||||
## Removed: Claude CLI backend
|
||||
|
||||
**Best for:** a single-user gateway host that already has Claude CLI installed
|
||||
and signed in with a Claude subscription.
|
||||
The bundled Anthropic `claude-cli` backend was removed.
|
||||
|
||||
Billing note: Anthropic's public Claude Code docs cover **direct** Claude Code
|
||||
usage under Pro/Max or Team/Enterprise plans. Separately, Anthropic told
|
||||
OpenClaw users that **OpenClaw-driven** Claude CLI usage is treated as
|
||||
third-party harness traffic. As of **April 4, 2026 at 12:00 PM PT / 8:00 PM
|
||||
BST**, Anthropic says this OpenClaw path requires **Extra Usage** instead of
|
||||
the included Claude subscription limits.
|
||||
|
||||
This path uses the local `claude` binary for model inference instead of calling
|
||||
the Anthropic API directly. OpenClaw treats it as a **CLI backend provider**
|
||||
with model refs like:
|
||||
|
||||
- `claude-cli/claude-sonnet-4-6`
|
||||
- `claude-cli/claude-opus-4-6`
|
||||
|
||||
How it works:
|
||||
|
||||
1. OpenClaw launches `claude -p --output-format stream-json --include-partial-messages ...`
|
||||
on the **gateway host** and sends the prompt over stdin.
|
||||
2. The first turn sends `--session-id <uuid>`.
|
||||
3. Follow-up turns reuse the stored Claude session via `--resume <sessionId>`.
|
||||
4. Your chat messages still go through the normal OpenClaw message pipeline, but
|
||||
the actual model reply is produced by Claude CLI.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Claude CLI installed on the gateway host and available on PATH, or configured
|
||||
with an absolute command path.
|
||||
- Claude CLI already authenticated on that same host:
|
||||
|
||||
```bash
|
||||
claude auth status
|
||||
```
|
||||
|
||||
- OpenClaw auto-loads the bundled Anthropic plugin at gateway startup when your
|
||||
config explicitly references `claude-cli/...` or `claude-cli` backend config.
|
||||
|
||||
### Config snippet
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "claude-cli/claude-sonnet-4-6",
|
||||
},
|
||||
models: {
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
},
|
||||
sandbox: { mode: "off" },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If the `claude` binary is not on the gateway host PATH:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
cliBackends: {
|
||||
"claude-cli": {
|
||||
command: "/opt/homebrew/bin/claude",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### What you get
|
||||
|
||||
- Claude subscription auth reused from the local CLI
|
||||
- Normal OpenClaw message/session routing
|
||||
- Claude CLI session continuity across turns
|
||||
|
||||
### Migrate from Anthropic auth to Claude CLI
|
||||
|
||||
If you currently use `anthropic/...` with a legacy token profile or API key and want to
|
||||
switch the same gateway host to Claude CLI, OpenClaw supports that as a normal
|
||||
provider-auth migration path.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Claude CLI installed on the **same gateway host** that runs OpenClaw
|
||||
- Claude CLI already signed in there: `claude auth login`
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider anthropic --method cli --set-default
|
||||
```
|
||||
|
||||
Or in onboarding:
|
||||
|
||||
```bash
|
||||
openclaw onboard --auth-choice anthropic-cli
|
||||
```
|
||||
|
||||
Interactive `openclaw onboard` and `openclaw configure` now prefer **Anthropic
|
||||
Claude CLI** first and **Anthropic API key** second.
|
||||
|
||||
What this does:
|
||||
|
||||
- verifies Claude CLI is already signed in on the gateway host
|
||||
- switches the default model to `claude-cli/...`
|
||||
- rewrites Anthropic default-model fallbacks like `anthropic/claude-opus-4-6`
|
||||
to `claude-cli/claude-opus-4-6`
|
||||
- adds matching `claude-cli/...` entries to `agents.defaults.models`
|
||||
|
||||
Quick verification:
|
||||
|
||||
```bash
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
You should see the resolved primary model under `claude-cli/...`.
|
||||
|
||||
What it does **not** do:
|
||||
|
||||
- delete your existing Anthropic auth profiles
|
||||
- remove every old `anthropic/...` config reference outside the main default
|
||||
model/allowlist path
|
||||
|
||||
That makes rollback simple: change the default model back to `anthropic/...` if
|
||||
you need to.
|
||||
|
||||
### Important limits
|
||||
|
||||
- This is **not** the Anthropic API provider. It is the local CLI runtime.
|
||||
- Tools are disabled on the OpenClaw side for CLI backend runs.
|
||||
- Text in, text out. No OpenClaw streaming handoff.
|
||||
- Best fit for a personal gateway host, not shared multi-user billing setups.
|
||||
|
||||
More details: [/gateway/cli-backends](/gateway/cli-backends)
|
||||
- Anthropic's April 4, 2026 notice says OpenClaw-driven Claude-login traffic is
|
||||
third-party harness usage and requires **Extra Usage**.
|
||||
- Our local repros also show that direct
|
||||
`claude -p --append-system-prompt ...` can hit the same guard when the
|
||||
appended prompt identifies OpenClaw.
|
||||
- The same OpenClaw-like system prompt does not hit that guard on the
|
||||
Anthropic SDK + `ANTHROPIC_API_KEY` path.
|
||||
- Use Anthropic API keys for Anthropic traffic in OpenClaw.
|
||||
|
||||
## Notes
|
||||
|
||||
- Anthropic's public Claude Code plan docs still cover direct Claude Code
|
||||
terminal use under Claude subscriptions. Anthropic's separate notice to
|
||||
OpenClaw users says the **OpenClaw** Claude-login path is third-party harness
|
||||
usage and requires **Extra Usage** (pay-as-you-go billed separately from the
|
||||
subscription).
|
||||
- Anthropic's public Claude Code docs still document direct CLI usage such as
|
||||
`claude -p`, but Anthropic's separate notice to OpenClaw users says the
|
||||
**OpenClaw** Claude-login path is third-party harness usage and requires
|
||||
**Extra Usage** (pay-as-you-go billed separately from the subscription).
|
||||
Our local repros also show that direct
|
||||
`claude -p --append-system-prompt ...` can hit the same guard when the
|
||||
appended prompt identifies OpenClaw, while the same prompt shape does not
|
||||
reproduce on the Anthropic SDK + `ANTHROPIC_API_KEY` path. For production, we
|
||||
recommend Anthropic API keys instead.
|
||||
- Anthropic setup-token is available again in OpenClaw as a legacy/manual path. Anthropic's OpenClaw-specific billing notice still applies, so use it with the expectation that Anthropic requires **Extra Usage** for this path.
|
||||
- Auth details + reuse rules are in [/concepts/oauth](/concepts/oauth).
|
||||
|
||||
@@ -358,7 +245,7 @@ More details: [/gateway/cli-backends](/gateway/cli-backends)
|
||||
**401 errors / token suddenly invalid**
|
||||
|
||||
- Legacy Anthropic token auth can expire or be revoked.
|
||||
- For new setup, migrate to an Anthropic API key or the local Claude CLI path on the gateway host.
|
||||
- For new setup, migrate to an Anthropic API key.
|
||||
|
||||
**No API key found for provider "anthropic"**
|
||||
|
||||
@@ -369,7 +256,7 @@ More details: [/gateway/cli-backends](/gateway/cli-backends)
|
||||
**No credentials found for profile `anthropic:default`**
|
||||
|
||||
- Run `openclaw models status` to see which auth profile is active.
|
||||
- Re-run onboarding, or configure an API key or Claude CLI for that profile path.
|
||||
- Re-run onboarding, or configure an API key for that profile path.
|
||||
|
||||
**No available auth profile (all in cooldown/unavailable)**
|
||||
|
||||
|
||||
91
docs/providers/bedrock-mantle.md
Normal file
91
docs/providers/bedrock-mantle.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
summary: "Use Amazon Bedrock Mantle (OpenAI-compatible) models with OpenClaw"
|
||||
read_when:
|
||||
- You want to use Bedrock Mantle hosted OSS models with OpenClaw
|
||||
- You need the Mantle OpenAI-compatible endpoint for GPT-OSS, Qwen, Kimi, or GLM
|
||||
title: "Amazon Bedrock Mantle"
|
||||
---
|
||||
|
||||
# Amazon Bedrock Mantle
|
||||
|
||||
OpenClaw includes a bundled **Amazon Bedrock Mantle** provider that connects to
|
||||
the Mantle OpenAI-compatible endpoint. Mantle hosts open-source and
|
||||
third-party models (GPT-OSS, Qwen, Kimi, GLM, and similar) through a standard
|
||||
`/v1/chat/completions` surface backed by Bedrock infrastructure.
|
||||
|
||||
## What OpenClaw supports
|
||||
|
||||
- Provider: `amazon-bedrock-mantle`
|
||||
- API: `openai-completions` (OpenAI-compatible)
|
||||
- Auth: bearer token via `AWS_BEARER_TOKEN_BEDROCK`
|
||||
- Region: `AWS_REGION` or `AWS_DEFAULT_REGION` (default: `us-east-1`)
|
||||
|
||||
## Automatic model discovery
|
||||
|
||||
When `AWS_BEARER_TOKEN_BEDROCK` is set, OpenClaw automatically discovers
|
||||
available Mantle models by querying the region's `/v1/models` endpoint.
|
||||
Discovery results are cached for 1 hour.
|
||||
|
||||
Supported regions: `us-east-1`, `us-east-2`, `us-west-2`, `ap-northeast-1`,
|
||||
`ap-south-1`, `ap-southeast-3`, `eu-central-1`, `eu-west-1`, `eu-west-2`,
|
||||
`eu-south-1`, `eu-north-1`, `sa-east-1`.
|
||||
|
||||
## Onboarding
|
||||
|
||||
1. Set the bearer token on the **gateway host**:
|
||||
|
||||
```bash
|
||||
export AWS_BEARER_TOKEN_BEDROCK="..."
|
||||
# Optional (defaults to us-east-1):
|
||||
export AWS_REGION="us-west-2"
|
||||
```
|
||||
|
||||
2. Verify models are discovered:
|
||||
|
||||
```bash
|
||||
openclaw models list
|
||||
```
|
||||
|
||||
Discovered models appear under the `amazon-bedrock-mantle` provider. No
|
||||
additional config is required unless you want to override defaults.
|
||||
|
||||
## Manual configuration
|
||||
|
||||
If you prefer explicit config instead of auto-discovery:
|
||||
|
||||
```json5
|
||||
{
|
||||
models: {
|
||||
providers: {
|
||||
"amazon-bedrock-mantle": {
|
||||
baseUrl: "https://bedrock-mantle.us-east-1.api.aws/v1",
|
||||
api: "openai-completions",
|
||||
auth: "api-key",
|
||||
apiKey: "env:AWS_BEARER_TOKEN_BEDROCK",
|
||||
models: [
|
||||
{
|
||||
id: "gpt-oss-120b",
|
||||
name: "GPT-OSS 120B",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 32000,
|
||||
maxTokens: 4096,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Mantle requires a bearer token today. Plain IAM credentials (instance roles,
|
||||
SSO, access keys) are not sufficient without a token.
|
||||
- The bearer token is the same `AWS_BEARER_TOKEN_BEDROCK` used by the standard
|
||||
[Amazon Bedrock](/providers/bedrock) provider.
|
||||
- Reasoning support is inferred from model IDs containing patterns like
|
||||
`thinking`, `reasoner`, or `gpt-oss-120b`.
|
||||
- If the Mantle endpoint is unavailable or returns no models, the provider is
|
||||
silently skipped.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user