mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 22:11:38 +08:00
Compare commits
1634 Commits
codeql-app
...
codex/code
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8364730c41 | ||
|
|
60c6915b6c | ||
|
|
a301df0668 | ||
|
|
4ad29d2d8e | ||
|
|
d647ba1c6f | ||
|
|
d5736710a9 | ||
|
|
84a3b50c11 | ||
|
|
3f002b10d2 | ||
|
|
579acc3a91 | ||
|
|
575854c096 | ||
|
|
ec59af3386 | ||
|
|
ea4d0a3ce7 | ||
|
|
e7f47f61ab | ||
|
|
51affb81b9 | ||
|
|
e2a465df4b | ||
|
|
8a77f299ee | ||
|
|
57fcd7b56d | ||
|
|
d29c470d7c | ||
|
|
dc1d6856bc | ||
|
|
476ac66d80 | ||
|
|
01595d60c1 | ||
|
|
6eae36282b | ||
|
|
91fbbccc10 | ||
|
|
8751464cb9 | ||
|
|
c1f31f3870 | ||
|
|
d9401c7deb | ||
|
|
fcc0f4996c | ||
|
|
ea1a6d250a | ||
|
|
aa5a0a36f8 | ||
|
|
3fd4d1d29d | ||
|
|
f858b5de22 | ||
|
|
bbc3384fda | ||
|
|
b388209eaf | ||
|
|
d0e83b0aea | ||
|
|
b7fd104a8b | ||
|
|
bce729f6ab | ||
|
|
7e06455e64 | ||
|
|
9c0975c1c2 | ||
|
|
a0035764b6 | ||
|
|
992dc8de88 | ||
|
|
2e50f167ce | ||
|
|
7df025f457 | ||
|
|
8bd9e227a0 | ||
|
|
456e1c0a6a | ||
|
|
4977c2d844 | ||
|
|
76c8f9ac3f | ||
|
|
8b62e0fa96 | ||
|
|
03bde3d65c | ||
|
|
757af70bf7 | ||
|
|
0d7d1aa09c | ||
|
|
8f16079623 | ||
|
|
0442417e1f | ||
|
|
42584964ac | ||
|
|
0ce0509856 | ||
|
|
3196abb064 | ||
|
|
71dd936312 | ||
|
|
b9fe26af7f | ||
|
|
36eec68fb9 | ||
|
|
efe6b37407 | ||
|
|
4f7286ce86 | ||
|
|
05f607c149 | ||
|
|
c2ffe77926 | ||
|
|
f0a2b09df6 | ||
|
|
b15faae92f | ||
|
|
df4136018e | ||
|
|
524d28bed0 | ||
|
|
f6d3363f31 | ||
|
|
85cfc91a70 | ||
|
|
6c4ecd8d25 | ||
|
|
c38d94677c | ||
|
|
9f55378745 | ||
|
|
05d8c27d85 | ||
|
|
ebbd80a6a2 | ||
|
|
5c95fc06fa | ||
|
|
ae0e57eefc | ||
|
|
4e9207c212 | ||
|
|
f99f6f164a | ||
|
|
5e33bfee10 | ||
|
|
62b1e0d8b8 | ||
|
|
15649228d4 | ||
|
|
77c0ecdf34 | ||
|
|
36e687edf0 | ||
|
|
24fc40b133 | ||
|
|
0e8cb3d94b | ||
|
|
0fe007f71b | ||
|
|
f221bc85a0 | ||
|
|
f9b47ad2a1 | ||
|
|
5fdde9353e | ||
|
|
bae211f72a | ||
|
|
d7ea6d9f8c | ||
|
|
a4f590a096 | ||
|
|
963c56e01c | ||
|
|
1e66728a55 | ||
|
|
cef2542cec | ||
|
|
472de0e1d5 | ||
|
|
84e9463eec | ||
|
|
6c07de05f4 | ||
|
|
a4fd45ca31 | ||
|
|
9b231e39ad | ||
|
|
49b1770b8e | ||
|
|
25446d3c0e | ||
|
|
6c86972fbe | ||
|
|
fb97e1cc88 | ||
|
|
70095f08f4 | ||
|
|
f0c7c430f5 | ||
|
|
73891eaca6 | ||
|
|
86251f4391 | ||
|
|
cba0a348dc | ||
|
|
3b75898bee | ||
|
|
3a24a25f4b | ||
|
|
56b10ddf17 | ||
|
|
0cf129f5d3 | ||
|
|
f86953f354 | ||
|
|
94b4b3c644 | ||
|
|
442f59508e | ||
|
|
7e8d95b413 | ||
|
|
2c152ffa7f | ||
|
|
27b35c5b24 | ||
|
|
023955b004 | ||
|
|
12882a88b1 | ||
|
|
394bc9c465 | ||
|
|
e6c1a6637a | ||
|
|
a6e79d42cf | ||
|
|
4de2e7487a | ||
|
|
1c0b4369ab | ||
|
|
755fa16a80 | ||
|
|
f85bd0f5a9 | ||
|
|
6c55106c80 | ||
|
|
bf8bdcb064 | ||
|
|
ad1e14af53 | ||
|
|
d0ec3d1f09 | ||
|
|
c07f29bcf7 | ||
|
|
cad2cef0fb | ||
|
|
debb8ac76c | ||
|
|
f6a1d70080 | ||
|
|
1076d6c124 | ||
|
|
4f02a57f65 | ||
|
|
5230b09ca9 | ||
|
|
6776129315 | ||
|
|
778b49b8fd | ||
|
|
6dac51569e | ||
|
|
c7a91f9632 | ||
|
|
6fb9e9e558 | ||
|
|
8be40059fe | ||
|
|
6f819280a3 | ||
|
|
32359e667b | ||
|
|
2fbe808a32 | ||
|
|
c3bac63c1b | ||
|
|
2ea47988dd | ||
|
|
578178faa4 | ||
|
|
f4fb9eb3ce | ||
|
|
a0f1293505 | ||
|
|
132b3e3940 | ||
|
|
e11787a564 | ||
|
|
f8f719ee23 | ||
|
|
200443e1b3 | ||
|
|
4ce031fd1a | ||
|
|
1b1b1b41a3 | ||
|
|
bcaf980015 | ||
|
|
ac0e3013ab | ||
|
|
942d46a4d5 | ||
|
|
c1fec482e8 | ||
|
|
54e2f4dc28 | ||
|
|
93222c5f12 | ||
|
|
22fa77de31 | ||
|
|
aaa2f32175 | ||
|
|
74bd209f48 | ||
|
|
25f832531c | ||
|
|
c6a12a6fd2 | ||
|
|
8e5c2efb8d | ||
|
|
1d47974f89 | ||
|
|
2ea00e1c35 | ||
|
|
0b4bc78496 | ||
|
|
e1a7c5b860 | ||
|
|
72f6016ce5 | ||
|
|
e073485c23 | ||
|
|
040f533f60 | ||
|
|
666ab0a00b | ||
|
|
29d9a30497 | ||
|
|
7b3dfbf214 | ||
|
|
42aaf0c98a | ||
|
|
ec69c07b27 | ||
|
|
050f0f50c9 | ||
|
|
4a4353e33f | ||
|
|
7719dd8804 | ||
|
|
12fbdd4ede | ||
|
|
524528944f | ||
|
|
5fbf406beb | ||
|
|
8fd9264ae7 | ||
|
|
e5d2273e05 | ||
|
|
caa7f7c4cc | ||
|
|
aa74888cf7 | ||
|
|
7301e57632 | ||
|
|
0e1af0d770 | ||
|
|
b48dcab1b5 | ||
|
|
ef832f83f6 | ||
|
|
b68f3de91b | ||
|
|
702e23835d | ||
|
|
dddf871ad9 | ||
|
|
f5fde074bd | ||
|
|
195a58224c | ||
|
|
1c9b4d871c | ||
|
|
1e3d240220 | ||
|
|
1b341f963b | ||
|
|
bbc47cb9e1 | ||
|
|
0a74037f6f | ||
|
|
5d519f1dc5 | ||
|
|
737fd808dd | ||
|
|
678ef019f3 | ||
|
|
8d288e2dfd | ||
|
|
2c488daaf4 | ||
|
|
b547286937 | ||
|
|
0d631fa701 | ||
|
|
b6daa922d6 | ||
|
|
39ab11425f | ||
|
|
fe022e409d | ||
|
|
404446f758 | ||
|
|
5f42438cf7 | ||
|
|
931e60723d | ||
|
|
f3d5c54884 | ||
|
|
5403df0bc2 | ||
|
|
cc2564615b | ||
|
|
213bfcf79b | ||
|
|
d4645373e7 | ||
|
|
19cb778451 | ||
|
|
bfa48c4025 | ||
|
|
3585d3e226 | ||
|
|
15adc741ff | ||
|
|
7f58e89731 | ||
|
|
d3bb5ce9e9 | ||
|
|
018f77cdc2 | ||
|
|
af34a5db6e | ||
|
|
1e6bdf3a55 | ||
|
|
d61c919106 | ||
|
|
bf7ac8d8c4 | ||
|
|
61db2e06d5 | ||
|
|
f9bb6e3515 | ||
|
|
9a051d2f9b | ||
|
|
7ddf28c0d4 | ||
|
|
6e3fd67084 | ||
|
|
90554ea048 | ||
|
|
ca620eaf35 | ||
|
|
48b39bffbe | ||
|
|
4b09c27398 | ||
|
|
4ed6a7c6b8 | ||
|
|
14ba8dc3f7 | ||
|
|
e8afaf512e | ||
|
|
996e0ae2f2 | ||
|
|
e26357fee8 | ||
|
|
4eec2843cd | ||
|
|
6387f83512 | ||
|
|
5c0388c253 | ||
|
|
8abf2977f4 | ||
|
|
bfd3c2a450 | ||
|
|
684001ae7b | ||
|
|
94543092be | ||
|
|
e5208bd331 | ||
|
|
c2cb648dc3 | ||
|
|
4cbd1b53cf | ||
|
|
f98ba66af6 | ||
|
|
88da533714 | ||
|
|
9e01d19db3 | ||
|
|
c9828635a8 | ||
|
|
ef186a06d9 | ||
|
|
38e03d3af3 | ||
|
|
298c2fbad4 | ||
|
|
22a74de693 | ||
|
|
86a563e899 | ||
|
|
9aad403b7f | ||
|
|
494eb01ac8 | ||
|
|
111432a7a6 | ||
|
|
067375cee3 | ||
|
|
61985cb1d2 | ||
|
|
9df0ae6767 | ||
|
|
29ed5266bf | ||
|
|
e131eaecb5 | ||
|
|
6efb44944c | ||
|
|
465d1b0d4b | ||
|
|
637525136e | ||
|
|
c48c3ecbc7 | ||
|
|
75b7ad2784 | ||
|
|
dffc295a74 | ||
|
|
2500b5d4ec | ||
|
|
ef0eb12615 | ||
|
|
37f8c3806a | ||
|
|
694598822f | ||
|
|
2e0acd9775 | ||
|
|
553e842fa6 | ||
|
|
ecf6cbf75d | ||
|
|
aec83af23d | ||
|
|
4ee6068ced | ||
|
|
8a399ec5b4 | ||
|
|
7d7b610a24 | ||
|
|
0ac1a07f7c | ||
|
|
0c3d1892cd | ||
|
|
250376f885 | ||
|
|
8ce44b057f | ||
|
|
9d21df251e | ||
|
|
a379ac0562 | ||
|
|
d0dac324c6 | ||
|
|
f42645037f | ||
|
|
e816235c2d | ||
|
|
6776345d0a | ||
|
|
c39b323ab3 | ||
|
|
bd32238a23 | ||
|
|
ad3e4dbcce | ||
|
|
c677861032 | ||
|
|
fc1c597dbf | ||
|
|
b0b627e5a9 | ||
|
|
be918636ab | ||
|
|
be14820b5d | ||
|
|
ec1b96cdfa | ||
|
|
d23c8a8eba | ||
|
|
7340c0322f | ||
|
|
0d2a201b27 | ||
|
|
ae07d57f9d | ||
|
|
42d73fd955 | ||
|
|
ffcc0d1fe1 | ||
|
|
e8810c04a4 | ||
|
|
ef270b7a28 | ||
|
|
9e94a9e418 | ||
|
|
97d42a9614 | ||
|
|
e5fd9c0582 | ||
|
|
9931603adb | ||
|
|
50d8ef2229 | ||
|
|
84920fad4e | ||
|
|
8b51d1fdc2 | ||
|
|
955a0e9c0f | ||
|
|
173f959613 | ||
|
|
1b6f2969aa | ||
|
|
2fd7c054ae | ||
|
|
3af8e17cc5 | ||
|
|
b2aac178d6 | ||
|
|
464e573602 | ||
|
|
e8f9c3e6de | ||
|
|
4ea0556f64 | ||
|
|
214b3d3336 | ||
|
|
1c300cec5d | ||
|
|
76930da7eb | ||
|
|
eabab1f64f | ||
|
|
54f44ec321 | ||
|
|
5d1ba08e3c | ||
|
|
07bc320a8a | ||
|
|
778902103d | ||
|
|
e8258fd4a6 | ||
|
|
f2d97df262 | ||
|
|
8093ae6029 | ||
|
|
3e67ee63b4 | ||
|
|
0260903f7f | ||
|
|
d0a7986638 | ||
|
|
60bdb96f2c | ||
|
|
e0fe02fb09 | ||
|
|
22d6e9564a | ||
|
|
45b8645079 | ||
|
|
40b0b1bfe0 | ||
|
|
3144e7a729 | ||
|
|
354084b1b3 | ||
|
|
5a69832833 | ||
|
|
8989ceee50 | ||
|
|
ce833acbdb | ||
|
|
6603a174bc | ||
|
|
619064b6d7 | ||
|
|
df0ee092f0 | ||
|
|
98d87b06e0 | ||
|
|
6cc7432cd1 | ||
|
|
4987482e4c | ||
|
|
fd0ca5987b | ||
|
|
206b5f78a2 | ||
|
|
6bc3458222 | ||
|
|
a853c5e8c2 | ||
|
|
e7dafaf2af | ||
|
|
f110c153c2 | ||
|
|
c709b17fef | ||
|
|
38da2ac6f8 | ||
|
|
4b98f09529 | ||
|
|
bd20f8e07e | ||
|
|
3c4851037b | ||
|
|
b277ae3f4c | ||
|
|
a102f4dede | ||
|
|
e47a7448e9 | ||
|
|
1ad50a36ac | ||
|
|
52bf20b07d | ||
|
|
1e87f6bf70 | ||
|
|
48794b9f88 | ||
|
|
c0058fda2f | ||
|
|
919a68c64d | ||
|
|
ef799fd57a | ||
|
|
5a3b75de33 | ||
|
|
c67629fe0c | ||
|
|
148a34679f | ||
|
|
359d871293 | ||
|
|
e311ffdcb9 | ||
|
|
f5e5256632 | ||
|
|
af5a1fbddb | ||
|
|
231e5c618f | ||
|
|
f48f31aac9 | ||
|
|
1d74ecd71f | ||
|
|
aa9db998f7 | ||
|
|
4429ee7d2e | ||
|
|
90419df663 | ||
|
|
bb3a0c9545 | ||
|
|
027ea5f08b | ||
|
|
7d77680d9f | ||
|
|
581fbea1d6 | ||
|
|
54e6e3d7da | ||
|
|
126dcb0d9e | ||
|
|
f98068dac2 | ||
|
|
7beebc9afc | ||
|
|
7969f1f07c | ||
|
|
388019f5b6 | ||
|
|
adc20fed0d | ||
|
|
46888f5afb | ||
|
|
1a2228d291 | ||
|
|
797d574dfd | ||
|
|
0eb8f34000 | ||
|
|
9289a502bb | ||
|
|
de1ac12f1c | ||
|
|
d6e568ec95 | ||
|
|
32d429e647 | ||
|
|
b85147ff76 | ||
|
|
b743506549 | ||
|
|
29a35f04a9 | ||
|
|
eb8e892df9 | ||
|
|
a3228977fb | ||
|
|
b40c679630 | ||
|
|
65c94df872 | ||
|
|
165d62b15f | ||
|
|
11a56db5c1 | ||
|
|
c5bc4b6892 | ||
|
|
cf772079c6 | ||
|
|
44ad65f02b | ||
|
|
8291537710 | ||
|
|
ac599c9e53 | ||
|
|
9d037d2f5a | ||
|
|
3b0ed18b86 | ||
|
|
4e168de6d9 | ||
|
|
98b96182f8 | ||
|
|
2a54427aba | ||
|
|
82ca6ecdde | ||
|
|
8b665e0d70 | ||
|
|
afb17eade9 | ||
|
|
3766bbb674 | ||
|
|
0f120c09ba | ||
|
|
f3145f6db8 | ||
|
|
ad7fa6c387 | ||
|
|
823f13c6e4 | ||
|
|
565f4314fe | ||
|
|
c9d9067931 | ||
|
|
2a4dd89253 | ||
|
|
4aa08e9d79 | ||
|
|
58a0b077c1 | ||
|
|
eecd758e39 | ||
|
|
29d3b65a83 | ||
|
|
52e2d4e16a | ||
|
|
9cb71f7672 | ||
|
|
20cbc1f216 | ||
|
|
099037cca6 | ||
|
|
9d68c6768a | ||
|
|
9f0bf1c71e | ||
|
|
d117ed183a | ||
|
|
005eeca06f | ||
|
|
d50ad19e4b | ||
|
|
62be4eb21e | ||
|
|
9061d1e4c3 | ||
|
|
e20147a1b6 | ||
|
|
62fb87641e | ||
|
|
30d9777b3f | ||
|
|
092321dc9e | ||
|
|
f31311d104 | ||
|
|
914287642d | ||
|
|
1e20babcf7 | ||
|
|
53dff569b8 | ||
|
|
77f904d35c | ||
|
|
dc0c54c7f1 | ||
|
|
83753535eb | ||
|
|
2d748e4ac1 | ||
|
|
13e917e292 | ||
|
|
f0721452a8 | ||
|
|
8d4928b505 | ||
|
|
25f0b5dda3 | ||
|
|
9720358bad | ||
|
|
554b32feea | ||
|
|
402b826ba2 | ||
|
|
7c5bf1c675 | ||
|
|
a093b5b2de | ||
|
|
02597caa8b | ||
|
|
8ca1f6d590 | ||
|
|
d18fdecd53 | ||
|
|
43ca7399e5 | ||
|
|
c4a4c189f1 | ||
|
|
e0c75cd0bd | ||
|
|
d55fafd130 | ||
|
|
423f6df5b1 | ||
|
|
6dbaa0a278 | ||
|
|
fbc145440f | ||
|
|
a265abaf29 | ||
|
|
3031726905 | ||
|
|
c34ed90822 | ||
|
|
e9d4cb2bb6 | ||
|
|
c259a90b3b | ||
|
|
c500b26bb6 | ||
|
|
897ca6abbb | ||
|
|
0c74952bcf | ||
|
|
9177fab07b | ||
|
|
3c9437ae54 | ||
|
|
1ff1fbe682 | ||
|
|
44296fcd2b | ||
|
|
b876ecdb84 | ||
|
|
0459206c40 | ||
|
|
a34ba362c6 | ||
|
|
1a9763f578 | ||
|
|
9189b16c1c | ||
|
|
59e7053464 | ||
|
|
ebf05be742 | ||
|
|
c6c518e6e9 | ||
|
|
4fc0981a52 | ||
|
|
3af4575a84 | ||
|
|
fa1b8a25b8 | ||
|
|
ccb43f95cb | ||
|
|
87a211d309 | ||
|
|
19d6404168 | ||
|
|
1c0b02a297 | ||
|
|
6308d2a1dc | ||
|
|
2d53b1d314 | ||
|
|
6689e414bb | ||
|
|
a6af23a1de | ||
|
|
54bebc5f5e | ||
|
|
4d8c155d33 | ||
|
|
9cb71bbaab | ||
|
|
8dc99feb50 | ||
|
|
3224075edc | ||
|
|
eea964330c | ||
|
|
2de6ad4544 | ||
|
|
38aac70830 | ||
|
|
5716428adc | ||
|
|
e648f38efc | ||
|
|
d363565375 | ||
|
|
d5e4ec9ea8 | ||
|
|
c976cf6ebd | ||
|
|
0142c79123 | ||
|
|
4b3f91c686 | ||
|
|
c894dbf0ae | ||
|
|
395ad91323 | ||
|
|
c4f9cf1a27 | ||
|
|
30774786f1 | ||
|
|
c316dbfc4a | ||
|
|
035b70aed1 | ||
|
|
96482b3e62 | ||
|
|
549624ffb2 | ||
|
|
34d62b0650 | ||
|
|
45082aaed3 | ||
|
|
d7396d4ffa | ||
|
|
2a6809467a | ||
|
|
5c46ccba0b | ||
|
|
56155e5048 | ||
|
|
0603c2327d | ||
|
|
d80a8eb3ad | ||
|
|
bb44909262 | ||
|
|
e4ca4c7fbf | ||
|
|
94cb213544 | ||
|
|
1f1f70a23f | ||
|
|
e6abd9e3d8 | ||
|
|
5f13af6b68 | ||
|
|
bbf932fd7d | ||
|
|
7a2bb2fcda | ||
|
|
a89fe0f5a0 | ||
|
|
6877360218 | ||
|
|
5138d3f8b6 | ||
|
|
09310931cf | ||
|
|
db18323551 | ||
|
|
9e5d6c7091 | ||
|
|
07605c79ad | ||
|
|
25d2e9bdac | ||
|
|
ffe67e9cdc | ||
|
|
6e73101df3 | ||
|
|
8672737f81 | ||
|
|
d25cfda54c | ||
|
|
a4af1e91da | ||
|
|
757894e201 | ||
|
|
6f7c89ce21 | ||
|
|
faad655c21 | ||
|
|
873df76132 | ||
|
|
0e97f962ac | ||
|
|
2d1523e573 | ||
|
|
329568905e | ||
|
|
c39ca49c71 | ||
|
|
df4faac71f | ||
|
|
c403ea9063 | ||
|
|
a6390efeba | ||
|
|
eab4024934 | ||
|
|
b07c7f6ab3 | ||
|
|
b7dd912541 | ||
|
|
5d8f4d8767 | ||
|
|
6421e1f36a | ||
|
|
57a3d7f6e8 | ||
|
|
11d8ba96f9 | ||
|
|
4329cee0c0 | ||
|
|
fc8fafbd2f | ||
|
|
8aed80d2fa | ||
|
|
35264ca034 | ||
|
|
c538906b72 | ||
|
|
30650fec73 | ||
|
|
58153d38af | ||
|
|
1a103088ba | ||
|
|
1f006dbc5f | ||
|
|
02ebac6250 | ||
|
|
f94d970cee | ||
|
|
cab86dc325 | ||
|
|
5e8c396bb8 | ||
|
|
64cb9c5b71 | ||
|
|
6331fec05e | ||
|
|
1fb096f0e6 | ||
|
|
9b1bde2561 | ||
|
|
10a9654674 | ||
|
|
78fc27fe7d | ||
|
|
6863694a22 | ||
|
|
80ec402d0f | ||
|
|
3bd6b54f0b | ||
|
|
5a631e1ee9 | ||
|
|
a548d8e1ea | ||
|
|
9c22a0133b | ||
|
|
485b875d72 | ||
|
|
a81e3ee888 | ||
|
|
950a9b5500 | ||
|
|
d4e88e7a2f | ||
|
|
cd0fb36c1c | ||
|
|
d86c5775b8 | ||
|
|
e9fcbe1533 | ||
|
|
08c4af0ddf | ||
|
|
c397486648 | ||
|
|
1ead1b2d18 | ||
|
|
442e14e359 | ||
|
|
cf125c125a | ||
|
|
30a2b3049a | ||
|
|
fabfab2b84 | ||
|
|
b20eea92d8 | ||
|
|
70ddeef0e8 | ||
|
|
52b57d0953 | ||
|
|
0b59964ec9 | ||
|
|
601596bfe2 | ||
|
|
68c99616a3 | ||
|
|
692211a758 | ||
|
|
e5c608f804 | ||
|
|
172bc9d043 | ||
|
|
59982c2aa5 | ||
|
|
b113d92c6f | ||
|
|
b7c3e19ed6 | ||
|
|
29de89a8d9 | ||
|
|
204d200be3 | ||
|
|
43f6c8b01a | ||
|
|
01254500df | ||
|
|
36bb723dfb | ||
|
|
d30b8dccfd | ||
|
|
845dd2a7d5 | ||
|
|
6662dcf209 | ||
|
|
e327c96ce6 | ||
|
|
a887df9cd5 | ||
|
|
7d4a0bb621 | ||
|
|
0d702f3e28 | ||
|
|
3b4d2d8886 | ||
|
|
d3c6a8f0fb | ||
|
|
a0cf07ec10 | ||
|
|
4109446c2f | ||
|
|
98e4c18e39 | ||
|
|
b4ecc814c5 | ||
|
|
2fe3e779ff | ||
|
|
c4e249114d | ||
|
|
1db2e63519 | ||
|
|
9307affe59 | ||
|
|
10b9adb010 | ||
|
|
b8c77c1bd7 | ||
|
|
68aedf3908 | ||
|
|
78f3470368 | ||
|
|
5a606947b5 | ||
|
|
dec5de8a2c | ||
|
|
225620a9b3 | ||
|
|
dd5b96c11d | ||
|
|
1d1edca92f | ||
|
|
4cba08df01 | ||
|
|
426107d2f8 | ||
|
|
d115faa367 | ||
|
|
847d8fa0e1 | ||
|
|
6acd588bdd | ||
|
|
8f50920c45 | ||
|
|
c7e2aceb05 | ||
|
|
4c712d3372 | ||
|
|
9a3a341d93 | ||
|
|
9a9cd0c0ab | ||
|
|
4396361f35 | ||
|
|
fa467c20e3 | ||
|
|
6717f8b334 | ||
|
|
a31b55a8d8 | ||
|
|
18faf22e15 | ||
|
|
36ff8d78e4 | ||
|
|
4a6e10ece8 | ||
|
|
83267e99b0 | ||
|
|
05627f797e | ||
|
|
aaf4cc6862 | ||
|
|
f0f1635f9f | ||
|
|
71ab341f46 | ||
|
|
b552e31563 | ||
|
|
ebff12e84f | ||
|
|
d51af16fab | ||
|
|
21e2168b8f | ||
|
|
bdbce3b1c2 | ||
|
|
fd8fae7af2 | ||
|
|
9b536ed3a9 | ||
|
|
6e351f2f05 | ||
|
|
aecde2b3ac | ||
|
|
11771ec172 | ||
|
|
7451415f36 | ||
|
|
f569ed0803 | ||
|
|
95bf450dc9 | ||
|
|
8e4035d09a | ||
|
|
95a1356278 | ||
|
|
cdb424a642 | ||
|
|
12a82aa788 | ||
|
|
0b48a69ff2 | ||
|
|
27fafa4754 | ||
|
|
7b97af4899 | ||
|
|
6378de91e7 | ||
|
|
fbe41fbdfc | ||
|
|
296d07c22f | ||
|
|
0e46240543 | ||
|
|
e4edefd0fc | ||
|
|
22ea08997e | ||
|
|
e2ab5b98cc | ||
|
|
bf5541b4bf | ||
|
|
6fb729a451 | ||
|
|
0e5d3cb0e5 | ||
|
|
df51878b0b | ||
|
|
ec7536078f | ||
|
|
e3b7b4cf63 | ||
|
|
8b8bba9621 | ||
|
|
01893d725a | ||
|
|
48a01798b0 | ||
|
|
1e4a37fbfb | ||
|
|
c160bec3d6 | ||
|
|
9ae7db5562 | ||
|
|
585c2bdba3 | ||
|
|
fdf8ffaf3c | ||
|
|
3b5dab372a | ||
|
|
9ccd015898 | ||
|
|
1a4d55de43 | ||
|
|
987af6805b | ||
|
|
b478262a9e | ||
|
|
cd6efd1a42 | ||
|
|
06fcd3b60e | ||
|
|
2fa5590a93 | ||
|
|
65c9eddae8 | ||
|
|
470098bd26 | ||
|
|
b83b639287 | ||
|
|
6516be1023 | ||
|
|
7fc0859a01 | ||
|
|
12ee7f696f | ||
|
|
f1ec163f77 | ||
|
|
576f64c242 | ||
|
|
0d1ac827b3 | ||
|
|
2bf2720632 | ||
|
|
63cc63e8e0 | ||
|
|
e3484727ba | ||
|
|
565cb9029b | ||
|
|
b31369a701 | ||
|
|
7e9326026e | ||
|
|
1738ebb385 | ||
|
|
c16a4dfce6 | ||
|
|
185a3fcab8 | ||
|
|
5802fcb405 | ||
|
|
2925c801f7 | ||
|
|
92c30630ad | ||
|
|
7c4c008de8 | ||
|
|
fef8203de1 | ||
|
|
82629225ac | ||
|
|
b8f4338a94 | ||
|
|
b799f6c451 | ||
|
|
9c19335276 | ||
|
|
b8372a714c | ||
|
|
60c2a90550 | ||
|
|
c9156cd9a8 | ||
|
|
65e969aeea | ||
|
|
fef42acda0 | ||
|
|
f05b789736 | ||
|
|
665b0ef542 | ||
|
|
7c51cd2baf | ||
|
|
21b3eb5c34 | ||
|
|
dac7237494 | ||
|
|
fdba408bce | ||
|
|
8a3507e310 | ||
|
|
4808361fca | ||
|
|
3abc90aac5 | ||
|
|
323985f4ca | ||
|
|
f55b810412 | ||
|
|
9b6670d5c9 | ||
|
|
cd00a6d6dd | ||
|
|
070129f34f | ||
|
|
d6918113f0 | ||
|
|
9ffc2f9d06 | ||
|
|
38694111aa | ||
|
|
ec0836693c | ||
|
|
e1c97cb24d | ||
|
|
9af0a7153e | ||
|
|
5de9145562 | ||
|
|
fe15e1e83e | ||
|
|
dba4487d05 | ||
|
|
fd98ce3c15 | ||
|
|
8b54c71f90 | ||
|
|
09d7c66c06 | ||
|
|
9168ad1b19 | ||
|
|
6e940d3cc8 | ||
|
|
e2ffc49f9a | ||
|
|
da0e96dbbb | ||
|
|
d2e55b01f2 | ||
|
|
4aedffd37a | ||
|
|
648ed69f82 | ||
|
|
3059702687 | ||
|
|
ccb8472daf | ||
|
|
4eb30fc13a | ||
|
|
945c910f20 | ||
|
|
26c03c761f | ||
|
|
06b1d4e0f7 | ||
|
|
4b4e0c82e4 | ||
|
|
b0ae867034 | ||
|
|
97e2f5b332 | ||
|
|
eb7d89f4b9 | ||
|
|
57e4994caf | ||
|
|
fc2d957923 | ||
|
|
e69da9d578 | ||
|
|
d001c3436b | ||
|
|
097eed8cd8 | ||
|
|
db6951088a | ||
|
|
dabf76b3de | ||
|
|
c728d604b2 | ||
|
|
86956f71e6 | ||
|
|
072e73d7c3 | ||
|
|
fbae2a6441 | ||
|
|
89f871679e | ||
|
|
f5aebe42e1 | ||
|
|
b1c515270e | ||
|
|
68912111cf | ||
|
|
4bc79f9737 | ||
|
|
39ecedb300 | ||
|
|
7e1acf2f1e | ||
|
|
6470a23504 | ||
|
|
b54c642bd6 | ||
|
|
edf579c406 | ||
|
|
db665a09cd | ||
|
|
43b084e5fa | ||
|
|
efefba2db1 | ||
|
|
2a7d83b6ad | ||
|
|
6e5a703dd2 | ||
|
|
14118d4bc1 | ||
|
|
b10dd5f3ae | ||
|
|
139815d6a9 | ||
|
|
a7414f728b | ||
|
|
0126692bf5 | ||
|
|
b0d649b4cb | ||
|
|
5ca401f974 | ||
|
|
6ba66c8a8d | ||
|
|
58405c70cd | ||
|
|
6ef98f48aa | ||
|
|
0b79e4a223 | ||
|
|
7e20f368dd | ||
|
|
35293d3eb2 | ||
|
|
e6acc74db6 | ||
|
|
bfdd255b92 | ||
|
|
5ed819af7b | ||
|
|
44ff41aa77 | ||
|
|
542606bea7 | ||
|
|
297f4c6e60 | ||
|
|
c85ff84334 | ||
|
|
fba8af4d62 | ||
|
|
587ce45ec9 | ||
|
|
1df1ee48c0 | ||
|
|
fceaecd123 | ||
|
|
69181342e1 | ||
|
|
be1037fd34 | ||
|
|
bfb6b82ffd | ||
|
|
87bd12b2d3 | ||
|
|
b418c08a22 | ||
|
|
a777b82da0 | ||
|
|
0e0ade80a0 | ||
|
|
09baec68ea | ||
|
|
a16f7fb6cd | ||
|
|
8a06db084d | ||
|
|
2bb16f771b | ||
|
|
e3af6fb3c8 | ||
|
|
dcd428e8c1 | ||
|
|
630629667c | ||
|
|
3215ab6de5 | ||
|
|
9f21335462 | ||
|
|
b53ec93ed9 | ||
|
|
e420592bdf | ||
|
|
6de9d71bfb | ||
|
|
c7aaa40848 | ||
|
|
0519107bd3 | ||
|
|
f4af0777a7 | ||
|
|
63fe2e12d7 | ||
|
|
4e115c5dbb | ||
|
|
9d03cd15a9 | ||
|
|
5201c42251 | ||
|
|
a512b5dde9 | ||
|
|
5e384fed6d | ||
|
|
df0074768c | ||
|
|
9a0b43c47e | ||
|
|
2dadc82cf4 | ||
|
|
e46dccb353 | ||
|
|
7471c2116f | ||
|
|
8cf724a381 | ||
|
|
86f473d8b9 | ||
|
|
763a88083e | ||
|
|
58db3d2d22 | ||
|
|
e8b82d1cf9 | ||
|
|
334f4624e0 | ||
|
|
61d53f98d3 | ||
|
|
c1a42dce86 | ||
|
|
b48f6ca1fc | ||
|
|
8d63ddce69 | ||
|
|
2d885a2402 | ||
|
|
3f0039e2ea | ||
|
|
c99d680714 | ||
|
|
616f24fd49 | ||
|
|
53e0874864 | ||
|
|
e8d23e5489 | ||
|
|
acae48b790 | ||
|
|
240362bf6d | ||
|
|
9fcae8458e | ||
|
|
8cbf77d997 | ||
|
|
7acb78852f | ||
|
|
04f651b783 | ||
|
|
1d9959b77e | ||
|
|
e6d72548b7 | ||
|
|
8c68e7535f | ||
|
|
606c881d27 | ||
|
|
46171d7848 | ||
|
|
e49703def6 | ||
|
|
4dd2768c4b | ||
|
|
1390eadd92 | ||
|
|
a2cf05c4fb | ||
|
|
c6b269154a | ||
|
|
508cd6f805 | ||
|
|
dc5a85d606 | ||
|
|
8935dd154a | ||
|
|
a0300378d6 | ||
|
|
2b4909e2db | ||
|
|
2d53b49b20 | ||
|
|
995aa4f428 | ||
|
|
b92d145252 | ||
|
|
beff88175e | ||
|
|
ca093d8402 | ||
|
|
204ef7f1c4 | ||
|
|
7108414009 | ||
|
|
587b537b47 | ||
|
|
542821cd1e | ||
|
|
b7db63751b | ||
|
|
34d11d5757 | ||
|
|
4bd6dd77ef | ||
|
|
8055e74485 | ||
|
|
b929701e97 | ||
|
|
16f604d7e7 | ||
|
|
6fcddbbd96 | ||
|
|
65b0927490 | ||
|
|
bf1a8eebba | ||
|
|
bea75406bb | ||
|
|
e3a0c7615b | ||
|
|
577438ca73 | ||
|
|
1dd37f5c90 | ||
|
|
f52958ad67 | ||
|
|
5a2c50275d | ||
|
|
0544c6d493 | ||
|
|
03e17d19e9 | ||
|
|
66cdbccc8a | ||
|
|
6bbacd14a3 | ||
|
|
a972c9ec45 | ||
|
|
3a875e7549 | ||
|
|
aaa194c58b | ||
|
|
d8b25506bb | ||
|
|
dda765c445 | ||
|
|
5605b31375 | ||
|
|
0be8d127d6 | ||
|
|
da6135d34c | ||
|
|
47b3530af3 | ||
|
|
32db9ff538 | ||
|
|
cea2da7049 | ||
|
|
f0adbd48e8 | ||
|
|
20e2117371 | ||
|
|
bbf985d50a | ||
|
|
abaa4326d8 | ||
|
|
d1b2d81752 | ||
|
|
9881a808f2 | ||
|
|
443ca4865d | ||
|
|
390a7598c9 | ||
|
|
88101e81ef | ||
|
|
03148a6a76 | ||
|
|
ad2516b1c8 | ||
|
|
8f2dd02d2d | ||
|
|
7a69069bfc | ||
|
|
56d2749b5b | ||
|
|
2f31184d07 | ||
|
|
cf43b92fc9 | ||
|
|
4e4f9204d7 | ||
|
|
1b56c7723b | ||
|
|
81551ac24b | ||
|
|
75286ef838 | ||
|
|
991da29481 | ||
|
|
49f94db5d4 | ||
|
|
9bf7b6bfca | ||
|
|
17d05269f9 | ||
|
|
234cbf5f46 | ||
|
|
40f820bda2 | ||
|
|
efb1a7cb02 | ||
|
|
64bd2a2cbe | ||
|
|
579334f9f8 | ||
|
|
1424982792 | ||
|
|
49a6bfe601 | ||
|
|
94a85e77de | ||
|
|
4f540c703f | ||
|
|
39f810911c | ||
|
|
016f5ae862 | ||
|
|
fc49f94ccf | ||
|
|
84d8d5d5e7 | ||
|
|
7214b40a7b | ||
|
|
1fb58ca5ee | ||
|
|
027337df79 | ||
|
|
c357235fe6 | ||
|
|
0207f9ceec | ||
|
|
f6c0dde1b3 | ||
|
|
de0f54b54a | ||
|
|
1446069707 | ||
|
|
d8b9ace39c | ||
|
|
d30ba5351e | ||
|
|
96dff27808 | ||
|
|
f79553bef6 | ||
|
|
8f6c72823e | ||
|
|
4d73cd52dc | ||
|
|
dc9f1b8525 | ||
|
|
89cd2b6362 | ||
|
|
d9c4fcf67d | ||
|
|
42b352c57e | ||
|
|
1f9fafb288 | ||
|
|
22c42b6b30 | ||
|
|
d4e52f4542 | ||
|
|
d2db67e693 | ||
|
|
2aa6abddbe | ||
|
|
ef7c528c8a | ||
|
|
1f8ccf2d2a | ||
|
|
34ec184dcb | ||
|
|
81ad827380 | ||
|
|
dce2513db2 | ||
|
|
1d494af03a | ||
|
|
9d1c5a77c2 | ||
|
|
6d7a77dcf9 | ||
|
|
77a5d82e64 | ||
|
|
fce62c6129 | ||
|
|
bdcd543ed7 | ||
|
|
af31fc938a | ||
|
|
e6cd90e3fd | ||
|
|
21a92ea0f6 | ||
|
|
4f73baf7d7 | ||
|
|
0c9f84451a | ||
|
|
7be65cd798 | ||
|
|
e5a5ea1072 | ||
|
|
1dac6ac4c6 | ||
|
|
6b4873d0c1 | ||
|
|
6b44dce0c8 | ||
|
|
4528682487 | ||
|
|
69e6f65237 | ||
|
|
427d5d4f69 | ||
|
|
38fdb42069 | ||
|
|
93d5cd1015 | ||
|
|
6cea276976 | ||
|
|
99950c7f12 | ||
|
|
a1197b9075 | ||
|
|
20ed597495 | ||
|
|
ae57eb635c | ||
|
|
07ca99d2a8 | ||
|
|
0f6fea813c | ||
|
|
923837accd | ||
|
|
06088c6b05 | ||
|
|
e1fd27fb24 | ||
|
|
1c45592e62 | ||
|
|
48683a7f71 | ||
|
|
af548bb07d | ||
|
|
d33c3f7da6 | ||
|
|
b52197427c | ||
|
|
412434a450 | ||
|
|
3af661384c | ||
|
|
d7cc8d0b03 | ||
|
|
146c0a7e1d | ||
|
|
1c17fd5edf | ||
|
|
3c19588fc5 | ||
|
|
4b99724a9c | ||
|
|
a3519e362f | ||
|
|
985000026e | ||
|
|
1d61862adb | ||
|
|
d95719d7c1 | ||
|
|
abed3a056d | ||
|
|
e4b09e1bf3 | ||
|
|
24adf2c8e6 | ||
|
|
8d58ad4c15 | ||
|
|
a98a4e6ca5 | ||
|
|
3b10b8cf74 | ||
|
|
fa8a7d70ee | ||
|
|
f28bc31ecd | ||
|
|
35cccbeb68 | ||
|
|
bc205836ca | ||
|
|
04c650c2c4 | ||
|
|
f5e7557c70 | ||
|
|
422d139ba0 | ||
|
|
c33968e10c | ||
|
|
0f078f2ea2 | ||
|
|
6350dd5ace | ||
|
|
ea9f17256a | ||
|
|
d35e6f79e1 | ||
|
|
928698d388 | ||
|
|
c881e0a176 | ||
|
|
a4e92c0aa4 | ||
|
|
8c8f396985 | ||
|
|
68ba1e7180 | ||
|
|
4fbd683819 | ||
|
|
eb82694217 | ||
|
|
a5cb171d73 | ||
|
|
fceaaa4494 | ||
|
|
c211b41f17 | ||
|
|
071e7610d6 | ||
|
|
14e8a2d00b | ||
|
|
cf6e4d0ed7 | ||
|
|
9bb1e59447 | ||
|
|
5762cc321a | ||
|
|
beb1d9b481 | ||
|
|
fed552c2ef | ||
|
|
2a64f1a2de | ||
|
|
dc810437e7 | ||
|
|
61b0cd3781 | ||
|
|
3a6d3dfa06 | ||
|
|
7a32d6a09f | ||
|
|
88237faed3 | ||
|
|
a7c8a3eed0 | ||
|
|
7877182b6f | ||
|
|
1a936f225e | ||
|
|
4d43daa7bb | ||
|
|
72cf700fbf | ||
|
|
2c0449571c | ||
|
|
b85cf280c7 | ||
|
|
115e763804 | ||
|
|
6b480e09b9 | ||
|
|
9d8de70c20 | ||
|
|
59a0457251 | ||
|
|
1c4262ef87 | ||
|
|
f3f614fae6 | ||
|
|
f2405c830b | ||
|
|
250fec85e1 | ||
|
|
bd3ffd0802 | ||
|
|
1e168b17b7 | ||
|
|
0f11dcd15f | ||
|
|
5cc834a11a | ||
|
|
ca972f692f | ||
|
|
a62c7e5a27 | ||
|
|
8ac2dd4cd2 | ||
|
|
2b811fe6d9 | ||
|
|
e52b660749 | ||
|
|
2a02b3bcec | ||
|
|
1d0e9a907e | ||
|
|
eb7f305737 | ||
|
|
f8faf40a9e | ||
|
|
a31342ab6b | ||
|
|
275b0f00b0 | ||
|
|
b69b508d20 | ||
|
|
34bd962a20 | ||
|
|
c01244e859 | ||
|
|
f6a2cf15c0 | ||
|
|
bd5afadc5c | ||
|
|
a0fd105e5e | ||
|
|
9b1967e5ef | ||
|
|
1dd500c495 | ||
|
|
6a3310bbda | ||
|
|
26546dfbcb | ||
|
|
7662a17b08 | ||
|
|
9ddd10b84c | ||
|
|
afc4f06ca3 | ||
|
|
7e5d6dba80 | ||
|
|
023d3371a5 | ||
|
|
e25b542100 | ||
|
|
6306e2fdcb | ||
|
|
13390fcac8 | ||
|
|
81f490f26a | ||
|
|
5fa0d282a8 | ||
|
|
ca427df924 | ||
|
|
8b71d2347f | ||
|
|
64387ad8e2 | ||
|
|
e71d7d48fb | ||
|
|
0bbbc99980 | ||
|
|
20c7a98fb8 | ||
|
|
13757465ba | ||
|
|
3367cfaa14 | ||
|
|
885d88c1ac | ||
|
|
99f0ea92fe | ||
|
|
6a4c866b6a | ||
|
|
1d87d757e9 | ||
|
|
1b25dcf57a | ||
|
|
71473e7448 | ||
|
|
32c2337095 | ||
|
|
5fe81cdf52 | ||
|
|
ad761975de | ||
|
|
2da2d506b5 | ||
|
|
4eba70b532 | ||
|
|
7d74c1f4b9 | ||
|
|
d28500ffd6 | ||
|
|
a887a512ef | ||
|
|
a9f58d34a2 | ||
|
|
926071762d | ||
|
|
fd72177830 | ||
|
|
47204a1db5 | ||
|
|
09a64bd77e | ||
|
|
b62e9e624d | ||
|
|
213f92a9ef | ||
|
|
6186ed2c07 | ||
|
|
bd1d1f0f2b | ||
|
|
fda8cc2a9d | ||
|
|
cfcb8f4eda | ||
|
|
ade863e08f | ||
|
|
358b4f24cd | ||
|
|
83df409d94 | ||
|
|
5580d8951c | ||
|
|
a7c3755327 | ||
|
|
67e8d35f1c | ||
|
|
95adc64326 | ||
|
|
4932e91517 | ||
|
|
39513771bb | ||
|
|
1825f611f8 | ||
|
|
548c280eff | ||
|
|
66b4324d41 | ||
|
|
450607847b | ||
|
|
364c67bcb5 | ||
|
|
996c9d71e9 | ||
|
|
3a6f7d8db9 | ||
|
|
7c7561f5a3 | ||
|
|
2f04731a48 | ||
|
|
1476e24af3 | ||
|
|
f6d23ab5c2 | ||
|
|
2b0b614417 | ||
|
|
8d78451e8b | ||
|
|
4d729d0aa8 | ||
|
|
d674225d88 | ||
|
|
07631fb931 | ||
|
|
f7aebf8cb7 | ||
|
|
e3bc985a6e | ||
|
|
16fd9a9d59 | ||
|
|
2f589aacf9 | ||
|
|
07104c80b3 | ||
|
|
7994833fac | ||
|
|
5e2f6ce294 | ||
|
|
0a8a255733 | ||
|
|
52a7e2264c | ||
|
|
d49ebe7bde | ||
|
|
9023b120a1 | ||
|
|
b3a8c7146b | ||
|
|
652f34103a | ||
|
|
9e34fb9feb | ||
|
|
02c4249632 | ||
|
|
3aadeba93f | ||
|
|
b85edb3f0c | ||
|
|
8c886e9438 | ||
|
|
0fc3032325 | ||
|
|
5eb9b3da34 | ||
|
|
806a0119f3 | ||
|
|
aa84b738b6 | ||
|
|
203213028e | ||
|
|
9ae629052a | ||
|
|
128115fb25 | ||
|
|
ab39f2b272 | ||
|
|
0382ac5f7d | ||
|
|
a2e077e468 | ||
|
|
09cb0b0e64 | ||
|
|
be445dd1c1 | ||
|
|
34ef403cb2 | ||
|
|
e5dc0e6d15 | ||
|
|
0015d34fda | ||
|
|
2b8c20c8a3 | ||
|
|
4cf2284667 | ||
|
|
ef08f59b9f | ||
|
|
d1b4dbffc3 | ||
|
|
2e406c05f8 | ||
|
|
706eb8833f | ||
|
|
7ddd815e46 | ||
|
|
8edb99f0e3 | ||
|
|
aa1bccfe80 | ||
|
|
04f6ffd8be | ||
|
|
eb5adc3cd2 | ||
|
|
9863bb964b | ||
|
|
5a9c0efa54 | ||
|
|
d130a77a3b | ||
|
|
e27fe55aa8 | ||
|
|
4f9f7fc8c2 | ||
|
|
e53c45ba94 | ||
|
|
c20a3f548f | ||
|
|
3dc6e408b9 | ||
|
|
6fcaf6ed58 | ||
|
|
ab5c8025c9 | ||
|
|
7475b27887 | ||
|
|
6e31de5847 | ||
|
|
64533ed7b1 | ||
|
|
74889462a8 | ||
|
|
855c220a63 | ||
|
|
eb332c2f32 | ||
|
|
aa03c5be82 | ||
|
|
5847c0ed58 | ||
|
|
5435591f6a | ||
|
|
68ef37011e | ||
|
|
e12eb9acdd | ||
|
|
d8c4d7c3c1 | ||
|
|
2613692298 | ||
|
|
7a5b419843 | ||
|
|
86c5f378d6 | ||
|
|
9bf50450de | ||
|
|
ba0f2e948f | ||
|
|
1f055d23fd | ||
|
|
18237bc015 | ||
|
|
e0008268ad | ||
|
|
180033eeae | ||
|
|
43da089790 | ||
|
|
c65ec4d68c | ||
|
|
c2e3b6e6f8 | ||
|
|
09e2cf1103 | ||
|
|
13fdeec2cc | ||
|
|
38e56972cd | ||
|
|
f4c9e71e4e | ||
|
|
b5a90b066d | ||
|
|
492e2a3060 | ||
|
|
a5790946f5 | ||
|
|
1e1fe80ae0 | ||
|
|
d6c2280aab | ||
|
|
bb6a15da04 | ||
|
|
df9d26eb43 | ||
|
|
79159f11f6 | ||
|
|
610e575844 | ||
|
|
1f41b8b44b | ||
|
|
7e5c3753f6 | ||
|
|
7a88117f42 | ||
|
|
51119f2ef1 | ||
|
|
d8a600f2ad | ||
|
|
12c52963ea | ||
|
|
46783d41e9 | ||
|
|
381c2e1d1a | ||
|
|
a968f4f437 | ||
|
|
a5824b9d01 | ||
|
|
28ff82dcda | ||
|
|
b96e7739a9 | ||
|
|
293348b429 | ||
|
|
8e5fcfff50 | ||
|
|
7229ec5e04 | ||
|
|
ceeb3a7398 | ||
|
|
4aa8da3756 | ||
|
|
e94e9347a4 | ||
|
|
c24c8bab13 | ||
|
|
a820a307df | ||
|
|
45f3074ee6 | ||
|
|
3286e99bc2 | ||
|
|
6249c32826 | ||
|
|
03b1731d0f | ||
|
|
054b2e1b7e | ||
|
|
fd2625a162 | ||
|
|
2eac4bacee | ||
|
|
0487cc59f0 | ||
|
|
212a32648f | ||
|
|
5a0702ecf8 | ||
|
|
8f4cbbbe66 | ||
|
|
d3683a61c5 | ||
|
|
7dc0041ca9 | ||
|
|
2a7ba582cb | ||
|
|
8cca1598d9 | ||
|
|
e4cb0f6683 | ||
|
|
de3f8af48e | ||
|
|
dd31a27e71 | ||
|
|
8c9cac244d | ||
|
|
516a91243f | ||
|
|
a6dfaaeb4e | ||
|
|
ef58307f84 | ||
|
|
b04c9380ed | ||
|
|
43fa40a35d | ||
|
|
a235a487d0 | ||
|
|
e2b825eba4 | ||
|
|
9c9dcd4d5d | ||
|
|
a0f0c964fd | ||
|
|
d86ad7a61b | ||
|
|
a3f74410e4 | ||
|
|
955b4df093 | ||
|
|
490e6d6dc5 | ||
|
|
bcc6a2400d | ||
|
|
75df09b9ec | ||
|
|
6ce1058296 | ||
|
|
7e41913a20 | ||
|
|
f4a9d34f98 | ||
|
|
baeba45be9 | ||
|
|
60861b3823 | ||
|
|
e583db63c6 | ||
|
|
eb970bdb42 | ||
|
|
1184925572 | ||
|
|
cc7a209982 | ||
|
|
5ef6e82685 | ||
|
|
e7947948b6 | ||
|
|
69fb7455c6 | ||
|
|
d9b46e0551 | ||
|
|
25f7e062e1 | ||
|
|
7b2b0d07e8 | ||
|
|
7a5638ea88 | ||
|
|
193c7432e3 | ||
|
|
969cb8b4c0 | ||
|
|
652bde387d | ||
|
|
35059d1e3a | ||
|
|
61960342b1 | ||
|
|
14f140d6f0 | ||
|
|
d84ce5e419 | ||
|
|
11d2128820 | ||
|
|
78d51dcebe | ||
|
|
4509420dd4 | ||
|
|
5e8d3130c6 | ||
|
|
5642653168 | ||
|
|
da1084caf2 | ||
|
|
7ee85a1dd6 | ||
|
|
7cefdd956a | ||
|
|
18990f4fea | ||
|
|
b8f071a139 | ||
|
|
2f7c4070f4 | ||
|
|
c244ab5667 | ||
|
|
5b1202e11e | ||
|
|
081e4be11e | ||
|
|
81fd4d560a | ||
|
|
8fe7d495bc | ||
|
|
b1195c6452 | ||
|
|
07089f11c7 | ||
|
|
6ade320421 | ||
|
|
4bd3d258cd | ||
|
|
9f97e8c521 | ||
|
|
96a21e2553 | ||
|
|
3aac8e650c | ||
|
|
5dfc14d49b | ||
|
|
3cad579c4e | ||
|
|
d1a7612bd6 | ||
|
|
c399fb750b | ||
|
|
0a2d635e68 | ||
|
|
3d736f67cf | ||
|
|
c1c217035d | ||
|
|
3b593bc561 | ||
|
|
87172dc9fe | ||
|
|
f0c8640d81 | ||
|
|
0dcab4e347 | ||
|
|
3ae69498e2 | ||
|
|
230f8886c6 | ||
|
|
170a961744 | ||
|
|
0f3a9d812b | ||
|
|
771846c5fa | ||
|
|
1f26e32f5f | ||
|
|
1824ceba54 | ||
|
|
aec5efed8d | ||
|
|
06a0cd88fb | ||
|
|
0608c1015b | ||
|
|
98f5fd12df | ||
|
|
c500e8704f | ||
|
|
933c7968dc | ||
|
|
1e9faa2a59 | ||
|
|
c2d31a5e59 | ||
|
|
c5c08c074a | ||
|
|
5de06ac00e | ||
|
|
cb8c513ce3 | ||
|
|
df8611c420 | ||
|
|
b014462690 | ||
|
|
0311e172e0 | ||
|
|
c89b67e6c8 | ||
|
|
9f37ff0c6c | ||
|
|
e61756f9e8 | ||
|
|
df4e2ecb87 | ||
|
|
4a24b23e3e | ||
|
|
f641691910 | ||
|
|
87fd216d9a | ||
|
|
702e5fc4a9 | ||
|
|
6d4599a796 | ||
|
|
f2f34e5f35 | ||
|
|
bb0461b682 | ||
|
|
6d542ebcee | ||
|
|
d22a851253 | ||
|
|
4b69dc6228 | ||
|
|
7191f1a1eb | ||
|
|
065284deab | ||
|
|
f351961173 | ||
|
|
dcd665cd05 | ||
|
|
e2295b33c1 | ||
|
|
2290adbf57 | ||
|
|
e476523082 | ||
|
|
cd2e13be8a | ||
|
|
84154bb09c | ||
|
|
53d34e7cde | ||
|
|
3f780bb27d | ||
|
|
4d82dc4fb4 | ||
|
|
6d323ee736 | ||
|
|
7d2d8732d0 | ||
|
|
c0ec58f4b6 | ||
|
|
a48ffda7f7 | ||
|
|
3d89b0f2ec | ||
|
|
3de5476f51 | ||
|
|
7120f5b254 | ||
|
|
8af50b5b4c | ||
|
|
195f704c74 | ||
|
|
7b91f06384 | ||
|
|
bdfb408ce6 | ||
|
|
230f7122dd | ||
|
|
b79e617ad1 | ||
|
|
c57960b8d1 | ||
|
|
c4f741e534 | ||
|
|
891c7d9f1c | ||
|
|
f256eeba43 | ||
|
|
dd643c82b5 | ||
|
|
16906780fd | ||
|
|
6d539db011 | ||
|
|
ba17b8b728 | ||
|
|
373e7fc242 | ||
|
|
12aaef9035 | ||
|
|
bdb75bd8c7 | ||
|
|
189c91eae6 | ||
|
|
037f197684 | ||
|
|
ccb3af556f | ||
|
|
7a23c18830 | ||
|
|
7a23b2d945 | ||
|
|
e4ff7c1620 | ||
|
|
c478aeca5a | ||
|
|
f155a5f955 | ||
|
|
e84ebeafbd | ||
|
|
2ccdbc7dd9 | ||
|
|
343c69d7a1 | ||
|
|
3eb2a9d371 | ||
|
|
e10f493160 | ||
|
|
75ba8398f9 | ||
|
|
9f7932fbcc | ||
|
|
9e5aa10e97 | ||
|
|
af10be59d8 | ||
|
|
2a0af6754e | ||
|
|
ba722fd126 | ||
|
|
8260b64f7a | ||
|
|
7b07a0ab8f | ||
|
|
d55c7ea997 | ||
|
|
5de284c2e3 | ||
|
|
dc541662f8 | ||
|
|
3c0eac31f1 | ||
|
|
adf166936a | ||
|
|
6559288d4a | ||
|
|
6dec2e1852 | ||
|
|
279e6453fc | ||
|
|
885806d5ca | ||
|
|
205d8d4994 | ||
|
|
aa1834a3ff | ||
|
|
d770a3b786 | ||
|
|
6a387afc53 | ||
|
|
94fc91e235 | ||
|
|
5a1ff1347d | ||
|
|
a722da3ed0 | ||
|
|
d70191f8af | ||
|
|
7150acba69 | ||
|
|
35bc13f9ef | ||
|
|
32c987626b | ||
|
|
92016b82ae | ||
|
|
7727e102a5 | ||
|
|
1bd4b7ac4d | ||
|
|
7950a18025 | ||
|
|
e2f3044b8f | ||
|
|
f12dedb5c8 | ||
|
|
1b13f53047 | ||
|
|
77192572f6 | ||
|
|
6cc6996a1c | ||
|
|
c9ead1b928 | ||
|
|
ade9aaae89 | ||
|
|
1fcf0a422f | ||
|
|
9da76c4255 | ||
|
|
17ef9ef895 | ||
|
|
5915489631 | ||
|
|
6f8792f3f1 | ||
|
|
0bc8b9a95a | ||
|
|
ab3feca0d5 | ||
|
|
9207660c87 | ||
|
|
ae63f76bbd | ||
|
|
c5cd7aabcf | ||
|
|
210cccb0fe | ||
|
|
a6bb0265f0 | ||
|
|
17811480da | ||
|
|
cfbf4d1fa4 | ||
|
|
058b57867e | ||
|
|
b4ffef5c5f | ||
|
|
1346a31861 | ||
|
|
f5922e6eb1 | ||
|
|
5820a48fca | ||
|
|
1f1b98e33b | ||
|
|
aa2f964bda | ||
|
|
ad954dd1ca | ||
|
|
5f3b8b4100 | ||
|
|
0f24a8d8e1 | ||
|
|
fac116cfa4 | ||
|
|
5741e40c14 | ||
|
|
9cdae734a7 | ||
|
|
1912e309f7 | ||
|
|
62997f7fce | ||
|
|
0876ff481b | ||
|
|
8f277e4b7f | ||
|
|
bca30b62be | ||
|
|
249cb54373 | ||
|
|
7fd9c152d1 | ||
|
|
47dc9f7fc0 | ||
|
|
6f3b5f8666 | ||
|
|
2790825ae5 | ||
|
|
11f0244cf4 | ||
|
|
b6a21cde34 | ||
|
|
76cd97289b | ||
|
|
02908db62b | ||
|
|
3ed3248d7b | ||
|
|
4c61040c52 | ||
|
|
fe7865aad6 | ||
|
|
8a98c08c8a | ||
|
|
28bf71d74b | ||
|
|
a3bbcf2792 | ||
|
|
3ee5490c60 | ||
|
|
e2bcec33b3 | ||
|
|
7e028917c0 | ||
|
|
5ac6d7661c | ||
|
|
f76c8322d3 | ||
|
|
474859aaaa | ||
|
|
99ceaaa76e | ||
|
|
a68ca1ae0b | ||
|
|
8178b62187 | ||
|
|
2276f660f3 | ||
|
|
8ff0ea50b0 | ||
|
|
bab403d0ee | ||
|
|
169dba2042 | ||
|
|
4f6dab852e | ||
|
|
09ec5d2c4d | ||
|
|
2a1e47ffcb | ||
|
|
732e5805e3 | ||
|
|
7092313b2f |
@@ -10,6 +10,9 @@ description: Run Blacksmith Testbox for CI-parity checks, secrets, hosted servic
|
||||
Use Testbox when you need remote CI parity, injected secrets, hosted services,
|
||||
or an OS/runtime image that your local machine cannot provide cheaply.
|
||||
|
||||
For OpenClaw, Crabbox is a supported alternative when Blacksmith is unavailable
|
||||
or owned cloud capacity is preferable.
|
||||
|
||||
Do not default to Testbox for every local test/build loop. If the repo has
|
||||
documented local commands for normal iteration, use those first so you keep
|
||||
warm caches, local build state, and fast feedback.
|
||||
@@ -123,17 +126,22 @@ instantly and boots the CI environment in the background while you work:
|
||||
blacksmith testbox warmup ci-check-testbox.yml
|
||||
# → tbx_01jkz5b3t9...
|
||||
|
||||
Save this ID. You need it for every `run` command.
|
||||
Save this ID in the current session. You need it for every `run` command.
|
||||
Treat `blacksmith testbox list` as diagnostics, not a reusable work queue.
|
||||
Listed boxes can be visible at the org/repo level while still being unusable or
|
||||
stale for the current local agent lane.
|
||||
|
||||
For OpenClaw maintainer Testbox mode, pre-warm at the start of longer or wider
|
||||
tasks:
|
||||
|
||||
blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90
|
||||
pnpm testbox:claim --id <ID>
|
||||
|
||||
Use the build-artifact warmup when e2e/package/build proof benefits from seeded
|
||||
`dist/`, `dist-runtime/`, and build-all caches:
|
||||
|
||||
blacksmith testbox warmup ci-build-artifacts-testbox.yml --ref main --idle-timeout 90
|
||||
pnpm testbox:claim --id <ID>
|
||||
|
||||
Warmup dispatches a GitHub Actions workflow that provisions a VM with the
|
||||
full CI environment: dependencies installed, services started, secrets
|
||||
@@ -178,6 +186,26 @@ The `run` command automatically waits for the testbox to become ready if
|
||||
it is still booting, so you can call `run` immediately after warmup without
|
||||
needing to check status first.
|
||||
|
||||
In OpenClaw, prefer the guarded runner wrapper so stale/reused ids fail before
|
||||
the Blacksmith CLI spends time syncing or emits a confusing missing-key error:
|
||||
|
||||
pnpm testbox:run --id <ID> -- "OPENCLAW_TESTBOX=1 pnpm check:changed"
|
||||
|
||||
The wrapper refuses to run when the local per-Testbox key is missing or when the
|
||||
id was not claimed by this OpenClaw checkout with `pnpm testbox:claim --id
|
||||
<ID>`. Treat that as the expected remediation, not as a GitHub account or
|
||||
normal SSH-key problem. A local key alone is not enough; a ready box may still
|
||||
carry stale rsync state from another lane.
|
||||
|
||||
If the agent crashes, the remote box relies on Blacksmith's idle timeout. The
|
||||
local OpenClaw claim marker is not deleted automatically, so the wrapper treats
|
||||
claims older than 12 hours as stale. Override only for intentional long-running
|
||||
work with `OPENCLAW_TESTBOX_CLAIM_TTL_MINUTES=<minutes>`.
|
||||
|
||||
Before spending a broad gate on a manually assembled command, you can also run:
|
||||
|
||||
pnpm testbox:sanity -- --id <ID>
|
||||
|
||||
## Downloading files from a testbox
|
||||
|
||||
Use the `download` command to retrieve files or directories from a running
|
||||
@@ -286,16 +314,17 @@ checks that need parity or remote state.
|
||||
1. Decide whether the repo's local loop is the right default. For OpenClaw,
|
||||
`OPENCLAW_TESTBOX=1` makes Testbox the maintainer default.
|
||||
2. If Testbox is warranted, warm up early:
|
||||
`blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90` → save the ID
|
||||
`blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90` → save the ID,
|
||||
then `pnpm testbox:claim --id <ID>`
|
||||
3. Write code while the testbox boots in the background.
|
||||
4. Run the remote command when needed:
|
||||
`blacksmith testbox run --id <ID> "pnpm check:changed"`
|
||||
`pnpm testbox:run --id <ID> -- "OPENCLAW_TESTBOX=1 pnpm check:changed"`
|
||||
5. If tests fail, fix code and re-run against the same warm box.
|
||||
6. If you changed dependency manifests (package.json, etc.), prepend
|
||||
the install command: `blacksmith testbox run --id <ID> "npm install && npm test"`
|
||||
7. If a narrow PR reports a full sync or the box was reused/expired, sanity
|
||||
check the remote copy before a slow gate:
|
||||
`blacksmith testbox run --id <ID> "pnpm testbox:sanity"`.
|
||||
`pnpm testbox:run --id <ID> -- "pnpm testbox:sanity"`.
|
||||
If it reports missing root files or mass tracked deletions, stop the box and
|
||||
warm a fresh one. Use `OPENCLAW_TESTBOX_ALLOW_MASS_DELETIONS=1` only for an
|
||||
intentional large deletion PR.
|
||||
|
||||
339
.agents/skills/clawsweeper/SKILL.md
Normal file
339
.agents/skills/clawsweeper/SKILL.md
Normal file
@@ -0,0 +1,339 @@
|
||||
---
|
||||
name: clawsweeper
|
||||
description: "Use for all ClawSweeper work: OpenClaw issue/PR sweep reports, commit-review reports, repair jobs, cloud fix PRs, @clawsweeper maintainer mention commands, trusted ClawSweeper-reviewed autofix/automerge, GitHub Actions monitoring, permissions, gates, and manual backfills."
|
||||
---
|
||||
|
||||
# ClawSweeper
|
||||
|
||||
ClawSweeper lives at `~/Projects/clawsweeper`. It is the one OpenClaw
|
||||
maintenance bot for sweeping, commit review, repair jobs, and guarded fix PRs.
|
||||
Use this skill whenever Peter asks about reports, findings, dispatch health,
|
||||
repair/cloud PR creation, comment commands, automerge, permissions, or gates.
|
||||
|
||||
## Start
|
||||
|
||||
```bash
|
||||
cd ~/Projects/clawsweeper
|
||||
git status --short --branch
|
||||
git pull --ff-only
|
||||
pnpm run build:all
|
||||
```
|
||||
|
||||
Do not overwrite unrelated edits. If the tree is dirty, inspect first and keep
|
||||
read-only report work read-only unless Peter asked to commit.
|
||||
|
||||
## One Bot, One App
|
||||
|
||||
Use the ClawSweeper repo and the `clawsweeper` GitHub App. Use only
|
||||
`CLAWSWEEPER_*` configuration for this automation. Do not use legacy apps,
|
||||
variables, labels, or skills.
|
||||
|
||||
Required app setup:
|
||||
|
||||
- `CLAWSWEEPER_APP_CLIENT_ID`: public app client ID for `clawsweeper`.
|
||||
- `CLAWSWEEPER_APP_PRIVATE_KEY`: private key used only inside
|
||||
`actions/create-github-app-token` steps.
|
||||
- Target app permissions: read target scan context; write issues and pull
|
||||
requests; contents write for report commits, repair branches, and workflow
|
||||
inputs; Actions write on `openclaw/clawsweeper` for comment-router
|
||||
re-review dispatch, workflow dispatch, run cancellation, and self-heal;
|
||||
optional Checks write for commit Check Runs.
|
||||
|
||||
Token boundary:
|
||||
|
||||
- Codex workers do not get mutation credentials.
|
||||
- Review workers run with stripped secret/token env.
|
||||
- Deterministic scripts own comments, labels, branch pushes, PR creation,
|
||||
closes, and merges through short-lived GitHub App tokens.
|
||||
- Merge and write gates default closed.
|
||||
|
||||
## Commit Reports
|
||||
|
||||
Canonical commit reports:
|
||||
|
||||
```text
|
||||
records/<repo-slug>/commits/<40-char-sha>.md
|
||||
```
|
||||
|
||||
Use the lister:
|
||||
|
||||
```bash
|
||||
pnpm commit-reports -- --since 6h
|
||||
pnpm commit-reports -- --since "24 hours ago" --findings
|
||||
pnpm commit-reports -- --since 7d --non-clean
|
||||
pnpm commit-reports -- --repo openclaw/openclaw --author steipete --since 7d
|
||||
pnpm commit-reports -- --since 24h --json
|
||||
```
|
||||
|
||||
Results: `nothing_found`, `findings`, `inconclusive`, `failed`,
|
||||
`skipped_non_code`. One report per SHA; reruns overwrite the SHA-named report.
|
||||
|
||||
Manual rerun/backfill:
|
||||
|
||||
```bash
|
||||
gh workflow run commit-review.yml --repo openclaw/clawsweeper \
|
||||
-f target_repo=openclaw/openclaw \
|
||||
-f commit_sha=<end-sha> \
|
||||
-f before_sha=<start-or-parent-sha> \
|
||||
-f create_checks=false \
|
||||
-f enabled=true
|
||||
```
|
||||
|
||||
Use `create_checks=true` only when Peter explicitly wants target commit Check
|
||||
Runs. Add `-f additional_prompt="..."` for focused one-off review instructions.
|
||||
|
||||
## Sweep Reports
|
||||
|
||||
Issue/PR reports live at:
|
||||
|
||||
```text
|
||||
records/<repo-slug>/items/<number>.md
|
||||
records/<repo-slug>/closed/<number>.md
|
||||
```
|
||||
|
||||
Lead with counts, concrete findings, and report links. Do not post unsolicited
|
||||
GitHub comments from report-reading work. Public surfaces are markdown reports,
|
||||
durable ClawSweeper review comments, and optional checks.
|
||||
|
||||
PR reports include Codex `/review`-style `reviewFindings` with priority,
|
||||
confidence, repository-relative file, and line range. Public PR comments show a
|
||||
short `Review findings:` list when findings exist; full review comments,
|
||||
evidence links, likely owners, and runtime details stay inside the collapsed
|
||||
`Review details` block.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
pnpm run status
|
||||
pnpm run audit
|
||||
pnpm run reconcile
|
||||
pnpm run apply-decisions -- --dry-run
|
||||
```
|
||||
|
||||
## Create One Repair Job
|
||||
|
||||
Create a job from issue/PR refs and a maintainer prompt:
|
||||
|
||||
```bash
|
||||
pnpm run repair:create-job -- \
|
||||
--repo openclaw/openclaw \
|
||||
--refs 123,456 \
|
||||
--prompt-file /tmp/clawsweeper-prompt.md
|
||||
```
|
||||
|
||||
Create from an existing ClawSweeper report:
|
||||
|
||||
```bash
|
||||
pnpm run repair:create-job -- \
|
||||
--from-report ../clawsweeper/records/openclaw-openclaw/items/123.md
|
||||
```
|
||||
|
||||
The job creator checks for an existing open PR, body match, or remote
|
||||
`clawsweeper/<cluster-id>` branch before writing another job. Use `--dry-run`
|
||||
to inspect. Use `--force` only after deciding the duplicate guard is stale.
|
||||
|
||||
Validate, commit, then dispatch:
|
||||
|
||||
```bash
|
||||
pnpm run repair:validate-job -- jobs/openclaw/inbox/clawsweeper-openclaw-openclaw-123.md
|
||||
pnpm run repair:dispatch -- jobs/openclaw/inbox/clawsweeper-openclaw-openclaw-123.md \
|
||||
--mode autonomous \
|
||||
--runner blacksmith-4vcpu-ubuntu-2404 \
|
||||
--execution-runner blacksmith-16vcpu-ubuntu-2404 \
|
||||
--model gpt-5.5
|
||||
```
|
||||
|
||||
Do not dispatch a just-created job before the job file is committed and pushed;
|
||||
the workflow reads the job path from GitHub.
|
||||
|
||||
## Replacement PRs
|
||||
|
||||
For a useful but uneditable/stale/unsafe source PR, make the maintainer prompt
|
||||
explicit:
|
||||
|
||||
```md
|
||||
Treat #123 as useful source work. If the source branch cannot be safely updated
|
||||
because it is uneditable, stale, draft-only, unmergeable, or unsafe, create a
|
||||
narrow ClawSweeper replacement PR instead of waiting. Preserve the source PR
|
||||
author as co-author, credit the source PR in the replacement PR body, and close
|
||||
only that source PR after the replacement PR is opened.
|
||||
```
|
||||
|
||||
The worker should emit `repair_strategy=replace_uneditable_branch` and list the
|
||||
source PR URL in `source_prs`. The deterministic executor opens or updates
|
||||
`clawsweeper/<cluster-id>`, adds non-bot source authors as `Co-authored-by`
|
||||
trailers, and closes superseded source PRs only after replacement exists.
|
||||
|
||||
## Gates
|
||||
|
||||
Open execution windows intentionally and close them after the run:
|
||||
|
||||
```bash
|
||||
gh variable set CLAWSWEEPER_ALLOW_EXECUTE --repo openclaw/clawsweeper --body 1
|
||||
gh variable set CLAWSWEEPER_ALLOW_FIX_PR --repo openclaw/clawsweeper --body 1
|
||||
gh variable set CLAWSWEEPER_ALLOW_MERGE --repo openclaw/clawsweeper --body 1
|
||||
gh variable set CLAWSWEEPER_ALLOW_AUTOMERGE --repo openclaw/clawsweeper --body 1
|
||||
```
|
||||
|
||||
Reset gates only when Peter asks; the active maintainer window may intentionally
|
||||
leave them at `1`.
|
||||
|
||||
Important gates:
|
||||
|
||||
- `CLAWSWEEPER_ALLOW_EXECUTE`: allows deterministic write lanes.
|
||||
- `CLAWSWEEPER_ALLOW_FIX_PR`: allows branch repair/replacement PRs.
|
||||
- `CLAWSWEEPER_ALLOW_MERGE`: allows merge-capable applicators.
|
||||
- `CLAWSWEEPER_ALLOW_AUTOMERGE`: allows comment-router automerge.
|
||||
- `CLAWSWEEPER_COMMENT_ROUTER_EXECUTE`: lets scheduled comment routing
|
||||
post replies and dispatch repair.
|
||||
|
||||
## Maintainer Mentions
|
||||
|
||||
Prefer `@clawsweeper` comments for all maintainer-facing control. Slash
|
||||
commands still parse as compatibility aliases, but examples and live guidance
|
||||
should use mentions.
|
||||
|
||||
```text
|
||||
@clawsweeper status
|
||||
@clawsweeper re-review
|
||||
@clawsweeper review
|
||||
@clawsweeper fix ci
|
||||
@clawsweeper address review
|
||||
@clawsweeper rebase
|
||||
@clawsweeper autofix
|
||||
@clawsweeper automerge
|
||||
@clawsweeper approve
|
||||
@clawsweeper explain
|
||||
@clawsweeper stop
|
||||
@clawsweeper <question or safe action request>
|
||||
@clawsweeper[bot] re-review
|
||||
@openclaw-clawsweeper fix ci
|
||||
@openclaw-clawsweeper[bot] fix ci
|
||||
```
|
||||
|
||||
Accepted aliases: `review`, `re-review`, `rereview`, `review again`,
|
||||
`rerun review`, and `run review`. `review` and `re-review` dispatch a fresh
|
||||
ClawSweeper issue/PR review without starting repair. `fix ci`,
|
||||
`address review`, and `rebase` dispatch the
|
||||
repair worker only for ClawSweeper PRs or PRs opted into
|
||||
`clawsweeper:autofix` or `clawsweeper:automerge`. `autofix` runs the bounded
|
||||
review/fix loop without merging. `automerge` runs the bounded review/fix/merge
|
||||
loop, but draft PRs stay fix-only until GitHub marks them ready for review.
|
||||
|
||||
Freeform maintainer mentions such as `@clawsweeper why did automerge stop?`
|
||||
or `@clawsweeper: can you explain this failure?` dispatch a read-only assist
|
||||
review with the mention text as one-off instructions. The answer lands in the
|
||||
next public ClawSweeper review comment. Action-looking prose does not directly
|
||||
mutate GitHub; it must map to existing structured recommendations and pass the
|
||||
normal deterministic gates.
|
||||
|
||||
Default accepted maintainers: `OWNER`, `MEMBER`, `COLLABORATOR`; fallback
|
||||
repository permission accepts `admin`, `maintain`, or `write`. Contributor
|
||||
comments are ignored without a reply.
|
||||
|
||||
Run router manually:
|
||||
|
||||
```bash
|
||||
pnpm run repair:comment-router -- --repo openclaw/openclaw --lookback-minutes 180
|
||||
pnpm run repair:comment-router -- --repo openclaw/openclaw --execute --wait-for-capacity
|
||||
```
|
||||
|
||||
Scheduled routing stays dry unless
|
||||
`CLAWSWEEPER_COMMENT_ROUTER_EXECUTE=1`.
|
||||
|
||||
## Trusted Autofix And Automerge
|
||||
|
||||
`@clawsweeper autofix` opts an existing PR into the bounded review/fix loop.
|
||||
`@clawsweeper automerge` opts an existing PR into the bounded review/fix/merge
|
||||
loop. The router:
|
||||
|
||||
- verifies maintainer authorization;
|
||||
- labels the PR `clawsweeper:autofix` or `clawsweeper:automerge`;
|
||||
- dispatches ClawSweeper review for the current head SHA;
|
||||
- creates or reuses a durable adopted job;
|
||||
- repairs at most the configured caps;
|
||||
- never merges autofix PRs or draft PRs;
|
||||
- merges automerge PRs only when ClawSweeper passed the exact current head,
|
||||
checks are green, GitHub says mergeable, no human-review label is present,
|
||||
the PR is not draft, required user-facing OpenClaw changelog entries are
|
||||
present, and both merge gates are open.
|
||||
|
||||
If ClawSweeper passes while merge gates are closed, it labels
|
||||
`clawsweeper:merge-ready` and comments instead of merging. `@clawsweeper stop`
|
||||
adds `clawsweeper:human-review`.
|
||||
|
||||
When Peter asks Codex to create a PR and enable ClawSweeper automerge, do not
|
||||
leave his local OpenClaw checkout on the PR branch. After the PR is created,
|
||||
pushed, and the `@clawsweeper automerge` request is posted or otherwise
|
||||
confirmed, return the local checkout to `main` and fast-forward it when the
|
||||
working tree is clean:
|
||||
|
||||
```bash
|
||||
git switch main
|
||||
git pull --ff-only
|
||||
```
|
||||
|
||||
If unrelated local edits or an in-progress rebase prevent switching, report the
|
||||
blocker instead of stashing, deleting, or overwriting work.
|
||||
|
||||
Repair caps:
|
||||
|
||||
```bash
|
||||
CLAWSWEEPER_MAX_REPAIRS_PER_PR=10
|
||||
CLAWSWEEPER_MAX_REPAIRS_PER_HEAD=1
|
||||
```
|
||||
|
||||
## Security Boundary
|
||||
|
||||
Do not stage unapproved security-sensitive work for ClawSweeper Repair. Route
|
||||
vulnerability reports, CVE/GHSA/advisory work, leaked secrets/tokens/keys,
|
||||
plaintext secret storage, SSRF, XSS, CSRF, RCE, auth bypass, privilege
|
||||
escalation, and sensitive data exposure to central OpenClaw security handling.
|
||||
|
||||
For PRs explicitly opted into `clawsweeper:autofix` or
|
||||
`clawsweeper:automerge`, security-sensitive review findings may dispatch
|
||||
bounded repair, but merge remains blocked until a later exact-head review is
|
||||
clean and the normal merge gates pass. Trust deterministic ClawSweeper security
|
||||
markers, labels, and job frontmatter; do not infer security handling from vague
|
||||
prose.
|
||||
|
||||
## Monitoring
|
||||
|
||||
Receiver workflows:
|
||||
|
||||
```bash
|
||||
gh run list --repo openclaw/clawsweeper --workflow "ClawSweeper Commit Review" \
|
||||
--limit 12 --json databaseId,displayTitle,event,status,conclusion,createdAt,updatedAt,url
|
||||
gh run list --repo openclaw/clawsweeper --workflow "repair cluster worker" \
|
||||
--limit 12 --json databaseId,displayTitle,event,status,conclusion,createdAt,updatedAt,url
|
||||
gh run list --repo openclaw/clawsweeper --workflow "repair comment router" \
|
||||
--limit 12 --json databaseId,displayTitle,event,status,conclusion,createdAt,updatedAt,url
|
||||
```
|
||||
|
||||
Target dispatcher:
|
||||
|
||||
```bash
|
||||
gh run list --repo openclaw/openclaw --workflow "ClawSweeper Dispatch" \
|
||||
--event push --limit 8 --json databaseId,displayTitle,event,status,conclusion,headSha,url
|
||||
```
|
||||
|
||||
Target commit check:
|
||||
|
||||
```bash
|
||||
gh api "repos/openclaw/openclaw/commits/<sha>/check-runs?per_page=100" \
|
||||
--jq '.check_runs[] | select(.name=="ClawSweeper Commit Review") | [.status,.conclusion,.details_url] | @tsv'
|
||||
```
|
||||
|
||||
## Reading Output
|
||||
|
||||
For findings or failures, summarize:
|
||||
|
||||
- target repo, item/PR/commit, run, report path
|
||||
- result, confidence, severity, and exact blocker
|
||||
- affected files or cluster refs
|
||||
- validation commands and whether they passed
|
||||
- whether mutation gates were open or closed
|
||||
- next deterministic action
|
||||
|
||||
Keep the broom small: one cluster, one branch, one PR, narrow proof, clear
|
||||
owner-visible evidence.
|
||||
4
.agents/skills/clawsweeper/agents/openai.yaml
Normal file
4
.agents/skills/clawsweeper/agents/openai.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "ClawSweeper"
|
||||
short_description: "Inspect ClawSweeper commit review reports and Actions runs."
|
||||
default_prompt: "Review recent ClawSweeper commit reports and summarize findings."
|
||||
81
.agents/skills/crabbox/SKILL.md
Normal file
81
.agents/skills/crabbox/SKILL.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
name: crabbox
|
||||
description: Use Crabbox for OpenClaw remote Linux validation, warmed reusable boxes, GitHub Actions hydration, sync timing, logs, results, caches, and lease cleanup.
|
||||
---
|
||||
|
||||
# Crabbox
|
||||
|
||||
Use Crabbox when OpenClaw needs remote Linux proof on owned capacity, a large
|
||||
runner class, reusable warm state, or a Blacksmith alternative.
|
||||
|
||||
## Before Running
|
||||
|
||||
- Run from the repo root. Crabbox sync mirrors the current checkout.
|
||||
- Prefer local targeted tests for tight edit loops.
|
||||
- Prefer Blacksmith Testbox when the task explicitly asks for Blacksmith or a
|
||||
Blacksmith-specific CI comparison.
|
||||
- Use Crabbox for broad OpenClaw gates when owned AWS/Hetzner capacity is the
|
||||
right remote lane.
|
||||
- Check `.crabbox.yaml` for repo defaults before adding flags.
|
||||
- Install with `brew install openclaw/tap/crabbox`; auth is required before use:
|
||||
`printf '%s' "$CRABBOX_COORDINATOR_TOKEN" | crabbox login --url https://crabbox-coordinator.steipete.workers.dev --provider aws --token-stdin`.
|
||||
- On macOS the user config is `~/Library/Application Support/crabbox/config.yaml`;
|
||||
it must include `broker.url`, `broker.token`, and usually `provider: aws`.
|
||||
|
||||
## OpenClaw Flow
|
||||
|
||||
Warm a reusable box:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:warmup -- --idle-timeout 90m
|
||||
```
|
||||
|
||||
Hydrate it through the repository workflow:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:hydrate -- --id <cbx_id-or-slug>
|
||||
```
|
||||
|
||||
Run broad proof:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:run -- --id <cbx_id-or-slug> --shell "OPENCLAW_TESTBOX=1 pnpm check:changed"
|
||||
pnpm crabbox:run -- --id <cbx_id-or-slug> --shell "corepack enable && pnpm install --frozen-lockfile && pnpm test"
|
||||
```
|
||||
|
||||
Stop boxes you created before handoff:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:stop -- <cbx_id-or-slug>
|
||||
```
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```sh
|
||||
crabbox status --id <id-or-slug> --wait
|
||||
crabbox inspect --id <id-or-slug> --json
|
||||
crabbox sync-plan
|
||||
crabbox history --lease <id-or-slug>
|
||||
crabbox logs <run_id>
|
||||
crabbox results <run_id>
|
||||
crabbox cache stats --id <id-or-slug>
|
||||
crabbox ssh --id <id-or-slug>
|
||||
```
|
||||
|
||||
Use `--debug` on `run` when measuring sync timing.
|
||||
|
||||
## Hydration Boundary
|
||||
|
||||
`.github/workflows/crabbox-hydrate.yml` is repo-specific on purpose. It owns
|
||||
OpenClaw checkout, setup-node, pnpm setup, provider env hydration, ready marker,
|
||||
and keepalive. Crabbox owns runner registration, workflow dispatch, SSH sync,
|
||||
command execution, logs/results, local lease claims, and idle cleanup.
|
||||
|
||||
Do not add OpenClaw-specific setup to Crabbox. Put repo setup in the hydration
|
||||
workflow and generic lease/sync behavior in Crabbox.
|
||||
|
||||
## Cleanup
|
||||
|
||||
Crabbox has coordinator-owned idle expiry and local lease claims, so OpenClaw
|
||||
does not need a custom ledger. Default idle timeout is 30 minutes unless config
|
||||
or flags set a different value. Still stop boxes you created when done.
|
||||
@@ -41,6 +41,28 @@ gitcrawl cluster-detail openclaw/openclaw --id <cluster-id> --member-limit 20 --
|
||||
- `invalid`
|
||||
- `dirty` for PRs only
|
||||
|
||||
## Select small high-confidence triage candidates
|
||||
|
||||
When asked for `X` issues or PRs to triage, `X` means qualified candidates, not sampled threads.
|
||||
|
||||
Only list candidates that pass all gates:
|
||||
|
||||
- small owner/surface, with a likely narrow fix and focused regression test
|
||||
- symptom is reproducible or provable with logs, failing test, live command, dependency contract, or current-main behavior
|
||||
- root cause is traceable to code with file/line and the proposed fix touches that path
|
||||
- no strong smell that a broader refactor, ownership rethink, migration, or product decision is the better fix
|
||||
- dependency-backed behavior checked against upstream docs/source/types; live or web proof used when local proof is insufficient
|
||||
|
||||
Loop:
|
||||
|
||||
1. Use `gitcrawl` / `gh` to gather candidate clusters.
|
||||
2. Read issue/PR body, comments, current code, adjacent tests, and dependency contracts.
|
||||
3. Try focused repro or proof.
|
||||
4. Reject unclear, stale, speculative, broad-refactor, or owner-ambiguous items.
|
||||
5. Continue until `X` qualified candidates or the bounded search is exhausted.
|
||||
|
||||
Output only qualifying candidates, with: ref, surface, proof, cause, fix sketch, why small, expected test/gate. If none qualify, say so; do not pad.
|
||||
|
||||
## Enforce the bug-fix evidence bar
|
||||
|
||||
- Never merge a bug-fix PR based only on issue text, PR text, or AI rationale.
|
||||
|
||||
234
.agents/skills/openclaw-pre-release-plugin-testing/SKILL.md
Normal file
234
.agents/skills/openclaw-pre-release-plugin-testing/SKILL.md
Normal file
@@ -0,0 +1,234 @@
|
||||
---
|
||||
name: openclaw-pre-release-plugin-testing
|
||||
description: Plan and run pre-release OpenClaw plugin validation across bundled plugins, package artifacts, lifecycle commands, doctor/fix, config round-trip, gateway startup, SDK compatibility, Docker E2E, Package Acceptance, and Testbox proof.
|
||||
---
|
||||
|
||||
# OpenClaw Pre-Release Plugin Testing
|
||||
|
||||
Use this skill when the user asks for plugin release confidence, plugin lifecycle
|
||||
sweeps, package-artifact plugin proof, or "what else should we test before
|
||||
release?" It complements `openclaw-testing`; use that skill too when choosing
|
||||
the cheapest safe runner or debugging a failing lane.
|
||||
|
||||
## Goal
|
||||
|
||||
Prove the plugin system as a product surface, not just as source tests:
|
||||
|
||||
- bundled plugin lifecycle: install, inspect, enable, disable, uninstall
|
||||
- package artifact behavior from a clean `HOME`
|
||||
- doctor/fix/config validation and idempotence
|
||||
- config discovery and config round-trip
|
||||
- status/log visibility and diagnostics
|
||||
- gateway startup/bootstrap with plugin metadata snapshots
|
||||
- public SDK compatibility for real external plugins
|
||||
- live-ish provider/channel probes only when safe credentials exist
|
||||
|
||||
## First Checks
|
||||
|
||||
From the OpenClaw repo root:
|
||||
|
||||
```bash
|
||||
pnpm docs:list
|
||||
git status --short --branch
|
||||
readlink node_modules
|
||||
pnpm changed:lanes --json
|
||||
```
|
||||
|
||||
In Codex worktrees under `.codex/worktrees`, `node_modules` must be a symlink to
|
||||
the main OpenClaw checkout. Do not run `pnpm install` there. For broad or
|
||||
package-heavy proof, use Blacksmith Testbox or GitHub Actions.
|
||||
|
||||
## Runner Choice
|
||||
|
||||
Prefer this order:
|
||||
|
||||
1. **GitHub Package Acceptance** for installable-package product proof.
|
||||
2. **`ci-build-artifacts-testbox.yml` Testbox** when Docker/package lanes need
|
||||
seeded `dist`, `dist-runtime`, and package caches.
|
||||
3. **`ci-check-testbox.yml` Testbox** for source checks, targeted Vitest,
|
||||
package-boundary checks, or focused Docker lanes.
|
||||
4. **Local targeted commands only** for small format/static/unit probes.
|
||||
|
||||
Avoid long package Docker runs from a stale sparse worktree. If Testbox sync
|
||||
reports hundreds of changed files or starts deleting package inputs, stop and
|
||||
warm a fresh box from current `main`, or switch to Package Acceptance.
|
||||
|
||||
## Existing Baseline
|
||||
|
||||
Run or verify these before inventing new coverage:
|
||||
|
||||
```bash
|
||||
OPENCLAW_TESTBOX=1 pnpm check:changed
|
||||
pnpm run test:extensions:package-boundary:canary
|
||||
pnpm run test:extensions:package-boundary:compile
|
||||
pnpm test:docker:plugins
|
||||
OPENCLAW_PLUGINS_E2E_CLAWHUB=0 pnpm test:docker:plugins
|
||||
pnpm test:docker:plugin-update
|
||||
pnpm test:docker:bundled-channel-deps:fast
|
||||
```
|
||||
|
||||
For full bundled install/uninstall proof, shard the packaged sweep:
|
||||
|
||||
```bash
|
||||
OPENCLAW_BUNDLED_PLUGIN_SWEEP_TOTAL=8 \
|
||||
OPENCLAW_BUNDLED_PLUGIN_SWEEP_INDEX=<0-7> \
|
||||
pnpm test:docker:bundled-plugin-install-uninstall
|
||||
```
|
||||
|
||||
Expected current packaged scope: 116 public bundled plugins over shards `0-7`.
|
||||
Private QA plugins are source-mode only unless a package explicitly includes
|
||||
them.
|
||||
|
||||
## Confidence Matrix
|
||||
|
||||
Use this matrix for pre-release signoff. Record pass/fail, run URL/Testbox ID,
|
||||
package SHA/version, and skipped-live reason.
|
||||
|
||||
| Surface | Proof | Preferred runner |
|
||||
| --- | --- | --- |
|
||||
| Package artifact | Package Acceptance `suite_profile=package` or custom lanes | GitHub Actions |
|
||||
| Bundled lifecycle | 8-shard `test:docker:bundled-plugin-install-uninstall` | Testbox or release Docker |
|
||||
| External plugins | `test:docker:plugins` and `plugins-offline` | Testbox/package acceptance |
|
||||
| Update no-op | `test:docker:plugin-update` | Testbox/package acceptance |
|
||||
| Channel runtime deps | `test:docker:bundled-channel-deps:fast` plus key channels | Testbox/package acceptance |
|
||||
| Doctor/fix | seeded bad configs + `doctor --fix --non-interactive` | new Docker/Testbox harness |
|
||||
| Config round-trip | `config set/get`, inspect, doctor, reload, diff hash | new Docker/Testbox harness |
|
||||
| Gateway bootstrap | clean `HOME`, plugin groups enabled/disabled, status JSON | new Docker/Testbox harness |
|
||||
| SDK compatibility | directory, tgz, and `file:` external plugins using SDK subpaths | `test:docker:plugins` plus new smoke |
|
||||
| Live-ish | redacted provider/channel probes only for present env | Testbox live lanes |
|
||||
|
||||
## Package Acceptance Plan
|
||||
|
||||
Use this when validating a release branch, beta, or candidate package:
|
||||
|
||||
```bash
|
||||
gh workflow run package-acceptance.yml \
|
||||
--repo openclaw/openclaw \
|
||||
--ref main \
|
||||
-f workflow_ref=main \
|
||||
-f source=ref \
|
||||
-f package_ref=<branch-or-sha> \
|
||||
-f suite_profile=custom \
|
||||
-f docker_lanes='plugins-offline plugin-update bundled-channel-deps-compat doctor-switch update-channel-switch config-reload mcp-channels npm-onboard-channel-agent' \
|
||||
-f telegram_mode=mock-openai
|
||||
```
|
||||
|
||||
Use `source=npm -f package_spec=openclaw@beta` for published beta proof. Keep
|
||||
`workflow_ref` as trusted current harness code unless the release process says
|
||||
otherwise.
|
||||
|
||||
## New Testbox Harness Plan
|
||||
|
||||
If more certainty is needed, add or run a `plugin-lifecycle-matrix` Docker lane
|
||||
that uses one package tarball and sharded plugin lists. Per plugin:
|
||||
|
||||
1. Start with a clean `HOME`.
|
||||
2. Capture `plugins list --json`.
|
||||
3. `plugins install <id>`.
|
||||
4. `plugins inspect <id> --json`.
|
||||
5. `plugins disable <id>`, then assert disabled visibility.
|
||||
6. `plugins enable <id>`, except config-required plugins without config.
|
||||
7. `plugins registry --refresh`.
|
||||
8. `doctor --non-interactive`.
|
||||
9. `plugins uninstall <id> --force`.
|
||||
10. Assert no config entry, allow/deny residue, install record, managed dir, or
|
||||
bundled `dist/extensions/...` load path remains.
|
||||
11. Assert diagnostics contain no `level: "error"` and output redacts
|
||||
secret-looking values.
|
||||
|
||||
Keep `memory-lancedb` special: it is config-required. First assert install does
|
||||
not enable it without embedding config, then run a second configured case.
|
||||
|
||||
## Doctor/Fix Matrix
|
||||
|
||||
Seed bad states and require `doctor --fix --non-interactive` to repair them,
|
||||
then run doctor again and require idempotence:
|
||||
|
||||
- stale `plugins.allow`
|
||||
- stale `plugins.entries`
|
||||
- stale channel config for missing channel plugin
|
||||
- invalid `plugins.entries.<id>.config`
|
||||
- packaged bundled path in `plugins.load.paths`
|
||||
- legacy `plugins.installs`
|
||||
- disabled channel/plugin config that must not stage runtime deps
|
||||
- root-owned global package tree that must remain unmodified
|
||||
|
||||
## Gateway Bootstrap Matrix
|
||||
|
||||
Start packaged OpenClaw in Docker with clean state:
|
||||
|
||||
- provider plugins enabled, no credentials: ready with warnings, no crash
|
||||
- channel plugins configured disabled: no runtime deps staged
|
||||
- startup-activation plugins enabled: ready and reflected in status
|
||||
- invalid single plugin config: bad plugin skipped/quarantined, others remain
|
||||
|
||||
Assert:
|
||||
|
||||
- gateway reaches ready
|
||||
- `openclaw status --json` includes plugin diagnostics
|
||||
- `openclaw plugins inspect --all --json` is parseable
|
||||
- package tree is not mutated
|
||||
- logs contain no raw tokens
|
||||
|
||||
## Config Round-Trip Representatives
|
||||
|
||||
Use representative plugin families instead of every plugin for deep config
|
||||
round-trip:
|
||||
|
||||
- providers: `openai`, `anthropic`, `mistral`, `openrouter`
|
||||
- channels: `telegram`, `discord`, `slack`, `whatsapp`
|
||||
- memory: `memory-lancedb`
|
||||
- feature/runtime: `browser`, `acpx`, `tokenjuice`
|
||||
|
||||
For each representative:
|
||||
|
||||
1. Write config through CLI when possible.
|
||||
2. Read it back through `config get` or JSON.
|
||||
3. Run `plugins inspect`.
|
||||
4. Run `doctor --non-interactive`.
|
||||
5. Trigger gateway config reload if applicable.
|
||||
6. Compare config hash before/after no-op commands.
|
||||
|
||||
## External SDK Smoke
|
||||
|
||||
In a package Docker lane, create tiny external plugins and install them from:
|
||||
|
||||
- local directory
|
||||
- `.tgz`
|
||||
- `file:` npm spec
|
||||
|
||||
Cover CJS and ESM shapes, plus at least one plugin importing focused
|
||||
`openclaw/plugin-sdk/*` subpaths. Assert `plugins inspect` sees its tool,
|
||||
gateway method, CLI command, or service.
|
||||
|
||||
## Live-Ish Probe Rules
|
||||
|
||||
Before live-ish work, source allowed env in Testbox and generate a redacted
|
||||
availability matrix: present/missing only, never values.
|
||||
|
||||
Only run probes for credentials that exist. Prefer auth/catalog/status probes
|
||||
over sending user-visible messages. If a probe might contact an external user,
|
||||
channel, or workspace, stop and ask the user.
|
||||
|
||||
## Reporting
|
||||
|
||||
Report in this shape:
|
||||
|
||||
```text
|
||||
package/ref:
|
||||
tbx ids / run urls:
|
||||
matrix:
|
||||
bundled lifecycle:
|
||||
package acceptance:
|
||||
doctor/fix:
|
||||
gateway bootstrap:
|
||||
config round-trip:
|
||||
sdk external:
|
||||
live-ish:
|
||||
failures:
|
||||
skips:
|
||||
next highest-value gap:
|
||||
```
|
||||
|
||||
Say clearly when a failure is Testbox sync/env damage rather than product
|
||||
behavior, and prove that with a clean rerun or current-main comparison.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Plugin Pre-Release Testing"
|
||||
short_description: "Plan plugin release validation"
|
||||
default_prompt: "Use $openclaw-pre-release-plugin-testing to plan or run pre-release OpenClaw plugin validation across package, lifecycle, doctor, gateway, SDK, and live-ish proof."
|
||||
@@ -41,9 +41,11 @@ Use this skill for release and publish-time workflow. Keep ordinary development
|
||||
recommended replacement can shift as plugin ownership, externalization, and
|
||||
config footprint move, so do not blindly copy stale replacement annotations
|
||||
into release notes.
|
||||
- Do not delete or rewrite beta tags after they leave the machine. If a
|
||||
published or pushed beta needs a fix, commit the fix on the release branch and
|
||||
increment to the next `-beta.N`.
|
||||
- Do not delete or rewrite beta tags after their matching npm package has been
|
||||
published. If a pushed beta tag fails preflight before npm publish, delete and
|
||||
recreate the tag and prerelease at the fixed commit so npm prerelease versions
|
||||
stay contiguous. If a published beta needs a fix, commit the fix on the
|
||||
release branch and increment to the next `-beta.N`.
|
||||
- For a beta release train, run the fast local preflight first, publish the
|
||||
beta to npm `beta`, then run the expensive published-package roster focused
|
||||
on install/update/Docker/Parallels/NPM Telegram. If anything fails, fix it on
|
||||
@@ -367,8 +369,10 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
- Any fix after preflight means a new commit. Delete and recreate the tag and
|
||||
matching GitHub release from the fixed commit, then rerun preflight from
|
||||
scratch before publishing.
|
||||
Exception: never delete or recreate a beta tag that has already been pushed or
|
||||
published; increment to the next beta number instead.
|
||||
Exception: never delete or recreate a beta tag whose matching npm package has
|
||||
already been published; increment to the next beta number instead. If only the
|
||||
pushed tag/prerelease exists and npm publish has not happened, recreate that
|
||||
same beta tag at the fixed commit.
|
||||
- For stable mac releases, generate the signed `appcast.xml` before uploading
|
||||
public release assets so the updater feed cannot lag the published binaries.
|
||||
- Serialize stable appcast-producing runs across tags so two releases do not
|
||||
@@ -561,6 +565,9 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
commit, and rerun all relevant preflights from scratch before continuing.
|
||||
Never reuse old preflight results after the commit changes. For pushed or
|
||||
published beta tags, do not delete/recreate; increment to the next beta tag.
|
||||
For preflight-only failures where npm did not publish the beta version,
|
||||
delete/recreate the same beta tag and prerelease at the fixed commit instead
|
||||
of skipping a prerelease number.
|
||||
20. Start `.github/workflows/openclaw-npm-release.yml` from the same branch with
|
||||
the same tag for the real publish, choose `npm_dist_tag` (`beta` default,
|
||||
`latest` only when you intentionally want direct stable publish), keep it
|
||||
@@ -573,9 +580,9 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
for critical fixes that landed after the release branch cut; backport only
|
||||
important low-risk fixes before starting expensive lanes, or increment to
|
||||
the next beta if the fix must change the already-published package. If any
|
||||
lane fails after the beta tag/package is pushed or published, fix,
|
||||
commit/push/pull, increment to the next beta tag, and rerun the affected
|
||||
beta evidence. Once the beta is live, start remote/manual rosters where they
|
||||
lane fails after the beta package is published, fix, commit/push/pull,
|
||||
increment to the next beta tag, and rerun the affected beta evidence. Once
|
||||
the beta is live, start remote/manual rosters where they
|
||||
can overlap safely, but keep local Docker and Parallels load controlled.
|
||||
Ensure the full expensive roster has passed at least once before
|
||||
stable/latest promotion. The roster includes the manual Actions >
|
||||
|
||||
63
.agents/skills/openclaw-small-bugfix-sweep/SKILL.md
Normal file
63
.agents/skills/openclaw-small-bugfix-sweep/SKILL.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: openclaw-small-bugfix-sweep
|
||||
description: Fix only small, high-certainty OpenClaw bugs from a pasted issue/PR list after deep code review.
|
||||
---
|
||||
|
||||
# OpenClaw Small Bugfix Sweep
|
||||
|
||||
Batch workflow for pasted OpenClaw issue/PR refs.
|
||||
Execute, do not summarize.
|
||||
Triage does not commit, push, create PRs, comment, close, label, land, or merge.
|
||||
|
||||
## Companion Skills
|
||||
|
||||
Use `$gitcrawl` first, `$openclaw-pr-maintainer` for live GitHub hygiene, `$github-deep-review` posture for source tracing, and `$openclaw-testing` for proof.
|
||||
|
||||
## Loop
|
||||
|
||||
For each ref:
|
||||
|
||||
1. Read live target with `gh`.
|
||||
2. Check `gitcrawl` for related, duplicate, closed, or already-fixed threads.
|
||||
3. Read body, comments, linked refs, changed files, current code, adjacent tests, and dependency contracts when relevant.
|
||||
4. Trace the real runtime path.
|
||||
5. For issues: fix locally only if this is a bug, current code proves root cause, the implicated path is clear, and a narrow patch is cleaner than refactor.
|
||||
6. For PRs: decide `ready-to-merge`, `needs-fixup`, or `skip`; do not alter PR branches unless explicitly asked.
|
||||
7. Add focused regression proof when practical for local issue fixes or PR readiness checks.
|
||||
8. Run the smallest meaningful gate.
|
||||
9. Continue until every pasted ref is fixed or classified.
|
||||
|
||||
No subagents unless explicitly requested.
|
||||
|
||||
## Skip If
|
||||
|
||||
- not a bug
|
||||
- config/docs/workflow/release/support/dependency/product work
|
||||
- repro or root cause is uncertain
|
||||
- larger refactor or owner-boundary change is cleaner
|
||||
- already fixed on current `main`
|
||||
- dependency behavior is guessed
|
||||
- no focused proof is feasible
|
||||
|
||||
Skip with terse reason. Do not pad with low-confidence fixes.
|
||||
|
||||
## Fix Rules
|
||||
|
||||
- owner module first; generic seam only when required
|
||||
- existing patterns/helpers/types
|
||||
- no drive-by refactors
|
||||
- tests near failing surface
|
||||
- docs only for changed public behavior
|
||||
- no commit/push/create PR/comment/close/label/land/merge unless explicitly asked
|
||||
|
||||
## PR Rules
|
||||
|
||||
- `ready-to-merge`: code is good, current head checked, required proof is green or clearly pending only external CI; list for maintainer merge or `@clawsweeper automerge`
|
||||
- `needs-fixup`: small bug is clear, but PR branch needs changes; list exact files/tests and wait for explicit fix/push/automerge instruction
|
||||
- `skip`: broad, stale, speculative, config/product/security/release, owner-boundary, or refactor-sized
|
||||
- if source PR is untrusted/uneditable, do not create a replacement PR during sweep
|
||||
|
||||
## Output Shape
|
||||
|
||||
Ledger: `fixed-local`, `ready-to-merge`, `needs-fixup`, `skipped`, `needs-human`.
|
||||
Final: issue files left on disk, PRs ready for merge/automerge, tests/gates, skip reasons.
|
||||
@@ -7,6 +7,8 @@ description: Investigate OpenClaw pnpm test memory growth, Vitest OOMs, RSS spik
|
||||
|
||||
Use this skill for test-memory investigations. Do not guess from RSS alone when heap snapshots are available. Treat snapshot-name deltas as triage evidence, not proof, until retainers or dominators support the call.
|
||||
|
||||
For **runtime fixes** (e.g., closure leaks in long-running services like the gateway), see [Validating runtime fixes](#validating-runtime-fixes-not-test-memory) below — that uses a dedicated harness, not the test-parallel snapshot machinery.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Reproduce the failing shape first.
|
||||
@@ -63,6 +65,38 @@ Use this skill for test-memory investigations. Do not guess from RSS alone when
|
||||
|
||||
Read the top positive deltas first. Large positive growth in module-transform artifacts suggests lane isolation; large positive growth in runtime objects suggests a real leak. If the names alone do not settle it, open the same snapshot pair in DevTools and inspect retainers/dominators for the top rows before declaring root cause.
|
||||
|
||||
## Validating runtime fixes (not test-memory)
|
||||
|
||||
The workflow above is for diagnosing Vitest worker memory growth. For
|
||||
validating that a runtime/closure fix actually releases captured state, use the
|
||||
dedicated harness:
|
||||
|
||||
- `pnpm leak:embedded-run` — runs `scripts/embedded-run-abort-leak.ts`. Loops N
|
||||
aborted runs in a function-shaped scope mimicking `runEmbeddedAttempt`,
|
||||
writes heap snapshots, and reports a PASS/FAIL verdict on retention growth
|
||||
using `FinalizationRegistry` for tracked-instance counting plus RSS delta.
|
||||
|
||||
Modes:
|
||||
|
||||
- `closure-extracted` (default) — production fix shape (helper at module scope).
|
||||
- `closure-inline` — pre-fix shape (closure inside the runner scope). Use as a
|
||||
sensitivity check: if it passes you've broken the harness, not fixed a bug.
|
||||
- `synthetic-leak` — deliberately retains via a module-level bucket. Use to
|
||||
confirm the harness can detect leaks before trusting a PASS on a real fix.
|
||||
|
||||
Snapshots land in `.tmp/embedded-run-abort-leak/`. Diff with the same script
|
||||
as above:
|
||||
|
||||
```
|
||||
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs \
|
||||
.tmp/embedded-run-abort-leak/baseline-*.heapsnapshot \
|
||||
.tmp/embedded-run-abort-leak/batch-N-*.heapsnapshot --top 30
|
||||
```
|
||||
|
||||
When fixing a different runtime leak, add a new harness alongside this one
|
||||
rather than retrofitting it. The fixture function should mimic the lexical
|
||||
scope of the function where the leak lives, not be a generic abort-loop.
|
||||
|
||||
## Output Expectations
|
||||
|
||||
When using this skill, report:
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
name: openclaw-test-performance
|
||||
description: Benchmark, diagnose, and optimize OpenClaw test runtime, import hotspots, CPU/RSS, and slow coverage paths.
|
||||
description: Benchmark, diagnose, and optimize OpenClaw test and plugin-suite runtime, import hotspots, CPU/RSS, heap growth, and slow coverage paths.
|
||||
---
|
||||
|
||||
# OpenClaw Test Performance
|
||||
|
||||
Use evidence first. The goal is real `pnpm test` speed/RSS improvement with
|
||||
coverage intact, not runner tuning by guesswork.
|
||||
Use evidence first. The goal is real `pnpm test`, plugin-suite, and
|
||||
plugin-inspector speed/RSS improvement with coverage intact, not runner tuning by
|
||||
guesswork.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -21,6 +22,9 @@ coverage intact, not runner tuning by guesswork.
|
||||
2. Establish a baseline before changing code:
|
||||
- Prefer `pnpm test:perf:groups --full-suite --allow-failures --output <file>`
|
||||
for full-suite ranking.
|
||||
- For bundled plugin breadth, run the smallest relevant `pnpm
|
||||
test:extensions:batch <plugin[,plugin...]>` or plugin-inspector command
|
||||
before jumping to the full extension sweep.
|
||||
- For a scoped hotspot use:
|
||||
`/usr/bin/time -l pnpm test <file-or-files> --maxWorkers=1 --reporter=verbose`
|
||||
- For import-heavy suspicion add:
|
||||
@@ -33,6 +37,8 @@ coverage intact, not runner tuning by guesswork.
|
||||
passed, capture that as harness/noise and verify the suspect file directly.
|
||||
4. Pick the next attack by return and risk:
|
||||
- High return: one file/test dominates seconds or RSS and has a clear root.
|
||||
- High leverage: one plugin or SDK barrel causes every plugin-inspector or
|
||||
extension-batch run to load broad runtime.
|
||||
- Lower risk: static descriptors, target parsing, routing, auth bypass,
|
||||
setup hints, registry fixtures, or test server lifecycle.
|
||||
- Higher risk: real memory/runtime behavior, live providers, protocol
|
||||
@@ -44,6 +50,8 @@ coverage intact, not runner tuning by guesswork.
|
||||
and pure helpers over broad mocks.
|
||||
- Reuse suite-level servers/clients when a fresh handshake is irrelevant.
|
||||
- Keep schedulers/background loops off unless the test proves scheduling.
|
||||
- In plugin paths, move static metadata into manifest/lightweight artifacts
|
||||
and keep runtime plugin loads behind explicit execution boundaries.
|
||||
6. Preserve coverage shape:
|
||||
- Do not delete a slow integration proof unless the exact production
|
||||
composition is extracted into a named helper and tested.
|
||||
@@ -57,6 +65,90 @@ coverage intact, not runner tuning by guesswork.
|
||||
9. Commit with `scripts/committer "<message>" <paths...>` and push when the
|
||||
user asked for commits/pushes. Stage only files touched for this attack.
|
||||
|
||||
## Plugin-Suite Workflow
|
||||
|
||||
Use this section when perf work involves bundled plugins, plugin-inspector, SDK
|
||||
barrels, package-boundary tests, or extension suites.
|
||||
|
||||
1. Map the suite shape first:
|
||||
- source tests: `pnpm test extensions/<id>` or `pnpm test:extensions:batch <id>`
|
||||
- package boundaries: `pnpm run test:extensions:package-boundary:canary` and
|
||||
`pnpm run test:extensions:package-boundary:compile`
|
||||
- all bundled source tests: `pnpm test:extensions`
|
||||
- plugin import memory: `pnpm test:extensions:memory -- --json .artifacts/test-perf/extensions-memory.json`
|
||||
- plugin-inspector/report work: keep report primitives in `plugin-inspector`;
|
||||
keep wrappers thin and collect peak RSS when the command supports it.
|
||||
2. Start narrow, then widen:
|
||||
- one plugin changed: run that plugin's tests and plugin-inspector slice.
|
||||
- SDK/public barrel changed: add representative provider, channel, memory,
|
||||
and feature plugins.
|
||||
- loader/runtime mirror changed: add package-boundary checks and build/package
|
||||
proof as needed.
|
||||
- unknown shared plugin behavior: run `test:extensions:batch` groups before
|
||||
`pnpm test:extensions`.
|
||||
3. Treat plugin-inspector failures as product signals:
|
||||
- JSON must parse.
|
||||
- warnings/errors must be classified, not hidden.
|
||||
- runtime capture should be quiet and config-tolerant.
|
||||
- command output should include wall time, exit code, and peak RSS when
|
||||
available.
|
||||
4. For broad or package-heavy plugin proof, use Blacksmith Testbox by default on
|
||||
maintainer machines. Warm once and reuse the same box:
|
||||
- `blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90`
|
||||
- `blacksmith testbox run --id <ID> "OPENCLAW_TESTBOX=1 pnpm test:extensions:batch <ids>"`
|
||||
- stop the box when done.
|
||||
5. If plugin performance is package-artifact sensitive, switch to
|
||||
`openclaw-pre-release-plugin-testing` and Package Acceptance rather than
|
||||
trusting source-only timing.
|
||||
|
||||
## Metric Collection
|
||||
|
||||
Collect at least one stable metric before and after. Prefer the same machine and
|
||||
same command. For Testbox comparisons, use the same `tbx_...` id when possible.
|
||||
|
||||
| Metric | Use for | Preferred source |
|
||||
| --------------- | ---------------------------------- | --------------------------------------------------------------------------- |
|
||||
| wall time | user-visible suite cost | `/usr/bin/time -l`, test wrapper duration, Testbox run time |
|
||||
| Vitest duration | test body/import cost | Vitest output per file/shard |
|
||||
| import duration | broad barrel/runtime loads | `OPENCLAW_VITEST_IMPORT_DURATIONS=1` |
|
||||
| max RSS | memory pressure and OOM risk | `/usr/bin/time -l`, `pnpm test:extensions:memory`, wrapper memory summaries |
|
||||
| CPU/user/sys | CPU-bound vs wait-bound split | `/usr/bin/time -l` locally, Testbox job timing when local CPU is noisy |
|
||||
| heap snapshots | real leak vs retained module graph | `openclaw-test-heap-leaks` workflow |
|
||||
|
||||
Local scoped command with CPU/RSS:
|
||||
|
||||
```bash
|
||||
timeout 240 /usr/bin/time -l pnpm test <file> --maxWorkers=1 --reporter=verbose
|
||||
```
|
||||
|
||||
Plugin import memory profile:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
pnpm test:extensions:memory -- --top 20 --json .artifacts/test-perf/extensions-memory.json
|
||||
```
|
||||
|
||||
Targeted plugin import memory:
|
||||
|
||||
```bash
|
||||
pnpm test:extensions:memory -- --extension discord --extension telegram --skip-combined
|
||||
```
|
||||
|
||||
Heap/RSS escalation:
|
||||
|
||||
```bash
|
||||
OPENCLAW_TEST_MEMORY_TRACE=1 \
|
||||
OPENCLAW_TEST_HEAPSNAPSHOT_INTERVAL_MS=60000 \
|
||||
OPENCLAW_TEST_HEAPSNAPSHOT_DIR=.tmp/heapsnap \
|
||||
OPENCLAW_TEST_WORKERS=2 \
|
||||
OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB=6144 \
|
||||
pnpm test
|
||||
```
|
||||
|
||||
Use `openclaw-test-heap-leaks` when RSS keeps growing across intervals, workers
|
||||
OOM, or the suspect command has app-object retention. Do not call RSS growth a
|
||||
leak until snapshots or retainers support it.
|
||||
|
||||
## Common Root Causes
|
||||
|
||||
- Full bundled channel/plugin runtime loaded for static data.
|
||||
@@ -64,6 +156,12 @@ coverage intact, not runner tuning by guesswork.
|
||||
parser would suffice.
|
||||
- Broad `api.ts`, `runtime-api.ts`, `test-api.ts`, or plugin-sdk barrels pulled
|
||||
into hot tests.
|
||||
- SDK root aliases or package barrels pulling focused subpaths back into a broad
|
||||
plugin graph.
|
||||
- Plugin-inspector loading runtime code just to render metadata, reports, or CI
|
||||
policy scores.
|
||||
- Bundled plugin capture reusing real config/home state instead of synthetic,
|
||||
redacted, isolated state.
|
||||
- Partial-real mocks using `importActual()` around broad modules.
|
||||
- `vi.resetModules()` plus fresh imports in per-test loops.
|
||||
- Test plugin registry seeded in `beforeAll` while runtime state resets in
|
||||
@@ -72,6 +170,10 @@ coverage intact, not runner tuning by guesswork.
|
||||
- Runtime/default model/auth selection paid by idle snapshots or fixtures.
|
||||
- Plugin-owned media/action discovery triggered before checking whether args
|
||||
contain plugin-owned fields.
|
||||
- Timings missing from `test/fixtures/test-timings.unit.json`, causing hotspot
|
||||
files to stay in shared workers.
|
||||
- Parallel Vitest runs sharing `node_modules/.experimental-vitest-cache` without
|
||||
distinct `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH` values.
|
||||
|
||||
## Benchmark Commands
|
||||
|
||||
@@ -97,6 +199,25 @@ pnpm test:perf:groups --full-suite --allow-failures \
|
||||
--output .artifacts/test-perf/<name>.json
|
||||
```
|
||||
|
||||
Extension batch:
|
||||
|
||||
```bash
|
||||
pnpm test:extensions:batch <plugin[,plugin...]> -- --reporter=verbose
|
||||
```
|
||||
|
||||
All extension tests:
|
||||
|
||||
```bash
|
||||
pnpm test:extensions
|
||||
```
|
||||
|
||||
Package-boundary plugin checks:
|
||||
|
||||
```bash
|
||||
pnpm run test:extensions:package-boundary:canary
|
||||
pnpm run test:extensions:package-boundary:compile
|
||||
```
|
||||
|
||||
Reuse an existing Vitest JSON report:
|
||||
|
||||
```bash
|
||||
@@ -107,19 +228,26 @@ pnpm test:perf:groups --report <vitest-json> \
|
||||
## Verification
|
||||
|
||||
- Always run the targeted test surface that proves the change.
|
||||
- Run `pnpm check` before commit unless the change is docs-only and the hook
|
||||
handles it.
|
||||
- For source changes, run `pnpm check:changed` before push; in maintainer
|
||||
Testbox mode run it in the warmed Testbox.
|
||||
- For test-only changes, run `pnpm test:changed` or the exact edited tests.
|
||||
- Run `pnpm build` when touching lazy-loading, bundled artifacts, package
|
||||
boundaries, dynamic imports, build output, or public surfaces.
|
||||
- For plugin SDK/barrel/runtime changes, add `pnpm plugin-sdk:api:check` or
|
||||
`pnpm plugin-sdk:api:gen` when the API surface may drift.
|
||||
- For plugin-suite perf fixes, verify at least one representative plugin batch
|
||||
plus the changed gate; use Package Acceptance if the bug only exists in a
|
||||
packed artifact.
|
||||
- If deps are missing/stale, run `pnpm install` and retry the exact failed
|
||||
command once.
|
||||
- Use the report format:
|
||||
|
||||
```markdown
|
||||
| Metric | Before | After | Gain |
|
||||
| -------------- | -----: | ----: | ------------: |
|
||||
| File wall time | `Xs` | `Ys` | `-Zs` (`P%`) |
|
||||
| Max RSS | `XMB` | `YMB` | `-ZMB` (`P%`) |
|
||||
| Metric | Before | After | Gain |
|
||||
| -------------- | -----: | -----: | ------------: |
|
||||
| File wall time | `Xs` | `Ys` | `-Zs` (`P%`) |
|
||||
| Max RSS | `XMB` | `YMB` | `-ZMB` (`P%`) |
|
||||
| CPU user/sys | `X/Ys` | `A/Bs` | explain |
|
||||
```
|
||||
|
||||
## Handoff
|
||||
@@ -127,8 +255,12 @@ pnpm test:perf:groups --report <vitest-json> \
|
||||
Keep the final concise:
|
||||
|
||||
- Root cause.
|
||||
- Suite/plugin scope.
|
||||
- Files changed.
|
||||
- Before/after numbers.
|
||||
- Before/after wall, Vitest/import, CPU, and RSS numbers where available.
|
||||
- Leak classification if memory was involved: real leak, retained module graph,
|
||||
or inconclusive.
|
||||
- Coverage retained.
|
||||
- Verification commands.
|
||||
- Testbox ID or workflow URL for remote proof.
|
||||
- Commit hash and push status.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Test Performance"
|
||||
short_description: "Benchmark and fix slow OpenClaw tests"
|
||||
default_prompt: "Use $openclaw-test-performance to reassess the OpenClaw test benchmark, identify the next real hotspot, fix it without losing coverage, update the report, and commit scoped changes."
|
||||
short_description: "Benchmark tests, plugin suites, CPU, RSS, and heap growth"
|
||||
default_prompt: "Use $openclaw-test-performance to reassess OpenClaw test and plugin-suite performance, collect wall/import/CPU/RSS metrics, investigate memory growth when needed, fix the next real hotspot without losing coverage, update the report, and commit scoped changes."
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
|
||||
@@ -36,6 +36,14 @@ Prove the touched surface first. Do not reflexively run the whole suite.
|
||||
- Prefer GitHub Actions for release/Docker proof when the workflow already has the prepared image and secrets.
|
||||
- Use `scripts/committer "<msg>" <paths...>` when committing; stage only your files.
|
||||
- If deps are missing, run `pnpm install`, retry once, then report the first actionable error.
|
||||
- For Blacksmith Testbox proof, reuse only an id warmed and claimed in this
|
||||
operator session. `blacksmith testbox list` is diagnostics only; a listed id
|
||||
can have a local key and still carry stale rsync state from another lane.
|
||||
After warmup, run `pnpm testbox:claim --id <id>`, then prefer
|
||||
`pnpm testbox:run --id <id> -- "<command>"` for OpenClaw gates so stale
|
||||
org-visible ids fail fast before syncing. Claims older than 12 hours are
|
||||
stale unless `OPENCLAW_TESTBOX_CLAIM_TTL_MINUTES` is explicitly set for long
|
||||
work.
|
||||
|
||||
## Local Test Shortcuts
|
||||
|
||||
@@ -111,7 +119,10 @@ rerun after a focused patch.
|
||||
the manual "everything before release" umbrella. It resolves a target ref, then
|
||||
dispatches:
|
||||
|
||||
- manual `CI` for the full normal CI graph
|
||||
- manual `CI` for the full normal CI graph, with Android enabled via
|
||||
`include_android=true`
|
||||
- `Plugin Prerelease` for release-only plugin static checks, extension shards,
|
||||
the release-only `agentic-plugins` shard, and plugin product Docker lanes
|
||||
- `OpenClaw Release Checks` for install smoke, cross-OS release checks, live and
|
||||
E2E checks, Docker release-path suites, OpenWebUI, QA Lab, fast Matrix, and
|
||||
Telegram release lanes
|
||||
@@ -138,13 +149,19 @@ Use `release_profile=minimum|stable|full` to control live/provider breadth:
|
||||
`minimum` keeps the fastest OpenAI/core release-critical set, `stable` adds the
|
||||
stable provider/backend set, and `full` adds the broad advisory provider/media
|
||||
matrix. Do not make `full` faster by silently dropping suites; optimize setup,
|
||||
artifact reuse, and sharding instead. The parent verifier job appends
|
||||
slowest-job tables for child runs; rerun only that verifier after a child rerun
|
||||
turns green.
|
||||
artifact reuse, and sharding instead. The parent verifier job appends a child
|
||||
overview plus slowest-job tables for child runs; rerun only that verifier after
|
||||
a child rerun turns green.
|
||||
|
||||
Standalone manual `CI` dispatches do not run the plugin prerelease suite, the
|
||||
extension batch sweep, or the release-only `agentic-plugins` Vitest shard. Those
|
||||
lanes are intentionally reserved for the separate `Plugin Prerelease` child so
|
||||
PRs, main pushes, and ad hoc broad CI checks do not spend Docker/package time or
|
||||
all-plugin runtime time on release-only product coverage.
|
||||
|
||||
If a full run is already active on a newer `origin/main`, prefer watching that
|
||||
run over dispatching a duplicate. If you accidentally dispatch a stale duplicate,
|
||||
cancel it and monitor the current run.
|
||||
run over dispatching a duplicate. Do not cancel release, release-check, or child
|
||||
workflow runs unless Peter explicitly asks for cancellation.
|
||||
|
||||
The child-dispatch jobs record the child run ids. The final
|
||||
`Verify full validation` job re-queries those child runs and is the canonical
|
||||
@@ -153,9 +170,15 @@ only the failed parent verifier job; do not dispatch a new full umbrella unless
|
||||
the release evidence is stale.
|
||||
|
||||
For bounded recovery after a focused fix, pass `-f rerun_group=<group>`.
|
||||
Supported umbrella groups are `all`, `ci`, `release-checks`, `install-smoke`,
|
||||
`cross-os`, `live-e2e`, `package`, `qa`, `qa-parity`, `qa-live`, and
|
||||
`npm-telegram`. Use the narrowest group that covers the failed box.
|
||||
Supported umbrella groups are `all`, `ci`, `plugin-prerelease`,
|
||||
`release-checks`, `install-smoke`, `cross-os`, `live-e2e`, `package`, `qa`,
|
||||
`qa-parity`, `qa-live`, and `npm-telegram`. Use the narrowest group that covers
|
||||
the failed box. After a targeted release-check fix, do not restart the full
|
||||
umbrella by habit: dispatch the matching `rerun_group` and rerun only the parent
|
||||
verifier/evidence step after the child is green unless the release evidence is
|
||||
stale. For a single failed live/E2E shard, use
|
||||
`-f rerun_group=live-e2e -f live_suite_filter=<suite_id>` so the Blacksmith
|
||||
workflow only spends setup and queue time on that suite.
|
||||
|
||||
### Release Evidence
|
||||
|
||||
@@ -222,6 +245,20 @@ When `Full Release Validation` dispatches release checks, it passes the requeste
|
||||
branch/tag plus an `expected_sha` so branch/tag refs resolve through the fast
|
||||
remote-ref path while the package and QA jobs still validate the exact SHA.
|
||||
|
||||
The full install-smoke child is split on purpose: one job prepares or reuses the
|
||||
target-SHA GHCR root Dockerfile smoke image, QR package install runs in its own
|
||||
job, root Dockerfile/gateway smokes pull the prepared image, and installer/Bun
|
||||
smokes pull the same image while building only their small installer images.
|
||||
If install-smoke gets slow again, first check whether the root image was reused
|
||||
or rebuilt before adding/removing coverage.
|
||||
|
||||
The full-profile native live media shards use the prebuilt
|
||||
`ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04` container so
|
||||
`ffmpeg`/`ffprobe` are already present. If those jobs suddenly spend minutes in
|
||||
dependency setup again, first check the `Live Media Runner Image` workflow and
|
||||
the `Verify preinstalled live media dependencies` step before assuming the media
|
||||
tests themselves slowed down.
|
||||
|
||||
The release Docker path intentionally shards the plugin/runtime tail. The
|
||||
workflow uses `plugins-runtime-plugins`, `plugins-runtime-services`, and
|
||||
`plugins-runtime-install-a` through `plugins-runtime-install-d`; aggregate
|
||||
|
||||
41
.crabbox.yaml
Normal file
41
.crabbox.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
profile: openclaw-check
|
||||
provider: aws
|
||||
class: beast
|
||||
capacity:
|
||||
market: spot
|
||||
strategy: most-available
|
||||
fallback: on-demand-after-120s
|
||||
regions:
|
||||
- eu-west-1
|
||||
actions:
|
||||
workflow: .github/workflows/crabbox-hydrate.yml
|
||||
job: hydrate
|
||||
ref: main
|
||||
runnerLabels:
|
||||
- crabbox
|
||||
- openclaw
|
||||
runnerVersion: latest
|
||||
ephemeral: true
|
||||
aws:
|
||||
region: eu-west-1
|
||||
rootGB: 400
|
||||
sync:
|
||||
delete: true
|
||||
checksum: false
|
||||
gitSeed: true
|
||||
fingerprint: true
|
||||
baseRef: main
|
||||
exclude:
|
||||
- .artifacts
|
||||
- .codex
|
||||
- .DS_Store
|
||||
- playwright-report
|
||||
- test-results
|
||||
env:
|
||||
allow:
|
||||
- CI
|
||||
- NODE_OPTIONS
|
||||
- OPENCLAW_*
|
||||
ssh:
|
||||
user: crabbox
|
||||
port: "2222"
|
||||
85
.github/CODEOWNERS
vendored
85
.github/CODEOWNERS
vendored
@@ -2,50 +2,51 @@
|
||||
/.github/CODEOWNERS @steipete
|
||||
|
||||
# WARNING: GitHub CODEOWNERS uses last-match-wins semantics.
|
||||
# If you add overlapping rules below the secops block, include @openclaw/secops
|
||||
# If you add overlapping rules below the secops block, include @openclaw/openclaw-secops
|
||||
# on those entries too or you can silently remove required secops review.
|
||||
# Security-sensitive code, config, and docs require secops review.
|
||||
/SECURITY.md @openclaw/secops
|
||||
/.github/dependabot.yml @openclaw/secops
|
||||
/.github/codeql/ @openclaw/secops
|
||||
/.github/workflows/codeql.yml @openclaw/secops
|
||||
/.github/workflows/codeql-critical-quality.yml @openclaw/secops
|
||||
/src/security/ @openclaw/secops
|
||||
/src/secrets/ @openclaw/secops
|
||||
/src/config/*secret*.ts @openclaw/secops
|
||||
/src/config/**/*secret*.ts @openclaw/secops
|
||||
/src/gateway/*auth*.ts @openclaw/secops
|
||||
/src/gateway/**/*auth*.ts @openclaw/secops
|
||||
/src/gateway/*secret*.ts @openclaw/secops
|
||||
/src/gateway/**/*secret*.ts @openclaw/secops
|
||||
/src/gateway/security-path*.ts @openclaw/secops
|
||||
/src/gateway/resolve-configured-secret-input-string*.ts @openclaw/secops
|
||||
/src/gateway/protocol/**/*secret*.ts @openclaw/secops
|
||||
/src/gateway/server-methods/secrets*.ts @openclaw/secops
|
||||
/src/agents/*auth*.ts @openclaw/secops
|
||||
/src/agents/**/*auth*.ts @openclaw/secops
|
||||
/src/agents/auth-profiles*.ts @openclaw/secops
|
||||
/src/agents/auth-health*.ts @openclaw/secops
|
||||
/src/agents/auth-profiles/ @openclaw/secops
|
||||
/src/agents/sandbox.ts @openclaw/secops
|
||||
/src/agents/sandbox-*.ts @openclaw/secops
|
||||
/src/agents/sandbox/ @openclaw/secops
|
||||
/src/infra/secret-file*.ts @openclaw/secops
|
||||
/src/cron/stagger.ts @openclaw/secops
|
||||
/src/cron/service/jobs.ts @openclaw/secops
|
||||
/docs/security/ @openclaw/secops
|
||||
/docs/gateway/authentication.md @openclaw/secops
|
||||
/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md @openclaw/secops
|
||||
/docs/gateway/sandboxing.md @openclaw/secops
|
||||
/docs/gateway/secrets-plan-contract.md @openclaw/secops
|
||||
/docs/gateway/secrets.md @openclaw/secops
|
||||
/docs/gateway/security/ @openclaw/secops
|
||||
/docs/cli/approvals.md @openclaw/secops
|
||||
/docs/cli/sandbox.md @openclaw/secops
|
||||
/docs/cli/security.md @openclaw/secops
|
||||
/docs/cli/secrets.md @openclaw/secops
|
||||
/docs/reference/secretref-credential-surface.md @openclaw/secops
|
||||
/docs/reference/secretref-user-supplied-credentials-matrix.json @openclaw/secops
|
||||
/SECURITY.md @openclaw/openclaw-secops
|
||||
/.github/dependabot.yml @openclaw/openclaw-secops
|
||||
/.github/codeql/ @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql-android-critical-security.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql-critical-quality.yml @openclaw/openclaw-secops
|
||||
/src/security/ @openclaw/openclaw-secops
|
||||
/src/secrets/ @openclaw/openclaw-secops
|
||||
/src/config/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/config/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/**/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/security-path*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/resolve-configured-secret-input-string*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/protocol/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/server-methods/secrets*.ts @openclaw/openclaw-secops
|
||||
/src/agents/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/agents/**/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/agents/auth-profiles*.ts @openclaw/openclaw-secops
|
||||
/src/agents/auth-health*.ts @openclaw/openclaw-secops
|
||||
/src/agents/auth-profiles/ @openclaw/openclaw-secops
|
||||
/src/agents/sandbox.ts @openclaw/openclaw-secops
|
||||
/src/agents/sandbox-*.ts @openclaw/openclaw-secops
|
||||
/src/agents/sandbox/ @openclaw/openclaw-secops
|
||||
/src/infra/secret-file*.ts @openclaw/openclaw-secops
|
||||
/src/cron/stagger.ts @openclaw/openclaw-secops
|
||||
/src/cron/service/jobs.ts @openclaw/openclaw-secops
|
||||
/docs/security/ @openclaw/openclaw-secops
|
||||
/docs/gateway/authentication.md @openclaw/openclaw-secops
|
||||
/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md @openclaw/openclaw-secops
|
||||
/docs/gateway/sandboxing.md @openclaw/openclaw-secops
|
||||
/docs/gateway/secrets-plan-contract.md @openclaw/openclaw-secops
|
||||
/docs/gateway/secrets.md @openclaw/openclaw-secops
|
||||
/docs/gateway/security/ @openclaw/openclaw-secops
|
||||
/docs/cli/approvals.md @openclaw/openclaw-secops
|
||||
/docs/cli/sandbox.md @openclaw/openclaw-secops
|
||||
/docs/cli/security.md @openclaw/openclaw-secops
|
||||
/docs/cli/secrets.md @openclaw/openclaw-secops
|
||||
/docs/reference/secretref-credential-surface.md @openclaw/openclaw-secops
|
||||
/docs/reference/secretref-user-supplied-credentials-matrix.json @openclaw/openclaw-secops
|
||||
|
||||
# Release workflow and its supporting release-path checks.
|
||||
/.github/workflows/openclaw-npm-release.yml @openclaw/openclaw-release-managers
|
||||
|
||||
1
.github/actionlint.yaml
vendored
1
.github/actionlint.yaml
vendored
@@ -4,6 +4,7 @@
|
||||
self-hosted-runner:
|
||||
labels:
|
||||
# Blacksmith CI runners
|
||||
- blacksmith-4vcpu-ubuntu-2404
|
||||
- blacksmith-8vcpu-ubuntu-2404
|
||||
- blacksmith-8vcpu-windows-2025
|
||||
- blacksmith-16vcpu-ubuntu-2404
|
||||
|
||||
2
.github/actions/setup-node-env/action.yml
vendored
2
.github/actions/setup-node-env/action.yml
vendored
@@ -90,9 +90,11 @@ runs:
|
||||
|
||||
install_args=(
|
||||
install
|
||||
--prefer-offline
|
||||
--ignore-scripts=false
|
||||
--config.engine-strict=false
|
||||
--config.enable-pre-post-scripts=true
|
||||
--config.side-effects-cache=true
|
||||
)
|
||||
if [ -n "$LOCKFILE_FLAG" ]; then
|
||||
install_args+=("$LOCKFILE_FLAG")
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
name: openclaw-codeql-actions-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- .github/actions
|
||||
- .github/workflows
|
||||
|
||||
53
.github/codeql/codeql-agent-runtime-boundary-critical-quality.yml
vendored
Normal file
53
.github/codeql/codeql-agent-runtime-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: openclaw-codeql-agent-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/acp/control-plane
|
||||
- src/agents/command
|
||||
- src/agents/cli-runner
|
||||
- src/agents/pi-embedded-runner
|
||||
- src/agents/tools
|
||||
- src/agents/*completion*.ts
|
||||
- src/agents/*transport*.ts
|
||||
- src/agents/model-*.ts
|
||||
- src/agents/openclaw-tools*.ts
|
||||
- src/agents/provider-*.ts
|
||||
- src/agents/session*.ts
|
||||
- src/agents/tool-call*.ts
|
||||
- src/auto-reply/reply/agent-runner*.ts
|
||||
- src/auto-reply/reply/commands*.ts
|
||||
- src/auto-reply/reply/directive-handling*.ts
|
||||
- src/auto-reply/reply/dispatch-*.ts
|
||||
- src/auto-reply/reply/get-reply-run*.ts
|
||||
- src/auto-reply/reply/provider-dispatcher*.ts
|
||||
- src/auto-reply/reply/queue*.ts
|
||||
- src/auto-reply/reply/reply-run-registry*.ts
|
||||
- src/auto-reply/reply/session*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
56
.github/codeql/codeql-channel-runtime-boundary-critical-quality.yml
vendored
Normal file
56
.github/codeql/codeql-channel-runtime-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: openclaw-codeql-channel-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- extensions/bluebubbles/src
|
||||
- extensions/discord/src
|
||||
- extensions/feishu/src
|
||||
- extensions/googlechat/src
|
||||
- extensions/imessage/src
|
||||
- extensions/irc/src
|
||||
- extensions/line/src
|
||||
- extensions/matrix/src
|
||||
- extensions/mattermost/src
|
||||
- extensions/msteams/src
|
||||
- extensions/nextcloud-talk/src
|
||||
- extensions/nostr/src
|
||||
- extensions/qa-channel/src
|
||||
- extensions/qqbot/src
|
||||
- extensions/signal/src
|
||||
- extensions/slack/src
|
||||
- extensions/synology-chat/src
|
||||
- extensions/telegram/src
|
||||
- extensions/tlon/src
|
||||
- extensions/twitch/src
|
||||
- extensions/whatsapp/src
|
||||
- extensions/zalo/src
|
||||
- extensions/zalouser/src
|
||||
- src/channels
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
48
.github/codeql/codeql-channel-runtime-boundary-critical-security.yml
vendored
Normal file
48
.github/codeql/codeql-channel-runtime-boundary-critical-security.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: openclaw-codeql-channel-runtime-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/channels
|
||||
- src/config/channel-*.ts
|
||||
- src/config/types.channel*.ts
|
||||
- src/gateway/server-channel*.ts
|
||||
- src/gateway/server-methods/channels.ts
|
||||
- src/gateway/protocol/schema/channels.ts
|
||||
- src/infra/channel-*.ts
|
||||
- src/infra/exec-approval-channel-runtime.ts
|
||||
- src/infra/outbound/channel-*.ts
|
||||
- src/plugin-sdk/channel-*.ts
|
||||
- src/plugins/channel-*.ts
|
||||
- src/plugins/bundled-channel-*.ts
|
||||
- src/plugins/runtime/*channel*.ts
|
||||
- src/secrets/channel-*.ts
|
||||
- src/secrets/runtime-config-collectors-channels.ts
|
||||
- src/security/audit-channel*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
33
.github/codeql/codeql-config-boundary-critical-quality.yml
vendored
Normal file
33
.github/codeql/codeql-config-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: openclaw-codeql-config-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/config
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
@@ -1,4 +1,4 @@
|
||||
name: openclaw-codeql-javascript-typescript-critical-quality
|
||||
name: openclaw-codeql-core-auth-secrets-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
@@ -22,7 +22,6 @@ paths:
|
||||
- src/agents/sandbox
|
||||
- src/agents/sandbox.ts
|
||||
- src/agents/sandbox-*.ts
|
||||
- src/config
|
||||
- src/cron/service/jobs.ts
|
||||
- src/cron/stagger.ts
|
||||
- src/gateway/*auth*.ts
|
||||
@@ -1,4 +1,4 @@
|
||||
name: openclaw-codeql-javascript-typescript-critical-security
|
||||
name: openclaw-codeql-core-auth-secrets-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
@@ -10,10 +10,8 @@ query-filters:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
- exclude:
|
||||
problem.severity:
|
||||
- recommendation
|
||||
- warning
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/agents/*auth*.ts
|
||||
37
.github/codeql/codeql-gateway-runtime-boundary-critical-quality.yml
vendored
Normal file
37
.github/codeql/codeql-gateway-runtime-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: openclaw-codeql-gateway-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/gateway/method-scopes.ts
|
||||
- src/gateway/protocol
|
||||
- src/gateway/server-methods
|
||||
- src/gateway/server-methods.ts
|
||||
- src/gateway/server-methods-list.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
35
.github/codeql/codeql-mcp-process-runtime-boundary-critical-quality.yml
vendored
Normal file
35
.github/codeql/codeql-mcp-process-runtime-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: openclaw-codeql-mcp-process-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/mcp
|
||||
- src/process
|
||||
- src/infra/outbound
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
56
.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml
vendored
Normal file
56
.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: openclaw-codeql-mcp-process-tool-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/mcp
|
||||
- src/process
|
||||
- src/infra/outbound
|
||||
- src/agents/bash-tools.exec*.ts
|
||||
- src/agents/bash-tools.process*.ts
|
||||
- src/agents/exec-*.ts
|
||||
- src/agents/execution-contract.ts
|
||||
- src/agents/openclaw-plugin-tools.ts
|
||||
- src/agents/openclaw-tools.runtime.ts
|
||||
- src/agents/openclaw-tools.registration.ts
|
||||
- src/agents/pi-tool-definition-adapter.ts
|
||||
- src/agents/pi-tools.abort.ts
|
||||
- src/agents/pi-tools.before-tool-call*.ts
|
||||
- src/agents/pi-tools.host-edit.ts
|
||||
- src/agents/pi-tools-parameter-schema.ts
|
||||
- src/agents/pi-embedded-runner/effective-tool-policy.ts
|
||||
- src/agents/pi-embedded-runner/tool-name-allowlist.ts
|
||||
- src/agents/pi-embedded-runner/tool-schema-runtime.ts
|
||||
- src/agents/tools/gateway-tool.ts
|
||||
- src/agents/tools/message-tool.ts
|
||||
- src/agents/tools/sessions-send-tool.ts
|
||||
- src/agents/tools/sessions-spawn-tool.ts
|
||||
- src/agents/tools/subagents-tool.ts
|
||||
- src/agents/tools/tool-runtime.helpers.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
41
.github/codeql/codeql-memory-runtime-boundary-critical-quality.yml
vendored
Normal file
41
.github/codeql/codeql-memory-runtime-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: openclaw-codeql-memory-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- packages/memory-host-sdk/src
|
||||
- src/memory
|
||||
- src/memory-host-sdk
|
||||
- src/plugin-sdk/memory-*.ts
|
||||
- src/plugin-sdk/memory-core-host-*.ts
|
||||
- src/plugins/memory-*.ts
|
||||
- src/gateway/server-startup-memory.ts
|
||||
- src/commands/doctor-memory-search.ts
|
||||
- src/commands/doctor-cron-dreaming-payload-migration.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
41
.github/codeql/codeql-network-ssrf-boundary-critical-security.yml
vendored
Normal file
41
.github/codeql/codeql-network-ssrf-boundary-critical-security.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: openclaw-codeql-network-ssrf-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/infra/net
|
||||
- src/shared/net
|
||||
- src/agents/tools/web-fetch.ts
|
||||
- src/agents/tools/web-guarded-fetch.ts
|
||||
- src/agents/tools/web-shared.ts
|
||||
- src/plugin-sdk/ssrf-policy.ts
|
||||
- src/web-fetch
|
||||
- src/web/provider-runtime-shared.ts
|
||||
- packages/memory-host-sdk/src/host/ssrf-policy.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
76
.github/codeql/codeql-plugin-boundary-critical-quality.yml
vendored
Normal file
76
.github/codeql/codeql-plugin-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: openclaw-codeql-plugin-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/plugins/activation-planner.ts
|
||||
- src/plugins/api-builder.ts
|
||||
- src/plugins/bundled-compat.ts
|
||||
- src/plugins/bundled-dir.ts
|
||||
- src/plugins/bundled-plugin-metadata.ts
|
||||
- src/plugins/bundled-public-surface-runtime-root.ts
|
||||
- src/plugins/bundled-runtime-deps.ts
|
||||
- src/plugins/bundled-runtime-root.ts
|
||||
- src/plugins/captured-registration.ts
|
||||
- src/plugins/config-activation-shared.ts
|
||||
- src/plugins/config-contracts.ts
|
||||
- src/plugins/config-normalization-shared.ts
|
||||
- src/plugins/config-policy.ts
|
||||
- src/plugins/config-schema.ts
|
||||
- src/plugins/config-state.ts
|
||||
- src/plugins/discovery.ts
|
||||
- src/plugins/effective-plugin-ids.ts
|
||||
- src/plugins/externalized-bundled-plugins.ts
|
||||
- src/plugins/installed-plugin-index*.ts
|
||||
- src/plugins/loader*.ts
|
||||
- src/plugins/manifest*.ts
|
||||
- src/plugins/module-export.ts
|
||||
- src/plugins/package-entrypoints.ts
|
||||
- src/plugins/plugin-registry*.ts
|
||||
- src/plugins/provider-contract-public-artifacts.ts
|
||||
- src/plugins/provider-public-artifacts.ts
|
||||
- src/plugins/public-surface*.ts
|
||||
- src/plugins/registry.ts
|
||||
- src/plugins/registry-types.ts
|
||||
- src/plugins/runtime
|
||||
- src/plugins/runtime-state.ts
|
||||
- src/plugins/runtime.ts
|
||||
- src/plugins/sdk-alias.ts
|
||||
- src/plugins/source-loader.ts
|
||||
- src/plugins/types.ts
|
||||
- src/plugins/validation-diagnostics.ts
|
||||
- src/plugins/web-provider-public-artifacts*.ts
|
||||
- src/plugin-sdk/*entry*.ts
|
||||
- src/plugin-sdk/*facade*.ts
|
||||
- src/plugin-sdk/api-baseline.ts
|
||||
- src/plugin-sdk/config-schema.ts
|
||||
- src/plugin-sdk/config-types.ts
|
||||
- src/plugin-sdk/core.ts
|
||||
- src/plugin-sdk/extension-shared.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
36
.github/codeql/codeql-plugin-sdk-package-contract-critical-quality.yml
vendored
Normal file
36
.github/codeql/codeql-plugin-sdk-package-contract-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: openclaw-codeql-plugin-sdk-package-contract-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- packages/plugin-sdk/src
|
||||
- packages/plugin-package-contract/src
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.spec.ts"
|
||||
- "**/*.spec.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
44
.github/codeql/codeql-plugin-sdk-reply-runtime-critical-quality.yml
vendored
Normal file
44
.github/codeql/codeql-plugin-sdk-reply-runtime-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: openclaw-codeql-plugin-sdk-reply-runtime-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/plugin-sdk/inbound-envelope.ts
|
||||
- src/plugin-sdk/inbound-reply-dispatch.ts
|
||||
- src/plugin-sdk/reply-*.ts
|
||||
- src/plugin-sdk/channel-reply-*.ts
|
||||
- src/plugin-sdk/delivery-queue-runtime.ts
|
||||
- src/plugin-sdk/outbound-runtime.ts
|
||||
- src/plugin-sdk/outbound-send-deps.ts
|
||||
- src/plugin-sdk/model-session-runtime.ts
|
||||
- src/plugin-sdk/session-*.ts
|
||||
- src/plugin-sdk/thread-bindings-runtime.ts
|
||||
- src/plugin-sdk/thread-bindings-session-runtime.ts
|
||||
- src/plugin-sdk/conversation-binding-runtime.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
87
.github/codeql/codeql-plugin-trust-boundary-critical-security.yml
vendored
Normal file
87
.github/codeql/codeql-plugin-trust-boundary-critical-security.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: openclaw-codeql-plugin-trust-boundary-critical-security
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-extended
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
precision:
|
||||
- high
|
||||
- very-high
|
||||
tags contain: security
|
||||
security-severity: /([7-9]|10)\.(\d)+/
|
||||
|
||||
paths:
|
||||
- src/cli/plugin-install-config-policy.ts
|
||||
- src/cli/plugin-registry-loader.ts
|
||||
- src/cli/plugins-command-helpers.ts
|
||||
- src/cli/plugins-install-command.ts
|
||||
- src/cli/plugins-install-record-commit.ts
|
||||
- src/plugins/activation-planner.ts
|
||||
- src/plugins/bundle-manifest.ts
|
||||
- src/plugins/bundled-compat.ts
|
||||
- src/plugins/bundled-dir.ts
|
||||
- src/plugins/bundled-plugin-metadata.ts
|
||||
- src/plugins/bundled-plugin-scan.ts
|
||||
- src/plugins/bundled-runtime-deps*.ts
|
||||
- src/plugins/bundled-runtime-root.ts
|
||||
- src/plugins/cli-registry-loader.ts
|
||||
- src/plugins/config-activation-shared.ts
|
||||
- src/plugins/config-contracts.ts
|
||||
- src/plugins/config-policy.ts
|
||||
- src/plugins/config-schema.ts
|
||||
- src/plugins/dependency-denylist.ts
|
||||
- src/plugins/discovery.ts
|
||||
- src/plugins/effective-plugin-ids.ts
|
||||
- src/plugins/externalized-bundled-plugins.ts
|
||||
- src/plugins/install.runtime.ts
|
||||
- src/plugins/install-source-info.ts
|
||||
- src/plugins/installed-plugin-index*.ts
|
||||
- src/plugins/loader*.ts
|
||||
- src/plugins/manifest*.ts
|
||||
- src/plugins/marketplace.ts
|
||||
- src/plugins/module-export.ts
|
||||
- src/plugins/package-entrypoints.ts
|
||||
- src/plugins/plugin-config-trust.ts
|
||||
- src/plugins/plugin-origin.types.ts
|
||||
- src/plugins/plugin-registry*.ts
|
||||
- src/plugins/public-surface*.ts
|
||||
- src/plugins/registry*.ts
|
||||
- src/plugins/runtime
|
||||
- src/plugins/runtime-state.ts
|
||||
- src/plugins/runtime.ts
|
||||
- src/plugins/source-loader.ts
|
||||
- src/plugins/update.ts
|
||||
- src/plugins/validation-diagnostics.ts
|
||||
- src/plugin-sdk/*entry*.ts
|
||||
- src/plugin-sdk/*facade*.ts
|
||||
- src/plugin-sdk/api-baseline.ts
|
||||
- src/plugin-sdk/config-schema.ts
|
||||
- src/plugin-sdk/config-types.ts
|
||||
- src/plugin-sdk/core.ts
|
||||
- src/plugin-sdk/extension-shared.ts
|
||||
- packages/plugin-package-contract/src
|
||||
- packages/plugin-sdk/src/plugin-entry.ts
|
||||
- packages/plugin-sdk/src/plugin-runtime.ts
|
||||
- packages/plugin-sdk/src/runtime-env.ts
|
||||
- packages/plugin-sdk/src/security-runtime.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.spec.ts"
|
||||
- "**/*.spec.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
44
.github/codeql/codeql-provider-runtime-boundary-critical-quality.yml
vendored
Normal file
44
.github/codeql/codeql-provider-runtime-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: openclaw-codeql-provider-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/model-catalog
|
||||
- src/plugins/provider-*.ts
|
||||
- src/plugins/providers*.ts
|
||||
- src/plugins/*provider*.ts
|
||||
- src/plugins/capability-provider-runtime.ts
|
||||
- src/plugins/compaction-provider.ts
|
||||
- src/plugins/memory-embedding-provider*.ts
|
||||
- src/plugins/memory-embedding-providers*.ts
|
||||
- src/plugins/migration-provider-runtime.ts
|
||||
- src/plugins/synthetic-auth.runtime.ts
|
||||
- src/plugins/web-fetch-providers*.ts
|
||||
- src/plugins/web-search-providers*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
48
.github/codeql/codeql-session-diagnostics-boundary-critical-quality.yml
vendored
Normal file
48
.github/codeql/codeql-session-diagnostics-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: openclaw-codeql-session-diagnostics-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/auto-reply/reply/queue
|
||||
- src/auto-reply/reply/post-compaction-context.ts
|
||||
- src/auto-reply/reply/startup-context.ts
|
||||
- src/infra/diagnostic-*.ts
|
||||
- src/infra/diagnostics-timeline.ts
|
||||
- src/infra/session-delivery-queue*.ts
|
||||
- src/infra/outbound/base-session-key.ts
|
||||
- src/infra/outbound/delivery-queue*.ts
|
||||
- src/infra/outbound/outbound-session.ts
|
||||
- src/infra/outbound/session-binding*.ts
|
||||
- src/infra/outbound/session-context.ts
|
||||
- src/infra/outbound/targets-session.ts
|
||||
- src/logging/diagnostic*.ts
|
||||
- src/commands/doctor-session-*.ts
|
||||
- src/commands/session-store-targets.ts
|
||||
- src/commands/sessions*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
36
.github/codeql/codeql-ui-control-plane-critical-quality.yml
vendored
Normal file
36
.github/codeql/codeql-ui-control-plane-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: openclaw-codeql-ui-control-plane-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- ui/src/main.ts
|
||||
- ui/src/local-storage.ts
|
||||
- ui/src/ui
|
||||
- src/tasks/task-registry-control*.ts
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
39
.github/codeql/codeql-web-media-runtime-boundary-critical-quality.yml
vendored
Normal file
39
.github/codeql/codeql-web-media-runtime-boundary-critical-quality.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: openclaw-codeql-web-media-runtime-boundary-critical-quality
|
||||
|
||||
disable-default-queries: true
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
|
||||
query-filters:
|
||||
- include:
|
||||
problem.severity:
|
||||
- error
|
||||
- exclude:
|
||||
tags:
|
||||
- security
|
||||
|
||||
paths:
|
||||
- src/web-fetch
|
||||
- src/web-search
|
||||
- src/web/provider-runtime-shared.ts
|
||||
- src/media
|
||||
- src/media-understanding
|
||||
- src/image-generation
|
||||
- src/media-generation
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
- "**/coverage"
|
||||
- "**/*.generated.ts"
|
||||
- "**/*.bundle.js"
|
||||
- "**/*-runtime.js"
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/*.e2e.test.ts"
|
||||
- "**/*.e2e.test.tsx"
|
||||
- "**/*test-support*"
|
||||
- "**/*test-helper*"
|
||||
- "**/*mock*"
|
||||
- "**/*fixture*"
|
||||
- "**/*bench*"
|
||||
16
.github/images/live-media-runner/Dockerfile
vendored
Normal file
16
.github/images/live-media-runner/Dockerfile
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
ffmpeg \
|
||||
git \
|
||||
openssh-client \
|
||||
unzip \
|
||||
xz-utils \
|
||||
zstd \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
9
.github/labeler.yml
vendored
9
.github/labeler.yml
vendored
@@ -9,6 +9,12 @@
|
||||
- "extensions/azure-speech/**"
|
||||
- "docs/providers/azure-speech.md"
|
||||
- "docs/tools/tts.md"
|
||||
"plugin: file-transfer":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/file-transfer/**"
|
||||
- "docs/nodes/index.md"
|
||||
- "docs/plugins/sdk-runtime.md"
|
||||
"channel: discord":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
@@ -238,8 +244,11 @@
|
||||
"security":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- ".github/workflows/opengrep-*.yml"
|
||||
- ".semgrepignore"
|
||||
- "docs/cli/security.md"
|
||||
- "docs/gateway/security.md"
|
||||
- "security/**"
|
||||
|
||||
"extensions: copilot-proxy":
|
||||
- changed-files:
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
timeout-minutes: 35
|
||||
steps:
|
||||
- name: Begin Testbox
|
||||
uses: useblacksmith/begin-testbox@v2
|
||||
uses: useblacksmith/begin-testbox@d0e04585c26905fdd92c94a09c159544c7ee1b67
|
||||
with:
|
||||
testbox_id: ${{ inputs.testbox_id }}
|
||||
|
||||
@@ -218,7 +218,7 @@ jobs:
|
||||
run: bash scripts/ci-hydrate-testbox-env.sh
|
||||
|
||||
- name: Run Testbox
|
||||
uses: useblacksmith/run-testbox@v2
|
||||
uses: useblacksmith/run-testbox@5ca05834db1d3813554d1dd109e5f2087a8d7cbc
|
||||
if: always()
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
4
.github/workflows/ci-check-testbox.yml
vendored
4
.github/workflows/ci-check-testbox.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Begin Testbox
|
||||
uses: useblacksmith/begin-testbox@v2
|
||||
uses: useblacksmith/begin-testbox@d0e04585c26905fdd92c94a09c159544c7ee1b67
|
||||
with:
|
||||
testbox_id: ${{ inputs.testbox_id }}
|
||||
- name: Checkout
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
run: bash scripts/ci-hydrate-testbox-env.sh
|
||||
|
||||
- name: Run Testbox
|
||||
uses: useblacksmith/run-testbox@v2
|
||||
uses: useblacksmith/run-testbox@5ca05834db1d3813554d1dd109e5f2087a8d7cbc
|
||||
if: always()
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
340
.github/workflows/ci.yml
vendored
340
.github/workflows/ci.yml
vendored
@@ -8,6 +8,11 @@ on:
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
include_android:
|
||||
description: Run Android lanes for this manual CI dispatch.
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
@@ -36,7 +41,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
checkout_sha: ${{ steps.checkout_ref.outputs.sha }}
|
||||
checkout_revision: ${{ steps.checkout_ref.outputs.sha }}
|
||||
docs_only: ${{ steps.manifest.outputs.docs_only }}
|
||||
docs_changed: ${{ steps.manifest.outputs.docs_changed }}
|
||||
run_node: ${{ steps.manifest.outputs.run_node }}
|
||||
@@ -49,8 +54,9 @@ jobs:
|
||||
run_checks_fast_core: ${{ steps.manifest.outputs.run_checks_fast_core }}
|
||||
run_checks_fast: ${{ steps.manifest.outputs.run_checks_fast }}
|
||||
checks_fast_core_matrix: ${{ steps.manifest.outputs.checks_fast_core_matrix }}
|
||||
run_plugin_contracts_shards: ${{ steps.manifest.outputs.run_plugin_contracts_shards }}
|
||||
plugin_contracts_matrix: ${{ steps.manifest.outputs.plugin_contracts_matrix }}
|
||||
channel_contracts_matrix: ${{ steps.manifest.outputs.channel_contracts_matrix }}
|
||||
checks_node_extensions_matrix: ${{ steps.manifest.outputs.checks_node_extensions_matrix }}
|
||||
run_checks: ${{ steps.manifest.outputs.run_checks }}
|
||||
checks_matrix: ${{ steps.manifest.outputs.checks_matrix }}
|
||||
run_checks_node_core_nondist: ${{ steps.manifest.outputs.run_checks_node_core_nondist }}
|
||||
@@ -117,13 +123,14 @@ jobs:
|
||||
OPENCLAW_CI_DOCS_CHANGED: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.docs_scope.outputs.docs_changed }}
|
||||
OPENCLAW_CI_RUN_NODE: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_node || 'false' }}
|
||||
OPENCLAW_CI_RUN_MACOS: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_macos || 'false' }}
|
||||
OPENCLAW_CI_RUN_ANDROID: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_android || 'false' }}
|
||||
OPENCLAW_CI_RUN_ANDROID: ${{ github.event_name == 'workflow_dispatch' && inputs.include_android && 'true' || steps.changed_scope.outputs.run_android || 'false' }}
|
||||
OPENCLAW_CI_RUN_WINDOWS: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_windows || 'false' }}
|
||||
OPENCLAW_CI_RUN_NODE_FAST_ONLY: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_scope.outputs.run_node_fast_only || 'false' }}
|
||||
OPENCLAW_CI_RUN_NODE_FAST_PLUGIN_CONTRACTS: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_scope.outputs.run_node_fast_plugin_contracts || 'false' }}
|
||||
OPENCLAW_CI_RUN_NODE_FAST_CI_ROUTING: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_scope.outputs.run_node_fast_ci_routing || 'false' }}
|
||||
OPENCLAW_CI_RUN_SKILLS_PYTHON: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_skills_python || 'false' }}
|
||||
OPENCLAW_CI_RUN_CONTROL_UI_I18N: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_control_ui_i18n || 'false' }}
|
||||
OPENCLAW_CI_CHECKOUT_REVISION: ${{ steps.checkout_ref.outputs.sha }}
|
||||
OPENCLAW_CI_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
node --input-type=module <<'EOF'
|
||||
@@ -134,10 +141,6 @@ jobs:
|
||||
import {
|
||||
createChannelContractTestShards,
|
||||
} from "./scripts/lib/channel-contract-test-plan.mjs";
|
||||
import {
|
||||
createExtensionTestShards,
|
||||
DEFAULT_EXTENSION_TEST_SHARD_COUNT,
|
||||
} from "./scripts/lib/extension-test-plan.mjs";
|
||||
|
||||
const parseBoolean = (value, fallback = false) => {
|
||||
if (value === undefined) return fallback;
|
||||
@@ -147,6 +150,24 @@ jobs:
|
||||
return fallback;
|
||||
};
|
||||
|
||||
const { createPluginContractTestShards } = await import(
|
||||
"./scripts/lib/plugin-contract-test-plan.mjs"
|
||||
).catch((error) => {
|
||||
if (error?.code !== "ERR_MODULE_NOT_FOUND") {
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
createPluginContractTestShards: () => [
|
||||
{
|
||||
checkName: "checks-fast-contracts-plugins-legacy",
|
||||
includePatterns: ["src/plugins/contracts/**/*.test.ts"],
|
||||
runtime: "node",
|
||||
task: "contracts-plugins",
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
const createMatrix = (include) => ({ include });
|
||||
const outputPath = process.env.GITHUB_OUTPUT;
|
||||
const isCanonicalRepository = process.env.OPENCLAW_CI_REPOSITORY === "openclaw/openclaw";
|
||||
@@ -160,7 +181,7 @@ jobs:
|
||||
runNode && parseBoolean(process.env.OPENCLAW_CI_RUN_NODE_FAST_PLUGIN_CONTRACTS);
|
||||
const runNodeFastCiRouting =
|
||||
runNode && parseBoolean(process.env.OPENCLAW_CI_RUN_NODE_FAST_CI_ROUTING);
|
||||
const runChecksFastCore = runNodeFull || runNodeFastPluginContracts || runNodeFastCiRouting;
|
||||
const runPluginContractShards = runNodeFull || runNodeFastPluginContracts;
|
||||
const runMacos =
|
||||
parseBoolean(process.env.OPENCLAW_CI_RUN_MACOS) && !docsOnly && isCanonicalRepository;
|
||||
const runAndroid =
|
||||
@@ -173,44 +194,13 @@ jobs:
|
||||
const runSkillsPython = parseBoolean(process.env.OPENCLAW_CI_RUN_SKILLS_PYTHON) && !docsOnly;
|
||||
const runControlUiI18n =
|
||||
parseBoolean(process.env.OPENCLAW_CI_RUN_CONTROL_UI_I18N) && !docsOnly;
|
||||
const extensionTestShardCount = isCanonicalRepository
|
||||
? DEFAULT_EXTENSION_TEST_SHARD_COUNT
|
||||
: Math.max(DEFAULT_EXTENSION_TEST_SHARD_COUNT, 36);
|
||||
const extensionShardMatrix = createMatrix(
|
||||
runNodeFull
|
||||
? createExtensionTestShards({
|
||||
shardCount: extensionTestShardCount,
|
||||
}).map((shard) => ({
|
||||
check_name: shard.checkName,
|
||||
extensions_csv: shard.extensionIds.join(","),
|
||||
runner: isCanonicalRepository && [0, 3, 4].includes(shard.index)
|
||||
? "blacksmith-8vcpu-ubuntu-2404"
|
||||
: isCanonicalRepository
|
||||
? "blacksmith-4vcpu-ubuntu-2404"
|
||||
: "ubuntu-24.04",
|
||||
shard_index: shard.index + 1,
|
||||
task: "extensions-batch",
|
||||
}))
|
||||
: [],
|
||||
);
|
||||
const checksFastCoreTasks = [];
|
||||
if (runNodeFull) {
|
||||
checksFastCoreTasks.push(
|
||||
{ check_name: "checks-fast-bundled", runtime: "node", task: "bundled" },
|
||||
{
|
||||
check_name: "checks-fast-contracts-plugins",
|
||||
runtime: "node",
|
||||
task: "contracts-plugins",
|
||||
},
|
||||
);
|
||||
} else {
|
||||
if (runNodeFastPluginContracts) {
|
||||
checksFastCoreTasks.push({
|
||||
check_name: "checks-fast-contracts-plugins",
|
||||
runtime: "node",
|
||||
task: runNodeFastCiRouting ? "contracts-plugins-ci-routing" : "contracts-plugins",
|
||||
});
|
||||
} else if (runNodeFastCiRouting) {
|
||||
if (runNodeFastCiRouting) {
|
||||
checksFastCoreTasks.push({
|
||||
check_name: "checks-fast-ci-routing",
|
||||
runtime: "node",
|
||||
@@ -220,7 +210,9 @@ jobs:
|
||||
}
|
||||
|
||||
const nodeTestShards = runNodeFull
|
||||
? createNodeTestShards().map((shard) => ({
|
||||
? createNodeTestShards({
|
||||
includeReleaseOnlyPluginShards: false,
|
||||
}).map((shard) => ({
|
||||
check_name: shard.checkName,
|
||||
runtime: "node",
|
||||
task: "test-shard",
|
||||
@@ -243,13 +235,16 @@ jobs:
|
||||
run_skills_python: runSkillsPython,
|
||||
run_windows: runWindows,
|
||||
run_build_artifacts: runNodeFull,
|
||||
run_checks_fast_core: runChecksFastCore,
|
||||
run_checks_fast_core: checksFastCoreTasks.length > 0,
|
||||
run_checks_fast: runNodeFull,
|
||||
checks_fast_core_matrix: createMatrix(checksFastCoreTasks),
|
||||
run_plugin_contracts_shards: runPluginContractShards,
|
||||
plugin_contracts_matrix: createMatrix(
|
||||
runPluginContractShards ? createPluginContractTestShards() : [],
|
||||
),
|
||||
channel_contracts_matrix: createMatrix(
|
||||
runNodeFull ? createChannelContractTestShards() : [],
|
||||
),
|
||||
checks_node_extensions_matrix: extensionShardMatrix,
|
||||
run_checks: runNodeFull,
|
||||
checks_matrix: createMatrix(
|
||||
runNodeFull
|
||||
@@ -468,7 +463,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -540,7 +535,7 @@ jobs:
|
||||
path: |
|
||||
dist/
|
||||
dist-runtime/
|
||||
key: ${{ runner.os }}-dist-build-${{ needs.preflight.outputs.checkout_sha }}
|
||||
key: ${{ runner.os }}-dist-build-${{ needs.preflight.outputs.checkout_revision }}
|
||||
|
||||
- name: Pack built runtime artifacts
|
||||
run: tar --posix -cf dist-runtime-build.tar.zst --use-compress-program zstdmt dist dist-runtime
|
||||
@@ -669,7 +664,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -748,6 +743,112 @@ jobs:
|
||||
;;
|
||||
esac
|
||||
|
||||
checks-fast-plugin-contracts-shard:
|
||||
permissions:
|
||||
contents: read
|
||||
name: ${{ matrix.checkName }}
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_plugin_contracts_shards == 'true'
|
||||
runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04' }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.plugin_contracts_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
workdir="$GITHUB_WORKSPACE"
|
||||
auth_header="$(printf 'x-access-token:%s' "$CHECKOUT_TOKEN" | base64 | tr -d '\n')"
|
||||
|
||||
reset_checkout_dir() {
|
||||
mkdir -p "$workdir"
|
||||
find "$workdir" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
|
||||
}
|
||||
|
||||
checkout_attempt() {
|
||||
local attempt="$1"
|
||||
|
||||
reset_checkout_dir
|
||||
git init "$workdir" >/dev/null
|
||||
git config --global --add safe.directory "$workdir"
|
||||
git -C "$workdir" remote add origin "https://github.com/${CHECKOUT_REPO}"
|
||||
git -C "$workdir" config gc.auto 0
|
||||
|
||||
timeout --signal=TERM 30s git -C "$workdir" \
|
||||
-c protocol.version=2 \
|
||||
-c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${auth_header}" \
|
||||
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
|
||||
"+${CHECKOUT_SHA}:refs/remotes/origin/ci-target" || return 1
|
||||
|
||||
git -C "$workdir" checkout --force --detach "$CHECKOUT_SHA" || return 1
|
||||
test -f "$workdir/.github/actions/setup-node-env/action.yml" || return 1
|
||||
echo "checkout attempt ${attempt}/5 succeeded"
|
||||
}
|
||||
|
||||
for attempt in 1 2 3 4 5; do
|
||||
if checkout_attempt "$attempt"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "checkout attempt ${attempt}/5 failed"
|
||||
sleep $((attempt * 5))
|
||||
done
|
||||
|
||||
echo "checkout failed after 5 attempts" >&2
|
||||
exit 1
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Run plugin contract shard
|
||||
env:
|
||||
OPENCLAW_CONTRACT_INCLUDE_PATTERNS_JSON: ${{ toJson(matrix.includePatterns) }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
include_file="$RUNNER_TEMP/plugin-contract-include.json"
|
||||
INCLUDE_FILE="$include_file" node --input-type=module <<'EOF'
|
||||
import { writeFileSync } from "node:fs";
|
||||
|
||||
const includePatterns = JSON.parse(process.env.OPENCLAW_CONTRACT_INCLUDE_PATTERNS_JSON ?? "[]");
|
||||
if (!Array.isArray(includePatterns) || includePatterns.length === 0) {
|
||||
console.error("Missing plugin contract include patterns");
|
||||
process.exit(1);
|
||||
}
|
||||
writeFileSync(process.env.INCLUDE_FILE, JSON.stringify(includePatterns), "utf8");
|
||||
EOF
|
||||
OPENCLAW_VITEST_INCLUDE_FILE="$include_file" pnpm test:contracts:plugins
|
||||
|
||||
checks-fast-plugin-contracts:
|
||||
permissions:
|
||||
contents: read
|
||||
name: checks-fast-contracts-plugins
|
||||
needs: [preflight, checks-fast-plugin-contracts-shard]
|
||||
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_plugin_contracts_shards == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Verify plugin contract shards
|
||||
env:
|
||||
SHARD_RESULT: ${{ needs.checks-fast-plugin-contracts-shard.result }}
|
||||
run: |
|
||||
if [ "$SHARD_RESULT" = "cancelled" ]; then
|
||||
echo "Plugin contract shards were cancelled, usually because a newer commit superseded this run." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$SHARD_RESULT" != "success" ]; then
|
||||
echo "Plugin contract shards failed: $SHARD_RESULT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
checks-fast-channel-contracts-shard:
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -764,7 +865,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -867,7 +968,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -919,97 +1020,6 @@ jobs:
|
||||
- name: Run protocol check
|
||||
run: pnpm protocol:check
|
||||
|
||||
checks-node-extensions-shard:
|
||||
permissions:
|
||||
contents: read
|
||||
name: ${{ matrix.check_name }}
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_checks_fast == 'true'
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.checks_node_extensions_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
workdir="$GITHUB_WORKSPACE"
|
||||
auth_header="$(printf 'x-access-token:%s' "$CHECKOUT_TOKEN" | base64 | tr -d '\n')"
|
||||
|
||||
reset_checkout_dir() {
|
||||
mkdir -p "$workdir"
|
||||
find "$workdir" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
|
||||
}
|
||||
|
||||
checkout_attempt() {
|
||||
local attempt="$1"
|
||||
|
||||
reset_checkout_dir
|
||||
git init "$workdir" >/dev/null
|
||||
git config --global --add safe.directory "$workdir"
|
||||
git -C "$workdir" remote add origin "https://github.com/${CHECKOUT_REPO}"
|
||||
git -C "$workdir" config gc.auto 0
|
||||
|
||||
timeout --signal=TERM 30s git -C "$workdir" \
|
||||
-c protocol.version=2 \
|
||||
-c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${auth_header}" \
|
||||
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
|
||||
"+${CHECKOUT_SHA}:refs/remotes/origin/ci-target" || return 1
|
||||
|
||||
git -C "$workdir" checkout --force --detach "$CHECKOUT_SHA" || return 1
|
||||
test -f "$workdir/.github/actions/setup-node-env/action.yml" || return 1
|
||||
echo "checkout attempt ${attempt}/5 succeeded"
|
||||
}
|
||||
|
||||
for attempt in 1 2 3 4 5; do
|
||||
if checkout_attempt "$attempt"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "checkout attempt ${attempt}/5 failed"
|
||||
sleep $((attempt * 5))
|
||||
done
|
||||
|
||||
echo "checkout failed after 5 attempts" >&2
|
||||
exit 1
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Run extension shard
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
OPENCLAW_EXTENSION_BATCH_PARALLEL: 2
|
||||
OPENCLAW_VITEST_MAX_WORKERS: 1
|
||||
OPENCLAW_EXTENSION_BATCH: ${{ matrix.extensions_csv }}
|
||||
run: pnpm test:extensions:batch -- "$OPENCLAW_EXTENSION_BATCH"
|
||||
|
||||
checks-node-extensions:
|
||||
permissions:
|
||||
contents: read
|
||||
name: checks-node-extensions
|
||||
needs: [preflight, checks-node-extensions-shard]
|
||||
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_checks_fast == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Verify extension shards
|
||||
env:
|
||||
SHARD_RESULT: ${{ needs.checks-node-extensions-shard.result }}
|
||||
run: |
|
||||
if [ "$SHARD_RESULT" != "success" ]; then
|
||||
echo "Extension shard checks failed: $SHARD_RESULT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
checks:
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -1055,7 +1065,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -1135,7 +1145,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -1308,6 +1318,9 @@ jobs:
|
||||
- check_name: check-lint
|
||||
task: lint
|
||||
runner: blacksmith-16vcpu-ubuntu-2404
|
||||
- check_name: check-dependencies
|
||||
task: dependencies
|
||||
runner: ubuntu-24.04
|
||||
- check_name: check-policy-guards
|
||||
task: policy-guards
|
||||
runner: ubuntu-24.04
|
||||
@@ -1322,7 +1335,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -1383,6 +1396,7 @@ jobs:
|
||||
pnpm check:no-conflict-markers
|
||||
pnpm tool-display:check
|
||||
pnpm check:host-env-policy:swift
|
||||
pnpm dup:check:coverage
|
||||
;;
|
||||
prod-types)
|
||||
pnpm tsgo:prod
|
||||
@@ -1390,6 +1404,14 @@ jobs:
|
||||
lint)
|
||||
pnpm lint --threads=8
|
||||
;;
|
||||
dependencies)
|
||||
if pnpm run --silent 2>/dev/null | grep -q '^ deadcode:dependencies$'; then
|
||||
pnpm deadcode:dependencies
|
||||
pnpm deadcode:unused-files
|
||||
else
|
||||
pnpm deadcode:ci
|
||||
fi
|
||||
;;
|
||||
policy-guards)
|
||||
pnpm lint:webhook:no-low-level-body-read
|
||||
pnpm lint:auth:no-pairing-store-group
|
||||
@@ -1454,7 +1476,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -1652,7 +1674,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -1715,7 +1737,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
@@ -1758,7 +1780,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
@@ -1863,7 +1885,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
@@ -1904,7 +1926,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
@@ -2005,7 +2027,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
CHECKOUT_REPO: ${{ github.repository }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
|
||||
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -2061,6 +2083,14 @@ jobs:
|
||||
apps/android/**/gradle-wrapper.properties
|
||||
apps/android/gradle/libs.versions.toml
|
||||
|
||||
- name: Cache Android SDK
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.android-sdk
|
||||
key: ${{ runner.os }}-android-sdk-v1-cmdline-12266719-platform-36-build-tools-36.0.0
|
||||
restore-keys: |
|
||||
${{ runner.os }}-android-sdk-v1-
|
||||
|
||||
- name: Setup Android SDK cmdline-tools
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -2069,11 +2099,13 @@ jobs:
|
||||
ARCHIVE="commandlinetools-linux-${CMDLINE_TOOLS_VERSION}_latest.zip"
|
||||
URL="https://dl.google.com/android/repository/${ARCHIVE}"
|
||||
|
||||
mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools"
|
||||
curl -fsSL "$URL" -o "/tmp/${ARCHIVE}"
|
||||
rm -rf "$ANDROID_SDK_ROOT/cmdline-tools/latest"
|
||||
unzip -q "/tmp/${ARCHIVE}" -d "$ANDROID_SDK_ROOT/cmdline-tools"
|
||||
mv "$ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools" "$ANDROID_SDK_ROOT/cmdline-tools/latest"
|
||||
if [ ! -x "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" ]; then
|
||||
mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools"
|
||||
curl -fsSL "$URL" -o "/tmp/${ARCHIVE}"
|
||||
rm -rf "$ANDROID_SDK_ROOT/cmdline-tools/latest"
|
||||
unzip -q "/tmp/${ARCHIVE}" -d "$ANDROID_SDK_ROOT/cmdline-tools"
|
||||
mv "$ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools" "$ANDROID_SDK_ROOT/cmdline-tools/latest"
|
||||
fi
|
||||
|
||||
echo "ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT" >> "$GITHUB_ENV"
|
||||
echo "ANDROID_HOME=$ANDROID_SDK_ROOT" >> "$GITHUB_ENV"
|
||||
|
||||
131
.github/workflows/clawsweeper-dispatch.yml
vendored
131
.github/workflows/clawsweeper-dispatch.yml
vendored
@@ -3,44 +3,78 @@ name: ClawSweeper Dispatch
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened, edited, labeled, unlabeled]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] maintainer-owned external dispatch; no checkout or untrusted PR code execution
|
||||
types: [opened, reopened, synchronize, ready_for_review, edited, labeled, unlabeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: clawsweeper-dispatch-${{ github.repository }}-${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: ${{ github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review' }}
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'issue_comment' || !(endsWith(github.actor, '[bot]') && (github.event.action == 'labeled' || github.event.action == 'unlabeled')) }}
|
||||
env:
|
||||
HAS_CLAWSWEEPER_APP_PRIVATE_KEY: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY != '' }}
|
||||
CLAWSWEEPER_APP_CLIENT_ID: Iv23liOECG0slfuhz093
|
||||
SUPERSEDES_IN_PROGRESS: ${{ (github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review') && 'true' || 'false' }}
|
||||
steps:
|
||||
- name: Debounce bursty metadata events
|
||||
if: ${{ github.event.action == 'labeled' || github.event.action == 'unlabeled' }}
|
||||
run: sleep 20
|
||||
|
||||
- name: Create ClawSweeper dispatch token
|
||||
id: token
|
||||
if: ${{ env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
|
||||
uses: actions/create-github-app-token@v2
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
with:
|
||||
app-id: 3306130
|
||||
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
|
||||
owner: openclaw
|
||||
repositories: clawsweeper
|
||||
permission-contents: write
|
||||
|
||||
- name: Create target comment token
|
||||
id: target_token
|
||||
if: ${{ github.event_name == 'issue_comment' && env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
with:
|
||||
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: ${{ github.event.repository.name }}
|
||||
permission-issues: write
|
||||
permission-pull-requests: read
|
||||
|
||||
- name: Dispatch exact ClawSweeper review
|
||||
if: ${{ github.event_name != 'push' && github.event_name != 'issue_comment' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.token.outputs.token || secrets.OPENCLAW_GH_TOKEN }}
|
||||
GH_TOKEN: ${{ steps.token.outputs.token }}
|
||||
TARGET_REPO: ${{ github.repository }}
|
||||
ITEM_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||
ITEM_KIND: ${{ github.event_name == 'pull_request_target' && 'pull_request' || 'issue' }}
|
||||
SOURCE_EVENT: ${{ github.event_name }}
|
||||
SOURCE_ACTION: ${{ github.event.action }}
|
||||
run: |
|
||||
if [ -z "$GH_TOKEN" ]; then
|
||||
echo "::notice::Skipping ClawSweeper dispatch because no dispatch credential is configured."
|
||||
echo "::notice::Skipping ClawSweeper dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
|
||||
exit 0
|
||||
fi
|
||||
payload="$(jq -nc \
|
||||
--arg target_repo "$TARGET_REPO" \
|
||||
--argjson item_number "$ITEM_NUMBER" \
|
||||
--arg item_kind "$ITEM_KIND" \
|
||||
'{event_type:"clawsweeper_item",client_payload:{target_repo:$target_repo,item_number:$item_number,item_kind:$item_kind}}')"
|
||||
--arg source_event "$SOURCE_EVENT" \
|
||||
--arg source_action "$SOURCE_ACTION" \
|
||||
--argjson supersedes_in_progress "$SUPERSEDES_IN_PROGRESS" \
|
||||
'{event_type:"clawsweeper_item",client_payload:{target_repo:$target_repo,item_number:$item_number,item_kind:$item_kind,source_event:$source_event,source_action:$source_action,supersedes_in_progress:$supersedes_in_progress}}')"
|
||||
if gh api repos/openclaw/clawsweeper/dispatches \
|
||||
--method POST \
|
||||
--input - <<< "$payload"; then
|
||||
@@ -48,3 +82,90 @@ jobs:
|
||||
else
|
||||
echo "::warning::Skipping ClawSweeper dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
||||
fi
|
||||
|
||||
- name: Acknowledge and dispatch ClawSweeper comment
|
||||
if: ${{ github.event_name == 'issue_comment' }}
|
||||
env:
|
||||
DISPATCH_TOKEN: ${{ steps.token.outputs.token }}
|
||||
TARGET_TOKEN: ${{ steps.target_token.outputs.token }}
|
||||
TARGET_REPO: ${{ github.repository }}
|
||||
ITEM_NUMBER: ${{ github.event.issue.number }}
|
||||
COMMENT_ID: ${{ github.event.comment.id }}
|
||||
COMMENT_BODY: ${{ github.event.comment.body }}
|
||||
SOURCE_ACTION: ${{ github.event.action }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$DISPATCH_TOKEN" ]; then
|
||||
echo "::notice::Skipping ClawSweeper comment dispatch because no ClawSweeper app token is configured."
|
||||
exit 0
|
||||
fi
|
||||
body_file="$RUNNER_TEMP/clawsweeper-comment-body.txt"
|
||||
printf '%s\n' "$COMMENT_BODY" > "$body_file"
|
||||
if ! grep -Eiq '(^|[[:space:]])@(clawsweeper|openclaw-clawsweeper)\b(\[bot\])?|(^|[[:space:]])/(clawsweeper|review|automerge|autoclose)\b' "$body_file"; then
|
||||
echo "No ClawSweeper command found in comment."
|
||||
exit 0
|
||||
fi
|
||||
if [ -n "$TARGET_TOKEN" ]; then
|
||||
err="$(mktemp)"
|
||||
if GH_TOKEN="$TARGET_TOKEN" gh api -X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"repos/$TARGET_REPO/issues/comments/$COMMENT_ID/reactions" \
|
||||
-f content="eyes" 2>"$err" >/dev/null; then
|
||||
echo "Acknowledged ClawSweeper command comment."
|
||||
elif grep -qi "HTTP 422\\|already exists" "$err"; then
|
||||
echo "ClawSweeper command comment already acknowledged."
|
||||
else
|
||||
cat "$err" >&2
|
||||
echo "::warning::Could not acknowledge ClawSweeper command comment."
|
||||
fi
|
||||
rm -f "$err"
|
||||
else
|
||||
echo "::notice::Skipping ClawSweeper comment acknowledgement because no target token is configured."
|
||||
fi
|
||||
payload="$(jq -nc \
|
||||
--arg target_repo "$TARGET_REPO" \
|
||||
--argjson item_number "$ITEM_NUMBER" \
|
||||
--argjson comment_id "$COMMENT_ID" \
|
||||
--arg source_event "issue_comment" \
|
||||
--arg source_action "$SOURCE_ACTION" \
|
||||
'{event_type:"clawsweeper_comment",client_payload:{target_repo:$target_repo,item_number:$item_number,comment_id:$comment_id,source_event:$source_event,source_action:$source_action}}')"
|
||||
if GH_TOKEN="$DISPATCH_TOKEN" gh api repos/openclaw/clawsweeper/dispatches \
|
||||
--method POST \
|
||||
--input - <<< "$payload"; then
|
||||
echo "Dispatched ClawSweeper comment router."
|
||||
else
|
||||
echo "::warning::Skipping ClawSweeper comment dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
||||
fi
|
||||
|
||||
- name: Dispatch ClawSweeper commit review
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.event.deleted != true }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.token.outputs.token }}
|
||||
TARGET_REPO: ${{ github.repository }}
|
||||
BEFORE_SHA: ${{ github.event.before }}
|
||||
AFTER_SHA: ${{ github.sha }}
|
||||
SOURCE_REF: ${{ github.ref }}
|
||||
CREATE_CHECKS: ${{ vars.CLAWSWEEPER_COMMIT_REVIEW_CREATE_CHECKS || 'false' }}
|
||||
run: |
|
||||
if [ -z "$GH_TOKEN" ]; then
|
||||
echo "::notice::Skipping ClawSweeper commit dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
|
||||
exit 0
|
||||
fi
|
||||
case "$CREATE_CHECKS" in
|
||||
true|TRUE|1|yes|YES|on|ON) create_checks=true ;;
|
||||
*) create_checks=false ;;
|
||||
esac
|
||||
payload="$(jq -nc \
|
||||
--arg target_repo "$TARGET_REPO" \
|
||||
--arg before_sha "$BEFORE_SHA" \
|
||||
--arg after_sha "$AFTER_SHA" \
|
||||
--arg ref "$SOURCE_REF" \
|
||||
--argjson create_checks "$create_checks" \
|
||||
'{event_type:"clawsweeper_commit_review",client_payload:{target_repo:$target_repo,before_sha:$before_sha,after_sha:$after_sha,ref:$ref,enabled:true,create_checks:$create_checks}}')"
|
||||
if gh api repos/openclaw/clawsweeper/dispatches \
|
||||
--method POST \
|
||||
--input - <<< "$payload"; then
|
||||
echo "Dispatched ClawSweeper commit review."
|
||||
else
|
||||
echo "::warning::Skipping ClawSweeper commit dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
||||
fi
|
||||
|
||||
51
.github/workflows/codeql-android-critical-security.yml
vendored
Normal file
51
.github/workflows/codeql-android-critical-security.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: CodeQL Android Critical Security
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 7 * * *"
|
||||
|
||||
concurrency:
|
||||
group: codeql-android-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
android:
|
||||
name: Critical Security (android)
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "21"
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: java-kotlin
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-android-critical-security.yml
|
||||
|
||||
- name: Build Android for CodeQL
|
||||
working-directory: apps/android
|
||||
run: ./gradlew --no-daemon :app:assemblePlayDebug
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-security/android"
|
||||
593
.github/workflows/codeql-critical-quality.yml
vendored
593
.github/workflows/codeql-critical-quality.yml
vendored
@@ -2,12 +2,135 @@ name: CodeQL Critical Quality
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
profile:
|
||||
description: CodeQL quality profile to run
|
||||
required: false
|
||||
default: all
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- agent-runtime-boundary
|
||||
- config-boundary
|
||||
- core-auth-secrets
|
||||
- channel-runtime-boundary
|
||||
- gateway-runtime-boundary
|
||||
- memory-runtime-boundary
|
||||
- mcp-process-runtime-boundary
|
||||
- plugin-boundary
|
||||
- plugin-sdk-package-contract
|
||||
- plugin-sdk-reply-runtime
|
||||
- provider-runtime-boundary
|
||||
- session-diagnostics-boundary
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- ".github/codeql/**"
|
||||
- ".github/workflows/codeql-critical-quality.yml"
|
||||
- "packages/plugin-package-contract/**"
|
||||
- "packages/plugin-sdk/**"
|
||||
- "packages/memory-host-sdk/**"
|
||||
- "src/config/**"
|
||||
- "extensions/bluebubbles/src/**"
|
||||
- "extensions/discord/src/**"
|
||||
- "extensions/feishu/src/**"
|
||||
- "extensions/googlechat/src/**"
|
||||
- "extensions/imessage/src/**"
|
||||
- "extensions/irc/src/**"
|
||||
- "extensions/line/src/**"
|
||||
- "extensions/matrix/src/**"
|
||||
- "extensions/mattermost/src/**"
|
||||
- "extensions/msteams/src/**"
|
||||
- "extensions/nextcloud-talk/src/**"
|
||||
- "extensions/nostr/src/**"
|
||||
- "extensions/qa-channel/src/**"
|
||||
- "extensions/qqbot/src/**"
|
||||
- "extensions/signal/src/**"
|
||||
- "extensions/slack/src/**"
|
||||
- "extensions/synology-chat/src/**"
|
||||
- "extensions/telegram/src/**"
|
||||
- "extensions/tlon/src/**"
|
||||
- "extensions/twitch/src/**"
|
||||
- "extensions/whatsapp/src/**"
|
||||
- "extensions/zalo/src/**"
|
||||
- "extensions/zalouser/src/**"
|
||||
- "src/agents/*auth*.ts"
|
||||
- "src/agents/**/*auth*.ts"
|
||||
- "src/agents/auth-health*.ts"
|
||||
- "src/agents/auth-profiles"
|
||||
- "src/agents/auth-profiles/**"
|
||||
- "src/agents/bash-tools.exec-host-shared.ts"
|
||||
- "src/agents/sandbox"
|
||||
- "src/agents/sandbox/**"
|
||||
- "src/agents/sandbox.ts"
|
||||
- "src/agents/sandbox-*.ts"
|
||||
- "src/acp/control-plane/**"
|
||||
- "src/agents/cli-runner/**"
|
||||
- "src/agents/command/**"
|
||||
- "src/agents/pi-embedded-runner/**"
|
||||
- "src/agents/tools/**"
|
||||
- "src/agents/*completion*.ts"
|
||||
- "src/agents/*transport*.ts"
|
||||
- "src/agents/model-*.ts"
|
||||
- "src/agents/openclaw-tools*.ts"
|
||||
- "src/agents/provider-*.ts"
|
||||
- "src/agents/session*.ts"
|
||||
- "src/agents/tool-call*.ts"
|
||||
- "src/auto-reply/reply/agent-runner*.ts"
|
||||
- "src/auto-reply/reply/commands*.ts"
|
||||
- "src/auto-reply/reply/directive-handling*.ts"
|
||||
- "src/auto-reply/reply/dispatch-*.ts"
|
||||
- "src/auto-reply/reply/get-reply-run*.ts"
|
||||
- "src/auto-reply/reply/provider-dispatcher*.ts"
|
||||
- "src/auto-reply/reply/queue*.ts"
|
||||
- "src/auto-reply/reply/reply-run-registry*.ts"
|
||||
- "src/auto-reply/reply/session*.ts"
|
||||
- "src/channels/**"
|
||||
- "src/auto-reply/reply/post-compaction-context.ts"
|
||||
- "src/auto-reply/reply/queue/**"
|
||||
- "src/auto-reply/reply/startup-context.ts"
|
||||
- "src/commands/doctor-cron-dreaming-payload-migration.ts"
|
||||
- "src/commands/doctor-memory-search.ts"
|
||||
- "src/commands/doctor-session-*.ts"
|
||||
- "src/commands/session-store-targets.ts"
|
||||
- "src/commands/sessions*.ts"
|
||||
- "src/cron/service/jobs.ts"
|
||||
- "src/cron/stagger.ts"
|
||||
- "src/gateway/*auth*.ts"
|
||||
- "src/gateway/**/*auth*.ts"
|
||||
- "src/gateway/*secret*.ts"
|
||||
- "src/gateway/**/*secret*.ts"
|
||||
- "src/gateway/protocol/**/*secret*.ts"
|
||||
- "src/gateway/resolve-configured-secret-input-string*.ts"
|
||||
- "src/gateway/security-path*.ts"
|
||||
- "src/gateway/server-methods/secrets*.ts"
|
||||
- "src/gateway/server-startup-memory.ts"
|
||||
- "src/gateway/method-scopes.ts"
|
||||
- "src/gateway/protocol/**"
|
||||
- "src/gateway/server-methods/**"
|
||||
- "src/gateway/server-methods.ts"
|
||||
- "src/gateway/server-methods-list.ts"
|
||||
- "src/infra/diagnostic-*.ts"
|
||||
- "src/infra/diagnostics-timeline.ts"
|
||||
- "src/infra/outbound/**"
|
||||
- "src/infra/secret-file*.ts"
|
||||
- "src/infra/session-delivery-queue*.ts"
|
||||
- "src/logging/diagnostic*.ts"
|
||||
- "src/memory/**"
|
||||
- "src/memory-host-sdk/**"
|
||||
- "src/mcp/**"
|
||||
- "src/model-catalog/**"
|
||||
- "src/plugin-sdk/**"
|
||||
- "src/plugins/**"
|
||||
- "src/process/**"
|
||||
- "src/secrets/**"
|
||||
- "src/security/**"
|
||||
schedule:
|
||||
- cron: "30 6 * * *"
|
||||
|
||||
concurrency:
|
||||
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -15,12 +138,171 @@ env:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
javascript-typescript:
|
||||
name: Critical Quality (javascript-typescript)
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
quality-shards:
|
||||
name: Select Critical Quality shards
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
agent: ${{ steps.detect.outputs.agent }}
|
||||
channel: ${{ steps.detect.outputs.channel }}
|
||||
config: ${{ steps.detect.outputs.config }}
|
||||
core_auth_secrets: ${{ steps.detect.outputs.core_auth_secrets }}
|
||||
gateway: ${{ steps.detect.outputs.gateway }}
|
||||
memory: ${{ steps.detect.outputs.memory }}
|
||||
mcp_process: ${{ steps.detect.outputs.mcp_process }}
|
||||
plugin: ${{ steps.detect.outputs.plugin }}
|
||||
plugin_sdk_package: ${{ steps.detect.outputs.plugin_sdk_package }}
|
||||
plugin_sdk_reply: ${{ steps.detect.outputs.plugin_sdk_reply }}
|
||||
provider: ${{ steps.detect.outputs.provider }}
|
||||
session_diagnostics: ${{ steps.detect.outputs.session_diagnostics }}
|
||||
steps:
|
||||
- name: Detect PR shard paths
|
||||
id: detect
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
agent=false
|
||||
channel=false
|
||||
config=false
|
||||
core_auth_secrets=false
|
||||
gateway=false
|
||||
memory=false
|
||||
mcp_process=false
|
||||
plugin=false
|
||||
plugin_sdk_package=false
|
||||
plugin_sdk_reply=false
|
||||
provider=false
|
||||
session_diagnostics=false
|
||||
|
||||
if [[ "${EVENT_NAME}" != "pull_request" ]]; then
|
||||
agent=true
|
||||
channel=true
|
||||
config=true
|
||||
core_auth_secrets=true
|
||||
gateway=true
|
||||
memory=true
|
||||
mcp_process=true
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
plugin_sdk_reply=true
|
||||
provider=true
|
||||
session_diagnostics=true
|
||||
else
|
||||
while IFS= read -r file; do
|
||||
case "${file}" in
|
||||
.github/codeql/*|.github/workflows/codeql-critical-quality.yml)
|
||||
agent=true
|
||||
channel=true
|
||||
config=true
|
||||
core_auth_secrets=true
|
||||
gateway=true
|
||||
memory=true
|
||||
mcp_process=true
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
plugin_sdk_reply=true
|
||||
provider=true
|
||||
session_diagnostics=true
|
||||
;;
|
||||
src/acp/control-plane/*|src/agents/cli-runner/*|src/agents/command/*|src/agents/pi-embedded-runner/*|src/agents/tools/*|src/agents/*completion*.ts|src/agents/*transport*.ts|src/agents/model-*.ts|src/agents/openclaw-tools*.ts|src/agents/provider-*.ts|src/agents/session*.ts|src/agents/tool-call*.ts|src/auto-reply/reply/agent-runner*.ts|src/auto-reply/reply/commands*.ts|src/auto-reply/reply/directive-handling*.ts|src/auto-reply/reply/dispatch-*.ts|src/auto-reply/reply/get-reply-run*.ts|src/auto-reply/reply/provider-dispatcher*.ts|src/auto-reply/reply/queue*.ts|src/auto-reply/reply/reply-run-registry*.ts|src/auto-reply/reply/session*.ts)
|
||||
agent=true
|
||||
;;
|
||||
src/auto-reply/reply/post-compaction-context.ts|src/auto-reply/reply/queue/*|src/auto-reply/reply/startup-context.ts|src/commands/doctor-session-*.ts|src/commands/session-store-targets.ts|src/commands/sessions*.ts|src/infra/diagnostic-*.ts|src/infra/diagnostics-timeline.ts|src/infra/session-delivery-queue*.ts|src/logging/diagnostic*.ts)
|
||||
session_diagnostics=true
|
||||
;;
|
||||
extensions/bluebubbles/src/*|extensions/discord/src/*|extensions/feishu/src/*|extensions/googlechat/src/*|extensions/imessage/src/*|extensions/irc/src/*|extensions/line/src/*|extensions/matrix/src/*|extensions/mattermost/src/*|extensions/msteams/src/*|extensions/nextcloud-talk/src/*|extensions/nostr/src/*|extensions/qa-channel/src/*|extensions/qqbot/src/*|extensions/signal/src/*|extensions/slack/src/*|extensions/synology-chat/src/*|extensions/telegram/src/*|extensions/tlon/src/*|extensions/twitch/src/*|extensions/whatsapp/src/*|extensions/zalo/src/*|extensions/zalouser/src/*|src/channels/*)
|
||||
channel=true
|
||||
;;
|
||||
src/config/*)
|
||||
config=true
|
||||
;;
|
||||
src/gateway/protocol/*secret*.ts|src/gateway/server-methods/secrets*.ts)
|
||||
core_auth_secrets=true
|
||||
gateway=true
|
||||
;;
|
||||
src/agents/*auth*.ts|src/agents/auth-health*.ts|src/agents/auth-profiles|src/agents/auth-profiles/*|src/agents/bash-tools.exec-host-shared.ts|src/agents/sandbox|src/agents/sandbox.ts|src/agents/sandbox-*.ts|src/agents/sandbox/*|src/cron/service/jobs.ts|src/cron/stagger.ts|src/gateway/*auth*.ts|src/gateway/*secret*.ts|src/gateway/resolve-configured-secret-input-string*.ts|src/gateway/security-path*.ts|src/infra/secret-file*.ts|src/secrets/*|src/security/*)
|
||||
core_auth_secrets=true
|
||||
;;
|
||||
src/gateway/method-scopes.ts|src/gateway/protocol/*|src/gateway/server-methods/*|src/gateway/server-methods.ts|src/gateway/server-methods-list.ts)
|
||||
gateway=true
|
||||
;;
|
||||
packages/memory-host-sdk/*|src/commands/doctor-cron-dreaming-payload-migration.ts|src/commands/doctor-memory-search.ts|src/gateway/server-startup-memory.ts|src/memory/*|src/memory-host-sdk/*)
|
||||
memory=true
|
||||
;;
|
||||
src/infra/outbound/base-session-key.ts|src/infra/outbound/delivery-queue*.ts|src/infra/outbound/outbound-session.ts|src/infra/outbound/session-binding*.ts|src/infra/outbound/session-context.ts|src/infra/outbound/targets-session.ts)
|
||||
mcp_process=true
|
||||
session_diagnostics=true
|
||||
;;
|
||||
src/infra/outbound/*|src/mcp/*|src/process/*)
|
||||
mcp_process=true
|
||||
;;
|
||||
src/plugin-sdk/inbound-envelope.ts|src/plugin-sdk/inbound-reply-dispatch.ts|src/plugin-sdk/reply-*.ts|src/plugin-sdk/channel-reply-*.ts|src/plugin-sdk/delivery-queue-runtime.ts|src/plugin-sdk/outbound-runtime.ts|src/plugin-sdk/outbound-send-deps.ts|src/plugin-sdk/model-session-runtime.ts|src/plugin-sdk/session-*.ts|src/plugin-sdk/thread-bindings-runtime.ts|src/plugin-sdk/thread-bindings-session-runtime.ts|src/plugin-sdk/conversation-binding-runtime.ts)
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
plugin_sdk_reply=true
|
||||
;;
|
||||
src/plugin-sdk/memory-*.ts|src/plugin-sdk/memory-core-host-*.ts)
|
||||
memory=true
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
;;
|
||||
src/plugin-sdk/*)
|
||||
plugin=true
|
||||
plugin_sdk_package=true
|
||||
;;
|
||||
src/plugins/provider-contract-public-artifacts.ts|src/plugins/provider-public-artifacts.ts|src/plugins/web-provider-public-artifacts*.ts)
|
||||
plugin=true
|
||||
provider=true
|
||||
;;
|
||||
src/plugins/memory-embedding-provider*.ts|src/plugins/memory-embedding-providers*.ts)
|
||||
memory=true
|
||||
provider=true
|
||||
;;
|
||||
src/plugins/memory-*.ts)
|
||||
memory=true
|
||||
;;
|
||||
src/model-catalog/*|src/plugins/*provider*.ts|src/plugins/capability-provider-runtime.ts|src/plugins/compaction-provider.ts|src/plugins/memory-embedding-provider*.ts|src/plugins/memory-embedding-providers*.ts|src/plugins/migration-provider-runtime.ts|src/plugins/synthetic-auth.runtime.ts|src/plugins/web-fetch-providers*.ts|src/plugins/web-search-providers*.ts)
|
||||
provider=true
|
||||
;;
|
||||
src/plugins/activation-planner.ts|src/plugins/api-builder.ts|src/plugins/bundled-*.ts|src/plugins/captured-registration.ts|src/plugins/config-*.ts|src/plugins/discovery.ts|src/plugins/effective-plugin-ids.ts|src/plugins/externalized-bundled-plugins.ts|src/plugins/installed-plugin-index*.ts|src/plugins/loader*.ts|src/plugins/manifest*.ts|src/plugins/module-export.ts|src/plugins/package-entrypoints.ts|src/plugins/plugin-registry*.ts|src/plugins/public-surface*.ts|src/plugins/registry.ts|src/plugins/registry-types.ts|src/plugins/runtime|src/plugins/runtime/*|src/plugins/runtime-state.ts|src/plugins/runtime.ts|src/plugins/sdk-alias.ts|src/plugins/source-loader.ts|src/plugins/types.ts|src/plugins/validation-diagnostics.ts)
|
||||
plugin=true
|
||||
;;
|
||||
packages/plugin-package-contract/*|packages/plugin-sdk/*)
|
||||
plugin_sdk_package=true
|
||||
;;
|
||||
esac
|
||||
done < <(gh api --paginate "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --jq '.[].filename')
|
||||
fi
|
||||
|
||||
{
|
||||
echo "agent=${agent}"
|
||||
echo "channel=${channel}"
|
||||
echo "config=${config}"
|
||||
echo "core_auth_secrets=${core_auth_secrets}"
|
||||
echo "gateway=${gateway}"
|
||||
echo "memory=${memory}"
|
||||
echo "mcp_process=${mcp_process}"
|
||||
echo "plugin=${plugin}"
|
||||
echo "plugin_sdk_package=${plugin_sdk_package}"
|
||||
echo "plugin_sdk_reply=${plugin_sdk_reply}"
|
||||
echo "provider=${provider}"
|
||||
echo "session_diagnostics=${session_diagnostics}"
|
||||
} >> "${GITHUB_OUTPUT}"
|
||||
|
||||
core-auth-secrets:
|
||||
name: Critical Quality (core-auth-secrets)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.core_auth_secrets == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'core-auth-secrets') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -32,9 +314,306 @@ jobs:
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-javascript-typescript-critical-quality.yml
|
||||
config-file: ./.github/codeql/codeql-core-auth-secrets-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/javascript-typescript"
|
||||
category: "/codeql-critical-quality/core-auth-secrets"
|
||||
|
||||
config-boundary:
|
||||
name: Critical Quality (config-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.config == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'config-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-config-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/config-boundary"
|
||||
|
||||
gateway-runtime-boundary:
|
||||
name: Critical Quality (gateway-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.gateway == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'gateway-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-gateway-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/gateway-runtime-boundary"
|
||||
|
||||
channel-runtime-boundary:
|
||||
name: Critical Quality (channel-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.channel == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'channel-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-channel-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/channel-runtime-boundary"
|
||||
|
||||
agent-runtime-boundary:
|
||||
name: Critical Quality (agent-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.agent == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'agent-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-agent-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/agent-runtime-boundary"
|
||||
|
||||
mcp-process-runtime-boundary:
|
||||
name: Critical Quality (mcp-process-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.mcp_process == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'mcp-process-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-mcp-process-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/mcp-process-runtime-boundary"
|
||||
|
||||
memory-runtime-boundary:
|
||||
name: Critical Quality (memory-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.memory == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'memory-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-memory-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/memory-runtime-boundary"
|
||||
|
||||
session-diagnostics-boundary:
|
||||
name: Critical Quality (session-diagnostics-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.session_diagnostics == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'session-diagnostics-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-session-diagnostics-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/session-diagnostics-boundary"
|
||||
|
||||
plugin-sdk-reply-runtime:
|
||||
name: Critical Quality (plugin-sdk-reply-runtime)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.plugin_sdk_reply == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'plugin-sdk-reply-runtime') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-plugin-sdk-reply-runtime-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/plugin-sdk-reply-runtime"
|
||||
|
||||
provider-runtime-boundary:
|
||||
name: Critical Quality (provider-runtime-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.provider == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'provider-runtime-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-provider-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/provider-runtime-boundary"
|
||||
|
||||
ui-control-plane:
|
||||
name: Critical Quality (ui-control-plane)
|
||||
if: ${{ github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || inputs.profile == 'all') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-ui-control-plane-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/ui-control-plane"
|
||||
|
||||
web-media-runtime-boundary:
|
||||
name: Critical Quality (web-media-runtime-boundary)
|
||||
if: ${{ github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || inputs.profile == 'all') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-web-media-runtime-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/web-media-runtime-boundary"
|
||||
|
||||
plugin-boundary:
|
||||
name: Critical Quality (plugin-boundary)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.plugin == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'plugin-boundary') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-plugin-boundary-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/plugin-boundary"
|
||||
|
||||
plugin-sdk-package-contract:
|
||||
name: Critical Quality (plugin-sdk-package-contract)
|
||||
needs: quality-shards
|
||||
if: ${{ needs.quality-shards.outputs.plugin_sdk_package == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'plugin-sdk-package-contract') }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
config-file: ./.github/codeql/codeql-plugin-sdk-package-contract-critical-quality.yml
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-quality/plugin-sdk-package-contract"
|
||||
|
||||
89
.github/workflows/codeql-macos-critical-security.yml
vendored
Normal file
89
.github/workflows/codeql-macos-critical-security.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
name: CodeQL macOS Critical Security
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 8 * * 1"
|
||||
|
||||
concurrency:
|
||||
group: codeql-macos-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
macos:
|
||||
name: Critical Security (macOS)
|
||||
runs-on: blacksmith-6vcpu-macos-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Select Xcode
|
||||
run: |
|
||||
sudo xcode-select -s /Applications/Xcode_26.1.app
|
||||
xcodebuild -version
|
||||
swift --version
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: swift
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-macos-critical-security.yml
|
||||
|
||||
- name: Build macOS for CodeQL
|
||||
run: swift build --package-path apps/macos --product OpenClaw
|
||||
|
||||
- name: Analyze
|
||||
id: analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
output: sarif-results
|
||||
upload: failure-only
|
||||
category: "/codeql-critical-security/macos"
|
||||
|
||||
- name: Remove dependency build results
|
||||
env:
|
||||
SARIF_OUTPUT: sarif-results
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
if [ ! -d "$SARIF_OUTPUT" ]; then
|
||||
echo "SARIF output directory not found: $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p sarif-results-filtered
|
||||
|
||||
files=("$SARIF_OUTPUT"/*.sarif)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No SARIF files found in $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
jq '
|
||||
def in_dependency_build:
|
||||
((.locations // []) | length > 0)
|
||||
and all(.locations[]; (.physicalLocation.artifactLocation.uri? // "") | test("^apps/macos/\\.build/"));
|
||||
|
||||
.runs |= map(.results = ((.results // []) | map(select(in_dependency_build | not))))
|
||||
' "$file" > "sarif-results-filtered/$(basename "$file")"
|
||||
done
|
||||
|
||||
- name: Upload filtered SARIF
|
||||
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
sarif_file: sarif-results-filtered
|
||||
category: "/codeql-critical-security/macos"
|
||||
150
.github/workflows/codeql.yml
vendored
150
.github/workflows/codeql.yml
vendored
@@ -11,14 +11,20 @@ on:
|
||||
options:
|
||||
- all
|
||||
- security
|
||||
- android-security
|
||||
- macos-security
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- ".github/actions/**"
|
||||
- ".github/codeql/**"
|
||||
- ".github/workflows/**"
|
||||
- "packages/**"
|
||||
- "src/**"
|
||||
schedule:
|
||||
- cron: "0 6 * * *"
|
||||
|
||||
concurrency:
|
||||
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
|
||||
cancel-in-progress: false
|
||||
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -29,9 +35,9 @@ permissions:
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
critical-security:
|
||||
name: Critical Security (${{ matrix.language }})
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'security' }}
|
||||
security-high:
|
||||
name: Security High (${{ matrix.category }})
|
||||
if: ${{ (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'security') }}
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
timeout-minutes: ${{ matrix.timeout_minutes }}
|
||||
strategy:
|
||||
@@ -39,10 +45,32 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- language: javascript-typescript
|
||||
category: core-auth-secrets
|
||||
runs_on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-javascript-typescript-critical-security.yml
|
||||
config_file: ./.github/codeql/codeql-core-auth-secrets-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: channel-runtime-boundary
|
||||
runs_on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-channel-runtime-boundary-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: network-ssrf-boundary
|
||||
runs_on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-network-ssrf-boundary-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: mcp-process-tool-boundary
|
||||
runs_on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-mcp-process-tool-boundary-critical-security.yml
|
||||
- language: javascript-typescript
|
||||
category: plugin-trust-boundary
|
||||
runs_on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout_minutes: 25
|
||||
config_file: ./.github/codeql/codeql-plugin-trust-boundary-critical-security.yml
|
||||
- language: actions
|
||||
category: actions
|
||||
runs_on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout_minutes: 10
|
||||
config_file: ./.github/codeql/codeql-actions-critical-security.yml
|
||||
@@ -61,108 +89,4 @@ jobs:
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-security/${{ matrix.language }}"
|
||||
|
||||
android-security:
|
||||
name: Critical Security (android)
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.profile == 'android-security' }}
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "21"
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: java-kotlin
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-android-critical-security.yml
|
||||
|
||||
- name: Build Android for CodeQL
|
||||
working-directory: apps/android
|
||||
run: ./gradlew --no-daemon :app:assemblePlayDebug
|
||||
|
||||
- name: Analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
category: "/codeql-critical-security/android"
|
||||
|
||||
macos-security:
|
||||
name: Critical Security (macOS)
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.profile == 'macos-security' }}
|
||||
runs-on: blacksmith-6vcpu-macos-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
submodules: false
|
||||
|
||||
- name: Select Xcode
|
||||
run: |
|
||||
sudo xcode-select -s /Applications/Xcode_26.1.app
|
||||
xcodebuild -version
|
||||
swift --version
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
languages: swift
|
||||
build-mode: manual
|
||||
config-file: ./.github/codeql/codeql-macos-critical-security.yml
|
||||
|
||||
- name: Build macOS for CodeQL
|
||||
run: swift build --package-path apps/macos --product OpenClaw
|
||||
|
||||
- name: Analyze
|
||||
id: analyze
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
output: sarif-results
|
||||
upload: failure-only
|
||||
category: "/codeql-critical-security/macos"
|
||||
|
||||
- name: Remove dependency build results
|
||||
env:
|
||||
SARIF_OUTPUT: sarif-results
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
if [ ! -d "$SARIF_OUTPUT" ]; then
|
||||
echo "SARIF output directory not found: $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p sarif-results-filtered
|
||||
|
||||
files=("$SARIF_OUTPUT"/*.sarif)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No SARIF files found in $SARIF_OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
jq '
|
||||
def in_dependency_build:
|
||||
((.locations // []) | length > 0)
|
||||
and all(.locations[]; (.physicalLocation.artifactLocation.uri? // "") | test("^apps/macos/\\.build/"));
|
||||
|
||||
.runs |= map(.results = ((.results // []) | map(select(in_dependency_build | not))))
|
||||
' "$file" > "sarif-results-filtered/$(basename "$file")"
|
||||
done
|
||||
|
||||
- name: Upload filtered SARIF
|
||||
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
||||
with:
|
||||
sarif_file: sarif-results-filtered
|
||||
category: "/codeql-critical-security/macos"
|
||||
category: "/codeql-security-high/${{ matrix.category }}"
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
all_locales_json='["zh-CN","zh-TW","pt-BR","de","es","ja-JP","ko","fr","tr","uk","id","pl","th"]'
|
||||
all_locales_json='["zh-CN","zh-TW","pt-BR","de","es","ja-JP","ko","fr","ar","it","tr","uk","id","pl","th","vi","nl","fa"]'
|
||||
|
||||
if [ "$EVENT_NAME" != "push" ]; then
|
||||
echo "has_locales=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
145
.github/workflows/crabbox-hydrate.yml
vendored
Normal file
145
.github/workflows/crabbox-hydrate.yml
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
name: Crabbox Hydrate
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
crabbox_id:
|
||||
description: "Crabbox lease ID"
|
||||
required: true
|
||||
type: string
|
||||
ref:
|
||||
description: "Git ref to hydrate"
|
||||
required: false
|
||||
type: string
|
||||
crabbox_runner_label:
|
||||
description: "Dynamic Crabbox runner label"
|
||||
required: true
|
||||
type: string
|
||||
crabbox_job:
|
||||
description: "Hydration job identifier expected by Crabbox"
|
||||
required: false
|
||||
default: "hydrate"
|
||||
type: string
|
||||
crabbox_keep_alive_minutes:
|
||||
description: "Minutes to keep the hydrated job alive"
|
||||
required: false
|
||||
default: "90"
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
jobs:
|
||||
hydrate:
|
||||
name: hydrate
|
||||
runs-on: [self-hosted, "${{ inputs.crabbox_runner_label }}"]
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Prepare Crabbox shell
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
|
||||
|
||||
node_bin="$(dirname "$(node -p 'process.execPath')")"
|
||||
pnpm_bin="$(command -v pnpm)"
|
||||
sudo ln -sf "$node_bin/node" /usr/local/bin/node
|
||||
sudo ln -sf "$node_bin/npm" /usr/local/bin/npm
|
||||
sudo ln -sf "$node_bin/npx" /usr/local/bin/npx
|
||||
sudo ln -sf "$node_bin/corepack" /usr/local/bin/corepack
|
||||
sudo ln -sf "$pnpm_bin" /usr/local/bin/pnpm
|
||||
|
||||
- name: Hydrate provider env helper
|
||||
shell: bash
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
|
||||
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
||||
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
|
||||
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
||||
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
|
||||
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
|
||||
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
||||
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
|
||||
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
|
||||
run: bash scripts/ci-hydrate-testbox-env.sh
|
||||
|
||||
- name: Mark Crabbox ready
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
job="${{ inputs.crabbox_job }}"
|
||||
if [ -z "$job" ]; then job=hydrate; fi
|
||||
mkdir -p "$HOME/.crabbox/actions"
|
||||
state="$HOME/.crabbox/actions/${{ inputs.crabbox_id }}.env"
|
||||
env_file="$HOME/.crabbox/actions/${{ inputs.crabbox_id }}.env.sh"
|
||||
services_file="$HOME/.crabbox/actions/${{ inputs.crabbox_id }}.services"
|
||||
write_export() {
|
||||
key="$1"
|
||||
value="${!key-}"
|
||||
if [ -n "$value" ]; then
|
||||
printf 'export %s=%q\n' "$key" "$value"
|
||||
fi
|
||||
}
|
||||
{
|
||||
for key in CI GITHUB_ACTIONS GITHUB_WORKSPACE GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_RUN_NUMBER GITHUB_RUN_ATTEMPT GITHUB_REF GITHUB_REF_NAME GITHUB_SHA GITHUB_EVENT_NAME GITHUB_ACTOR RUNNER_OS RUNNER_ARCH RUNNER_TEMP RUNNER_TOOL_CACHE; do
|
||||
write_export "$key"
|
||||
done
|
||||
} > "${env_file}.tmp"
|
||||
mv "${env_file}.tmp" "$env_file"
|
||||
{
|
||||
echo "# Docker containers visible from the hydrated runner"
|
||||
docker ps --format '{{.Names}}\t{{.Image}}\t{{.Ports}}' 2>/dev/null || true
|
||||
} > "${services_file}.tmp"
|
||||
mv "${services_file}.tmp" "$services_file"
|
||||
tmp="${state}.tmp"
|
||||
{
|
||||
echo "WORKSPACE=${GITHUB_WORKSPACE}"
|
||||
echo "RUN_ID=${GITHUB_RUN_ID}"
|
||||
echo "JOB=${job}"
|
||||
echo "ENV_FILE=${env_file}"
|
||||
echo "SERVICES_FILE=${services_file}"
|
||||
echo "READY_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
} > "$tmp"
|
||||
mv "$tmp" "$state"
|
||||
|
||||
- name: Keep Crabbox job alive
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
minutes="${{ inputs.crabbox_keep_alive_minutes }}"
|
||||
case "$minutes" in
|
||||
''|*[!0-9]*) minutes=90 ;;
|
||||
esac
|
||||
stop="$HOME/.crabbox/actions/${{ inputs.crabbox_id }}.stop"
|
||||
deadline=$(( $(date +%s) + minutes * 60 ))
|
||||
while [ "$(date +%s)" -lt "$deadline" ]; do
|
||||
if [ -f "$stop" ]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 15
|
||||
done
|
||||
2
.github/workflows/docs-agent.yml
vendored
2
.github/workflows/docs-agent.yml
vendored
@@ -149,7 +149,7 @@ jobs:
|
||||
|
||||
- name: Run Codex docs agent
|
||||
if: steps.gate.outputs.run_agent == 'true'
|
||||
uses: openai/codex-action@v1
|
||||
uses: openai/codex-action@5c3f4ccdb2b8790f73d6b21751ac00e602aa0c02
|
||||
env:
|
||||
DOCS_AGENT_BASE_SHA: ${{ steps.gate.outputs.review_base_sha }}
|
||||
DOCS_AGENT_HEAD_SHA: ${{ steps.gate.outputs.review_head_sha }}
|
||||
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
set -euo pipefail
|
||||
for event_type in \
|
||||
translate-zh-cn-release \
|
||||
translate-zh-tw-release \
|
||||
translate-ja-jp-release \
|
||||
translate-es-release \
|
||||
translate-pt-br-release \
|
||||
@@ -28,6 +29,9 @@ jobs:
|
||||
translate-fr-release \
|
||||
translate-ar-release \
|
||||
translate-it-release \
|
||||
translate-vi-release \
|
||||
translate-nl-release \
|
||||
translate-fa-release \
|
||||
translate-tr-release \
|
||||
translate-uk-release \
|
||||
translate-id-release \
|
||||
|
||||
275
.github/workflows/full-release-validation.yml
vendored
275
.github/workflows/full-release-validation.yml
vendored
@@ -29,7 +29,7 @@ on:
|
||||
release_profile:
|
||||
description: Release coverage profile for live/Docker/provider breadth
|
||||
required: false
|
||||
default: full
|
||||
default: stable
|
||||
type: choice
|
||||
options:
|
||||
- minimum
|
||||
@@ -43,6 +43,7 @@ on:
|
||||
options:
|
||||
- all
|
||||
- ci
|
||||
- plugin-prerelease
|
||||
- release-checks
|
||||
- install-smoke
|
||||
- cross-os
|
||||
@@ -52,6 +53,11 @@ on:
|
||||
- qa-parity
|
||||
- qa-live
|
||||
- npm-telegram
|
||||
live_suite_filter:
|
||||
description: Optional exact live suite id for focused live/E2E reruns; blank runs all selected live suites
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
npm_telegram_package_spec:
|
||||
description: Optional published package spec for the post-publish Telegram E2E lane
|
||||
required: false
|
||||
@@ -81,8 +87,8 @@ permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: full-release-validation-${{ inputs.ref }}
|
||||
cancel-in-progress: false
|
||||
group: full-release-validation-${{ inputs.ref }}-${{ inputs.rerun_group }}
|
||||
cancel-in-progress: ${{ inputs.ref == 'main' && inputs.rerun_group == 'all' }}
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -122,6 +128,7 @@ jobs:
|
||||
NPM_TELEGRAM_PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec }}
|
||||
EVIDENCE_PACKAGE_SPEC: ${{ inputs.evidence_package_spec }}
|
||||
RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
|
||||
run: |
|
||||
{
|
||||
echo "## Full release validation"
|
||||
@@ -130,12 +137,20 @@ jobs:
|
||||
echo "- Target SHA: \`${TARGET_SHA}\`"
|
||||
echo "- Child workflow ref: \`${CHILD_WORKFLOW_REF}\`"
|
||||
echo "- Rerun group: \`${RERUN_GROUP}\`"
|
||||
if [[ -n "${LIVE_SUITE_FILTER// }" ]]; then
|
||||
echo "- Live suite filter: \`${LIVE_SUITE_FILTER}\`"
|
||||
fi
|
||||
if [[ "$RERUN_GROUP" == "all" || "$RERUN_GROUP" == "ci" ]]; then
|
||||
echo "- Normal CI: \`CI\` with \`target_ref=${TARGET_SHA}\`"
|
||||
else
|
||||
echo "- Normal CI: skipped by rerun group"
|
||||
fi
|
||||
if [[ "$RERUN_GROUP" != "ci" && "$RERUN_GROUP" != "npm-telegram" ]]; then
|
||||
if [[ "$RERUN_GROUP" == "all" || "$RERUN_GROUP" == "plugin-prerelease" ]]; then
|
||||
echo "- Plugin prerelease: \`Plugin Prerelease\` with \`target_ref=${TARGET_SHA}\`"
|
||||
else
|
||||
echo "- Plugin prerelease: skipped by rerun group"
|
||||
fi
|
||||
if [[ "$RERUN_GROUP" == "all" || "$RERUN_GROUP" == "release-checks" || "$RERUN_GROUP" == "install-smoke" || "$RERUN_GROUP" == "cross-os" || "$RERUN_GROUP" == "live-e2e" || "$RERUN_GROUP" == "package" || "$RERUN_GROUP" == "qa" || "$RERUN_GROUP" == "qa-parity" || "$RERUN_GROUP" == "qa-live" ]]; then
|
||||
echo "- Release/live/Docker/package/QA: \`OpenClaw Release Checks\`"
|
||||
else
|
||||
echo "- Release/live/Docker/package/QA: skipped by rerun group"
|
||||
@@ -207,6 +222,14 @@ jobs:
|
||||
echo "Dispatched ${workflow}: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
|
||||
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cancel_child() {
|
||||
if [[ -n "${run_id:-}" ]]; then
|
||||
echo "Cancelling child workflow ${workflow}: ${run_id}" >&2
|
||||
gh run cancel "$run_id" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cancel_child EXIT INT TERM
|
||||
|
||||
while true; do
|
||||
status="$(gh run view "$run_id" --json status --jq '.status')"
|
||||
if [[ "$status" == "completed" ]]; then
|
||||
@@ -214,6 +237,7 @@ jobs:
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
trap - EXIT INT TERM
|
||||
|
||||
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
|
||||
url="$(gh run view "$run_id" --json url --jq '.url')"
|
||||
@@ -232,30 +256,26 @@ jobs:
|
||||
echo "- Target SHA: \`${TARGET_SHA}\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
dispatch_and_wait ci.yml -f target_ref="$TARGET_SHA"
|
||||
dispatch_and_wait ci.yml -f target_ref="$TARGET_SHA" -f include_android=true
|
||||
|
||||
release_checks:
|
||||
name: Run release/live/Docker/QA validation
|
||||
plugin_prerelease:
|
||||
name: Run plugin prerelease validation
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","release-checks","install-smoke","cross-os","live-e2e","package","qa","qa-parity","qa-live"]'), inputs.rerun_group)
|
||||
if: contains(fromJSON('["all","plugin-prerelease"]'), inputs.rerun_group)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 720
|
||||
timeout-minutes: 300
|
||||
outputs:
|
||||
run_id: ${{ steps.dispatch.outputs.run_id }}
|
||||
url: ${{ steps.dispatch.outputs.url }}
|
||||
conclusion: ${{ steps.dispatch.outputs.conclusion }}
|
||||
steps:
|
||||
- name: Dispatch and monitor release checks
|
||||
- name: Dispatch and monitor plugin prerelease
|
||||
id: dispatch
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TARGET_REF: ${{ inputs.ref }}
|
||||
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
|
||||
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
|
||||
PROVIDER: ${{ inputs.provider }}
|
||||
MODE: ${{ inputs.mode }}
|
||||
RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -295,6 +315,14 @@ jobs:
|
||||
echo "Dispatched ${workflow}: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
|
||||
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cancel_child() {
|
||||
if [[ -n "${run_id:-}" ]]; then
|
||||
echo "Cancelling child workflow ${workflow}: ${run_id}" >&2
|
||||
gh run cancel "$run_id" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cancel_child EXIT INT TERM
|
||||
|
||||
while true; do
|
||||
status="$(gh run view "$run_id" --json status --jq '.status')"
|
||||
if [[ "$status" == "completed" ]]; then
|
||||
@@ -302,6 +330,105 @@ jobs:
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
trap - EXIT INT TERM
|
||||
|
||||
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
|
||||
url="$(gh run view "$run_id" --json url --jq '.url')"
|
||||
echo "${workflow} finished with ${conclusion}: ${url}"
|
||||
echo "url=${url}" >> "$GITHUB_OUTPUT"
|
||||
echo "conclusion=${conclusion}" >> "$GITHUB_OUTPUT"
|
||||
if [[ "$conclusion" != "success" ]]; then
|
||||
gh run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, conclusion, url}' || true
|
||||
fi
|
||||
}
|
||||
|
||||
{
|
||||
echo "### Plugin prerelease"
|
||||
echo
|
||||
echo "- Target ref: \`${TARGET_REF}\`"
|
||||
echo "- Target SHA: \`${TARGET_SHA}\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
dispatch_and_wait plugin-prerelease.yml -f target_ref="$TARGET_SHA" -f expected_sha="$TARGET_SHA" -f full_release_validation=true
|
||||
|
||||
release_checks:
|
||||
name: Run release/live/Docker/QA validation
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","release-checks","install-smoke","cross-os","live-e2e","package","qa","qa-parity","qa-live"]'), inputs.rerun_group)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 720
|
||||
outputs:
|
||||
run_id: ${{ steps.dispatch.outputs.run_id }}
|
||||
url: ${{ steps.dispatch.outputs.url }}
|
||||
conclusion: ${{ steps.dispatch.outputs.conclusion }}
|
||||
steps:
|
||||
- name: Dispatch and monitor release checks
|
||||
id: dispatch
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TARGET_REF: ${{ inputs.ref }}
|
||||
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
|
||||
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
|
||||
PROVIDER: ${{ inputs.provider }}
|
||||
MODE: ${{ inputs.mode }}
|
||||
RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
dispatch_and_wait() {
|
||||
local workflow="$1"
|
||||
shift
|
||||
|
||||
local before_json dispatch_output run_id status conclusion url
|
||||
before_json="$(gh run list --workflow "$workflow" --event workflow_dispatch --limit 100 --json databaseId --jq '[.[].databaseId]')"
|
||||
|
||||
dispatch_output="$(gh workflow run "$workflow" --ref "$CHILD_WORKFLOW_REF" "$@" 2>&1)"
|
||||
printf '%s\n' "$dispatch_output"
|
||||
run_id="$(
|
||||
printf '%s\n' "$dispatch_output" |
|
||||
sed -nE 's#.*actions/runs/([0-9]+).*#\1#p' |
|
||||
tail -n 1
|
||||
)"
|
||||
|
||||
if [[ -z "$run_id" ]]; then
|
||||
for _ in $(seq 1 60); do
|
||||
run_id="$(
|
||||
BEFORE_IDS="$before_json" gh run list --workflow "$workflow" --event workflow_dispatch --limit 50 --json databaseId,createdAt \
|
||||
--jq 'map(select(.databaseId as $id | (env.BEFORE_IDS | fromjson | index($id) | not))) | sort_by(.createdAt) | reverse | .[0].databaseId // empty'
|
||||
)"
|
||||
if [[ -n "$run_id" ]]; then
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -z "${run_id:-}" ]]; then
|
||||
echo "Could not find dispatched run for ${workflow}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Dispatched ${workflow}: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
|
||||
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cancel_child() {
|
||||
if [[ -n "${run_id:-}" ]]; then
|
||||
echo "Cancelling child workflow ${workflow}: ${run_id}" >&2
|
||||
gh run cancel "$run_id" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cancel_child EXIT INT TERM
|
||||
|
||||
while true; do
|
||||
status="$(gh run view "$run_id" --json status --jq '.status')"
|
||||
if [[ "$status" == "completed" ]]; then
|
||||
break
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
trap - EXIT INT TERM
|
||||
|
||||
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
|
||||
url="$(gh run view "$run_id" --json url --jq '.url')"
|
||||
@@ -322,6 +449,9 @@ jobs:
|
||||
echo "- Cross-OS mode: \`${MODE}\`"
|
||||
echo "- Release profile: \`${RELEASE_PROFILE}\`"
|
||||
echo "- Rerun group: \`${RERUN_GROUP}\`"
|
||||
if [[ -n "${LIVE_SUITE_FILTER// }" ]]; then
|
||||
echo "- Live suite filter: \`${LIVE_SUITE_FILTER}\`"
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
child_rerun_group="$RERUN_GROUP"
|
||||
@@ -329,13 +459,19 @@ jobs:
|
||||
child_rerun_group=all
|
||||
fi
|
||||
|
||||
dispatch_and_wait openclaw-release-checks.yml \
|
||||
-f ref="$TARGET_REF" \
|
||||
-f expected_sha="$TARGET_SHA" \
|
||||
-f provider="$PROVIDER" \
|
||||
-f mode="$MODE" \
|
||||
-f release_profile="$RELEASE_PROFILE" \
|
||||
args=(
|
||||
-f ref="$TARGET_SHA"
|
||||
-f expected_sha="$TARGET_SHA"
|
||||
-f provider="$PROVIDER"
|
||||
-f mode="$MODE"
|
||||
-f release_profile="$RELEASE_PROFILE"
|
||||
-f rerun_group="$child_rerun_group"
|
||||
)
|
||||
if [[ -n "${LIVE_SUITE_FILTER// }" ]]; then
|
||||
args+=(-f live_suite_filter="$LIVE_SUITE_FILTER")
|
||||
fi
|
||||
|
||||
dispatch_and_wait openclaw-release-checks.yml "${args[@]}"
|
||||
|
||||
npm_telegram:
|
||||
name: Run post-publish Telegram E2E
|
||||
@@ -389,6 +525,14 @@ jobs:
|
||||
echo "Dispatched npm-telegram-beta-e2e.yml: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
|
||||
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cancel_child() {
|
||||
if [[ -n "${run_id:-}" ]]; then
|
||||
echo "Cancelling child workflow npm-telegram-beta-e2e.yml: ${run_id}" >&2
|
||||
gh run cancel "$run_id" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cancel_child EXIT INT TERM
|
||||
|
||||
while true; do
|
||||
status="$(gh run view "$run_id" --json status --jq '.status')"
|
||||
if [[ "$status" == "completed" ]]; then
|
||||
@@ -396,6 +540,7 @@ jobs:
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
trap - EXIT INT TERM
|
||||
|
||||
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
|
||||
url="$(gh run view "$run_id" --json url --jq '.url')"
|
||||
@@ -408,7 +553,7 @@ jobs:
|
||||
|
||||
summary:
|
||||
name: Verify full validation
|
||||
needs: [normal_ci, release_checks, npm_telegram]
|
||||
needs: [normal_ci, plugin_prerelease, release_checks, npm_telegram]
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
@@ -473,9 +618,11 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NORMAL_CI_RUN_ID: ${{ needs.normal_ci.outputs.run_id }}
|
||||
PLUGIN_PRERELEASE_RUN_ID: ${{ needs.plugin_prerelease.outputs.run_id }}
|
||||
RELEASE_CHECKS_RUN_ID: ${{ needs.release_checks.outputs.run_id }}
|
||||
NPM_TELEGRAM_RUN_ID: ${{ needs.npm_telegram.outputs.run_id }}
|
||||
NORMAL_CI_RESULT: ${{ needs.normal_ci.result }}
|
||||
PLUGIN_PRERELEASE_RESULT: ${{ needs.plugin_prerelease.result }}
|
||||
RELEASE_CHECKS_RESULT: ${{ needs.release_checks.result }}
|
||||
NPM_TELEGRAM_RESULT: ${{ needs.npm_telegram.result }}
|
||||
run: |
|
||||
@@ -495,20 +642,64 @@ jobs:
|
||||
return 1
|
||||
fi
|
||||
|
||||
local status conclusion url attempt
|
||||
status="$(gh run view "$run_id" --json status --jq '.status')"
|
||||
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
|
||||
url="$(gh run view "$run_id" --json url --jq '.url')"
|
||||
attempt="$(gh run view "$run_id" --json attempt --jq '.attempt')"
|
||||
local run_json status conclusion url attempt
|
||||
run_json="$(gh run view "$run_id" --json status,conclusion,url,attempt,jobs)"
|
||||
status="$(jq -r '.status' <<< "$run_json")"
|
||||
conclusion="$(jq -r '.conclusion' <<< "$run_json")"
|
||||
url="$(jq -r '.url' <<< "$run_json")"
|
||||
attempt="$(jq -r '.attempt' <<< "$run_json")"
|
||||
echo "${label}: ${status}/${conclusion} attempt ${attempt}: ${url}"
|
||||
|
||||
if [[ "$status" != "completed" || "$conclusion" != "success" ]]; then
|
||||
echo "::error::${label} child run ended with ${status}/${conclusion}: ${url}"
|
||||
gh run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, status, conclusion, url}' || true
|
||||
jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, status, conclusion, url}' <<< "$run_json" || true
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
append_child_overview() {
|
||||
{
|
||||
echo
|
||||
echo "### Child workflow overview"
|
||||
echo
|
||||
echo "| Child | Result | Minutes | Run |"
|
||||
echo "| --- | --- | ---: | --- |"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
append_child_row() {
|
||||
local label="$1"
|
||||
local run_id="$2"
|
||||
local result="$3"
|
||||
|
||||
if [[ -z "${run_id// }" ]]; then
|
||||
echo "| \`${label}\` | \`${result}\` | | skipped |" >> "$GITHUB_STEP_SUMMARY"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local run_json row
|
||||
run_json="$(gh run view "$run_id" --json status,conclusion,url,createdAt,updatedAt)"
|
||||
row="$(
|
||||
jq -r --arg label "$label" '
|
||||
def ts: fromdateiso8601;
|
||||
. as $run |
|
||||
($run.createdAt // "") as $created |
|
||||
($run.updatedAt // "") as $updated |
|
||||
(if ($created | length) > 0 and ($updated | length) > 0
|
||||
then (((($updated | ts) - ($created | ts)) / 60) * 10 | round / 10 | tostring)
|
||||
else ""
|
||||
end) as $minutes |
|
||||
"| `" + $label + "` | `" + ($run.status // "") + "/" + ($run.conclusion // "") + "` | " + $minutes + " | [run](" + ($run.url // "") + ") |"
|
||||
' <<< "$run_json"
|
||||
)"
|
||||
echo "$row" >> "$GITHUB_STEP_SUMMARY"
|
||||
}
|
||||
|
||||
append_child_row "normal_ci" "$NORMAL_CI_RUN_ID" "$NORMAL_CI_RESULT"
|
||||
append_child_row "plugin_prerelease" "$PLUGIN_PRERELEASE_RUN_ID" "$PLUGIN_PRERELEASE_RESULT"
|
||||
append_child_row "release_checks" "$RELEASE_CHECKS_RUN_ID" "$RELEASE_CHECKS_RESULT"
|
||||
append_child_row "npm_telegram" "$NPM_TELEGRAM_RUN_ID" "$NPM_TELEGRAM_RESULT"
|
||||
}
|
||||
|
||||
summarize_child_timing() {
|
||||
local label="$1"
|
||||
local run_id="$2"
|
||||
@@ -534,17 +725,46 @@ jobs:
|
||||
| map("| `" + (.name | gsub("\\|"; "\\|")) + "` | `" + ((.conclusion // "") | tostring) + "` | " + (.durationMin | tostring) + " |")
|
||||
| .[])
|
||||
' || echo "_Unable to summarize jobs for run ${run_id}._"
|
||||
echo
|
||||
echo "### Longest queues: ${label}"
|
||||
echo
|
||||
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${run_id}/jobs?per_page=100" --jq ".jobs[] | @json" | jq -sr '
|
||||
def ts: fromdateiso8601;
|
||||
"| Job | Result | Queue minutes | Run minutes |",
|
||||
"| --- | --- | ---: | ---: |",
|
||||
([.[]
|
||||
| select(.created_at != null and .started_at != null)
|
||||
| . + {
|
||||
queueMin: ((((.started_at | ts) - (.created_at | ts)) / 60) * 10 | round / 10),
|
||||
durationMin: (if .completed_at == null then null else ((((.completed_at | ts) - (.started_at | ts)) / 60) * 10 | round / 10) end)
|
||||
}
|
||||
| select(.queueMin > 0)
|
||||
| {name, conclusion, queueMin, durationMin}]
|
||||
| sort_by(.queueMin)
|
||||
| reverse
|
||||
| .[0:10]
|
||||
| map("| `" + (.name | gsub("\\|"; "\\|")) + "` | `" + ((.conclusion // "") | tostring) + "` | " + (.queueMin | tostring) + " | " + ((.durationMin // "") | tostring) + " |")
|
||||
| .[])
|
||||
' || echo "_Unable to summarize queue times for run ${run_id}._"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
}
|
||||
|
||||
failed=0
|
||||
|
||||
append_child_overview
|
||||
|
||||
if [[ "$NORMAL_CI_RESULT" == "skipped" && -z "${NORMAL_CI_RUN_ID// }" ]]; then
|
||||
check_child "normal_ci" "" 0 || failed=1
|
||||
else
|
||||
check_child "normal_ci" "$NORMAL_CI_RUN_ID" 1 || failed=1
|
||||
fi
|
||||
|
||||
if [[ "$PLUGIN_PRERELEASE_RESULT" == "skipped" && -z "${PLUGIN_PRERELEASE_RUN_ID// }" ]]; then
|
||||
check_child "plugin_prerelease" "" 0 || failed=1
|
||||
else
|
||||
check_child "plugin_prerelease" "$PLUGIN_PRERELEASE_RUN_ID" 1 || failed=1
|
||||
fi
|
||||
|
||||
if [[ "$RELEASE_CHECKS_RESULT" == "skipped" && -z "${RELEASE_CHECKS_RUN_ID// }" ]]; then
|
||||
check_child "release_checks" "" 0 || failed=1
|
||||
else
|
||||
@@ -558,6 +778,7 @@ jobs:
|
||||
fi
|
||||
|
||||
summarize_child_timing "normal_ci" "$NORMAL_CI_RUN_ID"
|
||||
summarize_child_timing "plugin_prerelease" "$PLUGIN_PRERELEASE_RUN_ID"
|
||||
summarize_child_timing "release_checks" "$RELEASE_CHECKS_RUN_ID"
|
||||
summarize_child_timing "npm_telegram" "$NPM_TELEGRAM_RUN_ID"
|
||||
|
||||
|
||||
281
.github/workflows/install-smoke.yml
vendored
281
.github/workflows/install-smoke.yml
vendored
@@ -34,10 +34,11 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.event_name == 'workflow_dispatch' && format('{0}-manual-{1}', github.workflow, github.run_id) || format('{0}-{1}', github.workflow, github.ref) }}
|
||||
cancel-in-progress: true
|
||||
group: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && format('{0}-{1}-{2}', github.workflow, github.event_name, github.run_id) || format('{0}-{1}', github.workflow, github.ref) }}
|
||||
cancel-in-progress: ${{ github.event_name != 'workflow_call' }}
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -51,6 +52,8 @@ jobs:
|
||||
run_fast_install_smoke: ${{ steps.manifest.outputs.run_fast_install_smoke }}
|
||||
run_full_install_smoke: ${{ steps.manifest.outputs.run_full_install_smoke }}
|
||||
run_bun_global_install_smoke: ${{ steps.manifest.outputs.run_bun_global_install_smoke }}
|
||||
target_sha: ${{ steps.manifest.outputs.target_sha }}
|
||||
dockerfile_image: ${{ steps.manifest.outputs.dockerfile_image }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -74,6 +77,9 @@ jobs:
|
||||
run_full_install_smoke=true
|
||||
run_bun_global_install_smoke=false
|
||||
run_install_smoke=true
|
||||
target_sha="$(git rev-parse HEAD)"
|
||||
owner="$(printf '%s' "${GITHUB_REPOSITORY_OWNER:-openclaw}" | tr '[:upper:]' '[:lower:]')"
|
||||
dockerfile_image="ghcr.io/${owner}/openclaw-dockerfile-smoke:${target_sha}"
|
||||
if [ "$event_name" = "schedule" ]; then
|
||||
run_bun_global_install_smoke=true
|
||||
elif [ "$event_name" = "workflow_dispatch" ] || [ "$event_name" = "workflow_call" ]; then
|
||||
@@ -87,6 +93,8 @@ jobs:
|
||||
echo "run_fast_install_smoke=$run_fast_install_smoke"
|
||||
echo "run_full_install_smoke=$run_full_install_smoke"
|
||||
echo "run_bun_global_install_smoke=$run_bun_global_install_smoke"
|
||||
echo "target_sha=$target_sha"
|
||||
echo "dockerfile_image=$dockerfile_image"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
install-smoke-fast:
|
||||
@@ -103,23 +111,23 @@ jobs:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
|
||||
|
||||
# Blacksmith's builder owns the Docker layer cache; keep smoke builds off
|
||||
# explicit gha cache directives so local tags still load cleanly.
|
||||
- name: Build root Dockerfile smoke image
|
||||
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
|
||||
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
OPENCLAW_EXTENSIONS=matrix
|
||||
tags: |
|
||||
openclaw-dockerfile-smoke:local
|
||||
openclaw-ext-smoke:local
|
||||
load: true
|
||||
push: false
|
||||
provenance: false
|
||||
max-cache-size-mb: 800000
|
||||
|
||||
# Keep release smoke builds bounded and log-producing. The Blacksmith
|
||||
# build action can leave jobs in-progress without step logs when a remote
|
||||
# builder stalls; an explicit buildx invocation fails closed instead.
|
||||
- name: Build root Dockerfile smoke image
|
||||
run: |
|
||||
timeout 45m docker buildx build \
|
||||
--progress=plain \
|
||||
--load \
|
||||
--build-arg OPENCLAW_EXTENSIONS=matrix \
|
||||
-t openclaw-dockerfile-smoke:local \
|
||||
-t openclaw-ext-smoke:local \
|
||||
-f ./Dockerfile \
|
||||
.
|
||||
|
||||
- name: Run root Dockerfile CLI smoke
|
||||
run: |
|
||||
@@ -196,10 +204,12 @@ jobs:
|
||||
"
|
||||
'
|
||||
|
||||
install-smoke:
|
||||
root_dockerfile_image:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_full_install_smoke == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
outputs:
|
||||
image_ref: ${{ steps.image.outputs.image_ref }}
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: "false"
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
@@ -209,51 +219,127 @@ jobs:
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Check for existing root Dockerfile smoke image
|
||||
id: existing
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if timeout 180s docker pull "$IMAGE_REF"; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Using existing root Dockerfile smoke image: \`$IMAGE_REF\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
echo "No existing root Dockerfile smoke image found for \`$IMAGE_REF\`; building it." >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
if: steps.existing.outputs.exists != 'true'
|
||||
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
|
||||
with:
|
||||
max-cache-size-mb: 800000
|
||||
|
||||
# Build once with the matrix extension and publish by target SHA. Use a
|
||||
# direct buildx command so release jobs emit Docker progress and time out.
|
||||
- name: Build and push root Dockerfile smoke image
|
||||
if: steps.existing.outputs.exists != 'true'
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
|
||||
run: |
|
||||
timeout 45m docker buildx build \
|
||||
--progress=plain \
|
||||
--push \
|
||||
--build-arg OPENCLAW_EXTENSIONS=matrix \
|
||||
-t "$IMAGE_REF" \
|
||||
-f ./Dockerfile \
|
||||
.
|
||||
|
||||
- name: Record root image output
|
||||
id: image
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
|
||||
run: echo "image_ref=$IMAGE_REF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Summarize root image
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.preflight.outputs.dockerfile_image }}
|
||||
TARGET_SHA: ${{ needs.preflight.outputs.target_sha }}
|
||||
run: |
|
||||
{
|
||||
echo "## Root Dockerfile smoke image"
|
||||
echo
|
||||
echo "- Target SHA: \`${TARGET_SHA}\`"
|
||||
echo "- Image: \`${IMAGE_REF}\`"
|
||||
echo "- Reused existing image: \`${{ steps.existing.outputs.exists }}\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
qr_package_install_smoke:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_full_install_smoke == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
# Blacksmith's builder owns the Docker layer cache; keep smoke builds off
|
||||
# explicit gha cache directives so local tags still load cleanly.
|
||||
- name: Run QR package install smoke
|
||||
env:
|
||||
OPENCLAW_QR_SMOKE_FORCE_INSTALL: "1"
|
||||
run: bash scripts/e2e/qr-import-docker.sh
|
||||
|
||||
# Build once with the matrix extension and tag both smoke names. This
|
||||
# keeps the build-arg coverage without a second Blacksmith build action.
|
||||
- name: Build root Dockerfile smoke image
|
||||
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
|
||||
root_dockerfile_smokes:
|
||||
needs: [preflight, root_dockerfile_image]
|
||||
if: needs.preflight.outputs.run_full_install_smoke == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
OPENCLAW_EXTENSIONS=matrix
|
||||
tags: |
|
||||
openclaw-dockerfile-smoke:local
|
||||
openclaw-ext-smoke:local
|
||||
load: true
|
||||
push: false
|
||||
provenance: false
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Pull root Dockerfile smoke image
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
run: timeout 300s docker pull "$IMAGE_REF"
|
||||
|
||||
- name: Run root Dockerfile CLI smoke
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
run: |
|
||||
docker run --rm --entrypoint sh openclaw-dockerfile-smoke:local -lc 'which openclaw && openclaw --version'
|
||||
docker run --rm --entrypoint sh "$IMAGE_REF" -lc 'which openclaw && openclaw --version'
|
||||
|
||||
- name: Run agents delete shared workspace Docker CLI smoke
|
||||
env:
|
||||
OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE: openclaw-dockerfile-smoke:local
|
||||
OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_SKIP_BUILD: "1"
|
||||
run: bash scripts/e2e/agents-delete-shared-workspace-docker.sh
|
||||
|
||||
- name: Run Docker gateway network e2e
|
||||
env:
|
||||
OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE: openclaw-dockerfile-smoke:local
|
||||
OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
OPENCLAW_GATEWAY_NETWORK_E2E_SKIP_BUILD: "1"
|
||||
run: bash scripts/e2e/gateway-network-docker.sh
|
||||
|
||||
- name: Smoke test Dockerfile with matrix extension build arg
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
run: |
|
||||
docker run --rm --entrypoint sh openclaw-ext-smoke:local -lc '
|
||||
docker run --rm --entrypoint sh "$IMAGE_REF" -lc '
|
||||
which openclaw &&
|
||||
openclaw --version &&
|
||||
node -e "
|
||||
@@ -296,39 +382,60 @@ jobs:
|
||||
"
|
||||
'
|
||||
|
||||
- name: Build installer smoke image
|
||||
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
|
||||
installer_smoke:
|
||||
needs: [preflight, root_dockerfile_image]
|
||||
if: needs.preflight.outputs.run_full_install_smoke == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: "false"
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
context: ./scripts/docker
|
||||
file: ./scripts/docker/install-sh-smoke/Dockerfile
|
||||
tags: openclaw-install-smoke:local
|
||||
load: true
|
||||
push: false
|
||||
provenance: false
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Pull root Dockerfile smoke image
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
run: timeout 300s docker pull "$IMAGE_REF"
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
|
||||
with:
|
||||
max-cache-size-mb: 800000
|
||||
|
||||
- name: Build installer smoke image
|
||||
run: |
|
||||
timeout 20m docker buildx build \
|
||||
--progress=plain \
|
||||
--load \
|
||||
-t openclaw-install-smoke:local \
|
||||
-f ./scripts/docker/install-sh-smoke/Dockerfile \
|
||||
./scripts/docker
|
||||
|
||||
- name: Build installer non-root image
|
||||
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
|
||||
with:
|
||||
context: ./scripts/docker
|
||||
file: ./scripts/docker/install-sh-nonroot/Dockerfile
|
||||
tags: openclaw-install-nonroot:local
|
||||
load: true
|
||||
push: false
|
||||
provenance: false
|
||||
run: |
|
||||
timeout 20m docker buildx build \
|
||||
--progress=plain \
|
||||
--load \
|
||||
-t openclaw-install-nonroot:local \
|
||||
-f ./scripts/docker/install-sh-nonroot/Dockerfile \
|
||||
./scripts/docker
|
||||
|
||||
- name: Setup Node environment for installer smoke
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: ${{ needs.preflight.outputs.run_bun_global_install_smoke }}
|
||||
install-bun: "false"
|
||||
install-deps: "true"
|
||||
|
||||
- name: Run Bun global install image-provider smoke
|
||||
if: needs.preflight.outputs.run_bun_global_install_smoke == 'true'
|
||||
env:
|
||||
OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE: openclaw-dockerfile-smoke:local
|
||||
OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD: "0"
|
||||
run: bash scripts/e2e/bun-global-install-smoke.sh
|
||||
|
||||
- name: Run installer docker tests
|
||||
env:
|
||||
OPENCLAW_INSTALL_URL: https://openclaw.ai/install.sh
|
||||
@@ -341,15 +448,49 @@ jobs:
|
||||
OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL: "1"
|
||||
OPENCLAW_INSTALL_SMOKE_SKIP_PREVIOUS: "1"
|
||||
OPENCLAW_INSTALL_SMOKE_UPDATE_BASELINE: ${{ inputs.update_baseline_version || 'latest' }}
|
||||
OPENCLAW_INSTALL_SMOKE_UPDATE_DIST_IMAGE: openclaw-dockerfile-smoke:local
|
||||
OPENCLAW_INSTALL_SMOKE_UPDATE_DIST_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
OPENCLAW_INSTALL_SMOKE_UPDATE_SKIP_LOCAL_BUILD: "1"
|
||||
run: bash scripts/test-install-sh-docker.sh
|
||||
|
||||
bun_global_install_smoke:
|
||||
needs: [preflight, root_dockerfile_image]
|
||||
if: needs.preflight.outputs.run_full_install_smoke == 'true' && needs.preflight.outputs.run_bun_global_install_smoke == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout CLI
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Pull root Dockerfile smoke image
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
run: timeout 300s docker pull "$IMAGE_REF"
|
||||
|
||||
- name: Setup Node environment for Bun smoke
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "true"
|
||||
install-deps: "true"
|
||||
|
||||
- name: Run Bun global install image-provider smoke
|
||||
env:
|
||||
OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE: ${{ needs.root_dockerfile_image.outputs.image_ref }}
|
||||
OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD: "0"
|
||||
run: bash scripts/e2e/bun-global-install-smoke.sh
|
||||
|
||||
docker-e2e-fast:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_fast_install_smoke == 'true' || needs.preflight.outputs.run_full_install_smoke == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 8
|
||||
timeout-minutes: 12
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: "false"
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
@@ -360,7 +501,9 @@ jobs:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
|
||||
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
|
||||
with:
|
||||
max-cache-size-mb: 800000
|
||||
|
||||
- name: Setup Node environment for package smoke
|
||||
uses: ./.github/actions/setup-node-env
|
||||
@@ -372,4 +515,4 @@ jobs:
|
||||
env:
|
||||
OPENCLAW_BUNDLED_CHANNEL_DEPS_E2E_IMAGE: openclaw-bundled-channel-fast:local
|
||||
OPENCLAW_BUNDLED_CHANNEL_DOCKER_RUN_TIMEOUT: 90s
|
||||
run: timeout 240s pnpm test:docker:bundled-channel-deps:fast
|
||||
run: timeout 480s pnpm test:docker:bundled-channel-deps:fast
|
||||
|
||||
27
.github/workflows/labeler.yml
vendored
27
.github/workflows/labeler.yml
vendored
@@ -278,6 +278,7 @@ jobs:
|
||||
const labelColor = "B60205";
|
||||
const labelDescription = `Author has more than ${activePrLimit} active PRs in this repo`;
|
||||
const authorLogin = pullRequest.user?.login;
|
||||
const headRefName = pullRequest.head?.ref ?? "";
|
||||
if (!authorLogin) {
|
||||
return;
|
||||
}
|
||||
@@ -295,6 +296,25 @@ jobs:
|
||||
.filter((name) => typeof name === "string"),
|
||||
);
|
||||
|
||||
if (pullRequest.user?.type === "Bot" || /\[bot\]$/i.test(authorLogin) || authorLogin.startsWith("app/")) {
|
||||
if (labelNames.has(activePrLimitLabel)) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
name: activePrLimitLabel,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error?.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
core.info(`Skipping active PR limit for GitHub App author ${authorLogin}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (labelNames.has(activePrLimitOverrideLabel)) {
|
||||
if (labelNames.has(activePrLimitLabel)) {
|
||||
try {
|
||||
@@ -374,7 +394,12 @@ jobs:
|
||||
return false;
|
||||
};
|
||||
|
||||
if (await isPrivilegedAuthor()) {
|
||||
const automationPrHeadPrefixes = ["clawsweeper/", "clownfish/"];
|
||||
const isAutomationPullRequest =
|
||||
typeof headRefName === "string" &&
|
||||
automationPrHeadPrefixes.some((prefix) => headRefName.startsWith(prefix));
|
||||
|
||||
if ((await isPrivilegedAuthor()) || isAutomationPullRequest) {
|
||||
if (labelNames.has(activePrLimitLabel)) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
|
||||
54
.github/workflows/live-media-runner-image.yml
vendored
Normal file
54
.github/workflows/live-media-runner-image.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: Live Media Runner Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/images/live-media-runner/Dockerfile"
|
||||
- ".github/workflows/live-media-runner-image.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
concurrency:
|
||||
group: live-media-runner-image-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build live media runner image
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
|
||||
with:
|
||||
max-cache-size-mb: 800000
|
||||
|
||||
- name: Build and push live media runner image
|
||||
uses: useblacksmith/build-push-action@fb9e3e6a9299c78462bfadd0d93352c316adc9b8 # v2
|
||||
with:
|
||||
context: .github/images/live-media-runner
|
||||
file: .github/images/live-media-runner/Dockerfile
|
||||
platforms: linux/amd64
|
||||
tags: |
|
||||
ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04
|
||||
ghcr.io/openclaw/openclaw-live-media-runner:${{ github.sha }}
|
||||
sbom: true
|
||||
provenance: mode=max
|
||||
push: true
|
||||
15
.github/workflows/macos-release.yml
vendored
15
.github/workflows/macos-release.yml
vendored
@@ -12,6 +12,11 @@ on:
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
public_release_branch:
|
||||
description: Public branch that contains the release tag commit, usually main or release/YYYY.M.D
|
||||
required: false
|
||||
default: main
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: macos-release-${{ inputs.tag }}
|
||||
@@ -66,13 +71,17 @@ jobs:
|
||||
- name: Validate release tag and package metadata
|
||||
env:
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
WORKFLOW_REF_NAME: ${{ github.ref_name }}
|
||||
PUBLIC_RELEASE_BRANCH: ${{ inputs.public_release_branch }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ "${PUBLIC_RELEASE_BRANCH}" != "main" && ! "${PUBLIC_RELEASE_BRANCH}" =~ ^release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*$ ]]; then
|
||||
echo "public_release_branch must be main or release/YYYY.M.D, got ${PUBLIC_RELEASE_BRANCH}." >&2
|
||||
exit 1
|
||||
fi
|
||||
RELEASE_SHA=$(git rev-parse HEAD)
|
||||
RELEASE_MAIN_REF="refs/remotes/origin/${WORKFLOW_REF_NAME}"
|
||||
RELEASE_MAIN_REF="refs/remotes/origin/${PUBLIC_RELEASE_BRANCH}"
|
||||
export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF
|
||||
git fetch --no-tags origin "+refs/heads/${WORKFLOW_REF_NAME}:refs/remotes/origin/${WORKFLOW_REF_NAME}"
|
||||
git fetch --no-tags origin "+refs/heads/${PUBLIC_RELEASE_BRANCH}:refs/remotes/origin/${PUBLIC_RELEASE_BRANCH}"
|
||||
pnpm release:openclaw:npm:check
|
||||
|
||||
- name: Summarize next step
|
||||
|
||||
99
.github/workflows/maintainer-command-reactions.yml
vendored
Normal file
99
.github/workflows/maintainer-command-reactions.yml
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
name: Maintainer Command Reactions
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: maintainer-command-reactions-${{ github.event.comment.id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
react:
|
||||
if: ${{ !endsWith(github.actor, '[bot]') }}
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
env:
|
||||
MAINTAINER_COMMAND_REACTIONS: ${{ vars.MAINTAINER_COMMAND_REACTIONS || '/autoclose,/clawsweeper autoclose,/clawsweeper automerge,/merge,/land,/landpr' }}
|
||||
steps:
|
||||
- name: React to maintainer slash command
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const comment = context.payload.comment;
|
||||
const issue = context.payload.issue;
|
||||
|
||||
const commands = (process.env.MAINTAINER_COMMAND_REACTIONS || "")
|
||||
.split(",")
|
||||
.map((command) => command.trim())
|
||||
.filter(Boolean);
|
||||
const commandLine = String(comment.body || "")
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trim())
|
||||
.find((line) => commands.some((command) => line === command || line.startsWith(`${command} `)));
|
||||
|
||||
if (!commandLine) {
|
||||
core.info(`Skipping comment ${comment.id}; no tracked maintainer command found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const isAutocloseCommand =
|
||||
commandLine === "/autoclose" ||
|
||||
commandLine.startsWith("/autoclose ") ||
|
||||
commandLine === "/clawsweeper autoclose" ||
|
||||
commandLine.startsWith("/clawsweeper autoclose ");
|
||||
if (!issue.pull_request && !isAutocloseCommand) {
|
||||
core.info("Skipping non-autoclose command reaction because the comment is not on a pull request.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maintainerPermissions = new Set(["admin", "maintain", "write"]);
|
||||
let permission = "none";
|
||||
try {
|
||||
const result = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username: comment.user.login,
|
||||
});
|
||||
permission = String(result.data.permission || "none").toLowerCase();
|
||||
} catch (error) {
|
||||
if (error.status !== 404) {
|
||||
core.info(`Could not resolve repository permission for ${comment.user.login}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!maintainerPermissions.has(permission)) {
|
||||
core.info(
|
||||
`Skipping non-maintainer command reaction for ${comment.user.login}; repository permission is ${permission}.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
async function react(content) {
|
||||
try {
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: comment.id,
|
||||
content,
|
||||
});
|
||||
core.info(`Added ${content} reaction to comment ${comment.id}.`);
|
||||
} catch (error) {
|
||||
if (error.status === 422 && /already exists/i.test(String(error.message))) {
|
||||
core.info(`${content} reaction already exists on comment ${comment.id}.`);
|
||||
return;
|
||||
}
|
||||
if (error.status === 403 && /resource not accessible by integration/i.test(String(error.message))) {
|
||||
core.warning(`${content} reaction could not be added with this token: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
await react("eyes");
|
||||
core.info(`Maintainer command observed on ${issue.pull_request ? "PR" : "issue"} #${issue.number}: ${commandLine}`);
|
||||
4
.github/workflows/npm-telegram-beta-e2e.yml
vendored
4
.github/workflows/npm-telegram-beta-e2e.yml
vendored
@@ -105,12 +105,12 @@ jobs:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Set up Blacksmith Docker Builder
|
||||
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
|
||||
uses: useblacksmith/setup-docker-builder@722e97d12b1d06a961800dd6c05d79d951ad3c80 # v1
|
||||
with:
|
||||
max-cache-size-mb: 800000
|
||||
|
||||
- name: Build Docker E2E image
|
||||
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
|
||||
uses: useblacksmith/build-push-action@fb9e3e6a9299c78462bfadd0d93352c316adc9b8 # v2
|
||||
with:
|
||||
context: .
|
||||
file: ./scripts/e2e/Dockerfile
|
||||
|
||||
@@ -158,7 +158,7 @@ permissions: read-all
|
||||
|
||||
concurrency:
|
||||
group: openclaw-cross-os-release-checks-${{ inputs.ref }}-${{ inputs.provider }}-${{ inputs.mode }}
|
||||
cancel-in-progress: false
|
||||
cancel-in-progress: ${{ inputs.ref == 'main' }}
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -166,10 +166,11 @@ env:
|
||||
PNPM_VERSION: "10.32.1"
|
||||
OPENCLAW_REPOSITORY: openclaw/openclaw
|
||||
TSX_VERSION: "4.21.0"
|
||||
OPENCLAW_CROSS_OS_OPENAI_MODEL: ${{ vars.OPENCLAW_CROSS_OS_OPENAI_MODEL || 'openai/gpt-5.4-mini' }}
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
baseline_file_name: ${{ steps.baseline_metadata.outputs.file_name }}
|
||||
baseline_spec: ${{ steps.baseline.outputs.value }}
|
||||
@@ -321,7 +322,7 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
@@ -333,6 +334,9 @@ jobs:
|
||||
cache: pnpm
|
||||
cache-dependency-path: ${{ inputs.candidate_artifact_name == '' && 'source/pnpm-lock.yaml' || 'workflow/pnpm-lock.yaml' }}
|
||||
|
||||
- name: Ensure pnpm store cache directory exists
|
||||
run: mkdir -p "$(pnpm store path --silent)"
|
||||
|
||||
- name: Build candidate artifact once
|
||||
if: inputs.candidate_artifact_name == ''
|
||||
env:
|
||||
@@ -343,12 +347,19 @@ jobs:
|
||||
--source-dir source \
|
||||
--output-dir "${OUTPUT_DIR}"
|
||||
|
||||
- name: Download provided candidate artifact
|
||||
if: inputs.candidate_artifact_name != ''
|
||||
- name: Download current-run candidate artifact
|
||||
if: inputs.candidate_artifact_name != '' && inputs.candidate_artifact_run_id == ''
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ inputs.candidate_artifact_name }}
|
||||
run-id: ${{ inputs.candidate_artifact_run_id || github.run_id }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package
|
||||
|
||||
- name: Download previous-run candidate artifact
|
||||
if: inputs.candidate_artifact_name != '' && inputs.candidate_artifact_run_id != ''
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ inputs.candidate_artifact_name }}
|
||||
run-id: ${{ inputs.candidate_artifact_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package
|
||||
|
||||
@@ -496,7 +507,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
175
.github/workflows/openclaw-release-checks.yml
vendored
175
.github/workflows/openclaw-release-checks.yml
vendored
@@ -33,7 +33,7 @@ on:
|
||||
release_profile:
|
||||
description: Release coverage profile for live/Docker/provider breadth
|
||||
required: false
|
||||
default: full
|
||||
default: stable
|
||||
type: choice
|
||||
options:
|
||||
- minimum
|
||||
@@ -53,30 +53,36 @@ on:
|
||||
- qa
|
||||
- qa-parity
|
||||
- qa-live
|
||||
live_suite_filter:
|
||||
description: Optional exact live suite id for focused live/E2E reruns; blank runs all selected live suites
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: openclaw-release-checks-${{ inputs.ref }}
|
||||
group: openclaw-release-checks-${{ inputs.expected_sha || inputs.ref }}-${{ inputs.rerun_group }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
NODE_VERSION: "24.x"
|
||||
PNPM_VERSION: "10.33.0"
|
||||
OPENCLAW_CI_OPENAI_MODEL: ${{ vars.OPENCLAW_CI_OPENAI_MODEL }}
|
||||
OPENCLAW_CI_OPENAI_MODEL: ${{ vars.OPENCLAW_CI_OPENAI_MODEL || 'openai/gpt-5.5' }}
|
||||
|
||||
jobs:
|
||||
resolve_target:
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
ref: ${{ steps.inputs.outputs.ref }}
|
||||
sha: ${{ steps.ref.outputs.sha }}
|
||||
revision: ${{ steps.ref.outputs.sha }}
|
||||
provider: ${{ steps.inputs.outputs.provider }}
|
||||
mode: ${{ steps.inputs.outputs.mode }}
|
||||
release_profile: ${{ steps.inputs.outputs.release_profile }}
|
||||
rerun_group: ${{ steps.inputs.outputs.rerun_group }}
|
||||
live_suite_filter: ${{ steps.inputs.outputs.live_suite_filter }}
|
||||
steps:
|
||||
- name: Require main or release workflow ref for release checks
|
||||
env:
|
||||
@@ -106,6 +112,7 @@ jobs:
|
||||
- name: Checkout trusted workflow helper
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.ref_name }}
|
||||
path: workflow
|
||||
fetch-depth: 1
|
||||
@@ -126,6 +133,7 @@ jobs:
|
||||
if: steps.fast_ref.outputs.fallback == 'true'
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ inputs.ref }}
|
||||
path: source
|
||||
fetch-depth: 0
|
||||
@@ -190,6 +198,7 @@ jobs:
|
||||
RELEASE_MODE_INPUT: ${{ inputs.mode }}
|
||||
RELEASE_PROFILE_INPUT: ${{ inputs.release_profile }}
|
||||
RELEASE_RERUN_GROUP_INPUT: ${{ inputs.rerun_group }}
|
||||
RELEASE_LIVE_SUITE_FILTER_INPUT: ${{ inputs.live_suite_filter }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
{
|
||||
@@ -198,6 +207,7 @@ jobs:
|
||||
printf 'mode=%s\n' "$RELEASE_MODE_INPUT"
|
||||
printf 'release_profile=%s\n' "$RELEASE_PROFILE_INPUT"
|
||||
printf 'rerun_group=%s\n' "$RELEASE_RERUN_GROUP_INPUT"
|
||||
printf 'live_suite_filter=%s\n' "$RELEASE_LIVE_SUITE_FILTER_INPUT"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Summarize validated ref
|
||||
@@ -209,6 +219,7 @@ jobs:
|
||||
RELEASE_MODE: ${{ inputs.mode }}
|
||||
RELEASE_PROFILE: ${{ inputs.release_profile }}
|
||||
RELEASE_RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
RELEASE_LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
|
||||
run: |
|
||||
{
|
||||
echo "## Release checks"
|
||||
@@ -220,17 +231,21 @@ jobs:
|
||||
echo "- Cross-OS mode: \`${RELEASE_MODE}\`"
|
||||
echo "- Release profile: \`${RELEASE_PROFILE}\`"
|
||||
echo "- Rerun group: \`${RELEASE_RERUN_GROUP}\`"
|
||||
if [[ -n "${RELEASE_LIVE_SUITE_FILTER// }" ]]; then
|
||||
echo "- Live suite filter: \`${RELEASE_LIVE_SUITE_FILTER}\`"
|
||||
fi
|
||||
echo "- This run will execute cross-OS release validation, install smoke, QA Lab parity, Matrix, and Telegram lanes, and the non-Parallels Docker/live/openwebui coverage from the CI migration plan."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
prepare_release_package:
|
||||
name: Prepare release package artifact
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","cross-os","live-e2e","package"]'), needs.resolve_target.outputs.rerun_group)
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
if: contains(fromJSON('["all","cross-os","package"]'), needs.resolve_target.outputs.rerun_group) || (needs.resolve_target.outputs.rerun_group == 'live-e2e' && needs.resolve_target.outputs.live_suite_filter == '')
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
artifact_name: ${{ steps.artifact.outputs.name }}
|
||||
package_sha256: ${{ steps.package.outputs.sha256 }}
|
||||
@@ -240,6 +255,7 @@ jobs:
|
||||
- name: Checkout trusted workflow ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.ref_name }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -259,7 +275,7 @@ jobs:
|
||||
id: package
|
||||
shell: bash
|
||||
env:
|
||||
PACKAGE_REF: ${{ needs.resolve_target.outputs.sha }}
|
||||
PACKAGE_REF: ${{ needs.resolve_target.outputs.revision }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node scripts/resolve-openclaw-package-candidate.mjs \
|
||||
@@ -296,9 +312,10 @@ jobs:
|
||||
if: contains(fromJSON('["all","install-smoke"]'), needs.resolve_target.outputs.rerun_group)
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
uses: ./.github/workflows/install-smoke.yml
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.sha }}
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
run_bun_global_install_smoke: true
|
||||
|
||||
cross_os_release_checks:
|
||||
@@ -307,11 +324,10 @@ jobs:
|
||||
permissions: read-all
|
||||
uses: ./.github/workflows/openclaw-cross-os-release-checks-reusable.yml
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.ref }}
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
provider: ${{ needs.resolve_target.outputs.provider }}
|
||||
mode: ${{ needs.resolve_target.outputs.mode }}
|
||||
candidate_artifact_name: ${{ needs.prepare_release_package.outputs.artifact_name }}
|
||||
candidate_artifact_run_id: ${{ github.run_id }}
|
||||
candidate_file_name: openclaw-current.tgz
|
||||
candidate_version: ${{ needs.prepare_release_package.outputs.package_version }}
|
||||
candidate_source_sha: ${{ needs.prepare_release_package.outputs.source_sha }}
|
||||
@@ -323,8 +339,9 @@ jobs:
|
||||
OPENCLAW_DISCORD_SMOKE_GUILD_ID: ${{ secrets.OPENCLAW_DISCORD_SMOKE_GUILD_ID }}
|
||||
OPENCLAW_DISCORD_SMOKE_CHANNEL_ID: ${{ secrets.OPENCLAW_DISCORD_SMOKE_CHANNEL_ID }}
|
||||
|
||||
live_and_e2e_release_checks:
|
||||
needs: [resolve_target, prepare_release_package]
|
||||
live_repo_e2e_release_checks:
|
||||
name: Run repo/live E2E validation
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","live-e2e"]'), needs.resolve_target.outputs.rerun_group)
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -333,15 +350,14 @@ jobs:
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.sha }}
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
include_repo_e2e: true
|
||||
include_release_path_suites: true
|
||||
include_openwebui: ${{ needs.resolve_target.outputs.release_profile != 'minimum' }}
|
||||
include_release_path_suites: false
|
||||
include_openwebui: false
|
||||
include_live_suites: true
|
||||
release_test_profile: ${{ needs.resolve_target.outputs.release_profile }}
|
||||
package_artifact_name: ${{ needs.prepare_release_package.outputs.artifact_name }}
|
||||
package_artifact_run_id: ${{ github.run_id }}
|
||||
secrets:
|
||||
live_suite_filter: ${{ needs.resolve_target.outputs.live_suite_filter }}
|
||||
secrets: &live_e2e_release_secrets
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
@@ -388,6 +404,26 @@ jobs:
|
||||
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
|
||||
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
||||
|
||||
docker_e2e_release_checks:
|
||||
name: Run Docker release-path validation
|
||||
needs: [resolve_target, prepare_release_package]
|
||||
if: contains(fromJSON('["all","live-e2e"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.live_suite_filter == ''
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
packages: write
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
include_repo_e2e: false
|
||||
include_release_path_suites: true
|
||||
include_openwebui: ${{ needs.resolve_target.outputs.release_profile != 'minimum' }}
|
||||
include_live_suites: false
|
||||
release_test_profile: ${{ needs.resolve_target.outputs.release_profile }}
|
||||
package_artifact_name: ${{ needs.prepare_release_package.outputs.artifact_name }}
|
||||
secrets: *live_e2e_release_secrets
|
||||
|
||||
package_acceptance_release_checks:
|
||||
name: Run package acceptance
|
||||
needs: [resolve_target, prepare_release_package]
|
||||
@@ -401,7 +437,6 @@ jobs:
|
||||
with:
|
||||
workflow_ref: ${{ github.ref_name }}
|
||||
source: artifact
|
||||
artifact_run_id: ${{ github.run_id }}
|
||||
artifact_name: ${{ needs.prepare_release_package.outputs.artifact_name }}
|
||||
package_sha256: ${{ needs.prepare_release_package.outputs.package_sha256 }}
|
||||
suite_profile: custom
|
||||
@@ -461,7 +496,7 @@ jobs:
|
||||
name: Run QA Lab parity lane (${{ matrix.lane }})
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","qa","qa-parity"]'), needs.resolve_target.outputs.rerun_group)
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -488,7 +523,8 @@ jobs:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -535,7 +571,7 @@ jobs:
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-qa-parity-${{ matrix.lane }}-${{ needs.resolve_target.outputs.sha }}
|
||||
name: release-qa-parity-${{ matrix.lane }}-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
@@ -544,7 +580,7 @@ jobs:
|
||||
name: Run QA Lab parity report
|
||||
needs: [resolve_target, qa_lab_parity_lane_release_checks]
|
||||
if: contains(fromJSON('["all","qa","qa-parity"]'), needs.resolve_target.outputs.rerun_group)
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -556,7 +592,8 @@ jobs:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -569,7 +606,7 @@ jobs:
|
||||
- name: Download parity lane artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: release-qa-parity-*-${{ needs.resolve_target.outputs.sha }}
|
||||
pattern: release-qa-parity-*-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
merge-multiple: true
|
||||
|
||||
@@ -590,7 +627,7 @@ jobs:
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-qa-parity-${{ needs.resolve_target.outputs.sha }}
|
||||
name: release-qa-parity-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
@@ -599,7 +636,7 @@ jobs:
|
||||
name: Run QA Lab live Matrix lane
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group)
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -612,7 +649,8 @@ jobs:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -622,18 +660,6 @@ jobs:
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
install-bun: "true"
|
||||
|
||||
- name: Validate required QA credential env
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "${OPENAI_API_KEY:-}" ]]; then
|
||||
echo "Missing required OPENAI_API_KEY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build private QA runtime
|
||||
run: pnpm build
|
||||
|
||||
@@ -641,8 +667,8 @@ jobs:
|
||||
id: run_lane
|
||||
shell: bash
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
|
||||
OPENCLAW_QA_MATRIX_CANARY_TIMEOUT_MS: "90000"
|
||||
OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS: "3000"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -652,10 +678,9 @@ jobs:
|
||||
|
||||
matrix_args=(
|
||||
--repo-root . \
|
||||
--output-dir "${output_dir}" \
|
||||
--provider-mode live-frontier \
|
||||
--model "${OPENCLAW_CI_OPENAI_MODEL}" \
|
||||
--alt-model "${OPENCLAW_CI_OPENAI_MODEL}" \
|
||||
--provider-mode mock-openai \
|
||||
--model mock-openai/gpt-5.5 \
|
||||
--alt-model mock-openai/gpt-5.5-alt \
|
||||
--profile fast \
|
||||
--fast
|
||||
)
|
||||
@@ -663,13 +688,23 @@ jobs:
|
||||
matrix_args+=(--fail-fast)
|
||||
fi
|
||||
|
||||
pnpm openclaw qa matrix "${matrix_args[@]}"
|
||||
for attempt in 1 2; do
|
||||
attempt_output_dir="${output_dir}/attempt-${attempt}"
|
||||
if pnpm openclaw qa matrix --output-dir "${attempt_output_dir}" "${matrix_args[@]}"; then
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${attempt}" == "2" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Matrix live lane failed on attempt ${attempt}; retrying once..." >&2
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Upload Matrix QA artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-qa-live-matrix-${{ needs.resolve_target.outputs.sha }}
|
||||
name: release-qa-live-matrix-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
@@ -678,7 +713,7 @@ jobs:
|
||||
name: Run QA Lab live Telegram lane
|
||||
needs: [resolve_target]
|
||||
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group)
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -691,7 +726,8 @@ jobs:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.resolve_target.outputs.sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.resolve_target.outputs.revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -703,7 +739,6 @@ jobs:
|
||||
|
||||
- name: Validate required QA credential env
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
|
||||
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
|
||||
shell: bash
|
||||
@@ -718,7 +753,6 @@ jobs:
|
||||
fi
|
||||
}
|
||||
|
||||
require_var OPENAI_API_KEY
|
||||
require_var OPENCLAW_QA_CONVEX_SITE_URL
|
||||
require_var OPENCLAW_QA_CONVEX_SECRET_CI
|
||||
|
||||
@@ -729,7 +763,6 @@ jobs:
|
||||
id: run_lane
|
||||
shell: bash
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
|
||||
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
|
||||
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
|
||||
@@ -740,21 +773,31 @@ jobs:
|
||||
output_dir=".artifacts/qa-e2e/telegram-live-release-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
|
||||
echo "output_dir=${output_dir}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
pnpm openclaw qa telegram \
|
||||
--repo-root . \
|
||||
--output-dir "${output_dir}" \
|
||||
--provider-mode live-frontier \
|
||||
--model "${OPENCLAW_CI_OPENAI_MODEL}" \
|
||||
--alt-model "${OPENCLAW_CI_OPENAI_MODEL}" \
|
||||
--fast \
|
||||
--credential-source convex \
|
||||
--credential-role ci
|
||||
for attempt in 1 2; do
|
||||
attempt_output_dir="${output_dir}/attempt-${attempt}"
|
||||
if pnpm openclaw qa telegram \
|
||||
--repo-root . \
|
||||
--output-dir "${attempt_output_dir}" \
|
||||
--provider-mode mock-openai \
|
||||
--model mock-openai/gpt-5.5 \
|
||||
--alt-model mock-openai/gpt-5.5-alt \
|
||||
--fast \
|
||||
--credential-source convex \
|
||||
--credential-role ci; then
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${attempt}" == "2" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Telegram live lane failed on attempt ${attempt}; retrying once..." >&2
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Upload Telegram QA artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-qa-live-telegram-${{ needs.resolve_target.outputs.sha }}
|
||||
name: release-qa-live-telegram-${{ needs.resolve_target.outputs.revision }}
|
||||
path: .artifacts/qa-e2e/
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
@@ -765,7 +808,8 @@ jobs:
|
||||
- prepare_release_package
|
||||
- install_smoke_release_checks
|
||||
- cross_os_release_checks
|
||||
- live_and_e2e_release_checks
|
||||
- live_repo_e2e_release_checks
|
||||
- docker_e2e_release_checks
|
||||
- package_acceptance_release_checks
|
||||
- qa_lab_parity_lane_release_checks
|
||||
- qa_lab_parity_report_release_checks
|
||||
@@ -785,7 +829,8 @@ jobs:
|
||||
"prepare_release_package=${{ needs.prepare_release_package.result }}" \
|
||||
"install_smoke_release_checks=${{ needs.install_smoke_release_checks.result }}" \
|
||||
"cross_os_release_checks=${{ needs.cross_os_release_checks.result }}" \
|
||||
"live_and_e2e_release_checks=${{ needs.live_and_e2e_release_checks.result }}" \
|
||||
"live_repo_e2e_release_checks=${{ needs.live_repo_e2e_release_checks.result }}" \
|
||||
"docker_e2e_release_checks=${{ needs.docker_e2e_release_checks.result }}" \
|
||||
"package_acceptance_release_checks=${{ needs.package_acceptance_release_checks.result }}" \
|
||||
"qa_lab_parity_lane_release_checks=${{ needs.qa_lab_parity_lane_release_checks.result }}" \
|
||||
"qa_lab_parity_report_release_checks=${{ needs.qa_lab_parity_report_release_checks.result }}" \
|
||||
|
||||
70
.github/workflows/opengrep-precise-full.yml
vendored
Normal file
70
.github/workflows/opengrep-precise-full.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: OpenGrep — Full
|
||||
|
||||
# Manual repository-wide scan for the high-precision OpenGrep rule super-config.
|
||||
# This is intentionally separate from PR scanning so broad/backlog findings do
|
||||
# not block unrelated pull requests.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: opengrep-full-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
name: Scan full repository (precise)
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install opengrep
|
||||
env:
|
||||
# Pin both the install script (by commit SHA) and the binary version.
|
||||
# The script SHA must match the v1.19.0 release tag in opengrep/opengrep
|
||||
# so a compromised or force-pushed `main` cannot RCE in our CI runner.
|
||||
# Bump both together when upgrading.
|
||||
OPENGREP_VERSION: v1.19.0
|
||||
OPENGREP_INSTALL_SHA: 9a4c0a68220618441608cd2bad4ff2eddccf8113
|
||||
run: |
|
||||
curl -fsSL "https://raw.githubusercontent.com/opengrep/opengrep/${OPENGREP_INSTALL_SHA}/install.sh" \
|
||||
| bash -s -- -v "$OPENGREP_VERSION"
|
||||
echo "$HOME/.opengrep/cli/latest" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Verify opengrep
|
||||
run: opengrep --version
|
||||
|
||||
- name: Run full opengrep scan
|
||||
# Manual full scans cover all first-party source paths so maintainers can
|
||||
# audit the complete rulepack without making PRs inherit unrelated backlog.
|
||||
run: |
|
||||
mkdir -p .opengrep-out
|
||||
scripts/run-opengrep.sh --sarif --error
|
||||
|
||||
- name: Upload SARIF to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
# Only upload if the scan actually produced a SARIF file.
|
||||
if: always() && hashFiles('.opengrep-out/precise.sarif') != ''
|
||||
with:
|
||||
sarif_file: .opengrep-out/precise.sarif
|
||||
category: opengrep-full
|
||||
|
||||
- name: Upload SARIF as workflow artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: opengrep-full-sarif
|
||||
path: .opengrep-out/precise.sarif
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
100
.github/workflows/opengrep-precise.yml
vendored
Normal file
100
.github/workflows/opengrep-precise.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: OpenGrep — PR Diff
|
||||
|
||||
# Runs the high-precision OpenGrep rule super-config against only first-party
|
||||
# source paths changed by a pull request. Keeping PR scans diff-scoped makes
|
||||
# findings attributable to the proposed change instead of surfacing unrelated
|
||||
# repository-wide backlog.
|
||||
#
|
||||
# For a repository-wide scan, use the manual OpenGrep — Full workflow.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- ".github/actions/ensure-base-commit/**"
|
||||
- ".github/workflows/opengrep-precise.yml"
|
||||
- ".github/workflows/opengrep-precise-full.yml"
|
||||
- ".semgrepignore"
|
||||
- "apps/**"
|
||||
- "extensions/**"
|
||||
- "packages/**"
|
||||
- "scripts/**"
|
||||
- "security/opengrep/**"
|
||||
- "src/**"
|
||||
|
||||
concurrency:
|
||||
group: opengrep-pr-diff-${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
name: Scan changed paths (precise)
|
||||
if: ${{ !github.event.pull_request.draft }}
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Ensure PR base commit
|
||||
uses: ./.github/actions/ensure-base-commit
|
||||
with:
|
||||
base-sha: ${{ github.event.pull_request.base.sha }}
|
||||
fetch-ref: ${{ github.event.pull_request.base.ref }}
|
||||
|
||||
- name: Install opengrep
|
||||
env:
|
||||
# Pin both the install script (by commit SHA) and the binary version.
|
||||
# The script SHA must match the v1.19.0 release tag in opengrep/opengrep
|
||||
# so a compromised or force-pushed `main` cannot RCE in our CI runner.
|
||||
# Bump both together when upgrading.
|
||||
OPENGREP_VERSION: v1.19.0
|
||||
OPENGREP_INSTALL_SHA: 9a4c0a68220618441608cd2bad4ff2eddccf8113
|
||||
run: |
|
||||
curl -fsSL "https://raw.githubusercontent.com/opengrep/opengrep/${OPENGREP_INSTALL_SHA}/install.sh" \
|
||||
| bash -s -- -v "$OPENGREP_VERSION"
|
||||
echo "$HOME/.opengrep/cli/latest" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Verify opengrep
|
||||
run: opengrep --version
|
||||
|
||||
- name: Run opengrep on PR diff
|
||||
env:
|
||||
OPENCLAW_OPENGREP_BASE_REF: ${{ github.event.pull_request.base.sha }}...HEAD
|
||||
# Findings from precise rules block this workflow. Pull requests scan
|
||||
# changed first-party source paths only so findings stay attributable to
|
||||
# the PR diff. Test/fixture/QA path exclusions live in `.semgrepignore`
|
||||
# at the repo root and are picked up automatically.
|
||||
run: |
|
||||
mkdir -p .opengrep-out
|
||||
scripts/run-opengrep.sh --changed --sarif --error
|
||||
|
||||
- name: Upload SARIF to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
# Only upload if the scan actually produced a SARIF file.
|
||||
if: always() && hashFiles('.opengrep-out/precise.sarif') != ''
|
||||
with:
|
||||
sarif_file: .opengrep-out/precise.sarif
|
||||
category: opengrep-pr-diff
|
||||
|
||||
- name: Upload SARIF as workflow artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: opengrep-pr-diff-sarif
|
||||
path: .opengrep-out/precise.sarif
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
105
.github/workflows/package-acceptance.yml
vendored
105
.github/workflows/package-acceptance.yml
vendored
@@ -64,6 +64,21 @@ on:
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_baseline:
|
||||
description: Published OpenClaw package baseline for the published-upgrade-survivor Docker lane
|
||||
required: false
|
||||
default: openclaw@latest
|
||||
type: string
|
||||
published_upgrade_survivor_baselines:
|
||||
description: Optional baseline list for published-upgrade-survivor; use release-history for last 6 plus key legacy releases
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_scenarios:
|
||||
description: Optional scenario list for published-upgrade-survivor; use reported-issues for known upgrade failure shapes
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
telegram_mode:
|
||||
description: Optional Telegram QA lane for the resolved package candidate
|
||||
required: true
|
||||
@@ -129,6 +144,21 @@ on:
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_baseline:
|
||||
description: Published OpenClaw package baseline for the published-upgrade-survivor Docker lane
|
||||
required: false
|
||||
default: openclaw@latest
|
||||
type: string
|
||||
published_upgrade_survivor_baselines:
|
||||
description: Optional baseline list for published-upgrade-survivor; use release-history for last 6 plus key legacy releases
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_scenarios:
|
||||
description: Optional scenario list for published-upgrade-survivor; use reported-issues for known upgrade failure shapes
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
telegram_mode:
|
||||
description: Optional Telegram QA lane for the resolved package candidate
|
||||
required: false
|
||||
@@ -254,7 +284,7 @@ env:
|
||||
jobs:
|
||||
resolve_package:
|
||||
name: Resolve package candidate
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
outputs:
|
||||
docker_lanes: ${{ steps.profile.outputs.docker_lanes }}
|
||||
@@ -262,8 +292,11 @@ jobs:
|
||||
include_openwebui: ${{ steps.profile.outputs.include_openwebui }}
|
||||
include_release_path_suites: ${{ steps.profile.outputs.include_release_path_suites }}
|
||||
package_artifact_name: ${{ steps.profile.outputs.package_artifact_name }}
|
||||
package_source_sha: ${{ steps.resolve.outputs.package_source_sha }}
|
||||
package_sha256: ${{ steps.resolve.outputs.sha256 }}
|
||||
package_version: ${{ steps.resolve.outputs.package_version }}
|
||||
published_upgrade_survivor_baselines: ${{ steps.upgrade_survivor_baselines.outputs.baselines }}
|
||||
published_upgrade_survivor_scenarios: ${{ inputs.published_upgrade_survivor_scenarios }}
|
||||
telegram_enabled: ${{ steps.profile.outputs.telegram_enabled }}
|
||||
telegram_mode: ${{ steps.profile.outputs.telegram_mode }}
|
||||
steps:
|
||||
@@ -281,8 +314,15 @@ jobs:
|
||||
install-bun: ${{ inputs.source == 'ref' && 'true' || 'false' }}
|
||||
install-deps: "false"
|
||||
|
||||
- name: Download package artifact input
|
||||
if: inputs.source == 'artifact'
|
||||
- name: Download current-run package artifact input
|
||||
if: inputs.source == 'artifact' && inputs.artifact_run_id == ''
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ inputs.artifact_name }}
|
||||
path: .artifacts/package-candidate-input
|
||||
|
||||
- name: Download previous-run package artifact input
|
||||
if: inputs.source == 'artifact' && inputs.artifact_run_id != ''
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
ARTIFACT_RUN_ID: ${{ inputs.artifact_run_id }}
|
||||
@@ -290,10 +330,6 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -z "${ARTIFACT_RUN_ID// }" ]]; then
|
||||
echo "artifact_run_id is required when source=artifact." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${ARTIFACT_NAME// }" ]]; then
|
||||
echo "artifact_name is required when source=artifact." >&2
|
||||
exit 1
|
||||
@@ -350,10 +386,10 @@ jobs:
|
||||
docker_lanes="npm-onboard-channel-agent gateway-network config-reload"
|
||||
;;
|
||||
package)
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch bundled-channel-deps-compat plugins-offline plugin-update"
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor bundled-channel-deps-compat plugins-offline plugin-update"
|
||||
;;
|
||||
product)
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch bundled-channel-deps-compat plugins plugin-update mcp-channels cron-mcp-cleanup openai-web-search-minimal openwebui"
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor bundled-channel-deps-compat plugins plugin-update mcp-channels cron-mcp-cleanup openai-web-search-minimal openwebui"
|
||||
include_openwebui=true
|
||||
;;
|
||||
full)
|
||||
@@ -391,6 +427,44 @@ jobs:
|
||||
echo "package_artifact_name=${PACKAGE_ARTIFACT_NAME}"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Resolve published upgrade survivor baselines
|
||||
id: upgrade_survivor_baselines
|
||||
env:
|
||||
FALLBACK_BASELINE: ${{ inputs.published_upgrade_survivor_baseline }}
|
||||
REQUESTED_BASELINES: ${{ inputs.published_upgrade_survivor_baselines }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -z "${REQUESTED_BASELINES// }" ]]; then
|
||||
echo "baselines=" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
releases_json=""
|
||||
npm_versions_json=""
|
||||
if [[ "$REQUESTED_BASELINES" == *"release-history"* ]]; then
|
||||
releases_json=".artifacts/package-candidate-input/openclaw-releases.json"
|
||||
npm_versions_json=".artifacts/package-candidate-input/openclaw-npm-versions.json"
|
||||
mkdir -p "$(dirname "$releases_json")"
|
||||
gh release list --repo "$GITHUB_REPOSITORY" --limit 100 --json tagName,publishedAt,isPrerelease > "$releases_json"
|
||||
npm view openclaw versions --json > "$npm_versions_json"
|
||||
fi
|
||||
args=(
|
||||
--requested "$REQUESTED_BASELINES"
|
||||
--fallback "$FALLBACK_BASELINE"
|
||||
--github-output "$GITHUB_OUTPUT"
|
||||
)
|
||||
if [[ -n "$releases_json" ]]; then
|
||||
args+=(
|
||||
--releases-json "$releases_json"
|
||||
--npm-versions-json "$npm_versions_json"
|
||||
--history-count 6
|
||||
--include-version 2026.4.23
|
||||
--pre-date 2026-03-15T00:00:00Z
|
||||
)
|
||||
fi
|
||||
node scripts/resolve-upgrade-survivor-baselines.mjs "${args[@]}" >/dev/null
|
||||
|
||||
- name: Upload package-under-test artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
@@ -409,6 +483,9 @@ jobs:
|
||||
SOURCE: ${{ inputs.source }}
|
||||
SUITE_PROFILE: ${{ inputs.suite_profile }}
|
||||
WORKFLOW_REF: ${{ inputs.workflow_ref }}
|
||||
PUBLISHED_UPGRADE_SURVIVOR_BASELINE: ${{ inputs.published_upgrade_survivor_baseline }}
|
||||
PUBLISHED_UPGRADE_SURVIVOR_BASELINES: ${{ steps.upgrade_survivor_baselines.outputs.baselines }}
|
||||
PUBLISHED_UPGRADE_SURVIVOR_SCENARIOS: ${{ inputs.published_upgrade_survivor_scenarios }}
|
||||
shell: bash
|
||||
run: |
|
||||
{
|
||||
@@ -422,6 +499,9 @@ jobs:
|
||||
echo "- Version: \`${PACKAGE_VERSION}\`"
|
||||
echo "- SHA-256: \`${PACKAGE_SHA256}\`"
|
||||
echo "- Profile: \`${SUITE_PROFILE}\`"
|
||||
echo "- Published upgrade survivor baseline: \`${PUBLISHED_UPGRADE_SURVIVOR_BASELINE}\`"
|
||||
echo "- Published upgrade survivor baselines: \`${PUBLISHED_UPGRADE_SURVIVOR_BASELINES}\`"
|
||||
echo "- Published upgrade survivor scenarios: \`${PUBLISHED_UPGRADE_SURVIVOR_SCENARIOS}\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
docker_acceptance:
|
||||
@@ -434,6 +514,9 @@ jobs:
|
||||
include_release_path_suites: ${{ needs.resolve_package.outputs.include_release_path_suites == 'true' }}
|
||||
include_openwebui: ${{ needs.resolve_package.outputs.include_openwebui == 'true' }}
|
||||
docker_lanes: ${{ needs.resolve_package.outputs.docker_lanes }}
|
||||
published_upgrade_survivor_baseline: ${{ inputs.published_upgrade_survivor_baseline }}
|
||||
published_upgrade_survivor_baselines: ${{ needs.resolve_package.outputs.published_upgrade_survivor_baselines }}
|
||||
published_upgrade_survivor_scenarios: ${{ needs.resolve_package.outputs.published_upgrade_survivor_scenarios }}
|
||||
package_artifact_name: ${{ needs.resolve_package.outputs.package_artifact_name }}
|
||||
include_live_suites: ${{ needs.resolve_package.outputs.include_live_suites == 'true' }}
|
||||
live_models_only: false
|
||||
@@ -493,7 +576,7 @@ jobs:
|
||||
package_spec: ${{ inputs.package_spec }}
|
||||
package_artifact_name: ${{ needs.resolve_package.outputs.package_artifact_name }}
|
||||
package_label: openclaw@${{ needs.resolve_package.outputs.package_version }}
|
||||
harness_ref: ${{ inputs.source == 'ref' && inputs.package_ref || inputs.workflow_ref }}
|
||||
harness_ref: ${{ needs.resolve_package.outputs.package_source_sha || inputs.workflow_ref }}
|
||||
provider_mode: ${{ needs.resolve_package.outputs.telegram_mode }}
|
||||
scenario: ${{ inputs.telegram_scenarios }}
|
||||
secrets:
|
||||
@@ -505,7 +588,7 @@ jobs:
|
||||
name: Verify package acceptance
|
||||
needs: [resolve_package, docker_acceptance, package_telegram]
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Verify package acceptance results
|
||||
|
||||
23
.github/workflows/parity-gate.yml
vendored
23
.github/workflows/parity-gate.yml
vendored
@@ -1,18 +1,10 @@
|
||||
name: Parity gate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, ready_for_review]
|
||||
paths:
|
||||
- "extensions/qa-lab/**"
|
||||
- "extensions/qa-channel/**"
|
||||
- "extensions/openai/**"
|
||||
- "qa/scenarios/**"
|
||||
- "src/agents/**"
|
||||
- "src/context-engine/**"
|
||||
- "src/gateway/**"
|
||||
- "src/media/**"
|
||||
- ".github/workflows/parity-gate.yml"
|
||||
schedule:
|
||||
- cron: "17 3 * * *"
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -25,7 +17,6 @@ concurrency:
|
||||
jobs:
|
||||
parity-gate:
|
||||
name: Run the OpenAI / Opus 4.6 parity gate against the qa-lab mock
|
||||
if: ${{ github.event.pull_request.draft != true }}
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
@@ -42,7 +33,7 @@ jobs:
|
||||
# followthrough gate that expects a fast post-approval read within a 30s
|
||||
# agent.wait timeout.
|
||||
QA_PARITY_CONCURRENCY: "1"
|
||||
OPENCLAW_CI_OPENAI_MODEL: ${{ vars.OPENCLAW_CI_OPENAI_MODEL }}
|
||||
OPENCLAW_CI_OPENAI_MODEL: ${{ vars.OPENCLAW_CI_OPENAI_MODEL || 'openai/gpt-5.5' }}
|
||||
OPENCLAW_QA_TRANSPORT_READY_TIMEOUT_MS: "180000"
|
||||
OPENAI_API_KEY: ""
|
||||
ANTHROPIC_API_KEY: ""
|
||||
@@ -57,9 +48,11 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v6
|
||||
|
||||
15
.github/workflows/plugin-clawhub-release.yml
vendored
15
.github/workflows/plugin-clawhub-release.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
ref_sha: ${{ steps.ref.outputs.sha }}
|
||||
ref_revision: ${{ steps.ref.outputs.sha }}
|
||||
has_candidates: ${{ steps.plan.outputs.has_candidates }}
|
||||
candidate_count: ${{ steps.plan.outputs.candidate_count }}
|
||||
skipped_published_count: ${{ steps.plan.outputs.skipped_published_count }}
|
||||
@@ -44,6 +44,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -150,7 +151,8 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preview_plugins_clawhub.outputs.ref_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.preview_plugins_clawhub.outputs.ref_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -164,6 +166,7 @@ jobs:
|
||||
- name: Checkout ClawHub CLI source
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: ${{ env.CLAWHUB_REPOSITORY }}
|
||||
ref: ${{ env.CLAWHUB_REF }}
|
||||
path: clawhub-source
|
||||
@@ -187,7 +190,7 @@ jobs:
|
||||
env:
|
||||
CLAWHUB_REGISTRY: ${{ env.CLAWHUB_REGISTRY }}
|
||||
SOURCE_REPO: ${{ github.repository }}
|
||||
SOURCE_COMMIT: ${{ needs.preview_plugins_clawhub.outputs.ref_sha }}
|
||||
SOURCE_COMMIT: ${{ needs.preview_plugins_clawhub.outputs.ref_revision }}
|
||||
SOURCE_REF: ${{ github.ref }}
|
||||
PACKAGE_TAG: ${{ matrix.plugin.publishTag }}
|
||||
PACKAGE_DIR: ${{ matrix.plugin.packageDir }}
|
||||
@@ -209,7 +212,8 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preview_plugins_clawhub.outputs.ref_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.preview_plugins_clawhub.outputs.ref_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -223,6 +227,7 @@ jobs:
|
||||
- name: Checkout ClawHub CLI source
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: ${{ env.CLAWHUB_REPOSITORY }}
|
||||
ref: ${{ env.CLAWHUB_REF }}
|
||||
path: clawhub-source
|
||||
@@ -266,7 +271,7 @@ jobs:
|
||||
env:
|
||||
CLAWHUB_REGISTRY: ${{ env.CLAWHUB_REGISTRY }}
|
||||
SOURCE_REPO: ${{ github.repository }}
|
||||
SOURCE_COMMIT: ${{ needs.preview_plugins_clawhub.outputs.ref_sha }}
|
||||
SOURCE_COMMIT: ${{ needs.preview_plugins_clawhub.outputs.ref_revision }}
|
||||
SOURCE_REF: ${{ github.ref }}
|
||||
PACKAGE_TAG: ${{ matrix.plugin.publishTag }}
|
||||
PACKAGE_DIR: ${{ matrix.plugin.packageDir }}
|
||||
|
||||
9
.github/workflows/plugin-npm-release.yml
vendored
9
.github/workflows/plugin-npm-release.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
ref_sha: ${{ steps.ref.outputs.sha }}
|
||||
ref_revision: ${{ steps.ref.outputs.sha }}
|
||||
has_candidates: ${{ steps.plan.outputs.has_candidates }}
|
||||
candidate_count: ${{ steps.plan.outputs.candidate_count }}
|
||||
matrix: ${{ steps.plan.outputs.matrix }}
|
||||
@@ -54,6 +54,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -151,7 +152,8 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preview_plugins_npm.outputs.ref_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.preview_plugins_npm.outputs.ref_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -185,7 +187,8 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preview_plugins_npm.outputs.ref_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.preview_plugins_npm.outputs.ref_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
|
||||
414
.github/workflows/plugin-prerelease.yml
vendored
Normal file
414
.github/workflows/plugin-prerelease.yml
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
name: Plugin Prerelease
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_ref:
|
||||
description: Branch, tag, or full commit SHA to validate
|
||||
required: false
|
||||
default: main
|
||||
type: string
|
||||
expected_sha:
|
||||
description: Optional full commit SHA that target_ref must resolve to
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
full_release_validation:
|
||||
description: Enable release-only Docker prerelease lanes from Full Release Validation
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: plugin-prerelease-${{ inputs.target_ref }}
|
||||
cancel-in-progress: ${{ inputs.target_ref == 'main' }}
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
jobs:
|
||||
preflight:
|
||||
name: Build plugin prerelease plan
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
outputs:
|
||||
checkout_revision: ${{ steps.manifest.outputs.checkout_revision }}
|
||||
run_plugin_prerelease_suite: ${{ steps.manifest.outputs.run_plugin_prerelease_suite }}
|
||||
run_plugin_prerelease_static: ${{ steps.manifest.outputs.run_plugin_prerelease_static }}
|
||||
plugin_prerelease_static_matrix: ${{ steps.manifest.outputs.plugin_prerelease_static_matrix }}
|
||||
run_plugin_prerelease_node: ${{ steps.manifest.outputs.run_plugin_prerelease_node }}
|
||||
plugin_prerelease_node_matrix: ${{ steps.manifest.outputs.plugin_prerelease_node_matrix }}
|
||||
run_plugin_prerelease_extensions: ${{ steps.manifest.outputs.run_plugin_prerelease_extensions }}
|
||||
plugin_prerelease_extension_matrix: ${{ steps.manifest.outputs.plugin_prerelease_extension_matrix }}
|
||||
run_plugin_prerelease_docker: ${{ steps.manifest.outputs.run_plugin_prerelease_docker }}
|
||||
plugin_prerelease_docker_lanes: ${{ steps.manifest.outputs.plugin_prerelease_docker_lanes }}
|
||||
steps:
|
||||
- name: Checkout target
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.target_ref }}
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Build plugin prerelease manifest
|
||||
id: manifest
|
||||
env:
|
||||
EXPECTED_SHA: ${{ inputs.expected_sha }}
|
||||
FULL_RELEASE_VALIDATION: ${{ inputs.full_release_validation && 'true' || 'false' }}
|
||||
run: |
|
||||
node --input-type=module <<'EOF'
|
||||
import { appendFileSync } from "node:fs";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const createMatrix = (include) => ({ include });
|
||||
const outputPath = process.env.GITHUB_OUTPUT;
|
||||
const checkoutRevision = execFileSync("git", ["rev-parse", "HEAD"], {
|
||||
encoding: "utf8",
|
||||
}).trim();
|
||||
const expectedSha = (process.env.EXPECTED_SHA ?? "").trim();
|
||||
const fullReleaseValidation = process.env.FULL_RELEASE_VALIDATION === "true";
|
||||
if (expectedSha && expectedSha !== checkoutRevision) {
|
||||
console.error(
|
||||
`target_ref resolved to ${checkoutRevision}, expected ${expectedSha}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let pluginPrereleasePlan = { staticChecks: [], dockerLanes: [] };
|
||||
let extensionShards = [];
|
||||
let nodeShards = [];
|
||||
|
||||
try {
|
||||
const { assertPluginPrereleaseTestPlanComplete } = await import(
|
||||
"./scripts/lib/plugin-prerelease-test-plan.mjs"
|
||||
);
|
||||
pluginPrereleasePlan = assertPluginPrereleaseTestPlanComplete();
|
||||
} catch (error) {
|
||||
const errorCode =
|
||||
error && typeof error === "object" && "code" in error ? error.code : "";
|
||||
const moduleUrl =
|
||||
error && typeof error === "object" && "url" in error ? String(error.url) : "";
|
||||
if (
|
||||
errorCode === "ERR_MODULE_NOT_FOUND" &&
|
||||
moduleUrl.endsWith("/scripts/lib/plugin-prerelease-test-plan.mjs")
|
||||
) {
|
||||
console.warn(
|
||||
"Plugin prerelease plan unavailable in target ref; skipping static and Docker plugin prerelease lanes.",
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const { createExtensionTestShards, DEFAULT_EXTENSION_TEST_SHARD_COUNT } = await import(
|
||||
"./scripts/lib/extension-test-plan.mjs"
|
||||
);
|
||||
extensionShards = createExtensionTestShards({
|
||||
shardCount: DEFAULT_EXTENSION_TEST_SHARD_COUNT,
|
||||
}).map((shard) => ({
|
||||
check_name: shard.checkName,
|
||||
extensions_csv: shard.extensionIds.join(","),
|
||||
runner: [0, 1, 2, 3].includes(shard.index)
|
||||
? "blacksmith-8vcpu-ubuntu-2404"
|
||||
: "blacksmith-4vcpu-ubuntu-2404",
|
||||
shard_index: shard.index + 1,
|
||||
task: "extensions-batch",
|
||||
}));
|
||||
} catch (error) {
|
||||
const errorCode =
|
||||
error && typeof error === "object" && "code" in error ? error.code : "";
|
||||
const moduleUrl =
|
||||
error && typeof error === "object" && "url" in error ? String(error.url) : "";
|
||||
if (
|
||||
errorCode === "ERR_MODULE_NOT_FOUND" &&
|
||||
moduleUrl.endsWith("/scripts/lib/extension-test-plan.mjs")
|
||||
) {
|
||||
console.warn(
|
||||
"Extension test plan unavailable in target ref; skipping extension prerelease shards.",
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const { createNodeTestShards } = await import("./scripts/lib/ci-node-test-plan.mjs");
|
||||
nodeShards = createNodeTestShards({
|
||||
includeReleaseOnlyPluginShards: true,
|
||||
})
|
||||
.filter((shard) => shard.shardName === "agentic-plugins")
|
||||
.map((shard) => ({
|
||||
check_name: shard.checkName,
|
||||
runtime: "node",
|
||||
task: "test-shard",
|
||||
shard_name: shard.shardName,
|
||||
configs: shard.configs,
|
||||
includePatterns: shard.includePatterns,
|
||||
runner: shard.runner,
|
||||
}));
|
||||
} catch (error) {
|
||||
const errorCode =
|
||||
error && typeof error === "object" && "code" in error ? error.code : "";
|
||||
const moduleUrl =
|
||||
error && typeof error === "object" && "url" in error ? String(error.url) : "";
|
||||
if (
|
||||
errorCode === "ERR_MODULE_NOT_FOUND" &&
|
||||
moduleUrl.endsWith("/scripts/lib/ci-node-test-plan.mjs")
|
||||
) {
|
||||
console.warn(
|
||||
"Node test plan unavailable in target ref; skipping release-only plugin Node shard.",
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const staticChecks = pluginPrereleasePlan.staticChecks.map((check) => ({
|
||||
check_name: check.checkName,
|
||||
command: check.command,
|
||||
task: check.check,
|
||||
}));
|
||||
const dockerLanes = pluginPrereleasePlan.dockerLanes;
|
||||
const runStatic = staticChecks.length > 0;
|
||||
const runNode = nodeShards.length > 0;
|
||||
const runExtensions = extensionShards.length > 0;
|
||||
const runDocker = fullReleaseValidation && dockerLanes.length > 0;
|
||||
const runSuite = runStatic || runNode || runExtensions || runDocker;
|
||||
|
||||
const manifest = {
|
||||
checkout_revision: checkoutRevision,
|
||||
run_plugin_prerelease_suite: runSuite,
|
||||
run_plugin_prerelease_static: runStatic,
|
||||
plugin_prerelease_static_matrix: createMatrix(staticChecks),
|
||||
run_plugin_prerelease_node: runNode,
|
||||
plugin_prerelease_node_matrix: createMatrix(nodeShards),
|
||||
run_plugin_prerelease_extensions: runExtensions,
|
||||
plugin_prerelease_extension_matrix: createMatrix(extensionShards),
|
||||
run_plugin_prerelease_docker: runDocker,
|
||||
plugin_prerelease_docker_lanes: dockerLanes.join(" "),
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(manifest)) {
|
||||
appendFileSync(
|
||||
outputPath,
|
||||
`${key}=${typeof value === "string" ? value : JSON.stringify(value)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
EOF
|
||||
|
||||
plugin-prerelease-static-shard:
|
||||
permissions:
|
||||
contents: read
|
||||
name: ${{ matrix.check_name }}
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_plugin_prerelease_static == 'true'
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.plugin_prerelease_static_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Run plugin prerelease static shard
|
||||
env:
|
||||
PLUGIN_PRERELEASE_COMMAND: ${{ matrix.command }}
|
||||
PLUGIN_PRERELEASE_TASK: ${{ matrix.task }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Running ${PLUGIN_PRERELEASE_TASK}: ${PLUGIN_PRERELEASE_COMMAND}"
|
||||
bash -c "$PLUGIN_PRERELEASE_COMMAND"
|
||||
|
||||
plugin-prerelease-node-shard:
|
||||
permissions:
|
||||
contents: read
|
||||
name: ${{ matrix.check_name }}
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_plugin_prerelease_node == 'true'
|
||||
runs-on: ${{ matrix.runner || 'ubuntu-24.04' }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.plugin_prerelease_node_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Configure Node test resources
|
||||
run: echo "OPENCLAW_VITEST_MAX_WORKERS=2" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run release-only plugin Node shard
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
OPENCLAW_NODE_TEST_CONFIGS_JSON: ${{ toJson(matrix.configs) }}
|
||||
OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON: ${{ toJson(matrix.includePatterns) }}
|
||||
OPENCLAW_VITEST_SHARD_NAME: ${{ matrix.shard_name }}
|
||||
OPENCLAW_TEST_PROJECTS_PARALLEL: "2"
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node --input-type=module <<'EOF'
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
const configs = JSON.parse(process.env.OPENCLAW_NODE_TEST_CONFIGS_JSON ?? "[]");
|
||||
if (!Array.isArray(configs) || configs.length === 0) {
|
||||
console.error("Missing node test shard configs");
|
||||
process.exit(1);
|
||||
}
|
||||
const includePatterns = JSON.parse(
|
||||
process.env.OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON ?? "null",
|
||||
);
|
||||
const childEnv = { ...process.env };
|
||||
if (Array.isArray(includePatterns) && includePatterns.length > 0) {
|
||||
const includeFile = join(
|
||||
process.env.RUNNER_TEMP ?? ".",
|
||||
`node-test-include-${process.env.GITHUB_JOB ?? "local"}-${Date.now()}.json`,
|
||||
);
|
||||
writeFileSync(includeFile, JSON.stringify(includePatterns), "utf8");
|
||||
childEnv.OPENCLAW_VITEST_INCLUDE_FILE = includeFile;
|
||||
}
|
||||
|
||||
const result = spawnSync(
|
||||
"pnpm",
|
||||
["exec", "node", "scripts/test-projects.mjs", ...configs],
|
||||
{
|
||||
env: childEnv,
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
process.exit(result.status ?? 1);
|
||||
EOF
|
||||
|
||||
plugin-prerelease-extension-shard:
|
||||
permissions:
|
||||
contents: read
|
||||
name: ${{ matrix.check_name }}
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_plugin_prerelease_extensions == 'true'
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.plugin_prerelease_extension_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
install-bun: "false"
|
||||
|
||||
- name: Run extension shard
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
OPENCLAW_EXTENSION_BATCH_PARALLEL: 2
|
||||
OPENCLAW_VITEST_MAX_WORKERS: 1
|
||||
OPENCLAW_EXTENSION_BATCH: ${{ matrix.extensions_csv }}
|
||||
run: pnpm test:extensions:batch -- "$OPENCLAW_EXTENSION_BATCH"
|
||||
|
||||
plugin-prerelease-docker-suite:
|
||||
name: plugin-prerelease-docker-suite
|
||||
needs: [preflight]
|
||||
if: ${{ inputs.full_release_validation && needs.preflight.outputs.run_plugin_prerelease_docker == 'true' }}
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
packages: write
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
include_repo_e2e: false
|
||||
include_release_path_suites: false
|
||||
include_openwebui: false
|
||||
docker_lanes: ${{ needs.preflight.outputs.plugin_prerelease_docker_lanes }}
|
||||
targeted_docker_lane_group_size: 4
|
||||
include_live_suites: false
|
||||
live_models_only: false
|
||||
|
||||
plugin-prerelease-suite:
|
||||
permissions:
|
||||
contents: read
|
||||
name: plugin-prerelease-suite
|
||||
needs:
|
||||
- preflight
|
||||
- plugin-prerelease-static-shard
|
||||
- plugin-prerelease-node-shard
|
||||
- plugin-prerelease-extension-shard
|
||||
- plugin-prerelease-docker-suite
|
||||
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_plugin_prerelease_suite == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Verify plugin prerelease suite
|
||||
env:
|
||||
RUN_STATIC: ${{ needs.preflight.outputs.run_plugin_prerelease_static }}
|
||||
RUN_NODE: ${{ needs.preflight.outputs.run_plugin_prerelease_node }}
|
||||
RUN_EXTENSIONS: ${{ needs.preflight.outputs.run_plugin_prerelease_extensions }}
|
||||
RUN_DOCKER: ${{ needs.preflight.outputs.run_plugin_prerelease_docker }}
|
||||
STATIC_RESULT: ${{ needs.plugin-prerelease-static-shard.result }}
|
||||
NODE_RESULT: ${{ needs.plugin-prerelease-node-shard.result }}
|
||||
EXTENSIONS_RESULT: ${{ needs.plugin-prerelease-extension-shard.result }}
|
||||
DOCKER_RESULT: ${{ needs.plugin-prerelease-docker-suite.result }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
failed=0
|
||||
check_required() {
|
||||
local name="$1"
|
||||
local required="$2"
|
||||
local status="$3"
|
||||
if [ "$required" != "true" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$status" != "success" ]; then
|
||||
echo "::error::${name} ended with ${status}"
|
||||
failed=1
|
||||
fi
|
||||
}
|
||||
|
||||
check_required "plugin-prerelease-static" "$RUN_STATIC" "$STATIC_RESULT"
|
||||
check_required "plugin-prerelease-node" "$RUN_NODE" "$NODE_RESULT"
|
||||
check_required "plugin-prerelease-extensions" "$RUN_EXTENSIONS" "$EXTENSIONS_RESULT"
|
||||
check_required "plugin-prerelease-docker" "$RUN_DOCKER" "$DOCKER_RESULT"
|
||||
exit "$failed"
|
||||
48
.github/workflows/qa-live-transports-convex.yml
vendored
48
.github/workflows/qa-live-transports-convex.yml
vendored
@@ -44,7 +44,7 @@ env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
NODE_VERSION: "24.x"
|
||||
PNPM_VERSION: "10.33.0"
|
||||
OPENCLAW_CI_OPENAI_MODEL: ${{ vars.OPENCLAW_CI_OPENAI_MODEL }}
|
||||
OPENCLAW_CI_OPENAI_MODEL: ${{ vars.OPENCLAW_CI_OPENAI_MODEL || 'openai/gpt-5.5' }}
|
||||
OPENCLAW_BUILD_PRIVATE_QA: "1"
|
||||
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
||||
|
||||
@@ -81,12 +81,13 @@ jobs:
|
||||
needs: authorize_actor
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
outputs:
|
||||
selected_sha: ${{ steps.validate.outputs.selected_sha }}
|
||||
selected_revision: ${{ steps.validate.outputs.selected_revision }}
|
||||
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -98,27 +99,27 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
selected_sha="$(git rev-parse HEAD)"
|
||||
selected_revision="$(git rev-parse HEAD)"
|
||||
trusted_reason=""
|
||||
|
||||
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
|
||||
|
||||
if git merge-base --is-ancestor "$selected_sha" refs/remotes/origin/main; then
|
||||
if git merge-base --is-ancestor "$selected_revision" refs/remotes/origin/main; then
|
||||
trusted_reason="main-ancestor"
|
||||
elif git tag --points-at "$selected_sha" | grep -Eq '^v'; then
|
||||
elif git tag --points-at "$selected_revision" | grep -Eq '^v'; then
|
||||
trusted_reason="release-tag"
|
||||
elif [[ "$INPUT_REF" =~ ^release/[0-9]{4}\.[0-9]+\.[0-9]+$ ]]; then
|
||||
git fetch --no-tags origin "+refs/heads/${INPUT_REF}:refs/remotes/origin/${INPUT_REF}"
|
||||
release_branch_sha="$(git rev-parse "refs/remotes/origin/${INPUT_REF}")"
|
||||
if [[ "$selected_sha" == "$release_branch_sha" ]]; then
|
||||
if [[ "$selected_revision" == "$release_branch_sha" ]]; then
|
||||
trusted_reason="release-branch-head"
|
||||
fi
|
||||
else
|
||||
pr_head_count="$(
|
||||
gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"repos/${GITHUB_REPOSITORY}/commits/${selected_sha}/pulls" \
|
||||
--jq '[.[] | select(.state == "open" and .head.repo.full_name == "'"${GITHUB_REPOSITORY}"'" and .head.sha == "'"${selected_sha}"'")] | length'
|
||||
"repos/${GITHUB_REPOSITORY}/commits/${selected_revision}/pulls" \
|
||||
--jq '[.[] | select(.state == "open" and .head.repo.full_name == "'"${GITHUB_REPOSITORY}"'" and .head.sha == "'"${selected_revision}"'")] | length'
|
||||
)"
|
||||
if [[ "$pr_head_count" != "0" ]]; then
|
||||
trusted_reason="open-pr-head"
|
||||
@@ -126,23 +127,23 @@ jobs:
|
||||
fi
|
||||
|
||||
if [[ -z "$trusted_reason" ]]; then
|
||||
echo "Ref '${INPUT_REF}' resolved to $selected_sha, which is not trusted for this secret-bearing QA run." >&2
|
||||
echo "Ref '${INPUT_REF}' resolved to $selected_revision, which is not trusted for this secret-bearing QA run." >&2
|
||||
echo "Allowed refs must be on main, point to a release tag, match a release branch head, or match an open PR head in ${GITHUB_REPOSITORY}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "selected_sha=$selected_sha" >> "$GITHUB_OUTPUT"
|
||||
echo "selected_revision=$selected_revision" >> "$GITHUB_OUTPUT"
|
||||
echo "trusted_reason=$trusted_reason" >> "$GITHUB_OUTPUT"
|
||||
{
|
||||
echo "Validated ref: \`${INPUT_REF}\`"
|
||||
echo "Resolved SHA: \`$selected_sha\`"
|
||||
echo "Resolved SHA: \`$selected_revision\`"
|
||||
echo "Trust reason: \`$trusted_reason\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
run_mock_parity:
|
||||
name: Run QA Lab parity gate
|
||||
needs: [validate_selected_ref]
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
QA_PARITY_CONCURRENCY: "1"
|
||||
@@ -157,7 +158,8 @@ jobs:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -213,14 +215,15 @@ jobs:
|
||||
name: Run Matrix live QA lane
|
||||
needs: [authorize_actor, validate_selected_ref]
|
||||
if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.matrix_profile == 'all') }}
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -287,7 +290,7 @@ jobs:
|
||||
name: Run Matrix live QA lane (${{ matrix.profile }})
|
||||
needs: [authorize_actor, validate_selected_ref]
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.matrix_profile == 'all' }}
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
environment: qa-live-shared
|
||||
strategy:
|
||||
@@ -303,7 +306,8 @@ jobs:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -368,14 +372,15 @@ jobs:
|
||||
run_live_telegram:
|
||||
name: Run Telegram live QA lane with Convex leases
|
||||
needs: [authorize_actor, validate_selected_ref]
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
@@ -460,14 +465,15 @@ jobs:
|
||||
run_live_discord:
|
||||
name: Run Discord live QA lane with Convex leases
|
||||
needs: [authorize_actor, validate_selected_ref]
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
timeout-minutes: 60
|
||||
environment: qa-live-shared
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
||||
persist-credentials: false
|
||||
ref: ${{ needs.validate_selected_ref.outputs.selected_revision }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node environment
|
||||
|
||||
293
.github/workflows/stale.yml
vendored
293
.github/workflows/stale.yml
vendored
@@ -4,6 +4,32 @@ on:
|
||||
schedule:
|
||||
- cron: "17 3 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
backfill_stale_closures:
|
||||
description: "Close currently stale-eligible issues and PRs with the Barnacle app"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
dry_run:
|
||||
description: "List matching stale-eligible items without closing them"
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
include_issues:
|
||||
description: "Include stale-eligible issues in the backfill"
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
include_prs:
|
||||
description: "Include stale-eligible pull requests in the backfill"
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
max_closures:
|
||||
description: "Maximum items to close when dry_run is false"
|
||||
required: false
|
||||
type: number
|
||||
default: 50
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
@@ -12,6 +38,7 @@ permissions: {}
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.backfill_stale_closures != true }}
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
@@ -35,10 +62,10 @@ jobs:
|
||||
uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: 7
|
||||
days-before-issue-close: 5
|
||||
days-before-pr-stale: 5
|
||||
days-before-pr-close: 3
|
||||
days-before-issue-stale: 14
|
||||
days-before-issue-close: 7
|
||||
days-before-pr-stale: 14
|
||||
days-before-pr-close: 7
|
||||
stale-issue-label: stale
|
||||
stale-pr-label: stale
|
||||
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
|
||||
@@ -95,7 +122,7 @@ jobs:
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: -1
|
||||
days-before-pr-stale: 27
|
||||
days-before-pr-close: 3
|
||||
days-before-pr-close: 7
|
||||
stale-pr-label: stale
|
||||
exempt-pr-labels: maintainer,no-stale,bad-barnacle
|
||||
operations-per-run: 2000
|
||||
@@ -139,10 +166,10 @@ jobs:
|
||||
uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ steps.app-token-fallback.outputs.token }}
|
||||
days-before-issue-stale: 7
|
||||
days-before-issue-close: 5
|
||||
days-before-pr-stale: 5
|
||||
days-before-pr-close: 3
|
||||
days-before-issue-stale: 14
|
||||
days-before-issue-close: 7
|
||||
days-before-pr-stale: 14
|
||||
days-before-pr-close: 7
|
||||
stale-issue-label: stale
|
||||
stale-pr-label: stale
|
||||
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
|
||||
@@ -197,7 +224,7 @@ jobs:
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: -1
|
||||
days-before-pr-stale: 27
|
||||
days-before-pr-close: 3
|
||||
days-before-pr-close: 7
|
||||
stale-pr-label: stale
|
||||
exempt-pr-labels: maintainer,no-stale,bad-barnacle
|
||||
operations-per-run: 2000
|
||||
@@ -213,7 +240,253 @@ jobs:
|
||||
If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.
|
||||
That channel is the escape hatch for high-quality PRs that get auto-closed.
|
||||
|
||||
backfill-stale-closures:
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.backfill_stale_closures == true }}
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v3
|
||||
id: app-token
|
||||
with:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
- name: Backfill stale closures
|
||||
uses: actions/github-script@v9
|
||||
env:
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
INCLUDE_ISSUES: ${{ inputs.include_issues }}
|
||||
INCLUDE_PRS: ${{ inputs.include_prs }}
|
||||
MAX_CLOSURES: ${{ inputs.max_closures }}
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const dayMs = 24 * 60 * 60 * 1000;
|
||||
const dryRun = process.env.DRY_RUN !== "false";
|
||||
const includeIssues = process.env.INCLUDE_ISSUES !== "false";
|
||||
const includePrs = process.env.INCLUDE_PRS !== "false";
|
||||
const maxClosures = Math.max(0, Number(process.env.MAX_CLOSURES || "50"));
|
||||
const nowMs = Date.now();
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
const issueExemptLabels = new Set([
|
||||
"enhancement",
|
||||
"maintainer",
|
||||
"pinned",
|
||||
"security",
|
||||
"no-stale",
|
||||
"bad-barnacle",
|
||||
]);
|
||||
const prExemptLabels = new Set(["maintainer", "no-stale", "bad-barnacle"]);
|
||||
const maintainerAssociations = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
|
||||
const maintainerLogins = new Set([
|
||||
"altaywtf",
|
||||
"BunsDev",
|
||||
"cpojer",
|
||||
"gumadeiras",
|
||||
"hydro13",
|
||||
"hxy91819",
|
||||
"jalehman",
|
||||
"joshavant",
|
||||
"joshp123",
|
||||
"mbelinky",
|
||||
"mukhtharcm",
|
||||
"ngutman",
|
||||
"obviyus",
|
||||
"odysseus0",
|
||||
"onutc",
|
||||
"osolmaz",
|
||||
"sebslight",
|
||||
"sliverp",
|
||||
"steipete",
|
||||
"thewilloftheshadow",
|
||||
"tyler6204",
|
||||
"velvet-shark",
|
||||
"vignesh07",
|
||||
"vincentkoc",
|
||||
"visionik",
|
||||
].map(login => login.toLowerCase()));
|
||||
|
||||
const issueCloseMessage = [
|
||||
"Closing due to inactivity.",
|
||||
"If this is still an issue, please retry on the latest OpenClaw release and share updated details.",
|
||||
"If you are absolutely sure it still happens on the latest release, open a new issue with fresh steps to reproduce.",
|
||||
].join("\n");
|
||||
const prCloseMessage = [
|
||||
"Closing due to inactivity.",
|
||||
"If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.",
|
||||
"That channel is the escape hatch for high-quality PRs that get auto-closed.",
|
||||
].join("\n");
|
||||
|
||||
const hasAny = (labels, exemptLabels) => {
|
||||
for (const label of labels) {
|
||||
if (exemptLabels.has(label)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const isOlderThan = (dateString, days) => {
|
||||
const timestamp = Date.parse(dateString);
|
||||
return Number.isFinite(timestamp) && timestamp < nowMs - days * dayMs;
|
||||
};
|
||||
|
||||
const candidates = [];
|
||||
const skipped = {
|
||||
missingStale: 0,
|
||||
exemptLabel: 0,
|
||||
maintainerAuthor: 0,
|
||||
maintainerAssignee: 0,
|
||||
notOldEnough: 0,
|
||||
disabledType: 0,
|
||||
};
|
||||
|
||||
for await (const response of github.paginate.iterator(github.rest.issues.listForRepo, {
|
||||
owner,
|
||||
repo,
|
||||
state: "open",
|
||||
sort: "updated",
|
||||
direction: "asc",
|
||||
per_page: 100,
|
||||
})) {
|
||||
for (const item of response.data) {
|
||||
const isPr = Boolean(item.pull_request);
|
||||
if ((isPr && !includePrs) || (!isPr && !includeIssues)) {
|
||||
skipped.disabledType += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const labels = new Set((item.labels || []).map(label => label.name));
|
||||
if (!labels.has("stale")) {
|
||||
skipped.missingStale += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const exemptLabels = isPr ? prExemptLabels : issueExemptLabels;
|
||||
if (hasAny(labels, exemptLabels)) {
|
||||
skipped.exemptLabel += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (maintainerAssociations.has(item.author_association)) {
|
||||
skipped.maintainerAuthor += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const assigned = (item.assignees || []).length > 0;
|
||||
const assignedToMaintainer = (item.assignees || []).some(assignee =>
|
||||
maintainerLogins.has(assignee.login.toLowerCase()),
|
||||
);
|
||||
if (assignedToMaintainer) {
|
||||
skipped.maintainerAssignee += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let eligible = false;
|
||||
let lane = "";
|
||||
if (isPr && assigned) {
|
||||
lane = "assigned-pr";
|
||||
eligible = isOlderThan(item.created_at, 34) && isOlderThan(item.updated_at, 7);
|
||||
} else if (isPr) {
|
||||
lane = "unassigned-pr";
|
||||
eligible = isOlderThan(item.updated_at, 7);
|
||||
} else if (assigned) {
|
||||
lane = "assigned-issue";
|
||||
eligible = isOlderThan(item.updated_at, 10);
|
||||
} else {
|
||||
lane = "unassigned-issue";
|
||||
eligible = isOlderThan(item.updated_at, 7);
|
||||
}
|
||||
|
||||
if (!eligible) {
|
||||
skipped.notOldEnough += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.push({
|
||||
number: item.number,
|
||||
title: item.title,
|
||||
lane,
|
||||
isPr,
|
||||
assigned,
|
||||
createdAt: item.created_at,
|
||||
updatedAt: item.updated_at,
|
||||
authorAssociation: item.author_association,
|
||||
url: item.html_url,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const countsByLane = candidates.reduce((counts, candidate) => {
|
||||
counts[candidate.lane] = (counts[candidate.lane] || 0) + 1;
|
||||
return counts;
|
||||
}, {});
|
||||
const selected = candidates.slice(0, maxClosures);
|
||||
|
||||
core.info(`Dry run: ${dryRun}`);
|
||||
core.info(`Candidates: ${candidates.length}`);
|
||||
core.info(`Selected: ${selected.length}`);
|
||||
core.info(`Counts by lane: ${JSON.stringify(countsByLane)}`);
|
||||
core.info(`Skipped: ${JSON.stringify(skipped)}`);
|
||||
for (const candidate of selected) {
|
||||
core.info(`${dryRun ? "Would close" : "Closing"} ${candidate.lane} #${candidate.number}: ${candidate.title} (${candidate.url})`);
|
||||
}
|
||||
|
||||
await core.summary
|
||||
.addHeading("Stale Closure Backfill")
|
||||
.addRaw(`Dry run: ${dryRun}\n\n`)
|
||||
.addRaw(`Candidates: ${candidates.length}\n\n`)
|
||||
.addRaw(`Selected: ${selected.length}\n\n`)
|
||||
.addCodeBlock(JSON.stringify({ countsByLane, skipped }, null, 2), "json")
|
||||
.addTable([
|
||||
[
|
||||
{ data: "Lane", header: true },
|
||||
{ data: "Number", header: true },
|
||||
{ data: "Title", header: true },
|
||||
{ data: "URL", header: true },
|
||||
],
|
||||
...selected.map(candidate => [
|
||||
candidate.lane,
|
||||
String(candidate.number),
|
||||
candidate.title,
|
||||
candidate.url,
|
||||
]),
|
||||
])
|
||||
.write();
|
||||
|
||||
if (dryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const candidate of selected) {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: candidate.number,
|
||||
body: candidate.isPr ? prCloseMessage : issueCloseMessage,
|
||||
});
|
||||
|
||||
if (candidate.isPr) {
|
||||
await github.rest.pulls.update({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: candidate.number,
|
||||
state: "closed",
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.update({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: candidate.number,
|
||||
state: "closed",
|
||||
state_reason: "not_planned",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
lock-closed-issues:
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.backfill_stale_closures != true }}
|
||||
permissions:
|
||||
issues: write
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
|
||||
2
.github/workflows/test-performance-agent.yml
vendored
2
.github/workflows/test-performance-agent.yml
vendored
@@ -129,7 +129,7 @@ jobs:
|
||||
|
||||
- name: Run Codex test performance agent
|
||||
if: steps.gate.outputs.run_agent == 'true'
|
||||
uses: openai/codex-action@v1
|
||||
uses: openai/codex-action@5c3f4ccdb2b8790f73d6b21751ac00e602aa0c02
|
||||
with:
|
||||
openai-api-key: ${{ secrets.OPENCLAW_TEST_PERF_AGENT_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
|
||||
prompt-file: .github/codex/prompts/test-performance-agent.md
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -3,8 +3,10 @@ node_modules
|
||||
.env
|
||||
docker-compose.override.yml
|
||||
docker-compose.extra.yml
|
||||
docker-compose.sandbox.yml
|
||||
dist
|
||||
dist-runtime/
|
||||
dist-sea/
|
||||
pnpm-lock.yaml
|
||||
bun.lock
|
||||
bun.lockb
|
||||
@@ -102,6 +104,8 @@ USER.md
|
||||
.agents/skills/*
|
||||
!.agents/skills/blacksmith-testbox/
|
||||
!.agents/skills/blacksmith-testbox/**
|
||||
!.agents/skills/crabbox/
|
||||
!.agents/skills/crabbox/**
|
||||
!.agents/skills/gitcrawl/
|
||||
!.agents/skills/gitcrawl/**
|
||||
!.agents/skills/openclaw-ghsa-maintainer/
|
||||
@@ -136,6 +140,7 @@ USER.md
|
||||
.agent/*.json
|
||||
!.agent/workflows/
|
||||
/local/
|
||||
/client_secret_*.json
|
||||
package-lock.json
|
||||
.claude/
|
||||
.agent/
|
||||
@@ -185,8 +190,13 @@ changelog/fragments/
|
||||
test/fixtures/openclaw-vitest-unit-report.json
|
||||
analysis/
|
||||
.artifacts/qa-e2e/
|
||||
/runs/
|
||||
/data/rtt.jsonl
|
||||
extensions/qa-lab/web/dist/
|
||||
|
||||
# Generated bundled plugin runtime dependency manifests
|
||||
extensions/**/.openclaw-runtime-deps.json
|
||||
extensions/**/.openclaw-runtime-deps-stamp.json
|
||||
|
||||
# Output dir for scripts/run-opengrep.sh (local opengrep scans)
|
||||
/.opengrep-out/
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"eslint/no-sequences": "error",
|
||||
"eslint/no-self-compare": "error",
|
||||
"eslint/no-shadow": "off",
|
||||
"eslint/no-underscore-dangle": "off",
|
||||
"eslint/no-var": "error",
|
||||
"eslint/no-useless-call": "error",
|
||||
"eslint/no-useless-computed-key": "error",
|
||||
|
||||
95
.semgrepignore
Normal file
95
.semgrepignore
Normal file
@@ -0,0 +1,95 @@
|
||||
# .semgrepignore — single source of truth for paths excluded from
|
||||
# opengrep / semgrep scans run against this repo.
|
||||
#
|
||||
# Syntax: gitignore-style globs (https://git-scm.com/docs/gitignore).
|
||||
# Consumed automatically by `opengrep scan` and `semgrep scan`. The compiled
|
||||
# detector rulepacks under security/opengrep/ and the GitHub Actions workflows
|
||||
# under .github/workflows/opengrep-*.yml all rely on this file rather than
|
||||
# duplicating exclude lists in 50+ places.
|
||||
#
|
||||
# When adding a new test naming convention, fixture directory, or QA-tooling
|
||||
# extension to the codebase, add its glob here so the security rulepacks
|
||||
# stop firing on it. Real product code should never match anything in this
|
||||
# file.
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Standard test file suffixes
|
||||
# ----------------------------------------------------------------------------
|
||||
*.test.*
|
||||
*.spec.*
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Fixture & mock file suffixes (cover both .foo and -foo styles used in repo)
|
||||
# ----------------------------------------------------------------------------
|
||||
*.fixture.*
|
||||
*-fixture.*
|
||||
*-fixtures.*
|
||||
*.mock.*
|
||||
*-mock.*
|
||||
*-mocks.*
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Test helper / harness / support / shared / utils naming conventions
|
||||
# ----------------------------------------------------------------------------
|
||||
*.test-helper.*
|
||||
*.test-helpers.*
|
||||
*-test-helpers.*
|
||||
*.test-harness.*
|
||||
*-test-harness.*
|
||||
*.test-support.*
|
||||
*-test-support.*
|
||||
*.test-shared.*
|
||||
*-test-shared.*
|
||||
*.test-mocks.*
|
||||
*-test-mocks.*
|
||||
*.test-utils.*
|
||||
*-test-utils.*
|
||||
*.test-fixtures.*
|
||||
*-test-fixtures.*
|
||||
*.e2e-test-helpers.*
|
||||
|
||||
# Bare top-of-dir test helper files (e.g. extensions/foo/src/test-helpers.ts)
|
||||
test-helper.*
|
||||
test-helpers.*
|
||||
test-harness.*
|
||||
test-support.*
|
||||
test-shared.*
|
||||
test-utils.*
|
||||
test-mocks.*
|
||||
test-fixtures.*
|
||||
test-fetch.*
|
||||
test-manager-helpers.*
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Test / mock / fixture directories anywhere in the tree
|
||||
# ----------------------------------------------------------------------------
|
||||
__tests__/
|
||||
__mocks__/
|
||||
test/
|
||||
tests/
|
||||
test-fixtures/
|
||||
test-fixture/
|
||||
test-helpers/
|
||||
test-utils/
|
||||
test-support/
|
||||
test-mocks/
|
||||
test-harness/
|
||||
fixtures/
|
||||
mocks/
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# QA tooling — entire QA-only directories and extensions, not product code
|
||||
# ----------------------------------------------------------------------------
|
||||
qa/
|
||||
qa-lab/
|
||||
extensions/qa-*/
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Top-level scripts that drive tests rather than ship product behavior
|
||||
# ----------------------------------------------------------------------------
|
||||
scripts/test-*
|
||||
scripts/run-vitest*
|
||||
scripts/run-tests*
|
||||
scripts/lib/test-*
|
||||
scripts/lib/extension-test-*
|
||||
scripts/lib/vitest-*
|
||||
@@ -72,6 +72,7 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
|
||||
- GH comments with markdown backticks, `$`, or shell snippets: avoid inline double-quoted `--body`; use single quotes or `--body-file`.
|
||||
- PR execution artifacts/screenshots: attach them to the PR, comment, or an external artifact store. Do not add `.github/pr-assets` or other PR-only assets to the repo.
|
||||
- PR review answer must explicitly cover: what bug/behavior we are trying to fix; PR/issue URL(s) and affected endpoint/surface; whether this is the best possible fix, with high-certainty evidence from code, tests, CI, and shipped/current behavior.
|
||||
- When working on an issue or PR, always end the user-facing final answer with the full GitHub URL.
|
||||
- CI polling: exact SHA, needed fields only. Example: `gh api repos/<owner>/<repo>/actions/runs/<id> --jq '{status,conclusion,head_sha,updated_at,name,path}'`.
|
||||
- Post-land wait: minimal. Exact landed SHA only. If superseded on `main`, same-branch `cancel-in-progress` cancellations are expected; stop once local touched-surface proof exists. Never wait for newer unrelated `main` unless asked.
|
||||
- Wait matrix:
|
||||
@@ -141,7 +142,8 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
|
||||
## Docs / Changelog
|
||||
|
||||
- Docs change with behavior/API. Use docs list/read_when hints; docs links per `docs/AGENTS.md`.
|
||||
- Changelog user-facing only; pure test/internal usually no entry.
|
||||
- Docs final answers: when doc files changed, end with the relevant full `https://docs.openclaw.ai/...` URL(s).
|
||||
- Changelog user-facing only; fixing an issue or landing/merging a PR needs one unless pure test/internal.
|
||||
- Changelog placement: active version `### Changes`/`### Fixes`; every added entry must include at least one `Thanks @author` attribution, using credited GitHub username(s). Never add `Thanks @codex`, `Thanks @openclaw`, or `Thanks @steipete`.
|
||||
- Changelog bullets are always single-line. No wrapping/continuation across multiple lines. Long entries stay on one long line so dedupe, PR-ref, and credit-audit tooling work and so the visual style stays uniform.
|
||||
|
||||
@@ -174,7 +176,7 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
|
||||
- Before simulator/emulator testing, check real iOS/Android devices.
|
||||
- "restart iOS/Android apps" = rebuild/reinstall/relaunch, not kill/launch.
|
||||
- SwiftUI: Observation (`@Observable`, `@Bindable`) over new `ObservableObject`.
|
||||
- Mac gateway: use app or `openclaw gateway restart/status --deep`; no ad-hoc tmux gateway. Logs: `./scripts/clawlog.sh`.
|
||||
- Mac gateway: dev watch = `pnpm gateway:watch` (tmux `openclaw-gateway-watch-main`, auto-attach). Noninteractive: `OPENCLAW_GATEWAY_WATCH_ATTACH=0 pnpm gateway:watch`; attach/stop: `tmux attach -t openclaw-gateway-watch-main` / `tmux kill-session -t openclaw-gateway-watch-main`. Managed installs: `openclaw gateway restart/status --deep`. No launchd/ad-hoc tmux. Logs: `./scripts/clawlog.sh`.
|
||||
- Version bump touches: `package.json`, `apps/android/app/build.gradle.kts`, `apps/ios/version.json` + `pnpm ios:version:sync`, macOS `Info.plist`, `docs/install/updating.md`. Appcast only for Sparkle release.
|
||||
- Mobile LAN pairing: plaintext `ws://` loopback-only. Private-network `ws://` needs `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1`; Tailscale/public use `wss://` or tunnel.
|
||||
- A2UI hash `src/canvas-host/a2ui/.bundle.hash`: generated; ignore unless running `pnpm canvas:a2ui:bundle`; commit separately.
|
||||
@@ -182,6 +184,8 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
|
||||
## Ops / Footguns
|
||||
|
||||
- Remote install docs: `docs/install/{exe-dev,fly,hetzner}.md`. Parallels smoke: `$openclaw-parallels-smoke`; Discord roundtrip: `parallels-discord-roundtrip`.
|
||||
- Memory wiki: keep prompt digest tiny. The prompt should only say the wiki exists, prefer `wiki_search` / `wiki_get`, start from `reports/person-agent-directory.md` for people routing, use search modes (`find-person`, `route-question`, `source-evidence`, `raw-claim`) when useful, and verify contact data before use.
|
||||
- People wiki provenance: generated identity, social, contact, and "fun detail" notes need explicit source class/confidence (`maintainer-whois`, Discrawl sample/stat, GitHub profile, maintainer repo file). Do not promote inferred details to facts.
|
||||
- Rebrand/migration/config warnings: run `openclaw doctor`.
|
||||
- Never edit `node_modules`.
|
||||
- Local-only `.agents` ignores: `.git/info/exclude`, not repo `.gitignore`.
|
||||
|
||||
642
CHANGELOG.md
642
CHANGELOG.md
@@ -2,58 +2,517 @@
|
||||
|
||||
Docs: https://docs.openclaw.ai
|
||||
|
||||
## Unreleased
|
||||
## 2026.4.30
|
||||
|
||||
### Changes
|
||||
|
||||
- Dependencies: refresh bundled runtime and plugin dependency pins, including Pi 0.71.1, OpenAI 6.35.0, Codex 0.128.0, Zod 4.4.1, and Matrix 41.4.0. Thanks @mariozechner.
|
||||
- Agents/workspace: add `agents.defaults.skipOptionalBootstrapFiles` for skipping selected optional workspace files during bootstrap without disabling required workspace setup. (#62110) Thanks @mainstay22.
|
||||
- Plugins/CLI: add first-class `git:` plugin installs with ref checkout, commit metadata, normal scanner/staging, and `plugins update` support for recorded git sources. Thanks @badlogic.
|
||||
- Google Meet: add live caption health for Chrome transcribe mode, including caption observer state, transcript counters, last caption text, and recent transcript lines in status and doctor output. Refs #72478. Thanks @DougButdorf.
|
||||
- Voice Call/Google Meet: add Twilio Meet join phase logs around pre-connect DTMF, realtime stream setup, and initial greeting handoff for easier live-call debugging. Thanks @donkeykong91 and @PfanP.
|
||||
- Agents/Codex: default Codex-harness direct source replies to the OpenClaw `message` tool when visible reply delivery is not explicitly configured, keeping channel-visible output as a deliberate tool call. Thanks @pashpashpash.
|
||||
- macOS app: move recent session context rows into a Context submenu while keeping usage and cost details root-level, so the menu bar companion stays compact with many active sessions. Thanks @guti.
|
||||
- Gateway/SDK: add SDK-facing tools.invoke RPC with shared HTTP policy, typed approval/refusal results, and SDK helper support. Refs #74705. Thanks @BunsDev and @ai-hpc.
|
||||
- Discord: keep active buttons, selects, and forms working across Gateway restarts until they expire, so multi-step Discord interactions are less likely to break during upgrades or restarts. Thanks @amknight.
|
||||
- Messages/docs: clarify that `BodyForAgent` is the primary inbound model text while `Body` is the legacy envelope fallback, and add Signal coverage so channel hardening patches target the real prompt path. Refs #66198. Thanks @defonota3box.
|
||||
- Slack: publish a safe default App Home tab view on `app_home_opened` and include the Home tab event in setup manifests. Fixes #11655; refs #52020. Thanks @TinyTb.
|
||||
- Slack: keep track of bot-participated threads across restarts, so ongoing threaded conversations can continue auto-replying after the Gateway is restarted. Thanks @amknight.
|
||||
- Control UI/Usage: add UTC quarter-hour token buckets for the Usage Mosaic and reuse them for hour filtering, keeping the legacy session-span fallback for older summaries. (#74337) Thanks @konanok.
|
||||
- BlueBubbles: add opt-in `channels.bluebubbles.replyContextApiFallback` that fetches the original message from the BlueBubbles HTTP API when the in-memory reply-context cache misses (multi-instance deployments sharing one BB account, post-restart, after long-lived TTL/LRU eviction). Off by default; channel-level setting propagates to accounts that omit the flag through `mergeAccountConfig`; routed through the typed `BlueBubblesClient` so every fetch is SSRF-guarded by the same three-mode policy as every other BB client request; reply-id shape is validated and part-index prefixes (`p:0/<guid>`) are stripped before the request; concurrent webhooks for the same `replyToId` coalesce into one fetch and successful responses populate the reply cache for subsequent hits. Also promotes BlueBubbles attachment download failures from verbose to runtime error so silently-dropped inbound images are visible at default log level, and extends `sanitizeForLog` to redact `?password=…`/`?token=…` query params and `Authorization:` headers before they reach the log sink (CWE-532). (#71820) Thanks @coletebou and @zqchris.
|
||||
- CLI/proxy: add `openclaw proxy validate` so operators can verify effective proxy configuration, proxy reachability, and expected allow/deny destination behavior before deploying proxy-routed OpenClaw commands. (#73438) Thanks @jesse-merhi.
|
||||
- Agents/Codex: default Codex app-server dynamic tools to native-first, keeping OpenClaw integration tools while leaving file, patch, exec, and process ownership to the Codex harness. (#75308) Thanks @pashpashpash.
|
||||
|
||||
### Fixes
|
||||
|
||||
- fix: block workspace CLOUDSDK_PYTHON override and always set trusted interpreter for gcloud. (#74492) Thanks @pgondhi987.
|
||||
- Providers/Z.AI: move the bundled GLM catalog and auth env metadata into the plugin manifest, so `models list --all --provider zai` shows the full known catalog without duplicated runtime seed data. Thanks @shakkernerd.
|
||||
- Providers/Qianfan and Providers/Stepfun: declare setup auth metadata (`api-key` method, `QIANFAN_API_KEY`, `STEPFUN_API_KEY`) in the plugin manifest so onboarding and `models setup` surface the expected env var without falling back to legacy `providerAuthEnvVars` runtime seed data. Thanks @shakkernerd.
|
||||
- fix(infra): block ambient Homebrew env vars from brew resolution. (#74463) Thanks @pgondhi987.
|
||||
- Onboarding/configure: avoid staging every default plugin runtime dependency after config writes, so skipped setup flows only prepare config-selected plugin deps instead of pulling broad feature-plugin packages. Thanks @vincentkoc.
|
||||
- Thinking/providers: resolve bundled provider thinking profiles through lightweight provider policy artifacts when startup-lazy providers are not active, so OpenAI Codex GPT-5.x keeps xhigh available in Gateway session validation. Fixes #74796. Thanks @maxschachere.
|
||||
- Security/Windows: ignore workspace `.env` system-path variables and resolve stale-process `taskkill.exe` from the validated Windows install root, preventing repository-local env files from redirecting cleanup helpers. Thanks @pgondhi987.
|
||||
- CLI/plugins: refresh persisted plugin registry policy in place for `plugins enable` and `plugins disable`, so routine toggles no longer rebuild and hash every plugin source when the target is already indexed. Thanks @vincentkoc.
|
||||
- CLI/plugins: scope install and enable slot selection to the selected plugin manifest/runtime fallback, so plugin installs no longer load every plugin runtime or broad status snapshot just to update memory/context slots. Thanks @vincentkoc.
|
||||
- Plugins/TTS: keep bundled speech-provider discovery available on cold package Gateway paths and add bundled plugin matrix runtime probes for health, readiness, RPC, TTS discovery, and post-ready runtime-deps watchdog coverage. Refs #75283. Thanks @vincentkoc.
|
||||
- Google Meet/Twilio: show delegated voice call ID, DTMF, and intro-greeting state in `googlemeet doctor`, and avoid claiming DTMF was sent when no Meet PIN sequence was configured. Refs #72478. Thanks @DougButdorf.
|
||||
- Plugins/tools: prefer built bundled plugin code during tool discovery and skip channel runtime hydration while preserving companion provider registrations, reducing per-run plugin-tool prep cost without dropping executable plugin tools. Fixes #75290. Thanks @thanos-openclaw.
|
||||
- Voice Call/Twilio: send notify-mode initial TwiML directly in the outbound create-call request while keeping conversation and pre-connect DTMF calls webhook-driven, so one-shot notify calls do not depend on a first-answer webhook fetch. Supersedes #72758. Thanks @tyshepps.
|
||||
- Discord/Slack: defer status-reaction cleanup until run finalization so queued, thinking, tool, and terminal reactions no longer flicker during normal progress updates. (#75582)
|
||||
- Discord/voice: leave Discord voice off for text-only configs unless `channels.discord.voice` is explicitly configured, avoiding default `GuildVoiceStates` traffic and idle gateway CPU pressure for bots that do not use `/vc`. Fixes #73753; refs #74044. Thanks @sanchezm86 and @SecureCloudProjO.
|
||||
- Discord/voice: rerun configured voice auto-join after Discord gateway RESUMED events and ignore already-destroyed stale voice connections during reconnect cleanup, so health-monitor account restarts can rejoin configured channels. Fixes #40665. Thanks @liz709.
|
||||
- Plugins/CLI: reuse the cold manifest registry while building plugin status and inspect reports, so large configured plugin sets no longer rediscover the bundled/plugin registry once per inspect row. Thanks @vincentkoc.
|
||||
- Discord/voice: lengthen the default voice join Ready wait, add configurable `voice.connectTimeoutMs`/`voice.reconnectGraceMs`, and warn before destroying unrecovered disconnected sessions so slow Discord voice handshakes and reconnects no longer fail silently. Fixes #63098; refs #39825 and #65039. Thanks @darealgege, @kzicherman, and @ayochim.
|
||||
- Gateway/health: refresh cached health RPC snapshots when channel runtime state diverges, so Discord and other channel status reads no longer report stale running or connected values until the cache TTL expires. (#75423) Thanks @clawsweeper.
|
||||
- Gateway/sessions: keep session-store reads from running stale prune and entry-count cap maintenance during startup, so oversized stores no longer block chat history readiness after updates while writes and `sessions cleanup --enforce` still preserve the cleanup safeguards. Fixes #70050. Thanks @tangda18.
|
||||
- Security/audit: keep plain `security audit` on the cold config/filesystem path and reserve plugin runtime security collectors for `--deep`, so large plugin installs cannot execute every plugin runtime during routine audits. Thanks @vincentkoc.
|
||||
- Discord/voice: merge configured media-understanding providers such as Deepgram into partial active provider registries, so follow-up voice turns keep transcribing after another media plugin is already active. Fixes #65687. Thanks @OneMintJulep.
|
||||
- WhatsApp: stage `qrcode` through root mirrored runtime dependencies so packaged QR pairing can render from staged plugin-runtime-deps installs. Fixes #75394. Thanks @FelipeX2001.
|
||||
- Discord/voice: apply per-channel Discord `systemPrompt` overrides to voice transcript turns by forwarding the trusted channel prompt through the voice agent run. Fixes #47095. Thanks @qearlyao.
|
||||
- Discord/native commands: send component-only interaction replies from slash command and status handlers instead of treating renderable Discord components as an empty response. Thanks @vincentkoc.
|
||||
- Slack/slash commands: send block-only slash command replies instead of dropping Slack block payloads with no plain-text fallback. Thanks @vincentkoc.
|
||||
- Telegram/messages: derive fallback text from interactive button/select labels before sending button-only payloads, so Telegram replies are not rejected as empty messages. Thanks @vincentkoc.
|
||||
- LINE/messages: send quick-reply-only payloads with fallback option text instead of accepting the payload and returning an empty delivery. Thanks @vincentkoc.
|
||||
- Auto-reply/docking: require `/dock-*` route switches to start from direct chats, so group or channel participants cannot reroute a shared session's future replies into a linked DM. Thanks @vincentkoc.
|
||||
- Discord: keep text-DM main-session route updates pinned to the configured DM owner, matching component interactions so another direct-message sender cannot redirect future main-session replies. Thanks @vincentkoc.
|
||||
- Mattermost/Matrix: keep direct-message main-session route updates pinned to the configured DM owner so paired or temporarily allowed senders cannot redirect future shared-session replies. Thanks @vincentkoc.
|
||||
- Discord: keep SecretRef-backed bot tokens discoverable for message actions without resolving the token during schema generation, and resolve scoped channel SecretRefs before outbound agent message sends even when the tool is built from a config snapshot. Fixes #75324. Thanks @slideshow-dingo and @Conan-Scott.
|
||||
- Updates: run package post-install doctor repair with the managed Gateway service profile and state paths when a daemon is installed, so shell/profile mismatches no longer repair the caller state while the restarted Gateway keeps stale config. Thanks @vincentkoc.
|
||||
- Models/DeepInfra: declare DeepInfra manifest catalog discovery and derive its runtime fallback catalog from the manifest, restoring provider-filtered `models list --all --provider deepinfra` rows without duplicated static model data. Thanks @shakkernerd.
|
||||
- CLI/update: verify managed gateway restarts against the installed service port instead of the caller shell port, so package updates do not report a healthy daemon as failed when profiles use different gateway ports. Thanks @vincentkoc.
|
||||
- Gateway/agent: reject strict `openclaw agent --deliver` requests with missing delivery targets before starting the agent run, so users do not wait for a completed turn that cannot send anywhere. Thanks @vincentkoc.
|
||||
- Setup/import: honor non-interactive `--import-from` onboarding flags by running the migration import path instead of silently completing normal setup without importing anything. Thanks @vincentkoc.
|
||||
- Discord/voice: run voice-channel turns under a voice-output policy that hides the agent `tts` tool and asks for spoken reply text, so `/vc join` sessions synthesize and play agent replies instead of ending with `NO_REPLY`. Fixes #61536. Thanks @aounakram.
|
||||
- Doctor/plugins: keep plain `doctor --non-interactive` from installing bundled plugin runtime dependencies, so headless health checks report missing deps while `doctor --fix` remains the explicit repair path. Thanks @vincentkoc.
|
||||
- Doctor/gateway: require an interactive confirmation before installing or rewriting the Gateway service, so `doctor --fix --non-interactive` can repair plugin/config drift without replacing the operator's launchd/systemd service from a temporary environment. Thanks @vincentkoc.
|
||||
- Plugins/runtime-deps: include packaged OpenClaw identity in bundled plugin loader cache keys, so same-path package upgrades stop reusing stale versioned runtime-deps mirrors. Fixes #75045. Thanks @sahilsatralkar.
|
||||
- Plugin SDK: restore reply-prefix and reply-pipeline helpers on the deprecated root/compat SDK surface so external plugins still using `openclaw/plugin-sdk` do not fail message dispatch after update. Fixes #75171. Thanks @zhangxiliang.
|
||||
- Plugins/runtime-deps: prune inactive same-package versioned runtime-deps roots after bundled dependency repair, so upgrades do not leave old `openclaw-<version>-<hash>` package caches behind after doctor runs. Thanks @vincentkoc.
|
||||
- Plugins/runtime-deps: prune legacy version-scoped plugin runtime-deps roots during bundled dependency repair and cover the path in Package Acceptance's upgrade-survivor matrix, so upgrades from 2026.4.x no longer leave stale per-plugin runtime trees after doctor runs. Thanks @vincentkoc.
|
||||
- Plugins/runtime-deps: keep Gateway startup plugin imports and runtime plugin fallback loads verify-only after startup/config repair planning, so packaged installs no longer spawn package-manager repair from hot paths after readiness. Refs #75283 and #75069. Thanks @brokemac79 and @xiaohuaxi.
|
||||
- Plugins/runtime-deps: treat package.json runtime-deps manifests as supersets when generated materialization metadata is absent, so bundled plugin activation stops restaging already-installed dependency subsets on every activation. Fixes #75429. (#75431) Thanks @loyur.
|
||||
- iMessage: add stdin write callback and error listener to IMessageRpcClient so async EPIPE from a closed child process rejects the pending request instead of crashing the gateway with uncaughtException. Fixes #75438.
|
||||
- MCP/stdio: settle MCP stdio transport send() from the write callback instead of resolving immediately on buffer acceptance, so async write errors reject the promise instead of being lost. Refs #75438.
|
||||
- Process/exec: add stdin error listener in runCommandWithTimeout so EPIPE from a prematurely-exited child is swallowed instead of escaping to uncaughtException. Refs #75438.
|
||||
- Voice Call/realtime: add default-off fast memory/session context for `openclaw_agent_consult`, giving live calls a bounded answer-or-miss path before the full agent consult. Fixes #71849. Thanks @amzzzzzzz.
|
||||
|
||||
- Google Meet: interrupt Realtime provider output when local barge-in clears playback, so command-pair audio stops model speech instead of only restarting Chrome playback. Fixes #73850. (#73834) Thanks @shhtheonlyperson.
|
||||
- Gateway/config: cap oversized plugin-owned schemas in the full `config.schema` response so large installed plugin sets cannot balloon Gateway RSS or crash schema clients. Thanks @vincentkoc.
|
||||
- Plugins/update: skip ClawHub and marketplace plugin updates when the bundled version is newer than the recorded installed version, so `openclaw update` no longer overwrites working bundled plugins with older external packages. Fixes #75447. Thanks @amknight.
|
||||
- Gateway/sessions: use bounded tail reads for sessions-list transcript usage fallbacks and cap bulk title/last-message hydration, keeping large session stores responsive when rows request derived previews. Thanks @vincentkoc.
|
||||
- Gateway/sessions: yield during bulk transcript title/preview hydration and copy compaction checkpoints asynchronously, keeping the Gateway event loop responsive for large session stores and large transcripts. Refs #75330 and #75414. Thanks @amknight.
|
||||
- Gateway/sessions: stream bounded transcript reads for session detail, history, artifacts, compaction, and send/subscribe sequence paths so small Gateway requests no longer materialize large transcripts or OOM on oversized session logs. Thanks @vincentkoc.
|
||||
- Gateway/chat: bound chat-history transcript reads to the requested display window so large session logs no longer OOM the Gateway when clients ask for a small history page. Thanks @vincentkoc.
|
||||
- Voice Call/Twilio: honor stored pre-connect TwiML before realtime webhook shortcuts and reject DTMF sequences outside conversation mode, so Meet PIN entry cannot be skipped or silently dropped. Thanks @donkeykong91 and @PfanP.
|
||||
- Docs/sandboxing: clarify that sandbox setup scripts (`sandbox-setup.sh`, `sandbox-common-setup.sh`, `sandbox-browser-setup.sh`) are only available from a source checkout, and add inline `docker build` commands for npm-installed users so sandbox image setup works without cloning the repo. Fixes #75485. Thanks @amknight.
|
||||
- Google Meet/Voice Call: play Twilio Meet DTMF before opening the realtime media stream and carry the intro as the initial Voice Call message, so the greeting is generated after Meet admits the phone participant instead of racing a live-call TwiML update. Thanks @donkeykong91 and @PfanP.
|
||||
- Google Meet/Voice Call: make Twilio setup preflight honor explicit `--transport twilio` and fail local/private Voice Call webhook URLs, including IPv6 loopback and unique-local forms, before joins. Thanks @donkeykong91 and @PfanP.
|
||||
- Voice Call/Twilio: retry transient 21220 live-call TwiML updates and catch answered-path initial-greeting failures, so a fast answered callback no longer crashes the Gateway or drops the Twilio greeting/listen transition. (#74606) Thanks @Sivan22.
|
||||
- CLI/startup: preserve `OPENCLAW_HIDE_BANNER` banner suppression for route-first startup callers that rely on the default process environment while keeping read-only status/channel paths from repairing bundled plugin runtime dependencies. Refs #75183.
|
||||
- Voice Call/Twilio: register accepted media streams immediately but wait for realtime transcription readiness before speaking the initial greeting, so reconnect grace handling stays live while OpenAI STT startup is no longer starved by TTS. Fixes #75197. (#75257) Thanks @donkeykong91 and @PfanP.
|
||||
- Voice Call CLI: run gateway-delegated `voicecall continue` through operation-id polling and protocol-shaped errors, so long conversational turns keep their transcript result without blocking a single Gateway RPC. (#75459) Thanks @serrurco and @DougButdorf.
|
||||
- Voice Call CLI: delegate operational `voicecall` commands to the running Gateway runtime and skip webhook startup during CLI-only plugin loading, preventing webhook port conflicts and `setup --json` hangs. Fixes #72345. Thanks @serrurco and @DougButdorf.
|
||||
- Agents/pi-embedded-runner: extract the `abortable` provider-call wrapper from `runEmbeddedAttempt` to module scope so its promise handlers no longer close over the run lexical context, releasing transcripts, tool buffers, and subscription callbacks when a provider call hangs past abort. (#74182) Thanks @cjboy007.
|
||||
- Docker: restore `python3` in the gateway runtime image after the slim-runtime switch. Fixes #75041.
|
||||
- Agents/session-repair: fix resumed sessions failing with repeated 400 errors on Anthropic and strict OpenAI-compatible providers (Qwen, mlx-vlm) after an interrupted conversation or blank user input. Fixes #75271 and #75313. Thanks @amknight.
|
||||
- CLI/Voice Call: scope `voicecall` command activation to the Voice Call plugin so setup and smoke checks no longer broad-load unrelated plugin runtimes or hang after printing JSON. Thanks @vincentkoc.
|
||||
- Doctor/plugins: warn when restrictive `plugins.allow` is paired with wildcard or plugin-owned tool allowlists, making the exclusive plugin allowlist behavior visible before users hit empty callable-tool runs. Refs #58009 and #64982. Thanks @KR-Python and @BKF-Gitty.
|
||||
- Google Meet/Voice Call: keep Twilio Meet joins in conversation mode and reuse the realtime intro prompt when no voice-call-specific intro is configured, so answered phone bridge calls speak instead of joining silently. Refs #72478. Thanks @DougButdorf.
|
||||
- Auto-reply/group chats: keep the `message` tool available for message-tool-only visible replies and apply group-scoped tool policy before deciding fallback delivery, so Discord/Slack-style rooms reply visibly in the correct channel after upgrades. Fixes #74842; refs #75207. Thanks @davelutztx and @aa-on-ai.
|
||||
- Agents/commitments: keep inferred follow-ups internal when heartbeat target is none, strip raw source text from stored commitments, disable tools during due-commitment heartbeat turns, bound hidden extraction queue growth, expire stale commitments, and add QA/Docker safety coverage. Thanks @vignesh07.
|
||||
- Telegram/agents: keep typing indicators and optional generation tools off the reply critical path, so fresh Telegram replies no longer stall while provider catalogs and media models load. (#75360) Thanks @obviyus.
|
||||
- Agents/commitments: run hidden follow-up extraction on the configured agent/default model instead of falling back to direct OpenAI, so OpenAI Codex OAuth-only gateways no longer spam background API-key failures. Fixes #75334. Thanks @sene1337.
|
||||
- Agents/media: keep async music generation completions on the requester-session wake path even when direct-send completion is enabled, so finished audio stays agent-mediated while video can still opt into direct channel delivery. (#75335) Thanks @vincentkoc.
|
||||
- Security/config-audit: redact CLI argv and execArgv secrets before persisting config audit records, covering write, observe, and recovery paths. Fixes #60826. Thanks @koshaji.
|
||||
- Gateway/models: keep default and configured model-list views responsive when provider catalog discovery stalls, without hiding real catalog load failures, while `--all` still waits for the exact full catalog. Fixes #75297; refs #74404. Thanks @lisandromachado and @najef1979-code.
|
||||
- Plugins/runtime-deps: accept already materialized package-level runtime-deps supersets as converged, so later lazy plugin activation no longer prunes and relaunches `pnpm install` after gateway startup pre-staging, reducing event-loop pressure from repeated runtime-deps repair on packaged installs. Fixes #75283; refs #75297 and #72338. Thanks @brokemac79, @lisandromachado, and @midhunmonachan.
|
||||
- Plugins/runtime-deps: remove OpenClaw-owned legacy runtime-deps symlinks before replacing staged bundled plugin dependencies, so updates can recover from older symlinked installs instead of failing the symlink safety guard. Thanks @goldmar.
|
||||
- Discord: retry queued REST 429s against learned bucket/global cooldowns and reacquire fresh voice upload URLs after CDN upload rate limits, so outbound sends recover without reusing stale single-use upload URLs. Thanks @discord.
|
||||
- TTS/providers: keep bundled speech-provider compat fallback available when plugins are globally disabled, so cold gateway and CLI startup can still resolve fallback speech providers instead of leaving explicit TTS provider selection with no registered providers. Refs #75265. Thanks @sliekens.
|
||||
- Discord: collapse repeated native slash-command deploy rate-limit startup logs into one non-fatal warning while keeping per-request REST timing in verbose output. Thanks @discord.
|
||||
- Discord: report native slash-command deploy aborts as REST timeouts with method, path, timeout budget, and observed duration, so startup logs explain slow Discord API calls instead of showing a generic aborted operation. Thanks @discord.
|
||||
- Security/logging: redact payment credential field names such as card number, CVC/CVV, shared payment token, and payment credential across default log and tool-payload redaction patterns so wallet-style MCP tools do not expose raw payment credentials in UI events or transcripts. Thanks @stainlu.
|
||||
- Providers/OpenAI Codex: preserve existing wrapped Codex streams during OpenAI attribution so PI OAuth bearer injection reaches ChatGPT/Codex Responses, and strip native Codex-only unsupported payload fields without touching custom compatible endpoints. (#75111) Thanks @keshavbotagent.
|
||||
- Plugins/runtime-deps: materialize newly required bundled plugin packages after local `openclaw onboard` and `openclaw configure` config writes, while keeping remote setup read-only, so first Gateway startup no longer discovers missing channel/provider deps after setup claimed success. Fixes #75309; refs #75069. Thanks @scottgl9 and @xiaohuaxi.
|
||||
- Plugins/runtime-deps: expire stale legacy install locks whose live PID cannot be tied to the current process incarnation, so Docker PID reuse no longer leaves bundled dependency repair stuck behind old `.openclaw-runtime-deps.lock` directories. Fixes #74948; refs #74950 and #74346. Thanks @dchekmarev.
|
||||
- Plugins/runtime-deps: recover interrupted bundled runtime-dependency installs whose package sentinels exist but generated materialization is incomplete, forcing npm/pnpm repair in Gateway startup, doctor, and lazy plugin loads instead of leaving channels crash-looping on missing packages. Fixes #75309; refs #75310, #75296, and #75304. Thanks @scottgl9.
|
||||
- Plugins/runtime-deps: treat no-main and export-map package sentinels without reachable entry files as incomplete, so Gateway startup, doctor, and lazy plugin loads repair interrupted bundled dependency installs instead of accepting package.json-only partial installs. Fixes #75309; refs #75183. Thanks @shakkernerd.
|
||||
- Plugins/runtime-deps: keep runtime inspection and channel maintenance commands from downloading bundled plugin dependencies, route explicit repairs through `openclaw plugins deps --repair`, and still allow Gateway/DO paths to repair missing deps before import. Refs #75069. Thanks @xiaohuaxi.
|
||||
- Updates: force non-deferred, no-cooldown update restarts after package-manager updates requested through the live Gateway control plane and fail release validation on post-swap stale chunk import crashes, so Telegram/Discord imports do not stay pointed at removed dist files. Fixes #75206. Thanks @xonaman and @faux123.
|
||||
- Agents/tool-result guard: use the resolved runtime context token budget for non-context-engine tool-result overflow checks, so long tool-heavy sessions no longer compact early when `contextTokens` is larger than native `contextWindow`. Fixes #74917. Thanks @kAIborg24.
|
||||
- Gateway/systemd: exit with sysexits 78 for supervised lock and `EADDRINUSE` conflicts so `RestartPreventExitStatus=78` stops `Restart=always` restart loops instead of repeatedly reloading plugins against an occupied port. Fixes #75115. Thanks @yhyatt.
|
||||
- Agents/runtime: skip blank visible user prompts at the embedded-runner boundary before provider submission while still allowing internal runtime-only turns and media-only prompts, so Telegram/group sessions no longer leak raw empty-input provider errors when replay history exists. Fixes #74137. Thanks @yelog, @Gracker, and @nhaener.
|
||||
- Agents/Codex: isolate local Codex app-server `CODEX_HOME` and `HOME` per agent and add a deliberate Codex migration path with selectable skill copies, so personal Codex CLI skills, plugins, config, and hooks no longer leak into OpenClaw agents unless the operator migrates them into the workspace. Thanks @pashpashpash.
|
||||
- Security/Nextcloud Talk: make webhook signature validation use the padded timing-safe compare path even when the supplied signature length is wrong, keep normalized header lookup behavior, and extend regression coverage for tampered bodies, wrong secrets, array-backed headers, and truncated signatures. Carries forward earlier contributor work from #50516 by teddytennant. (#58097) Thanks @gavyngong.
|
||||
- Plugins/runtime-deps: replace stale symlinked mirror target roots before writing runtime-mirror temp files and skip rewriting already materialized hardlinks, so cross-version container upgrades no longer crash-loop on read-only image-layer paths while warm mirrors do less churn. Fixes #75108; refs #75069. Thanks @coletebou and @xiaohuaxi.
|
||||
- Auto-reply/group chats: fall back to automatic source delivery when a channel precomputes message-tool-only replies but the `message` tool is unavailable, so Discord/Slack-style group turns do not silently complete without a visible reply. Fixes #74868. Thanks @kagura-agent.
|
||||
- Browser/gateway: share one browser control runtime across the HTTP control server and `browser.request`, and refresh browser profile config from the source snapshot, so CLI status/start honors configured `browser.executablePath`, `headless`, and `noSandbox` instead of falling back to stale auto-detection. Fixes #75087; repairs #73617. Thanks @civiltox and @martingarramon.
|
||||
- Agents/subagents: bound automatic orphan recovery with persisted recovery attempts and a wedged-session tombstone, and teach task maintenance/doctor to reconcile those sessions so restart loops no longer require manual `sessions.json` surgery. Fixes #74864. Thanks @solosage1.
|
||||
- Plugins/runtime-deps: keep bundled provider policy config loading from staging plugin runtime dependencies, so config reads no longer fail on locked-down `/var/lib/openclaw/plugin-runtime-deps` directories. Fixes #74971. Thanks @eurojojo.
|
||||
- Memory/runtime-deps: retain the native `node-llama-cpp` runtime only when local memory search is configured, so packaged installs can repair local embeddings without relying on unreachable global npm installs. Fixes #74777. Thanks @LLagoon3.
|
||||
- Gateway/startup: skip pre-bind web-fetch provider discovery for credential-free `tools.web.fetch` config, so Docker/Kubernetes gateways bind even when optional fetch limits are present. Fixes #74896. Thanks @KoykL.
|
||||
- Signal: match group allowlists against inbound Signal group ids as well as sender ids, and process explicitly configured Signal groups without requiring mentions unless `requireMention` is set. Fixes #53308. Thanks @minupla and @juan-flores077.
|
||||
- Signal: bound `signal-cli` installer release and archive downloads with explicit timeouts, declared and streamed size checks, and partial-file cleanup. Fixes #54153. Thanks @jinduwang1001-max and @juan-flores077.
|
||||
- Slack: require bot-authored room messages with `allowBots=true` to come from an explicitly channel-allowlisted bot or from a room where an explicit Slack owner is present, so broad bot relays cannot run unattended. Fixes #59284. Thanks @andrewhong-translucent.
|
||||
- Signal: derive `getAttachment` HTTP response caps from `channels.signal.mediaMaxMb` with base64 headroom, so inbound photos and videos no longer drop behind the 1 MiB RPC default. Fixes #73564. Thanks @heyhudson.
|
||||
- Signal: keep the long-lived receive SSE monitor open while idle instead of applying the 10s RPC/check deadline, so `signal-cli` 0.14.3 event streams no longer reconnect before inbound messages arrive. Fixes #74741. Thanks @fgabelmannjr and @k7n4n5t3w4rt.
|
||||
- CLI/progress: suppress nested progress spinners and line clears while TUI input owns raw stdin, so Crestodian `/status` no longer disturbs the active input row. (#75003) Thanks @velvet-shark.
|
||||
- Models/OpenAI Codex: restore `openai-codex/gpt-5.4-mini` for ChatGPT/Codex OAuth PI runs after live OAuth proof, and align the manifest, forward-compat metadata, docs, and regression tests so stale cron and heartbeat configs resolve again. Fixes #74451. Thanks @0xCyda, @hclsys, and @Marvae.
|
||||
- Plugins/runtime-deps: always write a dependency map in generated runtime-deps install manifests, so npm does not crash or prune staged bundled-plugin packages when the plan is empty. Fixes #74949. Thanks @hclsys.
|
||||
- Telegram: use durable message edits for streaming previews instead of native draft state, so generated replies no longer flicker through draft-to-message transitions that look like duplicates. (#75073) Thanks @obviyus.
|
||||
- Telegram: echo preflighted DM voice-note transcripts back to the originating chat, including Telegram DM topic thread metadata, instead of only echoing later media-understanding transcripts. Fixes #75084. Thanks @M-Lietz.
|
||||
- Telegram: clamp low long-polling client timeouts so configured `timeoutSeconds` values below the `getUpdates` poll window no longer force a fresh HTTPS connection every few seconds. Fixes #75114. Thanks @hpinho77.
|
||||
- Web search: describe `web_search` as using the configured provider instead of hard-coding Brave when DuckDuckGo or another provider is active. Fixes #75088. Thanks @sun-rongyang.
|
||||
- Infra/tmp: tolerate concurrent temp-dir permission repairs by rechecking directories that another process already tightened, so parallel ACP subprocess startup no longer throws `Unsafe fallback OpenClaw temp dir`. Fixes #66867. Thanks @Kane808-AI and @jarvisz8.
|
||||
- Agents/compaction: add an opt-in `agents.defaults.compaction.midTurnPrecheck` mid-turn precheck that detects tool-loop context pressure and triggers compaction before the next tool call instead of waiting for end-of-turn. (#73499) Thanks @marchpure and @haoxingjun.
|
||||
- Gateway/approvals: let loopback token/password-backed native approval clients resolve exec approvals without attaching stale paired Gateway identities, while remote and unauthenticated approval clients keep normal device identity behavior. (#74472)
|
||||
- Gateway/config: include rejected validation paths in foreground and service last-known-good recovery logs plus main-agent notices, so unsupported direct edits explain which key caused restore instead of looking like silent reversion. Fixes #75060. Thanks @amknight.
|
||||
- Plugins/runtime-deps: hash the OS-canonical `packageRoot` via `fs.realpathSync.native` (with `path.resolve` fallback) when computing the bundled runtime-deps stage key, so loader and channel `bundled-root` callers no longer derive divergent stage directories under `~/.openclaw/plugin-runtime-deps/openclaw-<version>-<hash>/` and bundled channels stop failing with `ENOENT` on shared dist chunks under Windows npm symlinks, junctions, or PM2 multi-instance worker layouts. Fixes #74963. (#75048) Thanks @openperf and @vincentkoc.
|
||||
- fix(logging): add redaction patterns for Tencent Cloud, Alibaba Cloud, HuggingFace and Replicate API keys (#58162). Thanks @gavyngong
|
||||
- Pairing: surface unexpected allowlist filesystem stat errors instead of treating the allowlist as missing, so permission and I/O failures are visible during pairing authorization checks. (#63324) Thanks @franciscomaestre.
|
||||
- macOS app: reserve layout space for exec approval command details so the allow dialog no longer overlaps the command, context, and action buttons. (#75470) Thanks @ngutman.
|
||||
- Agents/failover: carry `sessionId`, `lane`, `provider`, `model`, and `profileId` attribution through `FailoverError` and `describeFailoverError`/`coerceToFailoverError` so structured error logs (e.g. `gateway.err.log` ingestion) can attribute exhausted-fallback wrapper errors to the originating session and last-attempted provider instead of dropping the metadata after the per-profile errors. Fixes #42713. (#73506) Thanks @wenxu007.
|
||||
- Context Engine: treat assembled prompt as the default authority for preemptive overflow prechecks so engines that return a windowed, self-contained context no longer trigger false hard-fail compactions on huge raw history. Engines whose assembled view can hide overflow risk can opt back into the legacy behavior with `AssembleResult.promptAuthority: "preassembly_may_overflow"`. (#74255) Thanks @100yenadmin.
|
||||
- Mattermost: refresh current native slash command registrations before accepting callbacks so stale tokens from deleted or regenerated commands stop being accepted without a gateway restart while failed validations stay briefly cached and lookup starts are rate-limited per command, gate each callback against the resolved command's own startup token so a token leaked for one slash command cannot poison another command's failure cache, redact slash validation lookup errors, and add a body read timeout to the multi-account routing path so slow callback senders cannot tie up the dispatcher. Thanks @feynman-hou and @eleqtrizit.
|
||||
|
||||
## 2026.4.29
|
||||
|
||||
### Highlights
|
||||
|
||||
- Messaging and automation get active-run steering by default, visible-reply enforcement, spawned subagent routing metadata, and opt-in follow-up commitments for heartbeat-delivered reminders. Thanks @vincentkoc, @scoootscooob, @samzong, and @vignesh07.
|
||||
- Memory grows into a people-aware wiki with provenance views, per-conversation Active Memory filters, partial recall on timeout, and bounded REM preview diagnostics. Thanks @vincentkoc, @quengh, @joeykrug, and @samzong.
|
||||
- Provider/model coverage expands with NVIDIA onboarding/catalogs plus faster manifest-backed model/auth paths, Bedrock Opus 4.7 thinking parity, and safer Codex/OpenAI-compatible replay and streaming behavior. Thanks @eleqtrizit, @shakkernerd, @prasad-yashdeep, @woodhouse-bot, and @LyHug.
|
||||
- Gateway and packaged-plugin reliability focuses on slow-host startup, reusable model catalogs, event-loop readiness diagnostics, runtime-dependency repair, stale-session recovery, and version-scoped update caches. Thanks @lpendeavors, @DerFlash, @vincentkoc, @pashpashpash, and @jhsmith409.
|
||||
- Channel fixes cluster around Slack Block Kit limits, Telegram proxy/webhook/polling/send resilience, Discord startup/rate-limit handling, WhatsApp delivery/liveness, and Microsoft Teams/Matrix/Feishu edge cases. Thanks @slackapi, @SymbolStar, @djgeorg3, @TinyTb, @dseravalli, @nklock, and @alex-xuweilong.
|
||||
- Security and operations add OpenGrep scanning, sharper GHSA triage policy, safer exec/pairing/owner-scope handling, Docker/onboarding automation, and web-fetch IPv6 ULA opt-in for trusted proxy stacks. Thanks @jesse-merhi, @pgondhi987, @mmaps, @jinjimz, and @jeffrey701.
|
||||
|
||||
### Changes
|
||||
|
||||
- Security/tools: configured tool sections (`tools.exec`, `tools.fs`) no longer implicitly widen restrictive profiles (`messaging`, `minimal`). Users who need those tools under a restricted profile must add explicit `alsoAllow` entries; a startup warning identifies affected configs. Fixes #47487. Thanks @amknight.
|
||||
- Gateway/SDK: add SDK-facing artifact list/get/download RPCs and App SDK helpers with transcript provenance and download-source guardrails. Refs #74706. Thanks @tmimmanuel.
|
||||
- Agents/commitments: add opt-in inferred follow-up commitments with hidden batched extraction, per-agent/per-channel scoping, heartbeat delivery, CLI management, a simple `commitments.enabled`/`commitments.maxPerDay` config, and heartbeat-interval due-time clamping so magical check-ins do not echo immediately. (#74189) Thanks @vignesh07.
|
||||
- Messages/queue: make `steer` drain all pending Pi steering messages at the next model boundary, keep legacy one-at-a-time steering as `queue`, and add a dedicated steering queue docs page. Thanks @vincentkoc.
|
||||
- Messages/queue: default active-run queueing to `steer` with a 500ms followup fallback debounce, and document the queue modes, precedence, and drop policies on the command queue page. Thanks @vincentkoc.
|
||||
- Messages: add global `messages.visibleReplies` so operators can require visible output to go through `message(action=send)` for any source chat, while `messages.groupChat.visibleReplies` stays available as the group/channel override. Thanks @scoootscooob.
|
||||
- Gateway/events: surface `spawnedBy` on subagent chat and agent broadcast payloads so clients can route child session events without an extra session lookup. (#63244) Thanks @samzong.
|
||||
- Memory/wiki: add agent-facing people wiki metadata, canonical aliases, person cards, relationship graphs, privacy/provenance reports, evidence-kind drilldown, and search modes for person lookup, question routing, source evidence, and raw claims. Thanks @vincentkoc.
|
||||
- Active Memory: add optional per-conversation `allowedChatIds` and `deniedChatIds` filters so operators can enable recall only for selected direct, group, or channel conversations while keeping broad sessions skipped. (#67977) Thanks @quengh.
|
||||
- Active Memory: return bounded partial recall summaries when the hidden memory sub-agent times out, including the default temporary-transcript path, so useful recovered context is not discarded. (#73219) Thanks @joeykrug.
|
||||
- Gateway/memory: add a read-only `doctor.memory.remHarness` RPC so operator clients can preview bounded REM dreaming output without running mutation paths. (#66673) Thanks @samzong.
|
||||
- Providers/NVIDIA: add the NVIDIA provider with API-key onboarding, setup docs, static catalog metadata, and literal model-ref picker support so NVIDIA hosted models can be selected with their provider prefix intact. (#71204) Thanks @eleqtrizit.
|
||||
- Models: suppress explicitly configured openai-codex/gpt-5.4-mini inline entries so a stale models config written by `openclaw doctor --fix` cannot bypass the manifest capability block and cause repeated assistant-turn failures when the runtime switches to that model on ChatGPT-backed Codex accounts. Conditional suppressions (e.g. qwen Coding Plan endpoint guards) remain bypassable by explicit user configuration. (#74451) Thanks @0xCyda, @hclsys, and @Marvae.
|
||||
- Added SQLite-backed plugin state store (`api.runtime.state.openKeyedStore`) for restart-safe keyed registries with TTL, eviction, and automatic plugin isolation. Thanks @amknight.
|
||||
- Plugin SDK: mark remaining legacy alias exports and diffs tool/config aliases with deprecation metadata, and add a guard so future legacy alias comments require `@deprecated` tags. Thanks @vincentkoc.
|
||||
- CLI/QR/dependencies: internalize small terminal progress and QR wrapper helpers while keeping the real QR encoder dependency direct, reducing the default runtime dependency graph without changing QR output behavior. Thanks @vincentkoc.
|
||||
- Dependencies: refresh workspace runtime, plugin, and tooling packages, including ACP, Pi, AWS SDK, TypeBox, pnpm, oxlint, oxfmt, jsdom, pdfjs, ciao, and tokenjuice, while keeping patched ACP behavior and lint gates current. Thanks @mariozechner.
|
||||
- Gateway/dev: run `pnpm gateway:watch` through a named tmux session by default, with `gateway:watch:raw` and `OPENCLAW_GATEWAY_WATCH_TMUX=0` for foreground mode, so repeated starts respawn an inspectable watcher without trapping the invoking agent shell. Thanks @vincentkoc.
|
||||
- Gateway/diagnostics: emit an opt-in startup diagnostics timeline that records gateway lifecycle and plugin-load phases behind a config flag, so slow-start diagnosis no longer requires bespoke instrumentation. Thanks @shakkernerd.
|
||||
- Control UI/i18n: extend the locale registry with new Persian (fa), Dutch (nl), Vietnamese (vi), Italian (it), Arabic (ar), and Thai (th) entries and ship `fa`, `nl`, `vi`, and `zh-TW` docs glossaries, so the docs translation pipeline and the Control UI language picker stay aligned across surfaces. Thanks @vincentkoc.
|
||||
- Channels: add Yuanbao channel docs entrance so the Tencent Yuanbao bot appears in the channel listing and sidebar navigation. (#73443) Thanks @loongfay.
|
||||
- Channels/Yuanbao: update plugin GitHub location to YuanbaoTeam/yuanbao-openclaw-plugin and add "yuanbao" alias to channel catalog. (#74253) Thanks @loongfay.
|
||||
- Docker setup: add `OPENCLAW_SKIP_ONBOARDING` so automated Docker installs can skip the interactive onboarding step while still applying gateway defaults. (#55518) Thanks @jinjimz.
|
||||
- Security policy: classify media/base64 decode and format-conversion overhead after configured acceptance limits as performance-only for GHSA triage unless a report demonstrates a limit bypass, crash, exhaustion, data exposure, or another boundary bypass. (#74311)
|
||||
- Security/OpenGrep: add a precise OpenGrep rulepack, source-rule compiler, provenance metadata check, and PR/full scan workflows that validate first-party code and rulepack-only changes while uploading SARIF to GitHub Code Scanning. (#69483) Thanks @jesse-merhi.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Voice Call: resolve SecretRef-backed Twilio auth tokens and realtime/streaming provider API keys before initializing call providers, so SecretRef-backed voice-call credentials reach runtime as strings. (#73632) Thanks @VACInc.
|
||||
- Security/outbound: strip re-formed HTML tags during plain-text sanitization so nested tag fragments cannot leave a CodeQL-detected `<script>` sequence behind. Thanks @vincentkoc.
|
||||
- Security/secrets: compare credential bytes with padded timing-safe buffers instead of hashing candidate passwords before equality checks. Thanks @vincentkoc.
|
||||
- Security/QQBot: sanitize debug log arguments before writing to `console.*`, so gateway payload fields cannot forge extra log lines when debug logging is enabled. Thanks @vincentkoc.
|
||||
- QQBot: unify slash command auth and c2cOnly gating in the command registry, pass `allowQQBotDataDownloads` when sending slash command file attachments, align clear-storage with actual downloads directory, and add `/bot-me` to display sender user ID. (#73616) Thanks @cxyhhhhh.
|
||||
- CLI/agents/status: keep `openclaw agents`, text `agents list`, and plain text `status` on read-only metadata paths so human output no longer preloads plugin runtimes or live channel scans before printing. Fixes #74195. Thanks @NianJiuZst.
|
||||
- Agents/local models: derive context-window guard thresholds from the effective model window with 4k/8k safety floors, so small local models are no longer rejected by fixed 16k/32k preflight cutoffs. Fixes #42999. Thanks @chengjialu8888.
|
||||
- PDF extraction: resolve PDF.js standard fonts from the installed package root and pass a filesystem path to the Node fallback extractor, so built-in font PDFs render without `file://` URL lookup failures. Fixes #51455; carries forward #70936, #54447, and #62175. Thanks @anyech, @JuanRdBO, and @solomonneas.
|
||||
- Media: treat legacy Word/OLE attachments with `application/msword` or `application/x-cfb` MIME as binary so printable-looking `.doc` files are not embedded into prompts as text. Fixes #54176; carries forward #54380. Thanks @andyliu.
|
||||
- Config: accept documented `browser.tabCleanup` keys in strict root config validation, so configured tab cleanup no longer fails before runtime reads it. Fixes #74577. Thanks @lonexreb and @ezdlp.
|
||||
- Cron: validate disabled job schedule edits before persisting updates, so invalid cron changes no longer partially mutate stored jobs. Fixes #74459. Thanks @yfge.
|
||||
- CLI/cron: warn when `openclaw cron add --message` omits a nonblank `--agent`, including blank agent values and session-key jobs, so scheduled agent-turn jobs make default-agent fallback explicit while system events stay quiet. Fixes #42196; carries forward #42245. Thanks @ethanclaw.
|
||||
- Channels/status: keep Telegram, Slack, and Google Chat read-only allowlist/default-target accessors on config-only paths, so status and channel summaries do not resolve SecretRef-backed runtime credentials. Thanks @eusine.
|
||||
- Active Memory: clarify the deprecated `modelFallbackPolicy` warning and config help so `modelFallback` is described as a chain-resolution last resort, not runtime failover. (#74602) Thanks @jeffrey701.
|
||||
- Channels/Discord: keep read-only allowlist/default-target accessors from resolving SecretRef-backed bot tokens, so status and channel summaries no longer fail when tokens are only available in gateway runtime. (#74737) Thanks @eusine.
|
||||
- Gateway/sessions: align session abort wait semantics across `chat`, `agent`, and `sessions` server methods so abort RPCs return after the targeted sessions actually halt instead of resolving early while runs are still draining. (#74751) Thanks @BunsDev.
|
||||
- Agents/output: drop copied inbound metadata-only assistant replay turns before provider replay instead of synthesizing a placeholder, so Telegram and other channels cannot receive `[assistant copied inbound metadata omitted]` as model output. Fixes #74745. Thanks @adamwdear and @Marvae.
|
||||
- Doctor/memory: suppress skipped embedding-readiness warnings for key-optional providers such as Ollama and LM Studio while preserving timeout and not-ready diagnostics. Fixes #74608 and #73882. Thanks @hclsys.
|
||||
- Channels/groups: preserve observe-only turn suppression for prepared dispatch paths and restore deprecated channel turn runtime aliases, so passive observer/group flows stay silent while older plugins keep compiling. Thanks @vincentkoc.
|
||||
- Feishu: skip empty-text messages (e.g. `{"text":""}`) that carry no media, so no blank user turn is written to the session and downstream LLM providers cannot reject the request with "messages must not be empty". (#74634) Thanks @xdengli and @hclsys.
|
||||
- Feishu/Bitable: clean up newly created placeholder rows whose fields contain only default empty values while preserving meaningful link, attachment, user, number, boolean, and location values during create-app cleanup. (#73920) Carries forward #40602. Thanks @boat2moon.
|
||||
- macOS app: keep attach-only mode and the Debug Settings launchd toggle marker-only, so launching with `--attach-only`/`--no-launchd` no longer uninstalls the Gateway LaunchAgent or drops active sessions. (#72174) Thanks @DolencLuka.
|
||||
- macOS Canvas: stop auto-reloading the current A2UI host during push/eval/snapshot flows, so pushed A2UI content remains visible instead of returning to the empty Canvas shell. Fixes #73337. Thanks @Gr4via.
|
||||
- Plugin SDK: restore the deprecated `plugin-sdk/zalouser` command-auth facade so published Lark/Zalo plugins that import it load on current hosts. Fixes #74702. Thanks @Goron01.
|
||||
- Plugins/runtime-deps: include bundled provider plugins when `models.providers`, auth profiles, agent defaults, or subagent model refs configure that provider, while keeping inactive default-enabled provider plugins out of doctor repair. Refs #74307. Thanks @Skeptomenos.
|
||||
- Plugins/runtime: resolve relative plugin `api.resolvePath` inputs against the plugin root instead of the host working directory, while keeping absolute and home paths user-resolved. Fixes #74718. Thanks @jimdawdy-hub.
|
||||
- Plugins/runtime-deps: refresh mirrored root chunks through a temporary file before replacing the active copy, so failed refreshes do not delete chunks that running plugin imports still need. Thanks @shakkernerd.
|
||||
- Plugins/runtime-deps: prefer `require` conditional exports when building staged dependency aliases, so CommonJS-only plugin runtime deps such as `ws` do not resolve to ESM wrappers under Jiti. Fixes #74547. Thanks @aderius.
|
||||
- Bonjour/Gateway: cap flapping advertiser restarts in a sliding window, so mDNS probing/name-conflict loops disable discovery instead of churning indefinitely on constrained hosts. Refs #74209 and #74242. Thanks @ndj888 and @Sanjays2402.
|
||||
- Plugins/runtime-deps: verify staged package entry files before reusing mirrored runtime roots, so browser-control repairs incomplete `ajv`/MCP SDK installs after update instead of failing after restart on a missing `ajv/dist/ajv.js`. Refs #74630. Thanks @spickeringlr.
|
||||
- Heartbeat: resolve `responsePrefix` template variables with the selected provider, model, and thinking context before delivering alerts or suppressing prefixed `HEARTBEAT_OK` replies. Fixes #43064; repairs #43065; supersedes #46858. Thanks @yweiii and @JunJD.
|
||||
- Memory/LanceDB: show full memory UUIDs in the `memory_forget` candidate list so agents can pass the displayed ID back to targeted deletion without hitting the full-UUID validator. (#66913) Thanks @amittell.
|
||||
- File-transfer plugin: require canonical read-path preflight authorization for `file.fetch`, fail closed when `dir.fetch` preflight entries are missing, absolute, or traversing, and recheck returned archive entries before handing archive bytes to callers. Carries forward #74134. Thanks @omarshahine.
|
||||
- Channels/Feishu: retry file-typed iOS video resource downloads as `media` after a Feishu/Lark HTTP 502 and preserve the original 502 when the fallback also fails. Fixes #49855; carries forward #50164 and #73986. Thanks @alex-xuweilong.
|
||||
- Providers/Amazon Bedrock: expose the full Claude Opus 4.7 thinking profile (`xhigh`, `adaptive`, and `max`) for Bedrock model refs, while keeping Opus/Sonnet 4.6 on adaptive-by-default, so `/think` menus and validation match the Anthropic transport behavior. Fixes #74701. Thanks @prasad-yashdeep, @sparkleHazard, @Sanjays2402, and @hclsys.
|
||||
- Plugins/tokenjuice: compile the bundled plugin against tokenjuice 0.7.0's published OpenClaw host types instead of a local compatibility shim, so package contract drift fails in OpenClaw validation before release. Thanks @vincentkoc.
|
||||
- OAuth/secrets: ignore root-level Google OAuth `client_secret_*.json` downloads so local client-secret files do not appear as commit candidates. (#74689) Thanks @jeongdulee.
|
||||
- Memory: mirror `sqlite-vec` into packaged bundled-plugin runtime deps for the default memory plugin, so builtin vector search does not lose its SQLite extension after upgrading to 2026.4.27. Fixes #74692. Thanks @mozi1924.
|
||||
- Gateway/startup: bound local discovery advertisement during startup, so a stuck discovery plugin can no longer keep the Gateway from reaching ready. Fixes #73865; refs #74630 and #74633. Thanks @lpendeavors, @moltar-bot, and @Saboor711.
|
||||
- Gateway/models: serve the last successful model catalog while stale reloads refresh in the background, so Gateway control-plane and OpenAI-compatible requests no longer block behind model-provider rediscovery after model config changes. Refs #74135, #74630, and #74633. Thanks @DerFlash, @moltar-bot, and @Saboor711.
|
||||
- CLI/status: resolve read-only channel setup runtime fallback from the packaged OpenClaw dist root, so `status --all`, `status --deep`, channel, and doctor paths do not crash when an external channel plugin needs setup metadata. Fixes #74693. Thanks @giangthb.
|
||||
- SDK/events: keep per-run SDK event streams from surfacing duplicate raw chat projection frames, while normalizing chat-only projection frames and preserving raw access through `rawEvents`. Refs #74704. Thanks @BunsDev.
|
||||
- SDK: report Gateway terminal `agent.wait` timeout snapshots with lifecycle metadata as `timed_out` while keeping bare wait deadlines non-terminal. Thanks @clawsweeper.
|
||||
- Google Meet: block managed Chrome intro/test speech until browser health proves the participant is in-call, and expose `speechReady` diagnostics so login, admission, permission, and audio-bridge blockers no longer look like successful speech. Refs #72478. Thanks @DougButdorf.
|
||||
- Slack/commands: keep native command argument menus on select controls for encoded choice values up to Slack's option limit and truncate fallback button labels to Slack's button-text limit, so long valid choices no longer render invalid Slack blocks. Thanks @slackapi.
|
||||
- Agents/Codex: flush accepted debounced steering messages before normal app-server turn cleanup, so inbound follow-ups acknowledged as queued are not dropped when the turn completes before the debounce fires. Thanks @vincentkoc.
|
||||
- Slack/interactive replies: keep rendered buttons and selects within Slack Block Kit value and count limits, and align command argument select values with Slack's option limit, so overlong agent-authored choices no longer make Slack reject the whole block payload. Thanks @slackapi.
|
||||
- Slack/interactive replies: drop overlong Block Kit button URLs while preserving valid callback values, so malformed link buttons no longer make Slack reject the whole interactive reply. Thanks @slackapi.
|
||||
- Slack/commands: truncate native command argument-menu confirmation text to Slack's dialog limit, so long plugin arg names no longer make fallback buttons render invalid Block Kit payloads. Thanks @slackapi.
|
||||
- Slack/exec approvals: cap native approval metadata context to Slack's element and text limits, so large approval details no longer make Slack reject the approval card. Thanks @slackapi.
|
||||
- Slack/exec approvals: cap native approval update fallback text to Slack's message limit while preserving the rendered approval blocks, so long commands no longer make resolved or expired approval cards stay stale after `chat.update` rejects `msg_too_long`. Thanks @slackapi.
|
||||
- Slack/commands: cap native command argument-menu fallback rows to Slack's message block limit, so large plugin choice lists no longer make Slack reject the generated menu. Thanks @slackapi.
|
||||
- Slack/commands: drop fallback command argument buttons whose encoded values exceed Slack's button-value limit, so one oversized plugin choice no longer makes Slack reject the whole menu. Thanks @slackapi.
|
||||
- Slack/messages: merge message-tool presentation and interactive blocks on Slack sends, so buttons and selects are no longer dropped when a structured message body is also present. Thanks @slackapi.
|
||||
- Slack/messages: cap Block Kit fallback text to Slack's send limit while preserving the rendered blocks, so long context fallbacks no longer make rich Slack messages fail with `msg_too_long`. Thanks @slackapi.
|
||||
- Slack/messages: cap Block Kit fallback text on message edits while preserving the rendered blocks, so long context fallbacks no longer make Slack reject `chat.update` calls with `msg_too_long`. Thanks @slackapi.
|
||||
- Channels/WhatsApp: require Baileys outbound message ids before marking auto-replies delivered, so transcript text and ack reactions no longer make failed group replies look sent. Fixes #49225. Thanks @TinyTb.
|
||||
- CLI/update: scope packaged Node compile caches by OpenClaw version and install metadata, so global installs no longer reuse stale compiled chunks after package updates. Thanks @pashpashpash.
|
||||
- Channels/Voice call: keep pre-auth webhook in-flight limiting active when socket remote address metadata is missing, so slow-body requests from stripped-IP proxy paths still share the fallback bucket. (#74453) Thanks @davidangularme.
|
||||
- Plugin SDK/testing: lazy-load TypeScript from the plugin test-contract runtime and add release checks for critical SDK contract entrypoint imports and bundle size, so published packages fail preflight before shipping ESM-incompatible or oversized contract helpers. Thanks @vincentkoc.
|
||||
- Channels/Microsoft Teams: treat configured `19:...@thread.tacv2` and legacy `19:...@thread.skype` team/channel IDs as already resolved during startup, avoiding false `channels unresolved` warnings while preserving Graph name lookup for display-name entries. Fixes #74683. Thanks @dseravalli.
|
||||
- CLI/browser: preserve parent flags while lazy-loading browser subcommands, so `openclaw browser --json open` and `openclaw browser --json tabs` keep machine-readable output after reparsing. Fixes #74574. Thanks @devintegeritsm.
|
||||
- Exec/elevated: preserve `turnSourceChannel` as `messageProvider` on approval-followup runs so `tools.elevated.allowFrom.<provider>` checks no longer fail with `provider=null` after the user approves an async elevated command. Fixes #74646. Thanks @xhd2015.
|
||||
- Plugins/runtime-deps: add `openclaw plugins deps` inspection and repair with script-free package-manager defaults shared across plugin installers, so operators can repair missing bundled runtime deps without corrupting JSON output or blocking unrelated conflict-free deps. Thanks @vincentkoc.
|
||||
- Agents/output: strip internal `[tool calls omitted]` replay placeholders from user-facing replies while preserving visible reply whitespace. Fixes #74573. Thanks @blaspat.
|
||||
- Providers/Google Vertex: route authorized_user ADC credentials through OpenClaw's REST transport so Docker installs using gcloud application-default credentials no longer crash in the Google SDK before requests are sent. Fixes #74628. Thanks @frankhal2001-design.
|
||||
- ACP/resolver: fall through to thread-bound session resolution when an explicit `--session` token cannot be resolved while preserving the bad-token diagnostic when no thread binding exists, so Discord slash commands that auto-fill the current thread ID as the positional ACP target no longer return "Unable to resolve session target" errors. Fixes #66299. Thanks @hclsys, @kindomLee, and @martingarramon.
|
||||
- Agents/sessions: emit a terminal lifecycle backstop when embedded timeout/error turns return without `agent_end`, so Gateway sessions no longer stay stuck in `running` after failover surfaces a timeout. Fixes #74607. Thanks @millerc79.
|
||||
- Gateway/diagnostics: include stuck-session reason hints and recovery skip causes in warnings, so operators can tell whether a lane is waiting on active work, queued work, or stale bookkeeping. Thanks @vincentkoc.
|
||||
- Providers/DeepSeek: expose native DeepSeek V4 `xhigh` and `max` thinking levels through the provider `resolveThinkingProfile` hook so `/think xhigh|max` applies the intended effort instead of falling back to base levels. (#73008) Thanks @ai-hpc.
|
||||
- Agents/Codex: bound embedded-run cleanup, trajectory flushing, and command-lane task timeouts after runtime failures, so Discord and other chat sessions return to idle instead of staying stuck in processing. Thanks @vincentkoc.
|
||||
- Heartbeat/exec: consume successful metadata-only async exec completions silently so Telegram and other chat surfaces no longer ask users for missing command logs after `No session found`. Fixes #74595. Thanks @gkoch02.
|
||||
- Active Memory/Memory: materialize allowlisted memory plugin tools for lightweight embedded recall runs so Memory Core tools do not collapse to an empty runtime allowlist. Fixes #74572. (#74592) Thanks @LaFleurAdvertising and @vyctorbrzezowski.
|
||||
- Web fetch: add a documented `tools.web.fetch.ssrfPolicy.allowIpv6UniqueLocalRange` opt-in and thread it through cache keys and DNS/IP checks so trusted fake-IP proxy stacks using `fc00::/7` can work without broad private-network access. Fixes #74351. Thanks @jeffrey701.
|
||||
- OpenAI Codex: restore `/verbose full` persistence and app-server tool-output forwarding, and retry Gateway E2E temp-home cleanup so debug runs do not regress on stale validation or cleanup flakes. Thanks @vincentkoc.
|
||||
- Anthropic/Meridian: preserve text and thinking content seeded on `content_block_start` in anthropic-messages streams, so `[thinking, text]` replies no longer persist as empty turns or trigger empty-response fallbacks. Fixes #74410. Thanks @vyctorbrzezowski.
|
||||
- Channels/Matrix: complete the cross-signing handshake on `openclaw matrix verify confirm-sas` so the operator's other Matrix device clears its `Verifying…` loop instead of staying stuck after the agent confirms. (#74542) Thanks @nklock.
|
||||
- CLI/status: honor channel-specific model context-window overrides when reporting effective context, so channel-scoped sessions reflect the active window in `openclaw status`. Thanks @HemantSudarshan.
|
||||
- Sandbox/Docker: tolerate Docker daemon unavailability when sandbox mode is off, so doctor and preflight checks no longer fail on installs that do not run the Docker daemon. Fixes #73671. Thanks @kaseonedge.
|
||||
- Control UI/mobile: persist mobile chat settings through Lit-managed state and route mobile navigation through the same view-state path so chat panel toggles survive transitions on small viewports. Thanks @BunsDev.
|
||||
- Control UI/exports: align sidebar trigger affordances across the resizable divider, mobile layout, and exported-HTML transcript template so the sidebar toggle and exported transcript sidebar render with consistent hit areas and styling. Thanks @BunsDev.
|
||||
- Control UI/chat: disable the page refresh affordance while a chat run is active so accidental refreshes do not abort an in-flight reply. Thanks @Angfr95 and @BunsDev.
|
||||
- Memory/LanceDB: return real memory records from `openclaw ltm list` (with optional `--limit` and createdAt ordering) instead of an empty placeholder, so the CLI surface matches the documented LTM listing contract. (#67952) Thanks @zhangyue19921010.
|
||||
- Media: include redacted per-attempt resize failures and resolved model input capabilities in vision-pipeline errors so ARM64 image failures are diagnosable without closing the remaining routing investigation. Refs #74552. Thanks @1yihui.
|
||||
- Control UI/i18n: route zh-CN agent, debug, channel-refresh, and exec-approval copy through the locale source while preserving the English `Cron Jobs` agent tab label and the security-audit command styling. Carries forward #39692 repair context. Thanks @hepeng154833488 and @vincentkoc.
|
||||
- Auto-reply: honor explicit `silentReply.direct: "allow"` for clean empty or reasoning-only direct chat turns while keeping the default direct-chat empty-response guard conservative. Fixes #74409. Thanks @jesuskannolis.
|
||||
- OpenAI Codex: send a non-empty Responses input item when a Codex turn only has systemPrompt-backed instructions, avoiding ChatGPT backend 400s from `input: []`. Fixes #73820. Thanks @woodhouse-bot.
|
||||
- Ollama: normalize provider-prefixed tool-call names at the native stream boundary so Kimi/Ollama calls such as `functions.exec` dispatch as `exec` instead of missing configured tools. Fixes #74487. Thanks @afurm and @carreipeia.
|
||||
- Security/audit: resolve configured model aliases before model-tier and small-parameter checks, so alias-based GPT-5/Codex configs no longer report false weak-model warnings. Fixes #74455. Thanks @blaspat.
|
||||
- CLI/agent: isolate Gateway-timeout embedded fallback runs under explicit `gateway-fallback-*` sessions so accepted Gateway runs cannot race transcript locks or replace the routed conversation session. Fixes #62981. Thanks @HemantSudarshan.
|
||||
- CLI/QR/device-pair: reject malformed public setup URLs before issuing mobile pairing bootstrap tokens, while keeping valid bare host:port setup URLs supported. Thanks @Lucenx9.
|
||||
- Models/UI: hide unauthenticated providers from the default Web chat, `/models`, and model setup pickers while keeping explicit full-catalog browse paths through `view: "all"`, `/models <provider> all`, and `models list --all`. Fixes #74423. Thanks @guarismo and @SymbolStar.
|
||||
- Ollama: keep explicit local model runs on target-provider runtime hooks when PI discovery is skipped, so one-shot Ollama calls no longer cold-load unrelated provider runtimes before streaming. Fixes #74078. Thanks @sakalaboator.
|
||||
- Slack/prompts: rely on Slack `interactiveReplies` guidance instead of generic `inlineButtons` config hints so enabled Slack button directives are not contradicted. Fixes #46647. Thanks @jeremykoerber.
|
||||
- Slack/reactions: treat duplicate `already_reacted` responses as idempotent success so repeated agent reaction adds no longer surface as tool failures. Fixes #69005. Thanks @shipitsteven and @martingarramon.
|
||||
- Channels/Discord: cool down Cloudflare/Error 1015 HTML 429 REST failures during startup application lookup and gateway metadata fetches, add `channels.discord.applicationId` as an app-id lookup bypass, sanitize HTML bodies before logging, and honor Retry-After before falling back to a conservative cooldown. Fixes #38853. (#74489) Thanks @djgeorg3 and @Garyko0730.
|
||||
- Slack/tools: expose `fileId` in the shared message tool schema so `download-file` can receive Slack attachment IDs from inbound placeholders. Fixes #45574. Thanks @chadvegas.
|
||||
- Exec: reject invalid per-call `host` values instead of silently falling back to the default target, so hostname-like values fail before commands run. Fixes #74426. Thanks @scr00ge-00 and @vyctorbrzezowski.
|
||||
- Google/Gemini: send non-empty placeholder content when a Gemini run is triggered with empty or filtered user content, avoiding `contents is not specified` API errors. Thanks @CaoYuhaoCarl.
|
||||
- Heartbeat: preserve non-task `HEARTBEAT.md` context around `tasks:` blocks and apply `agents.defaults.heartbeat` to all agents unless per-agent heartbeat entries restrict scope. Thanks @Sekhar03.
|
||||
- Markdown: preserve paragraph breaks inside loose list items in shared outbound formatting while keeping tight list spacing stable. Thanks @Lucenx9.
|
||||
- Build/Gateway: route restart, shutdown, respawn, diagnostics, command-queue cleanup, and runtime cleanup through one stable gateway lifecycle runtime entry so rebuilt packages do not strand long-running gateways on stale hashed chunks. Carries forward #73964. Thanks @pashpashpash.
|
||||
- Memory/wiki: keep broad shared-source and generated related-link blocks from turning every page into a search hit, cap noisy backlinks, support all-term searches such as people-routing queries, and prefer readable page body snippets over generated metadata. Thanks @vincentkoc.
|
||||
- Cron/Gateway: abort and bounded-clean up timed-out isolated agent turns before recording the timeout, so stale cron sessions cannot leave Discord or other chat lanes stuck in `processing` after a timeout. Thanks @vincentkoc.
|
||||
- Agents/errors: suppress malformed streaming tool-call JSON fragments before they reach chat surfaces while preserving provider request-validation diagnostics. Fixes #59076; keeps #59080 as duplicate coverage. (#59118) Thanks @singleGanghood.
|
||||
- CLI/models: restore provider-filtered `models list --all --provider <id>` rows for providers without manifest/static catalog coverage, including Anthropic and Amazon Bedrock, while keeping the compatibility fallback off expensive availability and resolver paths. Thanks @shakkernerd.
|
||||
- CLI/models: keep manifest auth-evidence credentials visible across `models status`, auth probes, and PI model discovery so workspace-scoped provider auth does not disagree between listing, probing, and execution. Thanks @shakkernerd.
|
||||
- CLI/models: move local credential evidence such as Google Vertex ADC into generic plugin manifest setup metadata so the model-list auth index stays declarative without provider-specific runtime branches. Thanks @shakkernerd.
|
||||
- CLI/models: compute the `models list` Auth column through one command-local provider auth index so row rendering no longer repeats auth profile, env, configured-provider, AWS, or synthetic-auth checks per model row. Thanks @shakkernerd.
|
||||
- CLI/models: move the OpenAI listable catalog into the plugin manifest so `models list --all --provider openai` uses the manifest fast path instead of loading provider runtime normalization hooks. Thanks @shakkernerd.
|
||||
- CLI/tools: keep the Gateway `tools.*` RPC namespace out of plugin command discovery and managed proxy startup, so stray commands like `openclaw tools effective` fail quickly instead of cold-loading plugin metadata. Refs #73477. Thanks @oromeis.
|
||||
- CLI/status: keep default text `openclaw status --usage` on metadata-only channel scans unless `--deep` or `--all` is set, and send stray `openclaw tools --help` through the precomputed root-help fast path so latency-triage commands avoid plugin/runtime cold loads before printing. Refs #73477 and #74220. Thanks @oromeis and @NianJiuZst.
|
||||
- Agents/diagnostics: trace embedded-run startup and preparation stage timings before model I/O, and warn only on severe slow stages, so Docker/VPS latency reports can identify whether plugin loading, auth/model resolution, tool inventory, bootstrap, MCP/LSP, resource loading, or stream setup is dominating pre-run latency without noisy normal logs. Refs #73428. Thanks @Dimaoggg, @quangtran88, and @Heyvhuang.
|
||||
- Agents/subagents: cache persisted subagent run registry reads by file signature while preserving fresh-parse isolation, so busy gateways stop reparsing unchanged `subagents/runs.json` on controller/list/status hot paths. Refs #72338. Thanks @argus-as.
|
||||
- Gateway/clients: wait for the event loop to become responsive before opening Gateway WebSocket RPC/probe/client connections while charging that readiness wait to caller timeouts, so Windows deferred module-evaluation stalls no longer turn healthy loopback gateways into false handshake timeouts across status, TUI, ACP, MCP, node-host, and plugin client paths. Refs #74279 and #48270. Thanks @wongcode and @joost-heijden.
|
||||
- Gateway/Windows: read listener command lines via PowerShell before falling back to `wmic`, so restart health can recognize OpenClaw listeners on modern Windows installs and avoid long anonymous-port waits. Refs #74280. Thanks @zym951223.
|
||||
- Plugins/runtime-deps: record process start-time in bundled dependency install locks and expire recycled-PID locks, so Docker gateway restarts recover from stale `.openclaw-runtime-deps.lock` directories without waiting through repeated five-minute timeouts. Fixes #74346. (#74361) Thanks @jhsmith409.
|
||||
- Plugins/runtime-deps: memoize packaged bundled runtime dist-mirror preparation after the first successful pass while keeping source-checkout mirrors refreshable, so constrained Docker/VPS installs avoid repeated root scans before chat turns. Refs #73428, #73421, #73532, and #73477. Thanks @Dimaoggg, @oromeis, @oadiazp, @jmfraga, @bstanbury, @antoniusfelix, and @jkobject.
|
||||
- Channels/Discord: treat bare numeric outbound targets that match the effective Discord DM allowlist as user DMs while preserving account-specific legacy `dm.allowFrom` precedence over inherited root `allowFrom`. (#74303) Thanks @Squirbie.
|
||||
- Channels/Discord/Slack: share one DM policy/allowlist resolver across runtime, setup, allowlist editing, and doctor repair, so legacy `dm.policy` / `dm.allowFrom` compatibility migrates to canonical `dmPolicy` / `allowFrom` without divergent access checks. Thanks @Squirbie.
|
||||
- Control UI: make the chat sidebar split divider focusable, keyboard-resizable, ARIA-described, and pointer-event based so sidebar resizing works without a mouse. Thanks @BunsDev.
|
||||
- Control UI/chat: wire the slash-command autocomplete menu to the composer with stable ARIA relationships so screen readers announce the active command or argument option. Thanks @BunsDev.
|
||||
- Agents/usage: keep PI embedded-run telemetry attributed to the resolved model provider instead of the PI harness label, so OpenRouter and other provider-backed turns report the right provider in session usage and traces. Thanks @vincentkoc.
|
||||
- Agents/attribution: send OpenClaw attribution headers on native OpenAI and Codex traffic, including SDK transports, realtime voice and TTS, device-code auth, WHAM usage, and remote embeddings, so PI-origin defaults no longer leak into provider requests. Thanks @vincentkoc.
|
||||
- Agents/auth: keep OAuth auth profiles inherited from the main agent read-through instead of copying refresh tokens into secondary agents, and refresh Codex app-server tokens against the owning store so multi-agent swarms avoid reused refresh-token failures. Fixes #74055. Thanks @ClarityInvest.
|
||||
- Channels/Telegram: honor `ALL_PROXY` / `all_proxy` and service-level `OPENCLAW_PROXY_URL` when constructing the HTTP/1-only Telegram Bot API transport, so Windows and service installs that rely on those proxy settings no longer fall back to direct egress. Fixes #74014; refs #74086. Thanks @SymbolStar.
|
||||
- Channels/Telegram: keep raw host/network-unreachable Bot API connect failures non-fatal and route tagged polling uncaught exceptions through the Telegram restart path, so transient reachability failures no longer kill the Gateway or leave long polling stuck. Fixes #60515; refs #74540. Thanks @HemantSudarshan, @thacid22, and @ewimsatt.
|
||||
- Channels/Telegram: continue polling when `deleteWebhook` hits a transient network failure but `getWebhookInfo` confirms no webhook is configured, so startup does not retry cleanup forever after the webhook was already removed. Refs #74086; carries forward #47384. Thanks @clovericbot.
|
||||
- Channels/Telegram: retry native quote replies without `reply_parameters.quote` when Telegram returns `QUOTE_TEXT_INVALID`, so stale or truncated quote excerpts no longer drop the whole reply. Fixes #74581. Thanks @moeedahmed.
|
||||
- Channels/Telegram: apply strict safe-send retry to inbound final replies when grammY wraps a pre-connect failure, while leaving ambiguous plain network envelopes single-shot to avoid duplicate visible messages. Fixes #74203. Thanks @nanli2000cn.
|
||||
- Channels/Telegram: surface polling liveness warnings in channel status and doctor when a running long-poller has not completed `getUpdates` after startup grace or its transport activity is stale, so silent polling failures no longer look clean. Refs #74299. Thanks @lolaopenclaw.
|
||||
- Channels/Telegram: publish webhook runtime state and warn when `setWebhook` has not completed after startup grace, so webhook-mode accounts no longer look healthy while registration is still failing or retrying. Refs #74299. Thanks @lolaopenclaw and @martingarramon.
|
||||
- Channels/Telegram: bound native command menu `deleteMyCommands` and `setMyCommands` Bot API calls and allow the same timeout-triggered transport fallback retry as other startup control calls, so Windows/WSL network stalls cannot leave command sync hanging behind an otherwise running provider. Refs #74086. Thanks @SymbolStar.
|
||||
- ACP/commands: accept forwarded ACP timeout config controls in the OpenClaw bridge, treat unsupported discard-close controls as recoverable cleanup, and restore native `/verbose full` plus no-arg status behavior, so Discord command menus and nested ACP turns no longer fail on supported session controls. Thanks @vincentkoc.
|
||||
- Codex harness: interrupt and release native app-server turns that go quiet after an OpenClaw dynamic-tool response without sending `turn/completed`, so Discord and other chat lanes do not stay stuck in `processing`. Thanks @vincentkoc.
|
||||
- Codex harness: bound OpenClaw dynamic tool responses to 30 seconds and fail closed with an explicit tool result when the app-server bridge would otherwise strand the turn in `processing`. Thanks @vincentkoc.
|
||||
- TUI/status: clear stale `streaming` footer state when a final event arrives after the active run was already cleared and no tracked runs remain, while preserving concurrent-run ownership and inactive local `/btw` terminal handling. Fixes #64825; carries forward #64842, #64843, #64847, and #64862. Thanks @briandevans and @Yanhu007.
|
||||
- Channels/Discord: fail startup closed when Discord cannot resolve the bot's own identity and keep mention gating active when only configured mention patterns can detect mentions, so the provider no longer continues with a missing bot id. Fixes #42219; carries forward #46856 and #49218. Thanks @education-01 and @BenediktSchackenberg.
|
||||
- Channels/Discord: split long CJK replies at punctuation and code-point-safe fallback boundaries so Discord chunking stays readable without corrupting astral characters. Fixes #38597; repairs #71384. Thanks @p3nchan.
|
||||
- TUI: keep the streaming watchdog alive across active tool/lifecycle proof-of-life, pause it during disconnects, and reload history after stale reconnect runs so long-running chats stop flipping to false idle or hanging on stale streaming. Fixes #69081. Thanks @EenvoudJasper.
|
||||
- Browser/gateway: ignore Playwright dialog-close races from `Page.handleJavaScriptDialog` so browser automation no longer crashes the Gateway when a dialog disappears before Playwright accepts it. (#40067) Thanks @randyjtw.
|
||||
- Cron/Gateway: defer missed isolated agent-turn catch-up out of the channel startup window, so overdue cron work cannot starve Discord or Telegram while providers connect after a restart. Thanks @vincentkoc.
|
||||
- Heartbeat/cron: defer heartbeat turns while cron work is active or queued, add opt-in `heartbeat.skipWhenBusy` for subagent/nested lane pressure, and retry busy skips without advancing the schedule so local Ollama hosts do not run heartbeat and cron prompts concurrently. Fixes #50773. Thanks @scottgl9.
|
||||
- Agents/thinking: honor configured model `compat.supportedReasoningEfforts` entries that include `xhigh`, so custom OpenAI-compatible provider refs expose and validate `/think xhigh` consistently across command menus, Gateway sessions, agent CLI, and `llm-task`. Carries forward #48904. Thanks @Milchstrassse and @wufunc.
|
||||
- Vercel AI Gateway: expose provider-owned `/think xhigh` for trusted OpenAI/Codex upstream refs and Claude adaptive thinking for Anthropic upstream refs, while leaving untrusted namespaced refs on base levels. Carries forward #41561. Thanks @Zcg2021.
|
||||
- Plugins/runtime-deps: prune stale `openclaw-unknown-*` bundled runtime dependency roots during Gateway startup while keeping recent or locked roots, so old staging debris cannot keep growing across restarts. Thanks @vincentkoc.
|
||||
- Plugins/runtime-deps: include ten more root-package runtime dependencies (`@agentclientprotocol/sdk`, `@lydell/node-pty`, `croner`, `dotenv`, `jiti`, `json5`, `jszip`, `markdown-it`, `tar`, `web-push`) in `MIRRORED_CORE_RUNTIME_DEP_NAMES` so they are mirrored into the runtime-deps tree alongside `semver` and `tslog`, preventing `Cannot find package 'X'` failures from core dist code (for example `qmd-manager`, `cron/schedule`, `infra/archive`, `infra/push-web`, `infra/backup-create`, `process/supervisor/adapters/pty`) when no enabled extension owns the dependency. Adds a static drift guard test that scans `src/` for value imports of root-package deps and fails CI when one is missing from the mirror allowlist or extension-owned set. Refs #74199. Thanks @maxpuppet.
|
||||
- Ollama: compose caller abort signals with guarded-fetch timeouts for native `/api/chat` streams, so `/stop` and early cancellation still interrupt local Ollama requests that also carry provider timeout budgets. Refs #74133. Thanks @obviyus.
|
||||
- Doctor/TTS: migrate legacy `messages.tts.enabled`, agent TTS, channel TTS, and voice-call plugin TTS toggles to `auto` mode during `openclaw doctor --fix`, matching the documented TTS config contract. Thanks @vincentkoc.
|
||||
- CLI/logs: fall back to the configured Gateway file log when implicit loopback Gateway connections close or time out before or during `logs.tail`, so `openclaw logs` still works while diagnosing local-model Gateway disconnects. Refs #74078. Thanks @sakalaboator.
|
||||
- MCP/plugins: stringify non-array plugin tool results with chat-content coercion instead of default object stringification, so MCP callers receive useful JSON/text content from plugin tools. Thanks @vincentkoc.
|
||||
- Active Memory/QMD: make gateway-start QMD refresh opt-in via `memory.qmd.update.startup`, keep normal memory access lazy, preserve interactive file watching, and align watcher dependency/build ignores with QMD's scanner so cold gateway startup no longer imports or initializes QMD by default. Thanks @codexGW.
|
||||
- Channels/Discord: remove Discord-owned queued-run timeout replies through the shared channel lifecycle queue while preserving message ordering and compatibility timeout constants, so long Discord turns stay governed by session/tool/runtime lifecycle instead of channel fallback errors. Thanks @codexGW.
|
||||
- Agents/tools: clamp `process.poll` waits to 30 seconds, advertise that cap in the tool schema, and honor abort signals while waiting, so long command polls cannot pin agent responsiveness after cancellation. Thanks @vincentkoc.
|
||||
- Plugin SDK: add tracked Discord component-message helpers and a Telegram account-resolution compatibility facade, so existing plugins using those subpaths resolve while new plugins stay on generic channel SDK contracts. Thanks @vincentkoc.
|
||||
- Shared labels: preserve Unicode combining marks and NFC-equivalent accented text in group/channel slug normalization so non-Latin labels no longer lose meaningful characters. Fixes #58932; carries forward #58942 and #58995. Thanks @fengqing-git, @Starhappysh, and @koen666.
|
||||
- Channels/Telegram: include probed video width and height when sending regular Telegram videos, so portrait clips render with the correct orientation instead of being stretched by clients. (#18915) Thanks @storyarcade.
|
||||
- Docs/Hetzner: clarify that SSH tunnel access requires `AllowTcpForwarding local` before running `ssh -L`, so hardened VPS sshd configs do not block loopback Gateway access. Fixes #54557; carries forward #54564; refs #54954. Thanks @satishkc7, @blackstrype, and @Aftabbs.
|
||||
- Agents/config: preserve authored `agents.defaults.params` and per-model `agents.defaults.models[].params` during narrowed internal config writes, so OpenAI transport overrides such as `transport: "sse"` and `openaiWsWarmup: false` are not stripped from `openclaw.json`. Fixes #73607; refs #73428. Thanks @quangtran88.
|
||||
- Agents/model config: resolve per-model extra params through canonical model keys while preserving legacy double-prefixed fallback entries, so provider-prefixed model ids such as `openrouter/auto` keep their configured runtime params. (#44319) Thanks @HenryXiaoYang.
|
||||
- Gateway/shutdown: report structured shutdown warnings and HTTP close timeout warnings through `ShutdownResult` while preserving lifecycle hook hardening. Carries forward #41296. Thanks @edenfunf.
|
||||
- Control UI: keep Agents Overview and config-form select dropdowns on their configured value after options render while preserving inherited agent model placeholders. Fixes #40352; carries forward #52948. Thanks @xiaoquanidea.
|
||||
- Agents/exec: launch zsh, bash, and fish host exec shells with startup files suppressed while preserving existing PATH fallbacks, so daemon env is not overridden by shell startup files. Carries forward #40200; fixes #40179. Thanks @NewdlDewdl.
|
||||
- Plugins/QA: prebuild the private QA channel runtime before plugin gauntlet source runs so wrapper CPU/RSS measurements are not polluted by private QA dist rebuild work. Thanks @vincentkoc.
|
||||
- Plugins/QA: add a Kitchen Sink plugin gauntlet that installs the external package, checks command inventory, MCP tools, channel status, provider turns, gateway RSS, CPU, and fatal log anomalies. Thanks @vincentkoc.
|
||||
- Plugins/config: reuse the bundled plugin alias scan within a single config normalization pass, so Kitchen Sink-style plugin configs no longer peg Gateway CPU by repeatedly rescanning bundled metadata before agent turns. Thanks @vincentkoc.
|
||||
- Plugins/channels: reject malformed runtime channel registrations that omit required config helpers before they can poison channel status. Thanks @vincentkoc.
|
||||
- MCP/plugins: serialize raw plugin tool return values through the plugin-tools MCP bridge so Kitchen Sink-style tools no longer surface `undefined` content. Thanks @vincentkoc.
|
||||
- Gateway/reload: bound default restart deferral and SIGUSR1 restart drain to five minutes while preserving explicit `deferralTimeoutMs: 0` indefinite waits, so stale active work accounting cannot block config reloads forever. Thanks @vincentkoc.
|
||||
- Active Memory: register the prompt-build hook with the configured recall timeout plus setup grace instead of the 150s maximum budget, so default memory recall cannot delay turn startup for multiple minutes. Thanks @vincentkoc.
|
||||
- Gateway/readiness: include an `eventLoop` diagnostic block in local or authenticated `/readyz` responses with event-loop delay (p99 and max), event-loop utilization, CPU core ratio, and a `degraded` flag, so operators can see when slow startups or runaway turns stall the event loop. Thanks @vincentkoc.
|
||||
- Gateway/agents: schedule accepted agent runs after the accepted RPC frame has a chance to flush, so pre-turn prompt/context work is less likely to starve immediate `agent.wait` callers. Thanks @vincentkoc.
|
||||
- CLI/update: tolerate stale memory-runtime import failures during best-effort CLI process teardown, so `openclaw update` replacing hashed runtime chunks before the finalizer runs no longer surfaces as exit-time `Cannot find module` noise. Thanks @vincentkoc.
|
||||
- CLI/channels logs: reuse the rolling log-file resolver so `openclaw channels logs` falls back to the active dated log across date boundaries without reading unrelated custom log files. Fixes #42875; carries forward #42904 and #43043. Thanks @ethanclaw and @wdskuki.
|
||||
- CLI/update: skip tracked plugins disabled in config during post-update plugin sync before npm, ClawHub, or marketplace update checks, preserving their install records without failing the update. Fixes #73880. Thanks @islandpreneur007.
|
||||
- Control UI: fix Peak Error Hours showing incorrect hourly rates when the browser's timezone observes DST, by storing hourly message counts with UTC date keys and using DST-aware `Date.getHours()` for local conversion. Also extract `accumulateMessageCounts` helper to reduce duplicated daily/hourly aggregation logic. (#49396) Thanks @konanok.
|
||||
- iMessage: normalize known leading attributedBody corruption markers on sent-message echo text keys so delayed reflected echoes with U+FFFD/U+FFFE/U+FFFF/FEFF prefixes are dropped without collapsing interior text. Fixes #59973; carries forward #59980 and #62191. Thanks @neeravmakwana and @maguilar631697.
|
||||
- Security/audit: recognize dangerous node command IDs as valid `gateway.nodes.denyCommands` entries, so audit only warns on real typos or unsupported patterns. (#56923) Thanks @chziyue.
|
||||
- Cron: treat implicit text payloads with agent-turn overrides as agent turns, preserving model overrides for scheduled text prompts instead of pruning them as system events. Fixes #28905. (#64060) Thanks @liaoandi.
|
||||
- Telegram/exec approvals: stop treating general Telegram chat allowlists and `defaultTo` routes as native exec approvers; Telegram now uses explicit `execApprovals.approvers` or owner identity from `commands.ownerAllowFrom`, matching the first-pairing owner bootstrap path. Thanks @pashpashpash.
|
||||
- Plugins/providers: keep Gateway startup primary-model discovery on metadata-only provider entries and reuse active non-speech capability providers even with explicit plugin entries, avoiding unnecessary provider registry loads during startup and media capability checks. Fixes #73729, #73835, and #73793; carries forward #73853 and #73794. Thanks @sg1416-zg, @brokemac79, and @poolside-ventures.
|
||||
- Chat commands: route sensitive group `/diagnostics` and `/export-trajectory` approvals and results to a private owner route, preferring same-surface DMs before falling back to the first configured owner route, so Discord group invocations can land in Telegram when that is the primary owner interface. Thanks @pashpashpash.
|
||||
- Gateway/hooks: keep successful `deliver:false` agent hooks silent, log a hook audit record for suppressed success announcements, and suppress fallback summaries after attempted hook delivery while still surfacing failed hook runs. Repairs #55761; builds on #36332 and #49234. Thanks @EffortlessSteven, @cioclawcode, and @BrennerSpear.
|
||||
- Plugin SDK/Discord: restore a deprecated `openclaw/plugin-sdk/discord` compatibility facade and the legacy compat group-policy warning export for the published `@openclaw/discord@2026.3.13` package, covering its config, account, directory, status, and thread-binding imports while keeping new plugins on generic SDK subpaths. Fixes #73685; supersedes #73703. Thanks @rderickson9 and @SymbolStar.
|
||||
- Channels/Discord: suppress duplicate gateway monitors when multiple enabled accounts resolve to the same bot token, preferring config tokens over default env fallback and reporting skipped duplicates as disabled. Supersedes #73608. Thanks @kagura-agent.
|
||||
- CLI/health: build channel health summaries from inspected credential metadata plus runtime state, so `openclaw health --json` reports Discord `running`, `connected`, and `tokenSource` consistently with channel status. Fixes #44354. Thanks @ferenc-acs.
|
||||
- Control UI/Talk: decode Google Live binary WebSocket JSON frames and stop queued browser audio on interruption or shutdown, so browser Talk leaves `Connecting Talk...` and barge-in no longer plays stale audio. Fixes #73601 and #73460; supersedes #73466. Thanks @Spolen23 and @WadydX.
|
||||
- Channels/Discord: ignore stale route-shaped conversation bindings after a Discord channel is reconfigured to another agent, while preserving explicit focus and subagent bindings. Fixes #73626. Thanks @ramitrkar-hash.
|
||||
- Agents/bootstrap: pass pending BOOTSTRAP.md contents through the first-run user prompt while keeping them out of privileged system context, and show limited bootstrap guidance when workspace file access is unavailable. Fixes #73622. Thanks @mark1010.
|
||||
- ACP/tasks: classify parent-owned ACP sessions as background work regardless of persistent runtime mode, and close terminal stale ACP sessions when no active binding remains, so delegated ACP output reports through the parent task notifier instead of acting like a normal foreground chat session. Refs #73609. Thanks @joerod26.
|
||||
- Tasks: keep terminal mirrored TaskFlow timestamps pinned to task completion time and let maintenance repair stale mirrors, so ACP terminal delivery updates no longer leave inconsistent flow audits. Refs #73609. Thanks @joerod26.
|
||||
- Gateway/sessions: add conservative stuck-session recovery that releases only stale session lanes while active embedded runs, reply operations, and lane tasks remain serialized, so queued follow-ups can drain without aborting legitimate long-running turns. Refs #73581, #73655, #73652, #73705, #73647, #73602, #73592, and #73601. Thanks @WS-Q0758, @bryangauvin, @spenceryang1996-dot, @bmilne1981, @mattmcintyre, @Vksh07, and @Spolen23.
|
||||
- Plugins: cache unchanged plugin manifest loads by file signature, reducing repeated JSON/JSON5 parsing and manifest normalization in bursty startup and runtime registry paths. Refs #73532 and #73647; carries forward #73678. Thanks @TheDutchRuler.
|
||||
- Plugins/runtime-deps: cache unchanged bundled runtime mirror dist-file materialization decisions and close file-lock handles on owner-write failures, reducing repeated startup chunk scans and avoiding FileHandle-GC recovery stalls. Refs #73532. Thanks @oadiazp and @bstanbury.
|
||||
- Plugins/runtime-deps: retry and defer transient cleanup failures for owned runtime staging directories so CLI startup no longer aborts after a successful bundled dependency swap. Refs #73903. Thanks @bobfreeman1989.
|
||||
- Plugins/runtime-deps: cache bundled runtime-deps JSON/package files by file signature, reducing repeated staged-runtime metadata reads during bundled channel startup. Refs #73647 and #73705. Thanks @mattmcintyre and @bmilne1981.
|
||||
- Plugins/runtime-deps: delegate bundled plugin dependency staging to complete npm/pnpm install plans with durable runtime state, removing retained-manifest and source-checkout cache reconciliation from Gateway startup. Refs #73532. Thanks @oadiazp, @bstanbury, and @jmfraga.
|
||||
- Plugins/runtime-deps: replace Gateway-start root chunk dependency inference with explicit mirrored-root dependency metadata, reducing staged runtime scans while preserving lazy per-plugin installs. Refs #73532. Thanks @oadiazp and @bstanbury.
|
||||
- Plugins/runtime-deps: run pnpm staged installs outside the repository workspace and disable pnpm release-age gates for exact bundled runtime dependency materialization, so bundled plugin dependency repair writes packages into the generated stage without blocking fresh packaged dependencies. Refs #73532. Thanks @oadiazp and @bstanbury.
|
||||
- CLI/TUI: keep `chat.history` off model-catalog discovery so initial Gateway-backed TUI history loads cannot block behind slow provider/plugin model scans on low-core hosts. Refs #73524. Thanks @harshcatsystems-collab.
|
||||
- Channels/WhatsApp: flag recently reconnected linked accounts in channel status even when the socket is currently healthy, so flapping WhatsApp Web sessions no longer look clean after a brief reconnect. Refs #73602. Thanks @Vksh07.
|
||||
- Channels/WhatsApp: log shared dispatcher delivery failures with reply kind, message id, chat id, and connection id, so typing-without-send reports can identify whether the WhatsApp send path rejected a generated reply. Refs #74269. Thanks @tomcosta-git.
|
||||
- Feishu: suppress distinct late `final` text deliveries after a streaming card has already closed, while keeping media attachments deliverable, so late-finals no longer reopen duplicate Feishu cards. Fixes #71977. (#72294) Thanks @MonkeyLeeT.
|
||||
- Gateway: expose `gateway.handshakeTimeoutMs` in config, schema, and docs while preserving `OPENCLAW_HANDSHAKE_TIMEOUT_MS` precedence, so loaded or low-powered hosts can tune local WebSocket pre-auth handshakes without patching dist files. Supersedes #51282; refs #73592 and #73652. Thanks @henry-the-frog.
|
||||
- Gateway/TUI/status: align configured and env-based WebSocket handshake budgets across local clients, probes, and fallback RPCs while preserving explicit status timeouts and paired-device auth fallback, so slow local gateways are not marked unreachable by a shorter client watchdog. Refs #73524, #73535, #73592, and #73602. Thanks @harshcatsystems-collab, @DJBlackhawk, and @Vksh07.
|
||||
- Gateway/startup: return retryable `UNAVAILABLE` during the sidecar startup window and keep CLI/TUI/status clients retrying inside their existing timeout budget, so early connects no longer surface as terminal handshake failures. Fixes #73652. Thanks @spenceryang1996-dot.
|
||||
- Gateway/proxy: bypass inherited proxy environment for local Gateway control-plane WebSockets to `localhost` as well as loopback IPs, so Windows/WSL proxy settings cannot intercept local CLI/TUI Gateway connections. Supersedes #73474; refs #73602. Thanks @DhtIsCoding.
|
||||
- Doctor/Gateway: use a lightweight `status` RPC without channel summary work for doctor Gateway liveness, so slow health snapshots do not falsely drive service restart repair. Fixes #64400; supersedes #64511. Thanks @CHE10X and @EronFan.
|
||||
- Agents/auth: scope external CLI credential discovery to configured providers during model auth status and startup prewarm, so opencode-only and other single-provider gateways do not block on unrelated Claude CLI Keychain probes. Fixes #73908. Thanks @Ailuras.
|
||||
- Agents/model selection: resolve slash-form aliases before provider/model parsing and keep alias-resolved primary models subject to transient provider cooldowns, so cron and persisted sessions do not retry cooled-down raw aliases. Fixes #73573 and #73657. Thanks @akai-shuuichi and @hashslingers.
|
||||
- Agents/Claude CLI: reuse already-cached macOS Keychain credentials for no-prompt Claude credential reads, so doctor/runtime checks do not miss fresh interactive Claude auth. Fixes #73682. Thanks @RyanSandoval.
|
||||
- Agents/Claude CLI doctor: scope workspace and project-dir checks to agents that actually use the Claude CLI runtime, so non-default Claude agents no longer make the default agent look Claude-backed. Fixes #73903. Thanks @bobfreeman1989.
|
||||
- Gateway/sessions: expose effective agent runtime metadata on session rows, `sessions.patch`, and local `openclaw sessions --json`, while keeping Claude CLI-backed rows on the canonical model provider so runtime backend and model identity are no longer conflated. Fixes #73090. Thanks @vishutdhar.
|
||||
- Gateway/auth status: scope external CLI credential overlays to configured providers, runtimes, or profiles and keep status reads off new Keychain prompts, so single-provider Gateway configs no longer probe unrelated Claude/Codex/MiniMax auth on startup. Fixes #73908. Thanks @Ailuras.
|
||||
- Agents/runtime status: expose effective agent runtime metadata in `agents.list`, Control UI agent panels, and `/agents`, and avoid rendering stale or cumulative CLI token totals as live context usage. Fixes #73660, #73578, and #45268. Thanks @spartman, @DashLabsDev, and @xyooz.
|
||||
- Agents/transcripts: strip empty assistant text blocks while preserving valid text, images, and signatures, so Anthropic-style providers no longer reject sanitized transcript turns. Fixes #73640. Thanks @jowhee327.
|
||||
- Gateway/sessions: preserve session keys on hidden lifecycle events so channel-routed runs still persist terminal session state and do not strand session status as running after Codex turn completion. Thanks @cathrynlavery.
|
||||
- Providers/Bedrock: omit deprecated `temperature` for Claude Opus 4.7 Bedrock model ids, named and application inference profiles, including dotted `opus-4.7` refs, and classify the nested validation response for failover. Fixes #73663. Thanks @bstanbury.
|
||||
- Gateway: raise the preauth/connect-challenge timeout to 15s so cold CLI starts on slower hosts have more time to process the WebSocket challenge before the Gateway closes the connection. Fixes #51469; refs #73592 and #62060. Thanks @GothicFox and @jackychen-png.
|
||||
- CLI/status: fall back to a bounded local `status` RPC when loopback detail probes time out or report unknown capability, so reachable local gateways are no longer marked unreachable by slow read diagnostics. Fixes #73535; refs #48360, #62762, #51357, and #42019. Thanks @RacecarGuy, @justinschille, @DJBlackhawk, @tianyaqpzm, and @0xrsydn.
|
||||
- CLI/gateway: reuse cached paired-device auth during `gateway probe` and report post-connect diagnostic failures as degraded reachability, so healthy local gateways are no longer marked unreachable after loopback auth or read timeouts. Fixes #48360. Thanks @RacecarGuy.
|
||||
- Channels/Discord: give Discord Gateway WebSocket handshakes a 30s timeout so stalled TLS/network transitions emit an error and Carbon can continue its reconnect loop instead of leaving the bot silent until restart. Refs #50046. Thanks @codexGW.
|
||||
- Mattermost/WebSocket: send protocol ping/pong keepalives and terminate stale sessions when pongs stop arriving, so silent TCP drops reconnect instead of leaving monitoring idle. Fixes #41837; carries forward #57621; refs #50138, #44160, and #51104. Thanks @JasonWang1124.
|
||||
- Channels/Telegram: suppress standalone failed edit/write warning payloads when a user-facing assistant error reply already covers the turn, while keeping unresolved mutating failures visible behind success-looking or suppressed-error replies. Fixes #39631; refs #73750; carries forward #39636 and #39717; leaves #39406 for configurable delivery policy. Thanks @Bartok9 and @Bortlesboat.
|
||||
- Control UI/agents: persist the Set Default action through `agents.list[].default` instead of writing the unsupported `agents.defaultId` field, so saved default-agent changes survive config validation. Fixes #65565; carries forward #72585. Thanks @luyao618.
|
||||
- NVIDIA/NIM: persist the `NVIDIA_API_KEY` provider marker and mark bundled NVIDIA Chat Completions models as string-content compatible, so NIM models load from `models.json` and OpenAI-compatible subagent calls send plain text content. Fixes #73013 and #50107; refs #73014. Thanks @bautrey, @iot2edge, @ifearghal, and @futhgar.
|
||||
- Channels/Discord: let text-only configs drop the `GuildVoiceStates` gateway intent and expose a bounded `/gateway/bot` metadata timeout with rate-limited fallback logs, reducing idle CPU and warning floods. Fixes #73709 and #73585. Thanks @sanchezm86 and @trac3r00.
|
||||
- Agents/sessions: mark same-turn `sessions_send` and A2A reply prompts with an inter-session `isUser=false` envelope before they reach the model, so foreign session output no longer lands as bare active user text. Fixes #73702; refs #73698, #73609, #73595, and #73622. Thanks @alvelda.
|
||||
- Channels/Telegram: fail closed when account-level public DM settings conflict with a restrictive top-level `allowFrom`, and require an effective wildcard before `dmPolicy="open"` behaves as public access. Fixes #73756; refs #73698. Thanks @Hilo-Hilo and @xace1825.
|
||||
- Channels/security: move open-DM allowlist semantics into the shared policy helpers and align Discord, Slack, Mattermost, Matrix, Feishu, LINE, IRC, Google Chat, Zalo, Zalo User, QQ Bot, and Synology Chat so `dmPolicy="open"` is public only with an effective wildcard and otherwise still respects sender allowlists. Refs #73756 and #73698. Thanks @Hilo-Hilo and @xace1825.
|
||||
- ACP/tasks: sweep orphaned parent-owned ACP sessions whose task records are gone, preserving bound persistent sessions but clearing unbound stale ACPX metadata so old child sessions cannot silently respawn into chat. Fixes #73609. Thanks @joerod26.
|
||||
- Outbound/security: strip known internal runtime scaffolding such as `<system-reminder>` and `<previous_response>` at the final channel delivery boundary and keep Discord output on targeted tag stripping, so degraded harness replies cannot leak those tags to users. Fixes #73595. Thanks @gabrielexito-stack and @martingarramon.
|
||||
- Security/Telegram: load Telegram security adapters in read-only audit/doctor, audit malformed Telegram DM `allowFrom` entries even when groups are disabled, and keep allowlist DM audits from counting stale pairing-store senders, so public/shared-DM risk checks stay accurate. Refs #73698. Thanks @xace1825.
|
||||
- Plugins: remove hidden manifest, provider-owner, bootstrap, and channel metadata caches so plugin installs, manifest edits, and bundled-root changes are visible on the next metadata read while keeping runtime/module loader caches for actual plugin code. Thanks @shakkernerd.
|
||||
- Control UI/WebChat: create a fresh dashboard session from the New Chat button instead of resetting the current transcript with `/new`, while keeping explicit `/new` reset behavior, preserving in-progress composer edits during delayed session creation or when creation cannot safely switch sessions, and showing clear retry feedback when creation is blocked, refreshing, or returns no new session. Carries forward #52042 and #52746. Thanks @bobashopcashier and @vincentkoc.
|
||||
- CLI/plugins: use plugin metadata snapshots for install slot selection and add opt-in plugin lifecycle timing traces, so plugin install avoids runtime-loading the plugin registry for metadata-only decisions. Thanks @shakkernerd.
|
||||
- fix(plugins): restrict bundled plugin dir resolution to trusted package roots. (#73275) Thanks @pgondhi987.
|
||||
- fix(security): prevent workspace PATH injection via service env and trash helpers. (#73264) Thanks @pgondhi987.
|
||||
- Active Memory: allow `allowedChatTypes` to include explicit portal/webchat sessions and classify `agent:...:explicit:...` session keys before opaque session ids can shadow the chat type. Fixes #65775. (#66285) Thanks @Lidang-Jiang.
|
||||
- Active Memory: allow the hidden recall sub-agent to use both `memory_recall` and the legacy `memory_search`/`memory_get` memory tool contract, so bundled `memory-lancedb` recall works without breaking the default `memory-core` path. Fixes #73502. (#73584) Thanks @Takhoffman.
|
||||
- fix(device-pairing): validate callerScopes against resolved token scopes on repair [AI]. (#72925) Thanks @pgondhi987.
|
||||
- Active Memory docs: document the `cacheTtlMs` 1000-120000 ms range and 15000 ms default so setup snippets do not lead users past the schema limit. Fixes #65708. (#65737) Thanks @WuKongAI-CMU.
|
||||
- fix(agents): canonicalize provider aliases in byProvider tool policy lookup [AI]. (#72917) Thanks @pgondhi987.
|
||||
- fix(security): block npm_execpath injection from workspace .env [AI-assisted]. (#73262) Thanks @pgondhi987.
|
||||
- Tools/web_fetch: decode response bodies from raw bytes using declared HTTP, XML, or HTML meta charsets before extraction, so Shift_JIS and other legacy-charset pages no longer return mojibake. Fixes #72916. Thanks @amknight.
|
||||
- Active Memory: skip payload-less `memory_search` transcript tool results when building debug telemetry, so newer empty entries no longer hide the latest useful debug payload. (#68773) Thanks @SimbaKingjoe.
|
||||
- Active Memory: keep recall setup time from consuming the configured model timeout while giving the hook runner an explicit bounded budget for the plugin, so slow embedded-run setup no longer causes immediate recall timeouts. Fixes #72606. (#72620) Thanks @hyspacex.
|
||||
- Channels/Discord: bound message read/search REST calls, route those actions through Gateway execution, and fall back to `CommandTargetSessionKey` for inbound hook session keys so Discord reads do not hang and hooks still fire when `SessionKey` is empty. Fixes #73431. (#73521) Thanks @amknight.
|
||||
- Plugins/media: auto-enable provider plugins referenced by `agents.defaults.imageGenerationModel`, `videoGenerationModel`, and `musicGenerationModel` primary/fallback refs, so configured Google and MiniMax media providers do not stay disabled behind a restrictive plugin allowlist. Thanks @vincentkoc.
|
||||
- Memory-core/dreaming: retry managed dreaming cron registration after startup when the cron service is not reachable yet, so the scheduled Memory Dreaming Promotion sweep recovers without waiting for heartbeat traffic. Fixes #72841. Thanks @amknight.
|
||||
- Acpx/runtime: validate the runtime session mode at the `AcpxRuntime.ensureSession` wrapper boundary so callers that pass anything other than `persistent` or `oneshot` get a clear `ACP_INVALID_RUNTIME_OPTION` error instead of silently round-tripping through the encoded handle as a default `persistent` mode and later throwing `SessionResumeRequiredError`. Investigation context: #73071. (#73548) Thanks @amknight.
|
||||
- CLI/infer: keep web-search fallback on missing provider API keys, preserve structured validation errors from the selected provider, and let per-request image describe prompts override configured media-entry prompts. (#63263) Thanks @Spolen23.
|
||||
- Chat commands: include configured model-catalog reasoning metadata when building `/think` argument menus so Ollama Cloud and other provider-owned reasoning models show supported levels instead of only `off`. Fixes #73515; supersedes #73568. Thanks @danielzinhu99 and @neeravmakwana.
|
||||
- Channels/Telegram: suppress generic tool-progress chatter when preview streaming is off, so non-streaming Telegram turns only deliver final replies while approvals, media, and errors still route normally. Refs #72363 and #72482. Thanks @neeravmakwana and @SweetSophia.
|
||||
- CLI/model probes: add repeatable image `--file` inputs to `infer model run` for local and gateway multimodal model smokes, so vision models such as Ollama Qwen VL and Gemini can be tested through the raw model-probe surface. Fixes #63700. Thanks @cedricjanssens.
|
||||
- CLI/model probes: request trusted operator scope for `infer model run --gateway --model <provider/model>` so Gateway raw model smokes can use one-off provider/model overrides instead of being rejected before provider auth resolution. Fixes #73759. Thanks @chrislro.
|
||||
- CLI/image describe: pass `--prompt` and `--timeout-ms` through `infer image describe` and `describe-many`, so custom vision instructions and slow local model budgets reach media-understanding providers such as Ollama, OpenAI, Google, and OpenRouter. Refs #63700. Thanks @cedricjanssens.
|
||||
- Model selection: include the rejected provider/model ref and allowlist recovery hint when a stored session override is cleared, so local model selections such as Gemma GGUF variants do not fall back to the default with a generic message. Refs #71069. Thanks @CyberRaccoonTeam.
|
||||
- OpenAI-compatible providers: drop malformed event-only or blank-data SSE frames before the OpenAI SDK stream parser sees them, so proxies that split `event:` from `data:` no longer crash streaming runs with `Unexpected end of JSON input`. Fixes #52802. Thanks @LyHug.
|
||||
- Gateway/OpenAI-compatible streaming: strip `<final>` tags split across streamed model deltas before they reach SSE clients, so `/v1/chat/completions` no longer emits tag remnants or drops content when final-answer wrappers cross chunk boundaries. Fixes #63325. Thanks @tzwickl.
|
||||
- Ollama: resolve explicitly selected signed-in `:cloud` models through `/api/show` when `/api/tags` omits them, so working models such as `gemini-3-flash-preview:cloud` and `deepseek-v4-pro:cloud` do not fail dynamic model resolution before the native `/api/chat` transport runs. Fixes #73909. Thanks @chtse53.
|
||||
- Discord/exec approvals: keep the local `/approve` prompt when no native Discord approval runtime is active, and send a manual fallback notice when native approval delivery reaches no targets, so failed DM cards no longer leave approval turns silent or dependent on model-written shell commands. Fixes #73954; carries forward #74027. Thanks @guarismo and @brokemac79.
|
||||
- Local model prompt caching: keep stable Project Context above volatile channel/session prompt guidance and stop embedding current channel names in the message tool description, so Ollama, MLX, llama.cpp, and other prefix-cache backends avoid avoidable full prompt reprocessing across channel turns. Fixes #40256; supersedes #40296. Thanks @rhclaw and @sriram369.
|
||||
- Gateway/OpenAI-compatible API: guard provider policy lookup against runtime providers with non-array `models` values, so `/v1/chat/completions` no longer fails with `provider?.models?.some is not a function`. Fixes #66744; carries forward #66761. Thanks @MightyMoud, @MukundaKatta.
|
||||
- WhatsApp/Web: pass explicit Baileys socket timings into every WhatsApp Web socket and expose `web.whatsapp.*` keepalive, connect, and query timeout settings so unstable networks can avoid repeated 408 disconnect and opening-handshake timeout loops. Fixes #56365. (#73580) Thanks @velvet-shark.
|
||||
- WhatsApp/Web: recover recently active listeners when a post-408 reconnect keeps receiving transport frames but stops delivering app messages, while keeping group metadata fallback off Baileys sends. Fixes #63855 and #66920; refs #7433, #67986, #70856, #60007, and #72621. Thanks @legonhilltech-jpg, @octopuslabs-fl, @Kanorin-chan, and @stuswan.
|
||||
- Channels/Telegram: persist native command metadata on target sessions so topic, helper, and ACP-bound slash commands keep their session metadata attached to the routed conversation. (#57548) Thanks @GaosCode.
|
||||
- Channels/native commands: keep validated native slash command replies visible in group chats while preserving explicit owner allowlists for command authorization. (#73672) Thanks @obviyus.
|
||||
- Pairing/doctor: bootstrap `commands.ownerAllowFrom` from the first approved DM pairing when no command owner exists, and have doctor explain missing owners so privileged slash commands are not accidentally unusable after onboarding. Thanks @pashpashpash.
|
||||
- Telegram/exec: infer native exec approvers from `commands.ownerAllowFrom` and auto-enable the Telegram approval client when an owner is resolvable, so owner-only commands such as `/diagnostics` can be approved in Telegram without duplicate per-channel approver config. Thanks @pashpashpash.
|
||||
- Auto-reply/session: carry the tail of user/assistant turns into the freshly-rotated transcript on silent in-reply session resets (compaction failure, role-ordering conflict) so direct-chat continuity survives the rebind. Fixes #70853. (#70898) Thanks @neeravmakwana.
|
||||
- Skills: load grouped skill directories such as `skills/<group>/<skill>/SKILL.md` from configured skill roots while keeping grouped discovery capped for large directories. Fixes #56915. (#72534) Thanks @ottodeng, @MoerAI, and @i010542.
|
||||
- Config: skip malformed non-string `env.vars` entries before env-reference checks, so config loading no longer crashes on JSON values like numbers or booleans. (#42402) Thanks @MiltonHeYan.
|
||||
- Docker Compose: default missing config and workspace bind mounts to `${HOME:-/tmp}/.openclaw` so manual compose runs do not create invalid empty-source volume specs. (#64485) Thanks @jlapenna.
|
||||
- Agents/context engines: preserve the child agent's configured `agentDir` when subagent cleanup re-resolves a context engine, so `onSubagentEnded` hooks keep operating on the correct per-agent state. (#67243) Thanks @jarimustonen.
|
||||
- Channels/WhatsApp: restrict pairing verification replies to real inbound user content, preventing unsolicited prompts from receipts, typing indicators, presence updates, and other non-message Baileys upserts. Fixes #73797. (#73823) Thanks @hclsys.
|
||||
- Configure/Ollama: show the configured Ollama model allowlist after Cloud only or Cloud + Local setup and skip slow per-model cloud metadata fetches. (#73995) Thanks @obviyus.
|
||||
- Channels/WhatsApp: detect explicit group `@mentions` again when the bot's own E.164 is in `allowFrom`, so shared-number setups no longer skip group pings that directly mention the bot. Fixes #49317. (#73453) Thanks @juan-flores077.
|
||||
- WhatsApp/reliability: publish real transport-liveness into WhatsApp channel status and force earlier reconnects on silent transport stalls, so quiet healthy sessions stay connected while wedged sockets recover before the later remote 408 path. (#72656) Thanks @Sathvik-1007.
|
||||
- Core/channels: tighten selected runtime, media, and plugin edge-case handling while preserving existing behavior. Thanks @jesse-merhi.
|
||||
- Channels/WhatsApp: strip leaked plural tool-call XML wrappers on every WhatsApp-visible outbound path and keep channel error payloads out of WhatsApp chats. (#71830) Thanks @rubencu.
|
||||
- Agents/embedded-runner: inject the resolved OAuth bearer (and forward the run abort signal) on the boundary-aware embedded stream fallback so models that route through `openai-codex-responses` and other boundary-aware transports stop failing with `401 Unauthorized: Missing bearer or basic authentication in header`. Fixes #73559. (#73588) Thanks @openperf.
|
||||
- Telegram/gateway: bound outbound Bot API calls and cache bundled plugin alias lookup so slow Telegram sends or WSL2 filesystem scans no longer wedge gateway replies. (#74210) Thanks @obviyus.
|
||||
- Configure/GitHub Copilot: reuse existing Copilot auth during configure and show the provider's manifest model catalog in the model picker. (#74276) Thanks @obviyus.
|
||||
- Configure/models: keep the model picker scoped to the selected manifest provider and enable its bundled plugin before catalog lookup, so choosing GitHub Copilot no longer falls back to Ollama or skips the catalog. (#74322) Thanks @obviyus.
|
||||
- Auto-reply/subagents: reject `/focus` from leaf subagents and scope fallback target resolution to the requesting subagent's children, so subagents cannot bind conversations outside their control boundary. (#73613) Thanks @drobison00.
|
||||
- Gateway/startup: skip inherited workspace startup memory for sandboxed spawned sessions without real-workspace write access, so `/new` no longer preloads host workspace memory into isolated child runs. (#73611) Thanks @drobison00.
|
||||
- Agents/tool policy: validate caller group IDs against session or spawned context before applying group-scoped tool policies or persisting gateway group metadata, so forged group IDs cannot unlock more permissive tools. (#73720) Thanks @mmaps.
|
||||
- Commands: keep channel-prefixed owner allowlist entries scoped to matching providers so webchat command contexts cannot inherit external channel owners. Thanks @zsxsoft.
|
||||
- Auth/device pairing: bound bootstrap handoff token issuance, redemption, and approved pairing baselines to the documented per-role scope allowlist, so bootstrap approvals cannot persistently grant `operator.admin`, `operator.pairing`, or `node.exec` scopes. Thanks @eleqtrizit.
|
||||
- Providers/GitHub Copilot: support the GUI/RPC wizard device-code auth flow so onboarding from non-TTY clients (gateway RPC bridge, GUI wizards) completes instead of returning empty profiles. Dangerous-state handling now distinguishes `access_denied` and `expired_token` from transport errors. (#73290) Thanks @indierawk2k2.
|
||||
- Installer/Linux: warn before switching an unwritable npm global prefix to `~/.npm-global`, then tell users to run future global updates with `npm i -g openclaw@latest` without `sudo` so npm keeps using the redirected user prefix. Fixes #44365; carries forward #50479. Thanks @Sayeem3051.
|
||||
- Gateway/plugins: enable the native `require()` fast path on Windows for bundled plugin modules so plugin loading uses `require()` instead of Jiti's transform pipeline, reducing startup from ~39s to ~2s on typical 6-plugin setups. Fixes #68656. (#74173) Thanks @galiniliev.
|
||||
- macOS app: detect stale Gateway TLS certificate pins, automatically repair trusted Tailscale Serve rotations, and surface paired-but-disconnected Mac companion nodes so partial Gateway connections no longer look healthy. Thanks @guti.
|
||||
- Feishu: recreate WebSocket clients with monitor-owned backoff only after SDK reconnect exhaustion, preserving heartbeat defaults and shutdown cleanup without treating recoverable SDK callback errors as terminal, so persistent connections recover without manual gateway restart. Fixes #52618; duplicate evidence #59753; related #55532, #68766, #72411, and #73739. Thanks @vincentkoc, @schumilin, @alex-xuweilong, @120106835, @sirfengyu, and @tianhaocui.
|
||||
|
||||
## 2026.4.27
|
||||
|
||||
### Highlights
|
||||
|
||||
- Codex Computer Use setup now ships with status/install commands, marketplace discovery, and fail-closed MCP checks for Codex-mode desktop control. Thanks @pash-openai.
|
||||
- DeepInfra joins the bundled provider set with model discovery, media generation/editing, TTS, embeddings, and provider-owned onboarding policy. Thanks @ats3v.
|
||||
- Tencent Yuanbao and QQBot support expand channel coverage with Yuanbao docs/catalog entries and QQBot group chat, streaming, media upload, and pipeline refactors. Thanks @loongfay and @cxyhhhhh.
|
||||
- Plugin startup and model catalogs move toward manifest-first metadata, reducing Gateway boot work and making provider rows/aliases/suppressions easier to audit. Thanks @shakkernerd.
|
||||
- Reliability fixes cover Telegram startup/sends, Slack socket/media stalls, gateway startup prewarm, session/history defaults, update sync, and Windows restart handoffs. Thanks @joerod26, @obviyus, @shivasymbl, @freerk, @bassboy2k, @jpreagan, @islandpreneur007, and @Thatgfsj.
|
||||
|
||||
### Changes
|
||||
|
||||
- Sandbox/Docker: add opt-in `sandbox.docker.gpus` passthrough for Docker sandbox containers so local GPU workloads can run inside sandboxed agents when the host Docker runtime supports `--gpus`. Fixes #57976; carries forward #58124. Thanks @cyan-ember.
|
||||
- iOS/Gateway: add an authenticated `node.presence.alive` protocol event and `node.list` last-seen fields so background iOS wakes can mark paired nodes recently alive without treating them as connected. Carries forward #63123. Thanks @ngutman.
|
||||
- Android: publish authenticated `node.presence.alive` events after node connect and background transitions so paired Android nodes retain durable last-seen metadata after disconnects. Carries forward #63123. Thanks @ngutman.
|
||||
- Gateway/chat: accept non-image attachments through `chat.send` by staging them as agent-readable media paths, while keeping unsupported RPC attachment paths explicit instead of silently dropping files. Fixes #48123. (#67572) Thanks @samzong.
|
||||
- Security/networking: add opt-in operator-managed outbound proxy routing (proxy.enabled + proxy.proxyUrl/OPENCLAW_PROXY_URL) with strict http:// forward-proxy validation, loopback-only Gateway bypass, and cleanup of proxy env/dispatcher state on exit. (#70044) Thanks @jesse-merhi and @joshavant.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock, including dist-runtime canonical roots, so Docker Desktop/WSL cold starts no longer hold `.openclaw-runtime-mirror.lock` while scanning slow persisted volumes. Fixes #73339. Thanks @1yihui.
|
||||
- Channels/LINE: persist inbound image, video, audio, and file downloads in `~/.openclaw/media/inbound/` instead of temporary files so agents can still read LINE media after `/tmp` cleanup. Fixes #73370. Thanks @hijirii and @wenxu007.
|
||||
- Control UI/WebChat: keep large attachment payloads out of Lit state and optimistic chat messages, using object URL previews plus send-time payload serialization so PDF/image uploads no longer trigger `RangeError: Maximum call stack size exceeded`. Fixes #73360; refs #54378 and #63432. Thanks @hejunhui-73, @Ansub, and @christianhernandez3-afk.
|
||||
- Agents/Anthropic: cancel stalled Anthropic Messages SSE body reads when abort signals fire, so active-memory timeouts release transport resources instead of leaving hidden recall runs parked on `reader.read()`. Refs #72965 and #73120. Thanks @wdeveloper16.
|
||||
- Agents/models: keep per-agent primary models strict when `fallbacks` is omitted, so probe-only custom providers are not tried as hidden fallback candidates unless the agent explicitly opts in. Fixes #73332. Thanks @haumanto.
|
||||
- Gateway/models: add `models.pricing.enabled` so offline or restricted-network installs can skip startup OpenRouter and LiteLLM pricing-catalog fetches while keeping explicit model costs working. Fixes #53639. Thanks @callebtc, @palewire, and @rjdjohnston.
|
||||
- Onboarding: pin interactive and non-interactive health checks to the just-configured setup token/password so stale `OPENCLAW_GATEWAY_TOKEN` or `OPENCLAW_GATEWAY_PASSWORD` values do not produce false gateway-token-mismatch failures after setup. Fixes #72203. Thanks @galiniliev.
|
||||
- Doctor/state: require an interactive confirmation before archiving orphan transcript files, so `openclaw doctor --fix` no longer silently renames recoverable session history after upgrades regenerate `sessions.json`. Fixes #73106. Thanks @scottgl9.
|
||||
- Cron/Telegram: preserve explicit `:topic:` delivery targets over stale session-derived thread IDs when isolated cron announces to Telegram forum topics. Carries forward #59069; refs #49704 and #43808. Thanks @roytong9.
|
||||
- Build/runtime: write the runtime-postbuild stamp after `pnpm build` writes the build stamp, so the next CLI invocation does not re-sync runtime artifacts after a successful build. Fixes #73151. Thanks @bittoby.
|
||||
- Build/runtime: preserve staged bundled-plugin runtime dependency caches across source-checkout tsdown rebuilds, so local CLI and gateway-watch rebuilds no longer recreate large plugin dependency trees before starting. Refs #73205. Thanks @SymbolStar.
|
||||
- CLI/channels: list configured chat channel accounts from read-only setup metadata even when the standalone CLI has not loaded the runtime channel registry, so `openclaw channels list` shows Telegram accounts before auth providers. Fixes #73319 and #73322. Thanks @mlaihk.
|
||||
- CLI/model probes: keep `infer model run --gateway` raw by skipping prior session transcript, bootstrap context, context-engine assembly, tools, and bundled MCP servers, so local backends can be tested without full agent-context overhead. Fixes #73308. Thanks @ScientificProgrammer.
|
||||
- CLI/model probes: reject empty or whitespace-only `infer model run --prompt` values before calling local providers or the Gateway, so smoke checks do not spend provider calls on invalid turns. Fixes #73185. Thanks @iot2edge.
|
||||
- Gateway/media: route text-only `chat.send` image offloads through media-understanding fields so `agents.defaults.imageModel` can describe WebChat attachments instead of leaving only an opaque `media://inbound` marker. Fixes #72968. Thanks @vorajeeah.
|
||||
- Gateway/Windows: route no-listener restart handoffs through the Windows supervisor without leaving restart tokens in flight, so failed task scheduling can be retried and successful handoffs do not coalesce later restart requests. (#69056) Thanks @Thatgfsj.
|
||||
- Gateway/model pricing: skip plugin manifest discovery during background pricing refreshes when `plugins.enabled: false`, so disabled-plugin setups do not keep rebuilding plugin metadata from the Gateway hot path. Fixes #73291. Thanks @slideshow-dingo and @fishgills.
|
||||
- Ollama/thinking: validate `/think` commands against live Ollama catalog reasoning metadata, so models whose `/api/show` capabilities include `thinking` expose `low`, `medium`, `high`, and `max` instead of being stuck on `off`. Fixes #73366. Thanks @cymise.
|
||||
- Gateway/sessions: remove automatic oversized `sessions.json` rotation backups, deprecate `session.maintenance.rotateBytes`, and teach `openclaw doctor --fix` to remove the ignored key so hot session writes no longer copy multi-MB stores. Refs #72338. Thanks @midhunmonachan and @DougButdorf.
|
||||
- Channels/Telegram: fail fast when Telegram rejects the startup `getMe` token probe with 401, so invalid or stale BotFather tokens are reported as token auth failures instead of misleading `deleteWebhook` cleanup failures. Fixes #47674. Thanks @samaedan-arch.
|
||||
- ACPX: keep generated Codex and Claude ACP wrapper startup paths working when remote or special state filesystems reject chmod, since OpenClaw invokes the wrappers through Node instead of executing them directly. Fixes #73333. Thanks @david-garcia-garcia.
|
||||
- CLI/onboarding: infer image input for common custom-provider vision model IDs, ask only for unknown models, and keep `--custom-image-input`/`--custom-text-input` overrides so vision-capable proxies do not get saved as text-only configs. Fixes #51869. Thanks @Antsoldier1974.
|
||||
- Models/OpenAI Codex: stop listing or resolving unsupported `openai-codex/gpt-5.4-mini` rows through Codex OAuth, keep stale discovery rows suppressed with a clear API-key-route hint, and leave direct `openai/gpt-5.4-mini` available. Fixes #73242. Thanks @0xCyda.
|
||||
- Plugin SDK: restore the root `stringEnum` and `optionalStringEnum` exports on both the published SDK entry and runtime root-alias bridge, so older external plugins can keep building and loading while migrating to focused SDK subpaths. Fixes #68279. Thanks @marzliak.
|
||||
- Plugin SDK: restore the root-alias bridge for `registerContextEngine` and expose missing legacy compat helpers `normalizeAccountId` and `resolvePreferredOpenClawTmpDir` so older external plugins such as `openclaw-weixin` can keep loading while migrating to focused SDK subpaths. Fixes #53497. Thanks @alanxchen85.
|
||||
- Auth profiles: make `openclaw doctor --fix` migrate legacy flat `auth-profiles.json` files such as `{ "ollama-windows": { "apiKey": "ollama-local" } }` to canonical provider default API-key profiles with a backup, so custom Ollama/OpenAI-compatible providers recover cleanly after upgrading. Fixes #59629; supersedes #59642. Thanks @Xsanders555 and @Linux2010.
|
||||
- Memory/Dreaming: retry Dream Diary once with the session default when a configured dreaming model is unavailable, while leaving subagent trust and allowlist errors visible instead of silently masking configuration problems. Refs #67409 and #69209. Thanks @Ghiggins18 and @everySympathy.
|
||||
- Feishu/inbound files: recover CJK filenames from plain `Content-Disposition: filename=` download headers when Feishu exposes UTF-8 bytes through Latin-1 header decoding, while leaving valid Latin-1 and JSON-derived names unchanged. (#48578, #50435, #59431) Thanks @alex-xuweilong, @lishuaigit, and @DoChaoing.
|
||||
- Channels/Telegram: normalize accidental full `/bot<TOKEN>` Telegram `apiRoot` values at runtime and teach `openclaw doctor --fix` to remove the suffix, so startup control calls no longer 404 when direct Bot API curl commands work. Fixes #55387. Thanks @brendanmatthewjones-cmyk, @techfindubai-ux, and @Sivlerback-Chris.
|
||||
- Zalo Personal: persist refreshed `zca-js` session cookies after QR login, session restore, and successful API calls so gateway restarts restore the freshest local session. (#73277) Thanks @darkamenosa.
|
||||
- Logging/security: redact sensitive tokens (sk-\* keys, Bearer/Authorization values, etc.) at the subsystem console sink so `createSubsystemLogger().info/warn/error` output that bypasses the patched console-capture handler still applies the same redaction the file transport already does. Fixes #73284; refs #67953 and #64046. Thanks @edwin-rivera-dev.
|
||||
- Plugins/runtime deps: reuse enclosing versioned cache roots when bundled plugins resolve from nested staged paths, so plugin-runtime-deps no longer mints `openclaw-unknown-*` directories or loops on `ENOTEMPTY`. Fixes #72956. (#73205) Thanks @SymbolStar.
|
||||
- Agents/failover: classify CJK provider transport, quota, billing, auth, and overload error text so Chinese-language provider failures trigger fallback and user-facing transport copy instead of surfacing as unclassified raw errors. (#56242) Thanks @tomcatzh.
|
||||
- Agents/failover: seed non-claude-cli fallback prompts with Claude Code session context when a claude-cli attempt fails, so fallback models do not restart cold after billing or quota failover. (#72069) Thanks @stainlu.
|
||||
|
||||
## 2026.4.27
|
||||
|
||||
### Changes
|
||||
|
||||
- Dependencies: refresh provider and tooling dependencies, including AWS SDK, PI runtime packages, AJV, Feishu SDK, Anthropic SDK, tokenjuice, and native TypeScript/oxlint tooling. Thanks @dependabot.
|
||||
- Matrix/QA: add live Matrix approval scenarios for exec metadata, chunked fallback, plugin approvals, deny reactions, thread targeting, and `target: "both"` delivery, with redacted artifacts preserving safe approval summaries. Thanks @gumadeiras.
|
||||
- Diagnostics/Codex: add owner-only core `/diagnostics` with a sensitive-data preamble, docs link, and explicit Gateway export approval guidance; Codex harness sessions also ask before uploading Codex feedback for the attached thread and print the matching `codex resume <thread-id>` inspection command after confirmed upload. Thanks @pashpashpash.
|
||||
- Trajectory export: route `/export-trajectory` through per-run exec approval, send group-chat approval prompts and export results only to the owner privately, and add `openclaw sessions export-trajectory` for the approved command path. Thanks @pashpashpash.
|
||||
- Codex: add Computer Use setup for Codex-mode agents, including `/codex computer-use status/install`, marketplace discovery, optional auto-install, and fail-closed MCP server checks before Codex-mode turns start. Fixes #72094. (#71842) Thanks @pash-openai.
|
||||
- Apps: consume Peekaboo 3.0.0-beta4 and ElevenLabsKit 0.1.1, align Swabble on Commander 0.2.2, and refresh macOS/iOS SwiftPM resolutions against the released dependency graph. Thanks @Blaizzy.
|
||||
- Plugin SDK: expose shared channel route normalization, parser-driven target resolution, raw-target compact keys, parsed-target types, and route comparison helpers through `openclaw/plugin-sdk/channel-route`, switch native approval origin matching onto that route contract with optional delivery and match-only target normalization, and retire the internal channel-route shim behind dated compatibility aliases for legacy key/comparable-target helpers. Thanks @vincentkoc.
|
||||
@@ -63,6 +522,8 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugin SDK/models: add a shared manifest-backed provider catalog builder and move Qianfan, Xiaomi, NVIDIA, Cerebras, Mistral, Moonshot, DeepSeek, Tencent TokenHub, and StepFun provider catalogs onto their plugin manifest `modelCatalog` rows. Thanks @shakkernerd.
|
||||
- Plugin SDK/models: move BytePlus and Volcano Engine standard and plan-provider catalogs into plugin manifest `modelCatalog` rows and remove the now-unused Volcengine-family shared catalog SDK subpath. Thanks @shakkernerd.
|
||||
- CLI/models: move Fireworks and Together AI fixed provider catalogs into plugin manifest `modelCatalog` rows so provider-filtered listing can use manifest-backed static rows. Thanks @shakkernerd.
|
||||
- CLI/models: move Groq's fixed text model catalog into the Groq plugin manifest and declare its setup auth env metadata so provider-filtered listing can use manifest-backed rows without deprecated auth metadata. Thanks @shakkernerd.
|
||||
- CLI/models: move Venice's 41-row seed catalog into the Venice plugin manifest, derive runtime fallback rows from that manifest, and keep Venice API discovery as refreshable runtime work instead of a second hard-coded catalog. Thanks @shakkernerd.
|
||||
- Channels/Yuanbao: register the Tencent Yuanbao external channel plugin (`openclaw-plugin-yuanbao`) in the official channel catalog, contract suites, and community plugin docs, with a new `docs/channels/yuanbao.md` quick-start guide for WebSocket bot DMs and group chats. (#72756) Thanks @loongfay.
|
||||
- Channels/QQBot: add full group chat support (history tracking, @-mention gating, activation modes, per-group config, FIFO message queue with deliver debounce), C2C `stream_messages` streaming with a `StreamingController` lifecycle manager, unified `sendMedia` with chunked upload for large files, and refactor the engine into pipeline stages, focused outbound submodules, builtin slash-command modules, and explicit DI ports via `createEngineAdapters()`. (#70624) Thanks @cxyhhhhh.
|
||||
- Plugins/startup: migrate bundled plugin manifests to explicit `activation.onStartup` declarations so Gateway startup imports only the bundled plugins that intentionally register startup-time runtime surfaces. Thanks @shakkernerd.
|
||||
@@ -92,6 +553,92 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- CLI/channel-setup: auto-skip the redundant "Install \<plugin\>?" confirmation when only one install source (npm or local) exists, show `download from <npm-spec>` hints for installable catalog channels in the picker, and suppress misleading npm hints for already-bundled channels. Fixes #73419. Thanks @sliverp.
|
||||
- BlueBubbles: tighten DM-vs-group routing across the outbound session route (`chat_guid:iMessage;-;...` DMs no longer classified as groups), reaction handling (drop group reactions that arrive without any chat identifier instead of synthesizing a `"group"` literal peerId), inbound `chatGuid` fallback (no longer fall back to the sender's DM chatGuid when resolving a group whose webhook omits chatGuid+chatId+chatIdentifier), and short message id resolution (carry caller chat context so a numeric short id reused after a long group conversation cannot silently resolve to a message in a different chat, with the same cross-chat guard applied to full GUIDs so retries cannot bypass it). Thanks @zqchris.
|
||||
- Gateway/sessions: clone cached session stores through the persisted JSON shape instead of `structuredClone`, reducing native-memory growth on the remaining #54155 Gateway RSS/session-accumulation path while keeping #54155 as the broader tracker and carrying forward the #45438 session-cache hypothesis. Thanks @vincentkoc and the #45438 reporters/commenters.
|
||||
- Agents/approvals: fail restart-interrupted sessions whose transcript tail is still `approval-pending` instead of replaying stale exec approval IDs into the new Gateway process after restart. Fixes #65486. Thanks @mjmai20682068-create.
|
||||
- CLI/Gateway: use method-specific least-privilege scopes for classified CLI Gateway calls while preserving legacy broad scopes for unclassified plugin methods, so read-only commands no longer create admin/write/pairing scope-upgrade prompts. Fixes #68634. Thanks @nightmusher.
|
||||
- Gateway/sessions: align `chat.history` and `sessions.list` thinking defaults with owning-agent and catalog-aware resolution so Control UI session defaults match backend runtime state. (#63418) Thanks @jpreagan.
|
||||
- Devices/pairing: recover array-shaped device and node pairing state files before persisting approvals, so UUID-keyed pending and paired entries no longer disappear after a malformed JSON store write. Fixes #63035. Thanks @sar618.
|
||||
- Gateway/auth: clear reused stale device tokens and stop reconnecting on device-token mismatch in the Control UI and Node gateway clients, avoiding rate-limit loops after scope-upgrade or token-rotation handoffs. Fixes #71609. Thanks @ricksayhi.
|
||||
- Gateway/approvals: treat duplicate same-decision approval resolves as idempotent during the resolved-entry grace window, including consumed `allow-once` approvals, while returning an explicit already-resolved error for conflicting repeats. Fixes #59162; refs #58479 and #65486. Thanks @wikithoughts, @sajazuniga7-coder, and @mjmai20682068-create.
|
||||
- Channels/Telegram: honor `approvals.exec/plugin.targets[].accountId` when routing native approvals across multi-bot Telegram accounts while preserving unscoped Telegram targets for any account. Fixes #69916. Thanks @joerod26.
|
||||
- Agents/exec: omit the internal session-resume fallback preface from successful async exec completion messages sent directly back to chat. Fixes #67181. Thanks @raistlin88.
|
||||
- Agents/media: register detached `video_generate` and `music_generate` tool run contexts until terminal status, so Discord-backed provider jobs stay live in `/tasks` instead of becoming `lost` when the parent chat run context disappears. Thanks @vincentkoc.
|
||||
- Agents/media: prefer OpenAI image and video providers when the default model uses the OpenAI Codex auth alias, so auto media generation no longer falls through to Fal before GPT Image or Sora. Thanks @vincentkoc.
|
||||
- Tasks/media: infer agent ownership for session-scoped task records so `/tasks` agent-local fallback includes session-backed `video_generate` and other async media jobs even when the current chat session has no linked rows. Thanks @vincentkoc.
|
||||
- Agents/media: keep long-running `video_generate` and `music_generate` tasks fresh while provider jobs are still pending, so task maintenance does not mark active Discord media renders lost before completion. Thanks @vincentkoc.
|
||||
- CLI/status: treat scope-limited gateway probes as reachable-but-degraded in shared status scans, so `openclaw status --all` no longer reports a live gateway as unreachable after `missing scope: operator.read`. Fixes #49180; supersedes #47981. Thanks @openjay.
|
||||
- Slack/Socket Mode: use a 15s Slack SDK pong timeout by default and add `channels.slack.socketMode.clientPingTimeout`, `serverPingTimeout`, and `pingPongLoggingEnabled` overrides so stale-websocket handling no longer depends on app-event health heuristics. Fixes #14248; refs #58519, #64009, and #63488. Thanks @shivasymbl and @freerk.
|
||||
- Slack/media: bound private file and forwarded attachment downloads with idle and total timeouts while preserving placeholder fallback, so stalled Slack `file_share` media no longer wedges inbound message handling. Fixes #61850. Thanks @bassboy2k.
|
||||
- Plugins/inspector: keep bundled plugin runtime capture quiet and config-tolerant for Codex, memory-lancedb, Feishu, Mattermost, QQBot, and Tlon so plugin-inspector JSON checks can validate the full bundled set. Thanks @vincentkoc.
|
||||
- Slack/auto-reply: keep fully consumed text reset triggers such as `new session` out of `BodyForAgent` after directive cleanup, so configured Slack reset phrases do not leak into the fresh model turn. Fixes #73137. Thanks @neeravmakwana.
|
||||
- Plugins/runtime deps: prune stale retained bundled runtime deps and keep doctor/secret channel contract scans on lightweight artifacts, so disabled bundled channels stop preserving old dependency trees or importing heavy plugin surfaces. Thanks @SymbolStar and @vincentkoc.
|
||||
- Auto-reply: bound the post-run pending tool-result delivery drain with a progress-aware idle timeout, so a never-settling tool-result task no longer leaves the session active forever while slow healthy deliveries can keep draining. Fixes #53889; supersedes #64733 and #73434. Thanks @zijunl and @wujiaming88.
|
||||
- Gateway/startup: start chat channels without waiting for primary model prewarm, keeping model warmup bounded in the background so Slack and other channels come online promptly when provider discovery is slow. Supersedes #73420. Thanks @dorukardahan.
|
||||
- Gateway/install: carry env-backed config SecretRefs such as `channels.discord.token` into generated service environments when they are present only in the installing shell, while keeping gateway auth SecretRefs non-persisted. Fixes #67817; supersedes #73426. Thanks @wdimaculangan and @ztexydt-cqh.
|
||||
- Auto-reply/commands: stop bare `/reset` and `/new` after reset hooks acknowledge the command, so non-ACP channels no longer fall through into empty provider calls while `/reset <message>` and `/new <message>` still seed the next model turn. Fixes #73367 and #73412. Thanks @hoyanhan, @wenxu007, and @amdhelper.
|
||||
- Providers/DeepSeek: backfill DeepSeek V4 `reasoning_content` on plain assistant replay messages as well as tool-call turns, so thinking sessions with prior tool use no longer fail follow-up requests with missing reasoning content. Fixes #73417; refs #71372. Thanks @34262315716 and @Bartok9.
|
||||
- Agents/gateway tool: strip full config payloads from `config.patch` and `config.apply` tool responses while preserving direct RPC responses, so config-heavy sessions no longer replay large redacted configs into transcript history. Fixes #47610; supersedes #73439. Thanks @HanenVit and @juan-flores077.
|
||||
- Auto-reply: preserve voice-note media from silent turns while continuing to suppress text and non-voice media, so `NO_REPLY` TTS replies still deliver the requested audio bubble. (#73406) Thanks @zqchris.
|
||||
- Channels/Mattermost: stop enqueueing regular inbound posts as system events, so Mattermost user messages reach the model only as user-role inbound-envelope content instead of also appearing as `System: Mattermost message...` directives. Fixes #71795. Thanks @juan-flores077.
|
||||
- Agents/media: qualify bare `agents.defaults.imageModel` and `pdfModel` refs from unique configured image-capable providers, so Ollama vision models such as `moondream` and `qwen2.5vl:7b` do not fall through to the default provider. Fixes #38816; supersedes #73396. Thanks @alainasclaw and @vincentkoc.
|
||||
- Agents/Anthropic: send implicit Anthropic beta headers only to direct public Anthropic endpoints, including OAuth, so custom Anthropic-compatible providers no longer mis-handle unsupported beta flags unless explicitly configured. Refs #73346. Thanks @byBrodowski.
|
||||
- Skills: require explicit `skills.entries.coding-agent.enabled` before exposing the bundled coding-agent skill, so installs with Codex on PATH but no OpenAI auth do not silently offer Codex delegation. Fixes #73358. Thanks @LaFleurAdvertising and @Sanjays2402.
|
||||
- Plugins/startup: treat manifestless Claude bundles as valid installed-plugin registry entries instead of stale missing manifests, so workspace bundles no longer force repeated derived registry rebuilds or noisy `plugins.entries.workspace` warnings during Gateway startup. Fixes #73433. Thanks @AnneVoss.
|
||||
- Agents/subagents: preserve `sessions_yield` as a paused subagent state and ignore its wait text while freezing completion output, so parent sessions wait for the final post-compaction answer instead of receiving intermediate progress or `(no output)`. Fixes #73413. Thanks @Ask-sola.
|
||||
- Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock and keep Docker bundled plugin runtime deps/mirrors in a Docker-managed volume instead of the Windows/WSL config bind mount, so cold starts avoid slow host-volume mirror writes. Fixes #73339. Thanks @1yihui.
|
||||
- Plugins/runtime deps: refresh bundled runtime mirrors without deleting active import trees, so config-triggered restarts do not see transient missing plugin files during registration. Thanks @shakkernerd.
|
||||
- Channels/LINE: persist inbound image, video, audio, and file downloads in `~/.openclaw/media/inbound/` instead of temporary files so agents can still read LINE media after `/tmp` cleanup. Fixes #73370. Thanks @hijirii and @wenxu007.
|
||||
- CLI/plugins: keep bundled plugin installs out of `plugins.load.paths` while preserving install records, so install/inspect/doctor loops no longer warn about the current bundled plugin directory. Thanks @vincentkoc.
|
||||
- CLI/plugins: scope `plugins inspect <id>` runtime loading to the matched plugin so single-plugin inspection does not load every plugin before checking the target. Thanks @shakkernerd.
|
||||
- CLI/plugins: remove managed copied-path plugin directories during uninstall and plan uninstall from metadata instead of runtime-loading plugins, so plugin lifecycle commands avoid unnecessary bundled runtime-deps work. Thanks @shakkernerd.
|
||||
- Cron tool: infer the creating session's agentId for `cron.add` jobs when `agentId` is omitted or passed as undefined, keeping scheduled agentTurn jobs routed to the session agent; #40571 identified the guard bug and supplied the focused regression coverage. Thanks @ChanningYul.
|
||||
- Cron/Telegram: add `--thread-id` to `openclaw cron add` and `openclaw cron edit`, preserving Telegram forum topic delivery targets across scheduled announcements. Carries forward #51581, #60373, and #60890. Thanks @ChunHao-dev.
|
||||
- Cron/Telegram: preserve session-derived Telegram topic thread IDs when isolated cron delivery explicitly targets the parent chat, keeping bare chat targets in the active forum topic without leaking stale topics to other chats. Carries forward #64708. Thanks @addelh.
|
||||
- Memory/compaction: keep pre-compaction memory-flush prompts runtime-only so session transcripts and `chat.history` no longer expose them as normal user turns. Fixes #54408 and #58956; refs #43567. Thanks @markgong and @guoyuhang9.
|
||||
- Control UI/WebChat: keep large attachment payloads out of Lit state and optimistic chat messages, using object URL previews plus send-time payload serialization so PDF/image uploads no longer trigger `RangeError: Maximum call stack size exceeded`. Fixes #73360; refs #54378 and #63432. Thanks @hejunhui-73, @Ansub, and @christianhernandez3-afk.
|
||||
- Agents/Anthropic: cancel stalled Anthropic Messages SSE body reads when abort signals fire, so active-memory timeouts release transport resources instead of leaving hidden recall runs parked on `reader.read()`. Refs #72965 and #73120. Thanks @wdeveloper16.
|
||||
- Control UI/WebChat: keep pending run and typing state attached to the active client run, so unowned inject/announce/side-result finals no longer unlock unrelated active runs while completed owned runs still clear promptly. Fixes #57795; carries forward the narrow diagnosis from #57887. Thanks @haoyu-haoyu.
|
||||
- Sandbox/Docker: stop satisfying a missing default sandbox image by tagging plain Debian as `openclaw-sandbox:bookworm-slim`, preserving the Python tooling required by sandbox write/edit helpers and directing users to build the default image. Fixes #51185; refs #45108, #51099, #51609, and #57713. Thanks @dpalis, @Tin55FoilDev, @jbcohen2-coder, @macminihal-cyber, and @PraxoOnline.
|
||||
- Control UI/WebChat: confirm toolbar New Session button resets before dispatching `/new` while leaving typed `/new` and `/reset` commands immediate. Fixes #45800; refs #27065, #56611, #54499, and #27110. Thanks @aethnova, @kosta228-huli, @adambezemek, and @xss925175263 (xianshishan).
|
||||
- Agents/models: keep per-agent primary models strict when `fallbacks` is omitted, so probe-only custom providers are not tried as hidden fallback candidates unless the agent explicitly opts in. Fixes #73332. Thanks @haumanto.
|
||||
- Gateway/models: add `models.pricing.enabled` so offline or restricted-network installs can skip startup OpenRouter and LiteLLM pricing-catalog fetches while keeping explicit model costs working. Fixes #53639. Thanks @callebtc, @palewire, and @rjdjohnston.
|
||||
- Gateway/startup: warn when legacy `CLAWDBOT_*` or `MOLTBOT_*` environment variables are still present, pointing users to `OPENCLAW_*` names instead of failing silently. Fixes #53482; carries forward #53667. Thanks @lndyzwdxhs.
|
||||
- Onboarding: pin interactive and non-interactive health checks to the just-configured setup token/password so stale `OPENCLAW_GATEWAY_TOKEN` or `OPENCLAW_GATEWAY_PASSWORD` values do not produce false gateway-token-mismatch failures after setup. Fixes #72203. Thanks @galiniliev.
|
||||
- Doctor/state: require an interactive confirmation before archiving orphan transcript files, so `openclaw doctor --fix` no longer silently renames recoverable session history after upgrades regenerate `sessions.json`. Fixes #73106. Thanks @scottgl9.
|
||||
- Cron/Telegram: preserve explicit `:topic:` delivery targets over stale session-derived thread IDs when isolated cron announces to Telegram forum topics. Carries forward #59069; refs #49704 and #43808. Thanks @roytong9.
|
||||
- Build/runtime: write the runtime-postbuild stamp after `pnpm build` writes the build stamp, so the next CLI invocation does not re-sync runtime artifacts after a successful build. Fixes #73151. Thanks @bittoby.
|
||||
- Build/runtime: preserve staged bundled-plugin runtime dependency caches across source-checkout tsdown rebuilds, so local CLI and gateway-watch rebuilds no longer recreate large plugin dependency trees before starting. Refs #73205. Thanks @SymbolStar.
|
||||
- CLI/channels: list configured chat channel accounts from read-only setup metadata even when the standalone CLI has not loaded the runtime channel registry, so `openclaw channels list` shows Telegram accounts before auth providers. Fixes #73319 and #73322. Thanks @mlaihk.
|
||||
- CLI/model probes: keep `infer model run --gateway` raw by skipping prior session transcript, bootstrap context, context-engine assembly, tools, and bundled MCP servers, so local backends can be tested without full agent-context overhead. Fixes #73308. Thanks @ScientificProgrammer.
|
||||
- CLI/image describe: pass `--prompt` and `--timeout-ms` through `infer image describe` and `describe-many`, so custom vision instructions and slow local model budgets reach media-understanding providers such as Ollama, OpenAI, Google, and OpenRouter. Addresses #63700. Thanks @cedricjanssens.
|
||||
- Providers/Ollama: reject long non-linguistic Kimi/GLM symbol runs as provider failures instead of storing them as successful visible assistant replies, so fallback or error handling can recover from garbled cloud output. Fixes #64262; refs #67019. Thanks @Kloz813 and @xiaomenger123.
|
||||
- CLI/model probes: reject empty or whitespace-only `infer model run --prompt` values before calling local providers or the Gateway, so smoke checks do not spend provider calls on invalid turns. Fixes #73185. Thanks @iot2edge.
|
||||
- Gateway/media: route text-only `chat.send` image offloads through media-understanding fields so `agents.defaults.imageModel` can describe WebChat attachments instead of leaving only an opaque `media://inbound` marker. Fixes #72968. Thanks @vorajeeah.
|
||||
- Gateway/Windows: route no-listener restart handoffs through the Windows supervisor without leaving restart tokens in flight, so failed task scheduling can be retried and successful handoffs do not coalesce later restart requests. (#69056) Thanks @Thatgfsj.
|
||||
- Gateway/model pricing: skip plugin manifest discovery during background pricing refreshes when `plugins.enabled: false`, so disabled-plugin setups do not keep rebuilding plugin metadata from the Gateway hot path. Fixes #73291. Thanks @slideshow-dingo and @fishgills.
|
||||
- Ollama/thinking: validate `/think` commands against live Ollama catalog reasoning metadata and preserve explicit native `params.think`/`params.thinking`, so models whose `/api/show` capabilities include `thinking` expose `low`, `medium`, `high`, and `max` instead of being stuck on `off`. Fixes #73366. Thanks @cymise.
|
||||
- Gateway/sessions: remove automatic oversized `sessions.json` rotation backups, deprecate `session.maintenance.rotateBytes`, and teach `openclaw doctor --fix` to remove the ignored key so hot session writes no longer copy multi-MB stores. Refs #72338. Thanks @midhunmonachan and @DougButdorf.
|
||||
- Channels/Telegram: fail fast when Telegram rejects the startup `getMe` token probe with 401, so invalid or stale BotFather tokens are reported as token auth failures instead of misleading `deleteWebhook` cleanup failures. Fixes #47674. Thanks @samaedan-arch.
|
||||
- ACPX: keep generated Codex and Claude ACP wrapper startup paths working when remote or special state filesystems reject chmod, since OpenClaw invokes the wrappers through Node instead of executing them directly. Fixes #73333. Thanks @david-garcia-garcia.
|
||||
- CLI/onboarding: infer image input for common custom-provider vision model IDs, ask only for unknown models, and keep `--custom-image-input`/`--custom-text-input` overrides so vision-capable proxies do not get saved as text-only configs. Fixes #51869. Thanks @Antsoldier1974.
|
||||
- Models/OpenAI Codex: stop listing or resolving unsupported `openai-codex/gpt-5.4-mini` rows through Codex OAuth, keep stale discovery rows suppressed with a clear API-key-route hint, and leave direct `openai/gpt-5.4-mini` available. Fixes #73242. Thanks @0xCyda.
|
||||
- Plugin SDK: restore the root `stringEnum` and `optionalStringEnum` exports on both the published SDK entry and runtime root-alias bridge, so older external plugins can keep building and loading while migrating to focused SDK subpaths. Fixes #68279. Thanks @marzliak.
|
||||
- Plugin SDK: restore the root-alias bridge for `registerContextEngine` and expose missing legacy compat helpers `normalizeAccountId` and `resolvePreferredOpenClawTmpDir` so older external plugins such as `openclaw-weixin` can keep loading while migrating to focused SDK subpaths. Fixes #53497. Thanks @alanxchen85.
|
||||
- Auth profiles: make `openclaw doctor --fix` migrate legacy flat `auth-profiles.json` files such as `{ "ollama-windows": { "apiKey": "ollama-local" } }` to canonical provider default API-key profiles with a backup, so custom Ollama/OpenAI-compatible providers recover cleanly after upgrading. Fixes #59629; supersedes #59642. Thanks @Xsanders555 and @Linux2010.
|
||||
- Memory/Dreaming: retry Dream Diary once with the session default when a configured dreaming model is unavailable, while leaving subagent trust and allowlist errors visible instead of silently masking configuration problems. Refs #67409 and #69209. Thanks @Ghiggins18 and @everySympathy.
|
||||
- Feishu/inbound files: recover CJK filenames from plain `Content-Disposition: filename=` download headers when Feishu exposes UTF-8 bytes through Latin-1 header decoding, while leaving valid Latin-1 and JSON-derived names unchanged. (#48578, #50435, #59431) Thanks @alex-xuweilong, @lishuaigit, and @DoChaoing.
|
||||
- Channels/Telegram: normalize accidental full `/bot<TOKEN>` Telegram `apiRoot` values at runtime and teach `openclaw doctor --fix` to remove the suffix, so startup control calls no longer 404 when direct Bot API curl commands work. Fixes #55387. Thanks @brendanmatthewjones-cmyk, @techfindubai-ux, and @Sivlerback-Chris.
|
||||
- Zalo Personal: persist refreshed `zca-js` session cookies after QR login, session restore, and successful API calls so gateway restarts restore the freshest local session. (#73277) Thanks @darkamenosa.
|
||||
- Logging/security: redact sensitive tokens (sk-\* keys, Bearer/Authorization values, etc.) at the subsystem console sink so `createSubsystemLogger().info/warn/error` output that bypasses the patched console-capture handler still applies the same redaction the file transport already does. Fixes #73284; refs #67953 and #64046. Thanks @edwin-rivera-dev.
|
||||
- Plugins/runtime deps: reuse enclosing versioned cache roots when bundled plugins resolve from nested staged paths, so plugin-runtime-deps no longer mints `openclaw-unknown-*` directories or loops on `ENOTEMPTY`. Fixes #72956. (#73205) Thanks @SymbolStar.
|
||||
- Agents/failover: classify CJK provider transport, quota, billing, auth, and overload error text so Chinese-language provider failures trigger fallback and user-facing transport copy instead of surfacing as unclassified raw errors. (#56242) Thanks @tomcatzh.
|
||||
- Agents/failover: seed non-claude-cli fallback prompts with Claude Code session context when a claude-cli attempt fails, so fallback models do not restart cold after billing or quota failover. (#72069) Thanks @stainlu.
|
||||
- Agents/CLI runner: transfer bundle-MCP tempDir cleanup from the per-turn runner finally to the Claude live-session lifecycle, so persistent Claude CLI sessions keep their `--mcp-config` directory until the live subprocess closes. Fixes #73244. Thanks @edwin-rivera-dev.
|
||||
- Gateway/nodes: allow Windows companion nodes to use safe declared commands such as canvas, camera list, location, device info, and screen snapshot by default while keeping dangerous media commands opt-in. (#71884) Thanks @shanselman.
|
||||
- Agents/cron: clarify agent-tool and CLI cron timezone guidance so supplied `tz` values use local wall-clock cron fields and omitted cron `tz` falls back to the Gateway host local timezone. Fixes #53669; carries forward #46177. (#73372) Thanks @chen-zhang-cs-code and @maranello-o.
|
||||
- Providers/Qwen: allow explicitly configured `qwen/qwen3.6-plus` to resolve on Qwen Coding Plan endpoints while keeping the built-in catalog from advertising it there. Fixes #63654; carries forward #63987. Thanks @jepson-liu.
|
||||
- Channels/Telegram: keep Bot API network fallbacks sticky after failed attempts and retry timed-out startup control calls once on the fallback route, so `deleteWebhook` IPv6 stalls no longer trigger slow multi-account retry storms. Fixes #73255. Thanks @ttomiczek and @sktbrd.
|
||||
- Gateway/agents: accept heartbeat, cron, and webhook as internal channel hints for agent runs so `sessions_spawn` works from non-delivery parent sessions while unknown channel hints still fail closed. Fixes #73237. Thanks @KeWang0622.
|
||||
- Gateway/models: merge explicit `models.providers.*.models` rows into the Gateway model catalog with normalized provider/model dedupe, and use normalized image-capability lookup so custom vision models keep native image attachments even when Pi discovery omits them or model ID casing differs. Fixes #64213 and #65165. Thanks @billonese and @202233a.
|
||||
@@ -136,7 +683,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Doctor/channels: suppress disabled bundled-plugin blocker warnings when a trusted external plugin owns the configured channel, so Lark/Feishu installs no longer get Feishu repair noise after switching to `openclaw-lark`. Fixes #56794. Thanks @wuji-tech-dev.
|
||||
- CLI/status: show skipped fast-path memory checks as `not checked` and report active custom memory plugin runtime status from `status --json --all` without requiring built-in `agents.defaults.memorySearch`, so plugins such as memory-lancedb-pro and memory-cms no longer look unavailable when their own runtime is healthy. Fixes #56968. Thanks @Tony-ooo and @aderius.
|
||||
- Gateway/channels: record and log unexpected clean channel monitor exits so channels that return without throwing no longer appear stopped with no error. Fixes #73099. Thanks @balaji1968-kingler.
|
||||
- Discord/group chats: keep group/channel replies private by default unless the agent explicitly uses the message tool, so always-on rooms can lurk without leaking automatic final, block, preview, or status-reaction output; `messages.groupChat.visibleReplies: "automatic"` restores legacy auto-posting. (#73046) Thanks @scoootscooob.
|
||||
- Group/channel chats (all channels): keep group/channel replies private by default unless the agent explicitly uses the message tool, fall back to automatic visible replies when the message tool is unavailable, and have `openclaw doctor` warn about that policy mismatch; `messages.groupChat.visibleReplies: "automatic"` restores legacy auto-posting. (#73046) Thanks @scoootscooob.
|
||||
- Plugins/package: force nested bundled-plugin runtime dependency installs out of inherited npm dry-run mode during prepack and package smoke checks, so packed installs materialize required plugin modules instead of reporting missing bundled files. Refs #73128. Thanks @Adam-Researchh.
|
||||
- Discord: skip reaction events before REST channel fetch when notifications are off, guild reactions are disabled, or allowlist mode cannot match without channel overrides, reducing reconnect bursts that caused slow listener warnings. Fixes #73133. Thanks @isaacsummers.
|
||||
- Channels/Telegram: centralize polling update tracking so accepted offsets remain durable across restarts, same-process handler failures can still retry, and slow offset writes cannot overwrite newer accepted watermarks. Refs #73115. Thanks @vdruts.
|
||||
@@ -183,7 +730,7 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI/status: keep default `openclaw status` off the heavyweight security audit, plugin compatibility, and memory-vector probes while still showing configured Telegram channels through setup metadata, so routine health checks stay fast and no longer render an empty Channels table. Fixes #72993. Thanks @comick1.
|
||||
- Channels/Telegram: send a best-effort native typing cue immediately after an inbound message is accepted, so slow pre-dispatch turns show Telegram liveness before queueing, compaction, model, or tool work starts. Fixes #63759. Thanks @alessandropcostabr.
|
||||
- Channels/Telegram: stop native approval startup auth failures from retrying every second, while still waiting through retryable Gateway auth handoffs, so Telegram approval setup problems no longer create a reconnect/log loop during channel startup. Refs #72846 and #72867. Thanks @kiranvk-2011 and @porly1985.
|
||||
- Channels/Microsoft Teams: unwrap staged CommonJS JWT runtime dependencies before Bot Connector token validation so inbound Teams messages no longer 401 after the bundled runtime-deps move. Fixes #73026. Thanks @kbrown10000.
|
||||
- Channels/Microsoft Teams: unwrap staged CommonJS JWT runtime dependencies before Bot Connector token validation so inbound Teams messages no longer 401 after the bundled runtime-deps move. Fixes #73026 and #73167. Thanks @kbrown10000 and @mikelavrik.
|
||||
- Gateway/auth: allow local direct callers in trusted-proxy mode to use the configured gateway password as an internal fallback while keeping token fallback rejected. Fixes #17761. Thanks @dashed, @vincentkoc, and @jetd1.
|
||||
- Gateway/auth: add explicit `trustedProxy.allowLoopback` support for same-host loopback reverse proxies while keeping loopback trusted-proxy auth fail-closed by default and preserving required-header and allowlist checks. Fixes #59167; carries forward #63379. Thanks @Matir, @jeremyakers, and @mrosmarin.
|
||||
- Channels/sessions: prevent guarded inbound session recording from creating route-only phantom sessions while still allowing last-route updates for sessions that already exist. Carries forward #73009. Thanks @jzakirov.
|
||||
@@ -239,6 +786,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord/gateway: count failed health-monitor restart attempts toward cooldown and hourly caps, and evict stale account lifecycle state during channel reloads so repeated Discord gateway recovery cannot loop on old status. Fixes #38596. (#40413) Thanks @jellyAI-dev and @vashquez.
|
||||
- TTS/BlueBubbles: pre-transcode synthesized MP3 audio to opus-in-CAF (mono, 24 kHz — validated against macOS 15.x Messages.app's native voice-memo CAF descriptor) on macOS hosts before handing the file to BlueBubbles, so iMessage renders the result as a native voice-memo bubble with proper duration and waveform UI instead of a plain file attachment. Adds an opt-in `tts.voice.preferAudioFileFormat` channel capability and a magic-byte sniff for the CAF container so the host-local-media validator (which uses `file-type` and didn't recognize CAF natively) can verify the pre-transcoded buffer. Channels that don't opt in are unaffected. (#72586) Fixes #72506. Thanks @omarshahine.
|
||||
- Feishu: retry WebSocket startup failures with monitor-owned backoff while preserving SDK-local heartbeat defaults, so persistent-connection startup failures no longer leave the monitor hung. Fixes #68766; related #42354 and #55532. Thanks @alex-xuweilong, @120106835, @sirfengyu, and @tianhaocui.
|
||||
- Cron: normalize isolated job tool allowlists before granting the narrow self-removal cron tool path, keeping scheduled jobs aligned with shared tool policy normalization. (#73028) Thanks @jalehman.
|
||||
|
||||
## 2026.4.26
|
||||
|
||||
@@ -246,6 +794,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Control UI/Talk: add a generic browser realtime transport contract, Google Live browser Talk sessions with constrained ephemeral tokens, and a Gateway relay for backend-only realtime voice plugins. Thanks @VACInc.
|
||||
- CLI/models: route provider-filtered model listing through an explicit source plan so user config, installed manifest rows, Provider Index previews, and scoped runtime fallbacks keep a stable authority order without adding another catalog cache. Thanks @shakkernerd.
|
||||
- Plugins/cron: add a typed `cron_changed` hook for observing gateway-owned cron lifecycle updates without depending on internal cron events. Thanks @amknight.
|
||||
- Providers: add Cerebras as a bundled plugin with onboarding, static model catalog, docs, and manifest-owned endpoint metadata.
|
||||
- Memory/OpenAI-compatible: add optional `memorySearch.inputType`, `queryInputType`, and `documentInputType` config for asymmetric embedding endpoints, including direct query embeddings and provider batch indexing. Carries forward #63313 and #60727. Thanks @HOYALIM and @prospect1314521.
|
||||
- Ollama/memory: add model-specific retrieval query prefixes for `nomic-embed-text`, `qwen3-embedding`, and `mxbai-embed-large` memory-search queries while leaving document batches unchanged. Carries forward #45013. Thanks @laolin5564.
|
||||
@@ -402,6 +951,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/bootstrap: dedupe hook-injected bootstrap context files by workspace-relative path and store normalized resolved paths so duplicate relative and absolute hook paths no longer depend on the process cwd. (#59344; fixes #59319; related #56721, #56725, and #57587) Thanks @koen666.
|
||||
- Agents/bootstrap: refresh cached workspace bootstrap snapshots on long-lived main-session turns when `AGENTS.md`, `SOUL.md`, `MEMORY.md`, or `TOOLS.md` change on disk, while preserving unchanged snapshot identity through the workspace file cache. (#64871; related #43901, #26497, #28594, #30896) Thanks @aimqwest and @mikejuyoon.
|
||||
- macOS Gateway: detect installed-but-unloaded LaunchAgent split-brain states during status, doctor, and restart, and re-bootstrap launchd supervision before falling back to unmanaged listener restarts. Fixes #67335, #53475, and #71060; refs #58890, #60885, and #70801. Thanks @ze1tgeist88, @dafacto, and @vishutdhar.
|
||||
- WhatsApp: clear cached Web auth and active listener state after terminal 440/401 conflict/logout closes so linked/OK status no longer masks a dead inbound listener after relink or restart. Fixes #45474; refs #49305, #63855, #66920, and #70856. Thanks @juvenalmakoszay and @dsantoreis.
|
||||
- Gateway/restart: keep local restart-health probes on configured local daemon auth without falling back to remote gateway credentials. (#57374, #59439) Thanks @zssggle-rgb and @roytong9.
|
||||
- Plugins/install: treat mirrored core logger dependencies as staged bundled runtime deps so packaged Gateway starts do not crash when the external plugin-runtime-deps root is missing `tslog`. Fixes #72228; supersedes #72493. Thanks @deepujain.
|
||||
- Build/plugins: preserve active bundled runtime-dependency staging temp directories owned by live build processes so overlapping postbuild runs no longer delete each other's staged deps mid-prune. Supersedes #72220. Thanks @VACInc.
|
||||
@@ -413,6 +963,7 @@ Docs: https://docs.openclaw.ai
|
||||
- TTS/SecretRef: resolve `messages.tts.providers.*.apiKey` from the active runtime snapshot so SecretRef-backed MiniMax and other TTS provider keys work in runtime reply/audio paths. Fixes #68690. Thanks @joshavant.
|
||||
- Gateway/install: surface systemd user-bus recovery hints during Linux service activation and retry via the target user scope when `systemctl --user` reports no-medium bus failures, without letting stale `SUDO_USER` override `sudo -u` installs. Fixes #39673; refs #44417 and #63561. Thanks @Arbor4, @myrsu, @mssteuer, and @boyuaner.
|
||||
- CLI/nodes: make unfiltered `openclaw nodes list` prefer the effective paired-node view used by `nodes status` while preserving pending rows, pairing-scope fallback, terminal-safe table rendering, and paired JSON metadata. Fixes #46871; carries forward #65772 through the ProjectClownfish #72619 repair. Thanks @skainguyen1412.
|
||||
- Memory Wiki/CLI: route active bridge-mode status, doctor, and bridge imports through Gateway RPC so CLI checks use the runtime memory plugin context while disabled bridge imports stay local/offline. Carries forward #67208 and #71479; related #70185. Thanks @moorsecopers99, @vincentkoc, and @prasad-yashdeep.
|
||||
- CLI/startup: read generated startup metadata from the bundled `dist` layout before falling back to live help rendering, so root/browser help and channel-option bootstrap stay on the fast path. Thanks @vincentkoc.
|
||||
- Feishu/Lark: stop treating broadcast-only `@all`/`@_all` messages as bot mentions while preserving direct bot mentions, including messages that also include `@all`. Fixes #37706. Thanks @JosepLee.
|
||||
- CLI/help: treat positional `help` invocations like `openclaw channels help` as help paths for startup gating, avoiding model/auth warmup while preserving positional arguments such as `openclaw docs help`. Thanks @gumadeiras.
|
||||
@@ -923,6 +1474,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Google Meet joins OpenClaw as a bundled participant plugin, with personal Google auth, Chrome/Twilio realtime sessions, paired-node Chrome support, artifact/attendance exports, and recovery tooling for already-open Meet tabs.
|
||||
- DeepSeek V4 Flash and V4 Pro are in the bundled catalog, V4 Flash is the onboarding default, and DeepSeek thinking/replay behavior is fixed for follow-up tool-call turns.
|
||||
- Talk, Voice Call, and Google Meet can use realtime voice loops that consult the full OpenClaw agent for deeper tool-backed answers.
|
||||
- Providers/OpenRouter: add native video generation through `video_generate`, so OpenRouter video models work with `OPENROUTER_API_KEY`. (#72700) Thanks @notamicrodose.
|
||||
- Browser automation gets coordinate clicks, longer default action budgets, per-profile headless overrides, and steadier tab reuse/recovery.
|
||||
- Plugin and model infrastructure is lighter at startup: static model catalogs, manifest-backed model rows, lazy provider dependencies, and external runtime-dependency repair for packaged installs.
|
||||
|
||||
|
||||
22
Dockerfile
22
Dockerfile
@@ -63,6 +63,8 @@ COPY openclaw.mjs ./
|
||||
COPY ui/package.json ./ui/package.json
|
||||
COPY patches ./patches
|
||||
COPY scripts/postinstall-bundled-plugins.mjs scripts/preinstall-package-manager-warning.mjs scripts/npm-runner.mjs scripts/windows-cmd-helpers.mjs ./scripts/
|
||||
COPY scripts/lib/bundled-runtime-deps-install.mjs ./scripts/lib/bundled-runtime-deps-install.mjs
|
||||
COPY scripts/lib/package-dist-imports.mjs ./scripts/lib/package-dist-imports.mjs
|
||||
|
||||
COPY --from=ext-deps /out/ ./${OPENCLAW_BUNDLED_PLUGIN_DIR}/
|
||||
|
||||
@@ -130,7 +132,8 @@ RUN printf 'packages:\n - .\n - ui\n' > /tmp/pnpm-workspace.runtime.yaml && \
|
||||
cp /tmp/pnpm-workspace.runtime.yaml pnpm-workspace.yaml && \
|
||||
CI=true NPM_CONFIG_FROZEN_LOCKFILE=false pnpm prune --prod && \
|
||||
node scripts/postinstall-bundled-plugins.mjs && \
|
||||
find dist -type f \( -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' -o -name '*.map' \) -delete
|
||||
find dist -type f \( -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' -o -name '*.map' \) -delete && \
|
||||
node scripts/check-package-dist-imports.mjs /app
|
||||
|
||||
# ── Runtime base image ──────────────────────────────────────────
|
||||
FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-runtime
|
||||
@@ -164,7 +167,7 @@ RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,shar
|
||||
--mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
ca-certificates procps hostname curl git lsof openssl && \
|
||||
ca-certificates procps hostname curl git lsof openssl python3 && \
|
||||
update-ca-certificates
|
||||
|
||||
RUN chown node:node /app
|
||||
@@ -236,9 +239,16 @@ RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,shar
|
||||
ca-certificates curl gnupg && \
|
||||
install -m 0755 -d /etc/apt/keyrings && \
|
||||
# Verify Docker apt signing key fingerprint before trusting it as a root key.
|
||||
# Require exactly one primary key (`pub` in --with-colons; subkeys use `sub`) so we
|
||||
# never pin the first fingerprint while apt trusts extra keys from the same file.
|
||||
# Update OPENCLAW_DOCKER_GPG_FINGERPRINT when Docker rotates release keys.
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg -o /tmp/docker.gpg.asc && \
|
||||
expected_fingerprint="$(printf '%s' "$OPENCLAW_DOCKER_GPG_FINGERPRINT" | tr '[:lower:]' '[:upper:]' | tr -d '[:space:]')" && \
|
||||
docker_gpg_pub_count="$(gpg --batch --show-keys --with-colons /tmp/docker.gpg.asc | awk -F: '$1 == "pub" { c++ } END { print c+0 }')" && \
|
||||
if [ "$docker_gpg_pub_count" != "1" ]; then \
|
||||
echo "ERROR: Docker apt key must contain exactly one public key (found $docker_gpg_pub_count); refusing a multi-key file." >&2; \
|
||||
exit 1; \
|
||||
fi && \
|
||||
actual_fingerprint="$(gpg --batch --show-keys --with-colons /tmp/docker.gpg.asc | awk -F: '$1 == "fpr" { print toupper($10); exit }')" && \
|
||||
if [ -z "$actual_fingerprint" ] || [ "$actual_fingerprint" != "$expected_fingerprint" ]; then \
|
||||
echo "ERROR: Docker apt key fingerprint mismatch (expected $expected_fingerprint, got ${actual_fingerprint:-<empty>})" >&2; \
|
||||
@@ -258,10 +268,12 @@ RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,shar
|
||||
RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw \
|
||||
&& chmod 755 /app/openclaw.mjs
|
||||
|
||||
# Pre-create the default state dir so first-run Docker named volumes mounted
|
||||
# here inherit node ownership instead of starting as root-owned state.
|
||||
# Pre-create the default state and runtime-deps dirs so first-run Docker named
|
||||
# volumes mounted here inherit node ownership instead of root-owned state.
|
||||
RUN install -d -m 0700 -o node -g node /home/node/.openclaw && \
|
||||
stat -c '%U:%G %a' /home/node/.openclaw | grep -qx 'node:node 700'
|
||||
install -d -m 0700 -o node -g node /var/lib/openclaw/plugin-runtime-deps && \
|
||||
stat -c '%U:%G %a' /home/node/.openclaw | grep -qx 'node:node 700' && \
|
||||
stat -c '%U:%G %a' /var/lib/openclaw/plugin-runtime-deps | grep -qx 'node:node 700'
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
FROM debian:bookworm-slim@sha256:4724b8cc51e33e398f0e2e15e18d5ec2851ff0c2280647e1310bc1642182655d
|
||||
FROM debian:bookworm-slim@sha256:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
FROM debian:bookworm-slim@sha256:4724b8cc51e33e398f0e2e15e18d5ec2851ff0c2280647e1310bc1642182655d
|
||||
FROM debian:bookworm-slim@sha256:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
||||
146
SECURITY.md
146
SECURITY.md
@@ -1,8 +1,14 @@
|
||||
# Security Policy
|
||||
|
||||
If you believe you've found a security issue in OpenClaw, please report it privately.
|
||||
If you believe you've found a security issue in OpenClaw, report it privately first.
|
||||
|
||||
## Reporting
|
||||
This policy does two things: it gives researchers a clear disclosure path, and it spells out the trust model maintainers use when triaging reports. OpenClaw is local-first agent infrastructure for trusted operators; it is not designed as a shared multi-tenant boundary between adversarial users on one gateway.
|
||||
|
||||
The fastest useful reports show a current, reproducible boundary bypass with demonstrated impact. Scanner output, prompt-injection-only chains, or reports that rely on hostile users sharing one trusted gateway are usually not security vulnerabilities under this model.
|
||||
|
||||
Security work is shared across a number of OpenClaw maintainers, including engineers and security researchers from organizations such as NVIDIA and Tencent. See the [maintainer list](CONTRIBUTING.md#maintainers).
|
||||
|
||||
## Report a Security Issue
|
||||
|
||||
Report vulnerabilities directly to the repository where the issue lives:
|
||||
|
||||
@@ -15,22 +21,50 @@ Report vulnerabilities directly to the repository where the issue lives:
|
||||
|
||||
For issues that don't fit a specific repo, or if you're unsure, email **[security@openclaw.ai](mailto:security@openclaw.ai)** and we'll route it.
|
||||
|
||||
For OpenClaw core issues, submit through a private [GitHub Security Advisory](https://github.com/openclaw/openclaw/security/advisories/new). Do not open a public issue or PR that discloses an unpatched vulnerability, exploit path, secret, or security-sensitive proof of concept.
|
||||
|
||||
Maintainers may close, hide, delete, or otherwise take down public issues and PRs that disclose vulnerabilities or active security issues. We will redirect those reports through the private disclosure process so the issue can be triaged and fixed without giving attackers a public playbook.
|
||||
|
||||
For full reporting instructions see our [Trust page](https://trust.openclaw.ai).
|
||||
|
||||
### Required in Reports
|
||||
OpenClaw does not currently run a paid bug bounty program. Please still disclose responsibly so we can fix real issues quickly. The best way to help the project right now is to send high-signal reports and, when practical, focused PRs.
|
||||
|
||||
1. **Title**
|
||||
2. **Severity Assessment**
|
||||
3. **Impact**
|
||||
4. **Affected Component**
|
||||
5. **Technical Reproduction**
|
||||
6. **Demonstrated Impact**
|
||||
7. **Environment**
|
||||
8. **Remediation Advice**
|
||||
### What We Need
|
||||
|
||||
Reports without reproduction steps, demonstrated impact, and remediation advice will be deprioritized. Given the volume of AI-generated scanner findings, we must ensure we're receiving vetted reports from researchers who understand the issues.
|
||||
Make the report easy to reproduce and easy to route:
|
||||
|
||||
### Report Acceptance Gate (Triage Fast Path)
|
||||
- What you found and why you believe it is security-relevant.
|
||||
- The affected component, version, and commit SHA when possible.
|
||||
- Reproduction steps or a proof of concept against latest `main` or the latest released version.
|
||||
- The actual impact, including which OpenClaw trust boundary is crossed.
|
||||
- Any remediation advice or focused patch you can provide.
|
||||
|
||||
Reports without reproduction steps, demonstrated impact, and remediation advice are deprioritized. We receive a high volume of AI-generated scanner findings, so we prioritize vetted reports from researchers who can show how the issue crosses an OpenClaw security boundary.
|
||||
|
||||
### What Usually Is Not a Security Bug
|
||||
|
||||
These patterns are usually not vulnerabilities by themselves:
|
||||
|
||||
- Prompt injection without a policy, auth, approval, sandbox, or tool-boundary bypass.
|
||||
- A trusted operator using an intentional local feature, such as local shell access or browser/script execution.
|
||||
- A malicious plugin after a trusted operator installs or enables it.
|
||||
- Multiple adversarial users sharing one Gateway host/config and expecting per-user isolation.
|
||||
- Scanner-only, dependency-only, or stale-path reports without a working repro and demonstrated OpenClaw impact.
|
||||
- Public internet exposure or risky deployment choices that the docs already recommend against.
|
||||
|
||||
If you are unsure, report privately. We would rather route a careful report than miss a real boundary issue.
|
||||
|
||||
### Duplicate Report Handling
|
||||
|
||||
- Search existing advisories before filing.
|
||||
- Include likely duplicate GHSA IDs in your report when applicable.
|
||||
- Maintainers may close lower-quality/later duplicates in favor of the earliest high-quality canonical report.
|
||||
|
||||
## Security Posture and Report Rules
|
||||
|
||||
The sections below are the normative posture maintainers use for report triage. The headings are editorial; the policy text defines the boundary.
|
||||
|
||||
### Detailed Report Acceptance Gate
|
||||
|
||||
For fastest triage, include all of the following:
|
||||
|
||||
@@ -47,7 +81,7 @@ For fastest triage, include all of the following:
|
||||
|
||||
Reports that miss these requirements may be closed as `invalid` or `no-action`.
|
||||
|
||||
### Common False-Positive Patterns
|
||||
### Detailed False-Positive Patterns
|
||||
|
||||
These are frequently reported but are typically closed with no code change:
|
||||
|
||||
@@ -64,6 +98,7 @@ These are frequently reported but are typically closed with no code change:
|
||||
- Reports that only show differences in heuristic detection/parity (for example obfuscation-pattern detection on one exec path but not another, such as `node.invoke -> system.run` parity gaps) without demonstrating bypass of auth, approvals, allowlist enforcement, sandboxing, or other documented trust boundaries.
|
||||
- Reports that only show an ACP tool can indirectly execute, mutate, orchestrate sessions, or reach another tool/runtime without demonstrating bypass of ACP prompt/approval, allowlist enforcement, sandboxing, or another documented trust boundary. ACP silent approval is intentionally limited to narrow readonly classes; parity-only indirect-command findings are hardening, not vulnerabilities.
|
||||
- Reports that only show untrusted media bytes reaching a maintained native decoder dependency (for example Sharp/libvips/libheif) without proving the shipped dependency version is vulnerable and demonstrating crash, memory corruption, data exposure, or a boundary bypass through OpenClaw. JavaScript header sniffing and image dimension fast-paths are preflight/UX checks, not the security boundary for native decoder correctness.
|
||||
- Reports whose only impact is transient extra memory, CPU, or allocation work from decoding, base64 expansion, media transcoding, serialization, or other format conversion after the input was already accepted under OpenClaw's configured size/trust limits, including base64 decode-before-size-estimate findings. These are performance issues, not vulnerabilities, unless the report demonstrates unauthenticated amplification, bypass of configured limits, crash/process termination, persistent resource exhaustion, data exposure, or another documented boundary bypass.
|
||||
- ReDoS/DoS claims that require trusted operator configuration input (for example catastrophic regex in `sessionFilter` or `logging.redactPatterns`) without a trust-boundary bypass.
|
||||
- Archive/install extraction claims that require pre-existing local filesystem priming in trusted state (for example planting symlink/hardlink aliases under destination directories such as skills/tools paths) without showing an untrusted path that can create/control that primitive.
|
||||
- Reports that depend on replacing or rewriting an already-approved executable path on a trusted host (same-path inode/content swap) without showing an untrusted path to perform that write.
|
||||
@@ -75,27 +110,13 @@ These are frequently reported but are typically closed with no code change:
|
||||
- Claims that Microsoft Teams `fileConsent/invoke` `uploadInfo.uploadUrl` is attacker-controlled without demonstrating one of: auth boundary bypass, a real authenticated Teams/Bot Framework event carrying attacker-chosen URL, or compromise of the Microsoft/Bot trust path.
|
||||
- Scanner-only claims against stale/nonexistent paths, or claims without a working repro.
|
||||
- Reports that restate an already-fixed issue against later released versions without showing the vulnerable path still exists in the shipped tag or published artifact for that later version.
|
||||
- SSRF reports against the operator-managed HTTP/WebSocket proxy-routing feature whose only claim is that ordinary process-local HTTP clients (`fetch`, `node:http`, `node:https`, WebSocket clients, axios/got/node-fetch-style clients) can reach an internal, metadata, private, or otherwise sensitive destination when proxy routing is disabled, missing, or the operator-managed proxy policy allows it. For this feature, OpenClaw provides fail-closed proxy routing when enabled; the external proxy's destination policy is operator infrastructure, not an OpenClaw-controlled security boundary. See [Network proxy](https://docs.openclaw.ai/security/network-proxy).
|
||||
|
||||
### Duplicate Report Handling
|
||||
|
||||
- Search existing advisories before filing.
|
||||
- Include likely duplicate GHSA IDs in your report when applicable.
|
||||
- Maintainers may close lower-quality/later duplicates in favor of the earliest high-quality canonical report.
|
||||
|
||||
## Security & Trust
|
||||
|
||||
**Jamieson O'Reilly** ([@theonejvo](https://twitter.com/theonejvo)) is Security & Trust at OpenClaw. Jamieson is the founder of [Dvuln](https://dvuln.com) and brings extensive experience in offensive security, penetration testing, and security program development.
|
||||
|
||||
## Bug Bounties
|
||||
|
||||
OpenClaw is a labor of love. There is no bug bounty program and no budget for paid reports. Please still disclose responsibly so we can fix issues quickly.
|
||||
The best way to help the project right now is by sending PRs.
|
||||
|
||||
## Maintainers: GHSA Updates via CLI
|
||||
### Maintainer GHSA Updates via CLI
|
||||
|
||||
When patching a GHSA via `gh api`, include `X-GitHub-Api-Version: 2022-11-28` (or newer). Without it, some fields (notably CVSS) may not persist even if the request returns 200.
|
||||
|
||||
## Operator Trust Model (Important)
|
||||
### Operator Trust Model
|
||||
|
||||
OpenClaw does **not** model one gateway as a multi-tenant, adversarial user boundary.
|
||||
|
||||
@@ -120,7 +141,7 @@ OpenClaw does **not** model one gateway as a multi-tenant, adversarial user boun
|
||||
- Implicit exec calls (no explicit host in the tool call) follow the same behavior.
|
||||
- This is expected in OpenClaw's one-user trusted-operator model. If you need isolation, enable sandbox mode (`non-main`/`all`) and keep strict tool policy.
|
||||
|
||||
## Trusted Plugin Concept (Core)
|
||||
### Trusted Plugins
|
||||
|
||||
Plugins/extensions are part of OpenClaw's trusted computing base for a gateway.
|
||||
|
||||
@@ -128,7 +149,7 @@ Plugins/extensions are part of OpenClaw's trusted computing base for a gateway.
|
||||
- Plugin behavior such as reading env/files or running host commands is expected inside this trust boundary.
|
||||
- Security reports must show a boundary bypass (for example unauthenticated plugin load, allowlist/policy bypass, or sandbox/path-safety bypass), not only malicious behavior from a trusted-installed plugin.
|
||||
|
||||
## Out of Scope
|
||||
### Out of Scope
|
||||
|
||||
- Public Internet Exposure
|
||||
- Using OpenClaw in ways that the docs recommend not to
|
||||
@@ -148,11 +169,13 @@ Plugins/extensions are part of OpenClaw's trusted computing base for a gateway.
|
||||
- Reports whose only claim is that an ACP-exposed tool can indirectly execute commands, mutate host state, or reach another privileged tool/runtime without demonstrating a bypass of ACP prompt/approval, allowlist enforcement, sandboxing, or another documented trust boundary. These are hardening-only findings, not vulnerabilities.
|
||||
- Reports whose only claim is that exec approvals do not semantically model every interpreter/runtime loader form, subcommand, flag combination, package script, or transitive module/config import. Exec approvals bind exact request context and best-effort direct local file operands; they are not a complete semantic model of everything a runtime may load.
|
||||
- Reports whose only claim is parser reachability in an up-to-date maintained dependency without showing that the exact shipped dependency build is vulnerable. We keep native media dependencies current; dependency exposure alone is not a vulnerability.
|
||||
- Reports whose only claim is resource overhead from decode/encode, base64 expansion, media transcoding, serialization, or format-conversion order after input has already passed the applicable configured acceptance limits, including base64 decode-before-size-estimate findings. These are performance-only and should be ignored for GHSA triage unless the report demonstrates unauthenticated amplification, limit bypass, crash/process termination, persistent exhaustion, data exposure, or another documented boundary bypass.
|
||||
- Exposed secrets that are third-party/user-controlled credentials (not OpenClaw-owned and not granting access to OpenClaw-operated infrastructure/services) without demonstrated OpenClaw impact
|
||||
- Reports whose only claim is host-side exec when sandbox runtime is disabled/unavailable (documented default behavior in the trusted-operator model), without a boundary bypass.
|
||||
- Reports whose only claim is that a platform-provided upload destination URL is untrusted (for example Microsoft Teams `fileConsent/invoke` `uploadInfo.uploadUrl`) without proving attacker control in an authenticated production flow.
|
||||
- SSRF reports limited to the operator-managed HTTP/WebSocket proxy-routing feature where the demonstrated mitigation is to enable/configure `proxy.enabled` with a filtering `proxy.proxyUrl`/`OPENCLAW_PROXY_URL`, or where impact depends on a permissive/misconfigured operator proxy. This only covers normal process-local HTTP(S)/WebSocket egress (`fetch`, Node HTTP(S), and similar JavaScript clients); non-HTTP egress and other features are assessed separately. See [Network proxy](https://docs.openclaw.ai/security/network-proxy).
|
||||
|
||||
## Deployment Assumptions
|
||||
### Deployment Assumptions
|
||||
|
||||
OpenClaw security guidance assumes:
|
||||
|
||||
@@ -162,7 +185,7 @@ OpenClaw security guidance assumes:
|
||||
- Authenticated Gateway callers are treated as trusted operators. Session identifiers (for example `sessionKey`) are routing controls, not per-user authorization boundaries.
|
||||
- Multiple gateway instances can run on one machine, but the recommended model is clean per-user isolation (prefer one host/VPS per user).
|
||||
|
||||
## One-User Trust Model (Personal Assistant)
|
||||
### One-User Trust Model
|
||||
|
||||
OpenClaw's security model is "personal assistant" (one trusted operator, potentially many agents), not "shared multi-tenant bus."
|
||||
|
||||
@@ -174,7 +197,7 @@ OpenClaw's security model is "personal assistant" (one trusted operator, potenti
|
||||
- For company-shared setups, use a dedicated machine/VM/container and dedicated accounts; avoid mixing personal data on that runtime.
|
||||
- If that host/browser profile is logged into personal accounts (for example Apple/Google/personal password manager), you have collapsed the boundary and increased personal-data exposure risk.
|
||||
|
||||
## Context Visibility and Allowlists
|
||||
### Context Visibility and Allowlists
|
||||
|
||||
OpenClaw distinguishes:
|
||||
|
||||
@@ -192,7 +215,7 @@ Reports that only show supplemental-context visibility differences are typically
|
||||
|
||||
Hardening roadmap may add explicit visibility modes (for example `all`, `allowlist`, `allowlist_quote`) so operators can opt into stricter context filtering with predictable tradeoffs.
|
||||
|
||||
## Agent and Model Assumptions
|
||||
### Agent and Model Assumptions
|
||||
|
||||
- The model/agent is **not** a trusted principal. Assume prompt/content injection can manipulate behavior.
|
||||
- Security boundaries come from host/config trust, auth, tool policy, sandboxing, and exec approvals.
|
||||
@@ -200,7 +223,7 @@ Hardening roadmap may add explicit visibility modes (for example `all`, `allowli
|
||||
- Hook/webhook-driven payloads should be treated as untrusted content; keep unsafe bypass flags disabled unless doing tightly scoped debugging (`hooks.gmail.allowUnsafeExternalContent`, `hooks.mappings[].allowUnsafeExternalContent`).
|
||||
- Weak model tiers are generally easier to prompt-inject. For tool-enabled or hook-driven agents, prefer strong modern model tiers and strict tool policy (for example `tools.profile: "messaging"` or stricter), plus sandboxing where possible.
|
||||
|
||||
## Gateway and Node trust concept
|
||||
### Gateway and Node Trust Concept
|
||||
|
||||
OpenClaw separates routing from execution, but both remain inside the same operator trust boundary:
|
||||
|
||||
@@ -211,7 +234,7 @@ OpenClaw separates routing from execution, but both remain inside the same opera
|
||||
- Differences in command-risk warning heuristics between exec surfaces (`gateway`, `node`, `sandbox`) do not, by themselves, constitute a security-boundary bypass.
|
||||
- For untrusted-user isolation, split by trust boundary: separate gateways and separate OS users/hosts per boundary.
|
||||
|
||||
## Workspace Memory Trust Boundary
|
||||
### Workspace Memory Trust Boundary
|
||||
|
||||
`MEMORY.md` and `memory/*.md` are plain workspace files and are treated as trusted local operator state.
|
||||
|
||||
@@ -220,7 +243,7 @@ OpenClaw separates routing from execution, but both remain inside the same opera
|
||||
- Example report pattern considered out of scope: "attacker writes malicious content into `memory/*.md`, then `memory_search` returns it."
|
||||
- If you need isolation between mutually untrusted users, split by OS user or host and run separate gateways.
|
||||
|
||||
## Plugin Trust Boundary
|
||||
### Plugin Trust Boundary
|
||||
|
||||
Plugins/extensions are loaded **in-process** with the Gateway and are treated as trusted code.
|
||||
|
||||
@@ -228,7 +251,7 @@ Plugins/extensions are loaded **in-process** with the Gateway and are treated as
|
||||
- Runtime helpers (for example `runtime.system.runCommandWithTimeout`) are convenience APIs, not a sandbox boundary.
|
||||
- Only install plugins you trust, and prefer `plugins.allow` to pin explicit trusted plugin ids.
|
||||
|
||||
## Temp Folder Boundary (Media/Sandbox)
|
||||
### Temp Folder Boundary
|
||||
|
||||
OpenClaw uses a dedicated temp root for local media handoff and sandbox-adjacent temp artifacts:
|
||||
|
||||
@@ -245,19 +268,19 @@ Security boundary notes:
|
||||
- SDK temp helpers: `src/plugin-sdk/temp-path.ts`
|
||||
- messaging/channel tmp guardrail: `scripts/check-no-random-messaging-tmp.mjs`
|
||||
|
||||
## Operational Guidance
|
||||
### Operational Guidance
|
||||
|
||||
For threat model + hardening guidance (including `openclaw security audit --deep` and `--fix`), see:
|
||||
|
||||
- `https://docs.openclaw.ai/gateway/security`
|
||||
|
||||
### Tool filesystem hardening
|
||||
#### Tool Filesystem Hardening
|
||||
|
||||
- `tools.exec.applyPatch.workspaceOnly: true` (recommended): keeps `apply_patch` writes/deletes within the configured workspace directory.
|
||||
- `tools.fs.workspaceOnly: true` (optional): restricts `read`/`write`/`edit`/`apply_patch` paths and native prompt image auto-load paths to the workspace directory.
|
||||
- Avoid setting `tools.exec.applyPatch.workspaceOnly: false` unless you fully trust who can trigger tool execution.
|
||||
|
||||
### Sub-agent delegation hardening
|
||||
#### Sub-Agent Delegation Hardening
|
||||
|
||||
- Keep `sessions_spawn` denied unless you explicitly need delegated runs.
|
||||
- Keep `agents.list[].subagents.allowAgents` narrow, and only include agents with sandbox settings you trust.
|
||||
@@ -265,7 +288,7 @@ For threat model + hardening guidance (including `openclaw security audit --deep
|
||||
- `sandbox: "require"` rejects the spawn unless the target child runtime is sandboxed.
|
||||
- This prevents a less-restricted session from delegating work into an unsandboxed child by mistake.
|
||||
|
||||
### Web Interface Safety
|
||||
#### Web Interface Safety
|
||||
|
||||
OpenClaw's web interface (Gateway Control UI + HTTP endpoints) is intended for **local use only**.
|
||||
|
||||
@@ -317,12 +340,39 @@ docker run --read-only --cap-drop=ALL \
|
||||
|
||||
## Security Scanning
|
||||
|
||||
This project uses `detect-secrets` for automated secret detection in CI/CD.
|
||||
See `.detect-secrets.cfg` for configuration and `.secrets.baseline` for the baseline.
|
||||
OpenClaw uses several security and release-validation layers. No single scanner is treated as the boundary.
|
||||
|
||||
Run locally:
|
||||
### Secret Detection
|
||||
|
||||
OpenClaw uses `detect-secrets` with a checked-in baseline and local exclusion notes (`.secrets.baseline`, `.detect-secrets.cfg`). Secret-resolution behavior is also covered by the dedicated secrets test surface.
|
||||
|
||||
Run the baseline scan locally:
|
||||
|
||||
```bash
|
||||
pip install detect-secrets==1.5.0
|
||||
detect-secrets scan --baseline .secrets.baseline
|
||||
```
|
||||
|
||||
### Static Analysis
|
||||
|
||||
CI runs CodeQL across core TypeScript, GitHub Actions, Android, macOS, and high-risk runtime boundaries using `.github/workflows/codeql*.yml` and `.github/codeql/*.yml`.
|
||||
|
||||
OpenGrep provides a high-precision Semgrep-compatible layer. PRs run a changed-path scan; maintainers can run a full repository scan when needed. The rulepack lives under `security/opengrep/`, with `.semgrepignore` as the shared exclusion file.
|
||||
|
||||
Run the local OpenGrep wrapper after installing `opengrep`:
|
||||
|
||||
```bash
|
||||
scripts/run-opengrep.sh --changed --sarif --error
|
||||
pnpm check:opengrep-rule-metadata
|
||||
```
|
||||
|
||||
### E2E and Live Validation
|
||||
|
||||
Security-relevant behavior is also covered by runtime validation, not only static scanning:
|
||||
|
||||
- `pnpm test:e2e` for repo E2E coverage.
|
||||
- `pnpm test:live` for live provider/runtime coverage.
|
||||
- `pnpm test:docker:all` for Docker-packaged runtime scenarios.
|
||||
- Package acceptance and scheduled live/E2E workflows for release-path validation.
|
||||
|
||||
These lanes exercise packaged installs, gateway/runtime behavior, live model/provider paths, Docker scenarios, and platform smoke tests. They complement scanners by proving the security-sensitive flows still behave correctly in real runtime environments.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "e6910acc97de62dc423c0a391985c1c2f28207951e356081539abde41f9ffc72",
|
||||
"originHash" : "646c710cf04fdf9e6c6ca935f3184924db3397a816848a7f8a8a3c10a4d8e9c8",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "commander",
|
||||
@@ -15,8 +15,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-syntax.git",
|
||||
"state" : {
|
||||
"revision" : "0687f71944021d616d34d922343dcef086855920",
|
||||
"version" : "600.0.1"
|
||||
"revision" : "9de99a78f099e59caf2b2beec65a4c45d54b2081",
|
||||
"version" : "603.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24,8 +24,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-testing",
|
||||
"state" : {
|
||||
"revision" : "399f76dcd91e4c688ca2301fa24a8cc6d9927211",
|
||||
"version" : "0.99.0"
|
||||
"revision" : "937120cbc281cf29727fdfb8734482158508b4fc",
|
||||
"version" : "6.3.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -14,7 +14,7 @@ let package = Package(
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/steipete/Commander.git", exact: "0.2.2"),
|
||||
.package(url: "https://github.com/apple/swift-testing", from: "0.99.0"),
|
||||
.package(url: "https://github.com/apple/swift-testing", from: "6.3.1"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
||||
1107
appcast.xml
1107
appcast.xml
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,6 @@ indent_style = space
|
||||
indent_size = 2
|
||||
max_line_length = off
|
||||
ktlint_standard_filename = disabled
|
||||
ktlint_standard_function-expression-body = disabled
|
||||
ktlint_standard_function-naming = disabled
|
||||
ktlint_standard_if-else-bracing = disabled
|
||||
ktlint_standard_max-line-length = disabled
|
||||
ktlint_standard_no-wildcard-imports = disabled
|
||||
ktlint_standard_property-naming = disabled
|
||||
|
||||
@@ -33,10 +33,10 @@ if (wantsAndroidReleaseBuild && !hasAndroidReleaseSigning) {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jlleitschuh.gradle.ktlint")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.ktlint)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -65,8 +65,8 @@ android {
|
||||
applicationId = "ai.openclaw.app"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 2026042700
|
||||
versionName = "2026.4.27"
|
||||
versionCode = 2026043000
|
||||
versionName = "2026.4.30"
|
||||
ndk {
|
||||
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
@@ -78,13 +78,9 @@ android {
|
||||
productFlavors {
|
||||
create("play") {
|
||||
dimension = "store"
|
||||
buildConfigField("boolean", "OPENCLAW_ENABLE_SMS", "false")
|
||||
buildConfigField("boolean", "OPENCLAW_ENABLE_CALL_LOG", "false")
|
||||
}
|
||||
create("thirdParty") {
|
||||
dimension = "store"
|
||||
buildConfigField("boolean", "OPENCLAW_ENABLE_SMS", "true")
|
||||
buildConfigField("boolean", "OPENCLAW_ENABLE_CALL_LOG", "true")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,15 +129,7 @@ android {
|
||||
}
|
||||
|
||||
lint {
|
||||
disable +=
|
||||
setOf(
|
||||
"AndroidGradlePluginVersion",
|
||||
"GradleDependency",
|
||||
"HighAppVersionCode",
|
||||
"IconLauncherShape",
|
||||
"NewerVersionAvailable",
|
||||
"OldTargetApi",
|
||||
)
|
||||
lintConfig = file("lint.xml")
|
||||
warningsAsErrors = true
|
||||
}
|
||||
|
||||
@@ -184,57 +172,57 @@ ktlint {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val composeBom = platform("androidx.compose:compose-bom:2026.04.01")
|
||||
val composeBom = platform(libs.androidx.compose.bom)
|
||||
implementation(composeBom)
|
||||
androidTestImplementation(composeBom)
|
||||
|
||||
implementation("androidx.core:core-ktx:1.18.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0")
|
||||
implementation("androidx.activity:activity-compose:1.13.0")
|
||||
implementation("androidx.webkit:webkit:1.15.0")
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.webkit)
|
||||
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation(libs.androidx.compose.ui)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
// material-icons-extended pulled in full icon set (~20 MB DEX). Only ~18 icons used.
|
||||
// R8 will tree-shake unused icons when minify is enabled on release builds.
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation(libs.androidx.compose.material.icons.extended)
|
||||
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
|
||||
// Material Components (XML theme + resources)
|
||||
implementation("com.google.android.material:material:1.13.0")
|
||||
implementation(libs.material)
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0")
|
||||
implementation(libs.kotlinx.coroutines.android)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
implementation("androidx.security:security-crypto:1.1.0")
|
||||
implementation("androidx.exifinterface:exifinterface:1.4.2")
|
||||
implementation("com.squareup.okhttp3:okhttp:5.3.2")
|
||||
implementation("org.bouncycastle:bcprov-jdk18on:1.84")
|
||||
implementation("org.commonmark:commonmark:0.28.0")
|
||||
implementation("org.commonmark:commonmark-ext-autolink:0.28.0")
|
||||
implementation("org.commonmark:commonmark-ext-gfm-strikethrough:0.28.0")
|
||||
implementation("org.commonmark:commonmark-ext-gfm-tables:0.28.0")
|
||||
implementation("org.commonmark:commonmark-ext-task-list-items:0.28.0")
|
||||
implementation(libs.androidx.security.crypto)
|
||||
implementation(libs.androidx.exifinterface)
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.bcprov)
|
||||
implementation(libs.commonmark)
|
||||
implementation(libs.commonmark.ext.autolink)
|
||||
implementation(libs.commonmark.ext.gfm.strikethrough)
|
||||
implementation(libs.commonmark.ext.gfm.tables)
|
||||
implementation(libs.commonmark.ext.task.list.items)
|
||||
|
||||
// CameraX (for node.invoke camera.* parity)
|
||||
implementation("androidx.camera:camera-core:1.6.0")
|
||||
implementation("androidx.camera:camera-camera2:1.6.0")
|
||||
implementation("androidx.camera:camera-lifecycle:1.6.0")
|
||||
implementation("androidx.camera:camera-video:1.6.0")
|
||||
implementation("com.google.android.gms:play-services-code-scanner:16.1.0")
|
||||
implementation(libs.androidx.camera.core)
|
||||
implementation(libs.androidx.camera.camera2)
|
||||
implementation(libs.androidx.camera.lifecycle)
|
||||
implementation(libs.androidx.camera.video)
|
||||
implementation(libs.play.services.code.scanner)
|
||||
|
||||
// Unicast DNS-SD (Wide-Area Bonjour) for tailnet discovery domains.
|
||||
implementation("dnsjava:dnsjava:3.6.4")
|
||||
implementation(libs.dnsjava)
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:6.1.11")
|
||||
testImplementation("io.kotest:kotest-assertions-core-jvm:6.1.11")
|
||||
testImplementation("com.squareup.okhttp3:mockwebserver:5.3.2")
|
||||
testImplementation("org.robolectric:robolectric:4.16.1")
|
||||
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:6.0.3")
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
testImplementation(libs.kotest.runner.junit5)
|
||||
testImplementation(libs.kotest.assertions.core)
|
||||
testImplementation(libs.mockwebserver)
|
||||
testImplementation(libs.robolectric)
|
||||
testRuntimeOnly(libs.junit.vintage.engine)
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
|
||||
13
apps/android/app/lint.xml
Normal file
13
apps/android/app/lint.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<lint>
|
||||
<issue id="AndroidGradlePluginVersion" severity="ignore" />
|
||||
<issue id="GradleDependency" severity="ignore" />
|
||||
<issue id="IconLauncherShape" severity="ignore" />
|
||||
<issue id="NewerVersionAvailable" severity="ignore" />
|
||||
|
||||
<!-- OpenClaw uses date-based version codes (yyyyMMddNN), which are high but still below the Android max. -->
|
||||
<issue id="HighAppVersionCode" severity="ignore" />
|
||||
|
||||
<!-- Target SDK follows the current release train; bump only after platform compatibility testing. -->
|
||||
<issue id="OldTargetApi" severity="ignore" />
|
||||
</lint>
|
||||
@@ -13,7 +13,33 @@ import ai.openclaw.app.gateway.GatewaySession
|
||||
import ai.openclaw.app.gateway.GatewayTlsProbeFailure
|
||||
import ai.openclaw.app.gateway.GatewayTlsProbeResult
|
||||
import ai.openclaw.app.gateway.probeGatewayTlsFingerprint
|
||||
import ai.openclaw.app.node.*
|
||||
import ai.openclaw.app.node.A2UIHandler
|
||||
import ai.openclaw.app.node.CalendarHandler
|
||||
import ai.openclaw.app.node.CallLogHandler
|
||||
import ai.openclaw.app.node.CameraCaptureManager
|
||||
import ai.openclaw.app.node.CameraHandler
|
||||
import ai.openclaw.app.node.CanvasController
|
||||
import ai.openclaw.app.node.ConnectionManager
|
||||
import ai.openclaw.app.node.ContactsHandler
|
||||
import ai.openclaw.app.node.DEFAULT_SEAM_COLOR_ARGB
|
||||
import ai.openclaw.app.node.DebugHandler
|
||||
import ai.openclaw.app.node.DeviceHandler
|
||||
import ai.openclaw.app.node.DeviceNotificationListenerService
|
||||
import ai.openclaw.app.node.InvokeDispatcher
|
||||
import ai.openclaw.app.node.LocationCaptureManager
|
||||
import ai.openclaw.app.node.LocationHandler
|
||||
import ai.openclaw.app.node.MotionHandler
|
||||
import ai.openclaw.app.node.NodePresenceAliveBeacon
|
||||
import ai.openclaw.app.node.NotificationsHandler
|
||||
import ai.openclaw.app.node.PhotosHandler
|
||||
import ai.openclaw.app.node.Quad
|
||||
import ai.openclaw.app.node.SmsHandler
|
||||
import ai.openclaw.app.node.SmsManager
|
||||
import ai.openclaw.app.node.SystemHandler
|
||||
import ai.openclaw.app.node.asObjectOrNull
|
||||
import ai.openclaw.app.node.asStringOrNull
|
||||
import ai.openclaw.app.node.invokeErrorFromThrowable
|
||||
import ai.openclaw.app.node.parseHexColorArgb
|
||||
import ai.openclaw.app.protocol.OpenClawCanvasA2UIAction
|
||||
import ai.openclaw.app.voice.MicCaptureManager
|
||||
import ai.openclaw.app.voice.TalkModeManager
|
||||
@@ -103,8 +129,8 @@ class NodeRuntime(
|
||||
private val deviceHandler: DeviceHandler =
|
||||
DeviceHandler(
|
||||
appContext = appContext,
|
||||
smsEnabled = BuildConfig.OPENCLAW_ENABLE_SMS,
|
||||
callLogEnabled = BuildConfig.OPENCLAW_ENABLE_CALL_LOG,
|
||||
smsEnabled = SensitiveFeatureConfig.smsEnabled,
|
||||
callLogEnabled = SensitiveFeatureConfig.callLogEnabled,
|
||||
)
|
||||
|
||||
private val notificationsHandler: NotificationsHandler =
|
||||
@@ -163,10 +189,10 @@ class NodeRuntime(
|
||||
voiceWakeMode = { VoiceWakeMode.Off },
|
||||
motionActivityAvailable = { motionHandler.isActivityAvailable() },
|
||||
motionPedometerAvailable = { motionHandler.isPedometerAvailable() },
|
||||
sendSmsAvailable = { BuildConfig.OPENCLAW_ENABLE_SMS && sms.canSendSms() },
|
||||
readSmsAvailable = { BuildConfig.OPENCLAW_ENABLE_SMS && sms.canReadSms() },
|
||||
smsSearchPossible = { BuildConfig.OPENCLAW_ENABLE_SMS && sms.hasTelephonyFeature() },
|
||||
callLogAvailable = { BuildConfig.OPENCLAW_ENABLE_CALL_LOG },
|
||||
sendSmsAvailable = { SensitiveFeatureConfig.smsEnabled && sms.canSendSms() },
|
||||
readSmsAvailable = { SensitiveFeatureConfig.smsEnabled && sms.canReadSms() },
|
||||
smsSearchPossible = { SensitiveFeatureConfig.smsEnabled && sms.hasTelephonyFeature() },
|
||||
callLogAvailable = { SensitiveFeatureConfig.callLogEnabled },
|
||||
hasRecordAudioPermission = { hasRecordAudioPermission() },
|
||||
manualTls = { manualTls.value },
|
||||
)
|
||||
@@ -190,11 +216,11 @@ class NodeRuntime(
|
||||
isForeground = { _isForeground.value },
|
||||
cameraEnabled = { cameraEnabled.value },
|
||||
locationEnabled = { locationMode.value != LocationMode.Off },
|
||||
sendSmsAvailable = { BuildConfig.OPENCLAW_ENABLE_SMS && sms.canSendSms() },
|
||||
readSmsAvailable = { BuildConfig.OPENCLAW_ENABLE_SMS && sms.canReadSms() },
|
||||
smsFeatureEnabled = { BuildConfig.OPENCLAW_ENABLE_SMS },
|
||||
sendSmsAvailable = { SensitiveFeatureConfig.smsEnabled && sms.canSendSms() },
|
||||
readSmsAvailable = { SensitiveFeatureConfig.smsEnabled && sms.canReadSms() },
|
||||
smsFeatureEnabled = { SensitiveFeatureConfig.smsEnabled },
|
||||
smsTelephonyAvailable = { sms.hasTelephonyFeature() },
|
||||
callLogAvailable = { BuildConfig.OPENCLAW_ENABLE_CALL_LOG },
|
||||
callLogAvailable = { SensitiveFeatureConfig.callLogEnabled },
|
||||
debugBuild = { BuildConfig.DEBUG },
|
||||
refreshNodeCanvasCapability = { nodeSession.refreshNodeCanvasCapability() },
|
||||
onCanvasA2uiPush = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ai.openclaw.app.node
|
||||
|
||||
import ai.openclaw.app.BuildConfig
|
||||
import ai.openclaw.app.SensitiveFeatureConfig
|
||||
import ai.openclaw.app.gateway.GatewaySession
|
||||
import android.Manifest
|
||||
import android.app.ActivityManager
|
||||
@@ -25,8 +26,8 @@ import java.util.Locale
|
||||
|
||||
class DeviceHandler(
|
||||
private val appContext: Context,
|
||||
private val smsEnabled: Boolean = BuildConfig.OPENCLAW_ENABLE_SMS,
|
||||
private val callLogEnabled: Boolean = BuildConfig.OPENCLAW_ENABLE_CALL_LOG,
|
||||
private val smsEnabled: Boolean = SensitiveFeatureConfig.smsEnabled,
|
||||
private val callLogEnabled: Boolean = SensitiveFeatureConfig.callLogEnabled,
|
||||
) {
|
||||
companion object {
|
||||
internal fun hasAnySmsCapability(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package ai.openclaw.app.ui
|
||||
|
||||
import ai.openclaw.app.BuildConfig
|
||||
import ai.openclaw.app.LocationMode
|
||||
import ai.openclaw.app.MainViewModel
|
||||
import ai.openclaw.app.SensitiveFeatureConfig
|
||||
import ai.openclaw.app.gateway.GatewayEndpoint
|
||||
import ai.openclaw.app.node.DeviceNotificationListenerService
|
||||
import android.Manifest
|
||||
@@ -248,10 +248,10 @@ fun OnboardingFlow(
|
||||
|
||||
val smsAvailable =
|
||||
remember(context) {
|
||||
BuildConfig.OPENCLAW_ENABLE_SMS &&
|
||||
SensitiveFeatureConfig.smsEnabled &&
|
||||
context.packageManager?.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) == true
|
||||
}
|
||||
val callLogAvailable = remember { BuildConfig.OPENCLAW_ENABLE_CALL_LOG }
|
||||
val callLogAvailable = remember { SensitiveFeatureConfig.callLogEnabled }
|
||||
val motionAvailable =
|
||||
remember(context) {
|
||||
hasMotionCapabilities(context)
|
||||
|
||||
@@ -4,6 +4,7 @@ import ai.openclaw.app.BuildConfig
|
||||
import ai.openclaw.app.LocationMode
|
||||
import ai.openclaw.app.MainViewModel
|
||||
import ai.openclaw.app.NotificationPackageFilterMode
|
||||
import ai.openclaw.app.SensitiveFeatureConfig
|
||||
import ai.openclaw.app.node.DeviceNotificationListenerService
|
||||
import ai.openclaw.app.normalizeLocalHourMinute
|
||||
import android.Manifest
|
||||
@@ -204,10 +205,10 @@ fun SettingsSheet(viewModel: MainViewModel) {
|
||||
|
||||
val smsPermissionAvailable =
|
||||
remember {
|
||||
BuildConfig.OPENCLAW_ENABLE_SMS &&
|
||||
SensitiveFeatureConfig.smsEnabled &&
|
||||
context.packageManager?.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) == true
|
||||
}
|
||||
val callLogPermissionAvailable = remember { BuildConfig.OPENCLAW_ENABLE_CALL_LOG }
|
||||
val callLogPermissionAvailable = remember { SensitiveFeatureConfig.callLogEnabled }
|
||||
val photosPermission =
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package ai.openclaw.app
|
||||
|
||||
object SensitiveFeatureConfig {
|
||||
const val smsEnabled: Boolean = false
|
||||
const val callLogEnabled: Boolean = false
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package ai.openclaw.app.node
|
||||
|
||||
import ai.openclaw.app.gateway.GatewaySession
|
||||
import android.content.Context
|
||||
|
||||
internal data class CallLogRecord(
|
||||
val number: String?,
|
||||
val cachedName: String?,
|
||||
val date: Long,
|
||||
val duration: Long,
|
||||
val type: Int,
|
||||
)
|
||||
|
||||
internal data class CallLogSearchRequest(
|
||||
val limit: Int,
|
||||
val offset: Int,
|
||||
val cachedName: String?,
|
||||
val number: String?,
|
||||
val date: Long?,
|
||||
val dateStart: Long?,
|
||||
val dateEnd: Long?,
|
||||
val duration: Long?,
|
||||
val type: Int?,
|
||||
)
|
||||
|
||||
internal interface CallLogDataSource {
|
||||
fun hasReadPermission(context: Context): Boolean
|
||||
|
||||
fun search(
|
||||
context: Context,
|
||||
request: CallLogSearchRequest,
|
||||
): List<CallLogRecord>
|
||||
}
|
||||
|
||||
class CallLogHandler private constructor() {
|
||||
constructor(
|
||||
@Suppress("unused") appContext: Context,
|
||||
) : this()
|
||||
|
||||
fun handleCallLogSearch(
|
||||
@Suppress("unused") paramsJson: String?,
|
||||
): GatewaySession.InvokeResult =
|
||||
GatewaySession.InvokeResult.error(
|
||||
code = "CALL_LOG_UNAVAILABLE",
|
||||
message = "CALL_LOG_UNAVAILABLE: call log not available on this build",
|
||||
)
|
||||
|
||||
companion object {
|
||||
internal fun forTesting(
|
||||
@Suppress("unused") appContext: Context,
|
||||
@Suppress("unused") dataSource: CallLogDataSource,
|
||||
): CallLogHandler = CallLogHandler()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package ai.openclaw.app.node
|
||||
|
||||
import ai.openclaw.app.PermissionRequester
|
||||
import android.content.Context
|
||||
|
||||
class SmsManager(
|
||||
@Suppress("unused") private val context: Context,
|
||||
) {
|
||||
data class SendResult(
|
||||
val ok: Boolean,
|
||||
val to: String,
|
||||
val message: String?,
|
||||
val error: String? = null,
|
||||
val payloadJson: String,
|
||||
)
|
||||
|
||||
data class SmsMessage(
|
||||
val id: Long,
|
||||
val threadId: Long,
|
||||
val address: String?,
|
||||
val person: String?,
|
||||
val date: Long,
|
||||
val dateSent: Long,
|
||||
val read: Boolean,
|
||||
val type: Int,
|
||||
val body: String?,
|
||||
val status: Int,
|
||||
val transportType: String? = null,
|
||||
)
|
||||
|
||||
data class SearchResult(
|
||||
val ok: Boolean,
|
||||
val messages: List<SmsMessage>,
|
||||
val error: String? = null,
|
||||
val payloadJson: String,
|
||||
)
|
||||
|
||||
fun attachPermissionRequester(
|
||||
@Suppress("unused") requester: PermissionRequester,
|
||||
) {
|
||||
}
|
||||
|
||||
fun canSendSms(): Boolean = false
|
||||
|
||||
fun canSearchSms(): Boolean = false
|
||||
|
||||
fun canReadSms(): Boolean = false
|
||||
|
||||
fun hasTelephonyFeature(): Boolean = false
|
||||
|
||||
suspend fun send(paramsJson: String?): SendResult =
|
||||
SendResult(
|
||||
ok = false,
|
||||
to = "",
|
||||
message = null,
|
||||
error = "SMS_PERMISSION_REQUIRED: grant SMS permission",
|
||||
payloadJson = unavailablePayload(paramsJson),
|
||||
)
|
||||
|
||||
suspend fun search(paramsJson: String?): SearchResult =
|
||||
SearchResult(
|
||||
ok = false,
|
||||
messages = emptyList(),
|
||||
error = "SMS_PERMISSION_REQUIRED: grant READ_SMS permission",
|
||||
payloadJson = unavailablePayload(paramsJson),
|
||||
)
|
||||
|
||||
private fun unavailablePayload(paramsJson: String?): String = """{"ok":false,"error":"SMS_UNAVAILABLE","paramsProvided":${!paramsJson.isNullOrBlank()}}"""
|
||||
}
|
||||
6
apps/android/app/src/thirdParty/java/ai/openclaw/app/SensitiveFeatureConfig.kt
vendored
Normal file
6
apps/android/app/src/thirdParty/java/ai/openclaw/app/SensitiveFeatureConfig.kt
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package ai.openclaw.app
|
||||
|
||||
object SensitiveFeatureConfig {
|
||||
const val smsEnabled: Boolean = true
|
||||
const val callLogEnabled: Boolean = true
|
||||
}
|
||||
39
apps/android/app/src/thirdParty/java/ai/openclaw/app/node/SmsHandler.kt
vendored
Normal file
39
apps/android/app/src/thirdParty/java/ai/openclaw/app/node/SmsHandler.kt
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package ai.openclaw.app.node
|
||||
|
||||
import ai.openclaw.app.gateway.GatewaySession
|
||||
|
||||
class SmsHandler(
|
||||
private val sms: SmsManager,
|
||||
) {
|
||||
suspend fun handleSmsSend(paramsJson: String?): GatewaySession.InvokeResult {
|
||||
val res = sms.send(paramsJson)
|
||||
if (res.ok) {
|
||||
return GatewaySession.InvokeResult.ok(res.payloadJson)
|
||||
}
|
||||
return errorResult(res.error, defaultCode = "SMS_SEND_FAILED")
|
||||
}
|
||||
|
||||
suspend fun handleSmsSearch(paramsJson: String?): GatewaySession.InvokeResult {
|
||||
val res = sms.search(paramsJson)
|
||||
if (res.ok) {
|
||||
return GatewaySession.InvokeResult.ok(res.payloadJson)
|
||||
}
|
||||
return errorResult(res.error, defaultCode = "SMS_SEARCH_FAILED")
|
||||
}
|
||||
|
||||
private fun errorResult(
|
||||
error: String?,
|
||||
defaultCode: String,
|
||||
): GatewaySession.InvokeResult {
|
||||
val rawMessage = error ?: defaultCode
|
||||
val idx = rawMessage.indexOf(':')
|
||||
val code = if (idx > 0) rawMessage.substring(0, idx).trim() else defaultCode
|
||||
val message =
|
||||
if (idx > 0 && code == rawMessage.substring(0, idx).trim()) {
|
||||
rawMessage.substring(idx + 1).trim().ifEmpty { rawMessage }
|
||||
} else {
|
||||
rawMessage
|
||||
}
|
||||
return GatewaySession.InvokeResult.error(code = code, message = message)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user