Skip to content

Commit 1df314d

Browse files
authored
Merge branch 'main' into codex/browser-proxy-jsonencoder-fix
2 parents d1a6941 + 8ad0ca3 commit 1df314d

File tree

142 files changed

+5070
-541
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+5070
-541
lines changed

.github/actions/setup-node-env/action.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
name: Setup Node environment
22
description: >
3-
Initialize submodules with retry, install Node 22, pnpm, optionally Bun,
3+
Initialize submodules with retry, install Node 24 by default, pnpm, optionally Bun,
44
and optionally run pnpm install. Requires actions/checkout to run first.
55
inputs:
66
node-version:
77
description: Node.js version to install.
88
required: false
9-
default: "22.x"
9+
default: "24.x"
10+
cache-key-suffix:
11+
description: Suffix appended to the pnpm store cache key.
12+
required: false
13+
default: "node24"
1014
pnpm-version:
1115
description: pnpm version for corepack.
1216
required: false
@@ -16,7 +20,7 @@ inputs:
1620
required: false
1721
default: "true"
1822
use-sticky-disk:
19-
description: Use Blacksmith sticky disks for pnpm store caching.
23+
description: Request Blacksmith sticky-disk pnpm caching on trusted runs; pull_request runs fall back to actions/cache.
2024
required: false
2125
default: "false"
2226
install-deps:
@@ -54,7 +58,7 @@ runs:
5458
uses: ./.github/actions/setup-pnpm-store-cache
5559
with:
5660
pnpm-version: ${{ inputs.pnpm-version }}
57-
cache-key-suffix: "node22"
61+
cache-key-suffix: ${{ inputs.cache-key-suffix }}
5862
use-sticky-disk: ${{ inputs.use-sticky-disk }}
5963

6064
- name: Setup Bun

.github/actions/setup-pnpm-store-cache/action.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ inputs:
88
cache-key-suffix:
99
description: Suffix appended to the cache key.
1010
required: false
11-
default: "node22"
11+
default: "node24"
1212
use-sticky-disk:
13-
description: Use Blacksmith sticky disks instead of actions/cache for pnpm store.
13+
description: Use Blacksmith sticky disks instead of actions/cache for pnpm store on trusted runs; pull_request runs fall back to actions/cache.
1414
required: false
1515
default: "false"
1616
use-restore-keys:
1717
description: Whether to use restore-keys fallback for actions/cache.
1818
required: false
1919
default: "true"
2020
use-actions-cache:
21-
description: Whether to restore/save pnpm store with actions/cache.
21+
description: Whether to restore/save pnpm store with actions/cache, including pull_request fallback when sticky disks are disabled.
2222
required: false
2323
default: "true"
2424
runs:
@@ -51,21 +51,23 @@ runs:
5151
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
5252

5353
- name: Mount pnpm store sticky disk
54-
if: inputs.use-sticky-disk == 'true'
54+
# Keep persistent sticky-disk state off untrusted PR runs.
55+
if: inputs.use-sticky-disk == 'true' && github.event_name != 'pull_request'
5556
uses: useblacksmith/stickydisk@v1
5657
with:
57-
key: ${{ github.repository }}-pnpm-store-${{ runner.os }}-${{ inputs.cache-key-suffix }}
58+
key: ${{ github.repository }}-pnpm-store-${{ runner.os }}-${{ github.ref_name }}-${{ inputs.cache-key-suffix }}-${{ hashFiles('pnpm-lock.yaml') }}
5859
path: ${{ steps.pnpm-store.outputs.path }}
5960

6061
- name: Restore pnpm store cache (exact key only)
61-
if: inputs.use-actions-cache == 'true' && inputs.use-sticky-disk != 'true' && inputs.use-restore-keys != 'true'
62+
# PRs that request sticky disks still need a safe cache restore path.
63+
if: inputs.use-actions-cache == 'true' && (inputs.use-sticky-disk != 'true' || github.event_name == 'pull_request') && inputs.use-restore-keys != 'true'
6264
uses: actions/cache@v4
6365
with:
6466
path: ${{ steps.pnpm-store.outputs.path }}
6567
key: ${{ runner.os }}-pnpm-store-${{ inputs.cache-key-suffix }}-${{ hashFiles('pnpm-lock.yaml') }}
6668

6769
- name: Restore pnpm store cache (with fallback keys)
68-
if: inputs.use-actions-cache == 'true' && inputs.use-sticky-disk != 'true' && inputs.use-restore-keys == 'true'
70+
if: inputs.use-actions-cache == 'true' && (inputs.use-sticky-disk != 'true' || github.event_name == 'pull_request') && inputs.use-restore-keys == 'true'
6971
uses: actions/cache@v4
7072
with:
7173
path: ${{ steps.pnpm-store.outputs.path }}

.github/workflows/ci.yml

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,40 @@ jobs:
233233
- name: Check docs
234234
run: pnpm check:docs
235235

236+
compat-node22:
237+
name: "compat-node22"
238+
needs: [docs-scope, changed-scope]
239+
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
240+
runs-on: blacksmith-16vcpu-ubuntu-2404
241+
steps:
242+
- name: Checkout
243+
uses: actions/checkout@v4
244+
with:
245+
submodules: false
246+
247+
- name: Setup Node 22 compatibility environment
248+
uses: ./.github/actions/setup-node-env
249+
with:
250+
node-version: "22.x"
251+
cache-key-suffix: "node22"
252+
install-bun: "false"
253+
use-sticky-disk: "true"
254+
255+
- name: Configure Node 22 test resources
256+
run: |
257+
# Keep the compatibility lane aligned with the default Node test lane.
258+
echo "OPENCLAW_TEST_WORKERS=2" >> "$GITHUB_ENV"
259+
echo "OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB=6144" >> "$GITHUB_ENV"
260+
261+
- name: Build under Node 22
262+
run: pnpm build
263+
264+
- name: Run tests under Node 22
265+
run: pnpm test
266+
267+
- name: Verify npm pack under Node 22
268+
run: pnpm release:check
269+
236270
skills-python:
237271
needs: [docs-scope, changed-scope]
238272
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true' || needs.changed-scope.outputs.run_skills_python == 'true')
@@ -401,14 +435,14 @@ jobs:
401435
- name: Setup Node.js
402436
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
403437
with:
404-
node-version: 22.x
438+
node-version: 24.x
405439
check-latest: false
406440

407441
- name: Setup pnpm + cache store
408442
uses: ./.github/actions/setup-pnpm-store-cache
409443
with:
410444
pnpm-version: "10.23.0"
411-
cache-key-suffix: "node22"
445+
cache-key-suffix: "node24"
412446
# Sticky disk mount currently retries/fails on every shard and adds ~50s
413447
# before install while still yielding zero pnpm store reuse.
414448
# Try exact-key actions/cache restores instead to recover store reuse

.github/workflows/openclaw-npm-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ concurrency:
1010
cancel-in-progress: false
1111

1212
env:
13-
NODE_VERSION: "22.x"
13+
NODE_VERSION: "24.x"
1414
PNPM_VERSION: "10.23.0"
1515

1616
jobs:

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,16 @@ Docs: https://docs.openclaw.ai
1515
- Security/browser.request: block persistent browser profile create/delete routes from write-scoped `browser.request` so callers can no longer persist admin-only browser profile changes through the browser control surface. (`GHSA-vmhq-cqm9-6p7q`)(#43800) Thanks @tdjackey and @vincentkoc.
1616
- Security/agent: reject public spawned-run lineage fields and keep workspace inheritance on the internal spawned-session path so external `agent` callers can no longer override the gateway workspace boundary. (`GHSA-2rqg-gjgv-84jm`)(#43801) Thanks @tdjackey and @vincentkoc.
1717
- Security/exec allowlist: preserve POSIX case sensitivity and keep `?` within a single path segment so exact-looking allowlist patterns no longer overmatch executables across case or directory boundaries. (`GHSA-f8r2-vg7x-gh8m`)(#43798) Thanks @zpbrent and @vincentkoc.
18+
- Security/Feishu webhook: require `encryptKey` alongside `verificationToken` in webhook mode so unsigned forged events are rejected instead of being processed with token-only configuration. (`GHSA-g353-mgv3-8pcj`)(#44087) Thanks @lintsinghua and @vincentkoc.
19+
- Security/exec detection: normalize compatibility Unicode and strip invisible formatting code points before obfuscation checks so zero-width and fullwidth command tricks no longer suppress heuristic detection. (`GHSA-9r3v-37xh-2cf6`)(#44091) Thanks @wooluo and @vincentkoc.
20+
- Security/WebSocket preauth: shorten unauthenticated handshake retention and reject oversized pre-auth frames before application-layer parsing to reduce pre-pairing exposure on unsupported public deployments. (`GHSA-jv4g-m82p-2j93`)(#44089) (`GHSA-xwx2-ppv2-wx98`)(#44089) Thanks @ez-lbz and @vincentkoc.
21+
- Security/Feishu reactions: preserve looked-up group chat typing and fail closed on ambiguous reaction context so group authorization and mention gating cannot be bypassed through synthetic `p2p` reactions. (`GHSA-m69h-jm2f-2pv8`)(#44088) Thanks @zpbrent and @vincentkoc.
22+
- Security/LINE webhook: require signatures for empty-event POST probes too so unsigned requests no longer confirm webhook reachability with a `200` response. (`GHSA-mhxh-9pjm-w7q5`)(#44090) Thanks @TerminalsandCoffee and @vincentkoc.
1823

1924
### Changes
2025

26+
- Docs/Kubernetes: Add a starter K8s install path with raw manifests, Kind setup, and deployment docs. Thanks @sallyom @dzianisv @egkristi
27+
2128
### Fixes
2229

2330
- Cron/proactive delivery: keep isolated direct cron sends out of the write-ahead resend queue so transient-send retries do not replay duplicate proactive messages after restart. (#40646) Thanks @openperf and @vincentkoc.
@@ -27,6 +34,13 @@ Docs: https://docs.openclaw.ai
2734
- Mattermost/block streaming: fix duplicate message delivery (one threaded, one top-level) when block streaming is active by excluding `replyToId` from the block reply dedup key and adding an explicit `threading` dock to the Mattermost plugin. (#41362) Thanks @mathiasnagler and @vincentkoc.
2835
- BlueBubbles/self-chat echo dedupe: drop reflected duplicate webhook copies only when a matching `fromMe` event was just seen for the same chat, body, and timestamp, preventing self-chat loops without broad webhook suppression. Related to #32166. (#38442) Thanks @vincentkoc.
2936
- Models/Kimi Coding: send `anthropic-messages` tools in native Anthropic format again so `kimi-coding` stops degrading tool calls into XML/plain-text pseudo invocations instead of real `tool_use` blocks. (#38669, #39907, #40552) Thanks @opriz.
37+
- Subagents/completion announce retries: raise the default announce timeout to 90 seconds and stop retrying gateway-timeout failures for externally delivered completion announces, preventing duplicate user-facing completion messages after slow gateway responses. Fixes #41235. Thanks @vasujain00 and @vincentkoc.
38+
- Sandbox/write: preserve pinned mutation-helper payload stdin so sandboxed `write` no longer reports success while creating empty files. (#43876) Thanks @glitch418x.
39+
- Gateway/main-session routing: keep TUI and other `mode:UI` main-session sends on the internal surface when `deliver` is enabled, so replies no longer inherit the session's persisted Telegram/WhatsApp route. (#43918) Thanks @obviyus.
40+
- Doctor/gateway service audit: canonicalize service entrypoint paths before comparing them so symlink-vs-realpath installs no longer trigger false "entrypoint does not match the current install" repair prompts. (#43882) Thanks @ngutman.
41+
- Doctor/gateway service audit: earlier groundwork for this fix landed in the superseded #28338 branch. Thanks @realriphub.
42+
- Telegram/model picker: make inline model button selections persist the chosen session model correctly, clear overrides when selecting the configured default, and include effective fallback models in `/models` button validation. (#40105) Thanks @avirweb.
43+
- Mattermost/reply media delivery: pass agent-scoped `mediaLocalRoots` through shared reply delivery so allowed local files upload correctly from button, slash-command, and model-picker replies. (#44021) Thanks @LyleLiu666.
3044

3145
## 2026.3.11
3246

@@ -51,7 +65,9 @@ Docs: https://docs.openclaw.ai
5165
- Gateway/node pending work: add narrow in-memory pending-work queue primitives (`node.pending.enqueue` / `node.pending.drain`) and wake-helper reuse as a foundation for dormant-node work delivery. (#41409) Thanks @mbelinky.
5266
- Git/runtime state: ignore the gateway-generated `.dev-state` file so local runtime state does not show up as untracked repo noise. (#41848) Thanks @smysle.
5367
- Exec/child commands: mark child command environments with `OPENCLAW_CLI` so subprocesses can detect when they were launched from the OpenClaw CLI. (#41411) Thanks @vincentkoc.
68+
<<<<<<< HEAD
5469
- LLM Task/Lobster: add an optional `thinking` override so workflow calls can explicitly set embedded reasoning level with shared validation for invalid values and unsupported `xhigh` modes. (#15606) Thanks @xadenryan and @ImLukeF.
70+
- Mattermost/reply threading: add `channels.mattermost.replyToMode` for channel and group messages so top-level posts can start thread-scoped sessions without the manual reply-then-thread workaround. (#29587) Thanks @teconomix.
5571

5672
### Breaking
5773

@@ -151,6 +167,9 @@ Docs: https://docs.openclaw.ai
151167
- Telegram/direct delivery: bridge direct delivery sends to internal `message:sent` hooks so internal hook listeners observe successful Telegram deliveries. (#40185) Thanks @vincentkoc.
152168
- Dependencies: refresh workspace dependencies except the pinned Carbon package, and harden ACP session-config writes against non-string SDK values so newer ACP clients fail fast instead of tripping type/runtime mismatches.
153169
- Telegram/polling restarts: clear bounded cleanup timeout handles after `runner.stop()` and `bot.stop()` settle so stall recovery no longer leaves stray 15-second timers behind on clean shutdown. (#43188) thanks @kyohwang.
170+
- Gateway/config errors: surface up to three validation issues in top-level `config.set`, `config.patch`, and `config.apply` error messages while preserving structured issue details. (#42664) Thanks @huntharo.
171+
- Hooks/plugin context parity followup: pass `trigger` and `channelId` through embedded `llm_input`, `agent_end`, and `llm_output` hook contexts so plugins receive the same agent metadata across hook phases. (#42362) Thanks @zhoulf1006.
172+
- Status/context windows: normalize provider-qualified override cache keys so `/status` resolves the active provider's configured context window even when `models.providers` keys use mixed case or surrounding whitespace. (#36389) Thanks @haoruilee.
154173

155174
## 2026.3.8
156175

@@ -225,6 +244,9 @@ Docs: https://docs.openclaw.ai
225244
- macOS/browser proxy: serialize non-GET browser proxy request bodies through `AnyCodable.foundationValue` so nested JSON bodies no longer crash the macOS app with `Invalid type in JSON write (__SwiftValue)`. (#43069) Thanks @Effet.
226245
- CLI/skills tables: keep terminal table borders aligned for wide graphemes, use full reported terminal width, and switch a few ambiguous skill icons to Terminal-safe emoji so `openclaw skills` renders more consistently in Terminal.app and iTerm. Thanks @vincentkoc.
227246
- Memory/Gemini: normalize returned Gemini embeddings across direct query, direct batch, and async batch paths so memory search uses consistent vector handling for Gemini too. (#43409) Thanks @gumadeiras.
247+
- Agents/failover: recognize additional serialized network errno strings plus `EHOSTDOWN` and `EPIPE` structured codes so transient transport failures trigger timeout failover more reliably. (#42830) Thanks @jnMetaCode.
248+
- Telegram/model picker: make inline model button selections persist the chosen session model correctly, clear overrides when selecting the configured default, and include effective fallback models in `/models` button validation. (#40105) Thanks @avirweb.
249+
- Agents/embedded runner: carry provider-observed overflow token counts into compaction so overflow retries and diagnostics use the rejected live prompt size instead of only transcript estimates. (#40357) thanks @rabsef-bicrym.
228250

229251
## 2026.3.7
230252

Dockerfile

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
# Slim (bookworm-slim): docker build --build-arg OPENCLAW_VARIANT=slim .
1515
ARG OPENCLAW_EXTENSIONS=""
1616
ARG OPENCLAW_VARIANT=default
17-
ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:22-bookworm@sha256:b501c082306a4f528bc4038cbf2fbb58095d583d0419a259b2114b5ac53d12e9"
18-
ARG OPENCLAW_NODE_BOOKWORM_DIGEST="sha256:b501c082306a4f528bc4038cbf2fbb58095d583d0419a259b2114b5ac53d12e9"
19-
ARG OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE="node:22-bookworm-slim@sha256:9c2c405e3ff9b9afb2873232d24bb06367d649aa3e6259cbe314da59578e81e9"
20-
ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:9c2c405e3ff9b9afb2873232d24bb06367d649aa3e6259cbe314da59578e81e9"
17+
ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"
18+
ARG OPENCLAW_NODE_BOOKWORM_DIGEST="sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"
19+
ARG OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE="node:24-bookworm-slim@sha256:e8e2e91b1378f83c5b2dd15f0247f34110e2fe895f6ca7719dbb780f929368eb"
20+
ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:e8e2e91b1378f83c5b2dd15f0247f34110e2fe895f6ca7719dbb780f929368eb"
2121

2222
# Base images are pinned to SHA256 digests for reproducible builds.
2323
# Trade-off: digests must be updated manually when upstream tags move.
24-
# To update, run: docker manifest inspect node:22-bookworm (or podman)
24+
# To update, run: docker buildx imagetools inspect node:24-bookworm (or podman)
2525
# and replace the digest below with the current multi-arch manifest list entry.
2626

2727
FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps
@@ -92,12 +92,12 @@ RUN CI=true pnpm prune --prod && \
9292
# ── Runtime base images ─────────────────────────────────────────
9393
FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS base-default
9494
ARG OPENCLAW_NODE_BOOKWORM_DIGEST
95-
LABEL org.opencontainers.image.base.name="docker.io/library/node:22-bookworm" \
95+
LABEL org.opencontainers.image.base.name="docker.io/library/node:24-bookworm" \
9696
org.opencontainers.image.base.digest="${OPENCLAW_NODE_BOOKWORM_DIGEST}"
9797

9898
FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-slim
9999
ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST
100-
LABEL org.opencontainers.image.base.name="docker.io/library/node:22-bookworm-slim" \
100+
LABEL org.opencontainers.image.base.name="docker.io/library/node:24-bookworm-slim" \
101101
org.opencontainers.image.base.digest="${OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST}"
102102

103103
# ── Stage 3: Runtime ────────────────────────────────────────────
@@ -209,7 +209,7 @@ RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw \
209209
ENV NODE_ENV=production
210210

211211
# Security hardening: Run as non-root user
212-
# The node:22-bookworm image includes a 'node' user (uid 1000)
212+
# The node:24-bookworm image includes a 'node' user (uid 1000)
213213
# This reduces the attack surface by preventing container escape via root privileges
214214
USER node
215215

docs/channels/feishu.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,18 @@ Edit `~/.openclaw/openclaw.json`:
193193
}
194194
```
195195

196-
If you use `connectionMode: "webhook"`, set `verificationToken`. The Feishu webhook server binds to `127.0.0.1` by default; set `webhookHost` only if you intentionally need a different bind address.
196+
If you use `connectionMode: "webhook"`, set both `verificationToken` and `encryptKey`. The Feishu webhook server binds to `127.0.0.1` by default; set `webhookHost` only if you intentionally need a different bind address.
197197

198-
#### Verification Token (webhook mode)
198+
#### Verification Token and Encrypt Key (webhook mode)
199199

200-
When using webhook mode, set `channels.feishu.verificationToken` in your config. To get the value:
200+
When using webhook mode, set both `channels.feishu.verificationToken` and `channels.feishu.encryptKey` in your config. To get the values:
201201

202202
1. In Feishu Open Platform, open your app
203203
2. Go to **Development****Events & Callbacks** (开发配置 → 事件与回调)
204204
3. Open the **Encryption** tab (加密策略)
205-
4. Copy **Verification Token**
205+
4. Copy **Verification Token** and **Encrypt Key**
206+
207+
The screenshot below shows where to find the **Verification Token**. The **Encrypt Key** is listed in the same **Encryption** section.
206208

207209
![Verification Token location](../images/feishu-verification-token.png)
208210

@@ -600,6 +602,7 @@ Key options:
600602
| `channels.feishu.connectionMode` | Event transport mode | `websocket` |
601603
| `channels.feishu.defaultAccount` | Default account ID for outbound routing | `default` |
602604
| `channels.feishu.verificationToken` | Required for webhook mode | - |
605+
| `channels.feishu.encryptKey` | Required for webhook mode | - |
603606
| `channels.feishu.webhookPath` | Webhook route path | `/feishu/events` |
604607
| `channels.feishu.webhookHost` | Webhook bind host | `127.0.0.1` |
605608
| `channels.feishu.webhookPort` | Webhook bind port | `3000` |

0 commit comments

Comments
 (0)