diff --git a/.detect-secrets.cfg b/.detect-secrets.cfg
index 38912567c9b..34f4ff85f07 100644
--- a/.detect-secrets.cfg
+++ b/.detect-secrets.cfg
@@ -7,10 +7,6 @@
[exclude-files]
# pnpm lockfiles contain lots of high-entropy package integrity blobs.
pattern = (^|/)pnpm-lock\.yaml$
-# Generated output and vendored assets.
-pattern = (^|/)(dist|vendor)/
-# Local config file with allowlist patterns.
-pattern = (^|/)\.detect-secrets\.cfg$
[exclude-lines]
# Fastlane checks for private key marker; not a real key.
@@ -28,3 +24,22 @@ pattern = "talk\.apiKey"
pattern = === "string"
# specific optional-chaining password check that didn't match the line above.
pattern = typeof remote\?\.password === "string"
+# Docker apt signing key fingerprint constant; not a secret.
+pattern = OPENCLAW_DOCKER_GPG_FINGERPRINT=
+# Credential matrix metadata field in docs JSON; not a secret value.
+pattern = "secretShape": "(secret_input|sibling_ref)"
+# Docs line describing API key rotation knobs; not a credential.
+pattern = API key rotation \(provider-specific\): set `\*_API_KEYS`
+# Docs line describing remote password precedence; not a credential.
+pattern = passw[o]rd: `OPENCLAW_GATEWAY_PASSW[O]RD` -> `gateway\.auth\.passw[o]rd` -> `gateway\.remote\.passw[o]rd`
+pattern = passw[o]rd: `OPENCLAW_GATEWAY_PASSW[O]RD` -> `gateway\.remote\.passw[o]rd` -> `gateway\.auth\.passw[o]rd`
+# Test fixture starts a multiline fake private key; detector should ignore the header line.
+pattern = const key = `-----BEGIN PRIVATE KEY-----
+# Docs examples: literal placeholder API key snippets and shell heredoc helper.
+pattern = export CUSTOM_API_K[E]Y="your-key"
+pattern = grep -q 'N[O]DE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc \|\| cat >> ~/.bashrc <<'EOF'
+pattern = env: \{ MISTRAL_API_K[E]Y: "sk-\.\.\." \},
+pattern = "ap[i]Key": "xxxxx",
+pattern = ap[i]Key: "A[I]za\.\.\.",
+# Sparkle appcast signatures are release metadata, not credentials.
+pattern = sparkle:edSignature="[A-Za-z0-9+/=]+"
diff --git a/.dockerignore b/.dockerignore
index 73d00fff147..3a8e436d515 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -51,6 +51,10 @@ vendor/
# Keep the rest of apps/ and vendor/ excluded to avoid a large build context.
!apps/shared/
!apps/shared/OpenClawKit/
+!apps/shared/OpenClawKit/Sources/
+!apps/shared/OpenClawKit/Sources/OpenClawKit/
+!apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/
+!apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json
!apps/shared/OpenClawKit/Tools/
!apps/shared/OpenClawKit/Tools/CanvasA2UI/
!apps/shared/OpenClawKit/Tools/CanvasA2UI/**
diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml
index f02fbddb3e8..be79a1ce593 100644
--- a/.github/actionlint.yaml
+++ b/.github/actionlint.yaml
@@ -8,6 +8,7 @@ self-hosted-runner:
- blacksmith-8vcpu-windows-2025
- blacksmith-16vcpu-ubuntu-2404
- blacksmith-16vcpu-windows-2025
+ - blacksmith-32vcpu-windows-2025
- blacksmith-16vcpu-ubuntu-2404-arm
# Ignore patterns for known issues
diff --git a/.github/actions/ensure-base-commit/action.yml b/.github/actions/ensure-base-commit/action.yml
new file mode 100644
index 00000000000..b2c4322aa84
--- /dev/null
+++ b/.github/actions/ensure-base-commit/action.yml
@@ -0,0 +1,47 @@
+name: Ensure base commit
+description: Ensure a shallow checkout has enough history to diff against a base SHA.
+inputs:
+ base-sha:
+ description: Base commit SHA to diff against.
+ required: true
+ fetch-ref:
+ description: Branch or ref to deepen/fetch from origin when base-sha is missing.
+ required: true
+runs:
+ using: composite
+ steps:
+ - name: Ensure base commit is available
+ shell: bash
+ env:
+ BASE_SHA: ${{ inputs.base-sha }}
+ FETCH_REF: ${{ inputs.fetch-ref }}
+ run: |
+ set -euo pipefail
+
+ if [ -z "$BASE_SHA" ] || [[ "$BASE_SHA" =~ ^0+$ ]]; then
+ echo "No concrete base SHA available; skipping targeted fetch."
+ exit 0
+ fi
+
+ if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
+ echo "Base commit already present: $BASE_SHA"
+ exit 0
+ fi
+
+ for deepen_by in 25 100 300; do
+ echo "Base commit missing; deepening $FETCH_REF by $deepen_by."
+ git fetch --no-tags --deepen="$deepen_by" origin "$FETCH_REF" || true
+ if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
+ echo "Resolved base commit after deepening: $BASE_SHA"
+ exit 0
+ fi
+ done
+
+ echo "Base commit still missing; fetching full history for $FETCH_REF."
+ git fetch --no-tags origin "$FETCH_REF" || true
+ if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
+ echo "Resolved base commit after full ref fetch: $BASE_SHA"
+ exit 0
+ fi
+
+ echo "Base commit still unavailable after fetch attempts: $BASE_SHA"
diff --git a/.github/actions/setup-node-env/action.yml b/.github/actions/setup-node-env/action.yml
index 334cd3c24fb..c46387517e4 100644
--- a/.github/actions/setup-node-env/action.yml
+++ b/.github/actions/setup-node-env/action.yml
@@ -1,7 +1,7 @@
name: Setup Node environment
description: >
Initialize submodules with retry, install Node 22, pnpm, optionally Bun,
- and run pnpm install. Requires actions/checkout to run first.
+ and optionally run pnpm install. Requires actions/checkout to run first.
inputs:
node-version:
description: Node.js version to install.
@@ -15,6 +15,14 @@ inputs:
description: Whether to install Bun alongside Node.
required: false
default: "true"
+ use-sticky-disk:
+ description: Use Blacksmith sticky disks for pnpm store caching.
+ required: false
+ default: "false"
+ install-deps:
+ description: Whether to run pnpm install after environment setup.
+ required: false
+ default: "true"
frozen-lockfile:
description: Whether to use --frozen-lockfile for install.
required: false
@@ -40,19 +48,20 @@ runs:
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ inputs.node-version }}
- check-latest: true
+ check-latest: false
- name: Setup pnpm + cache store
uses: ./.github/actions/setup-pnpm-store-cache
with:
pnpm-version: ${{ inputs.pnpm-version }}
cache-key-suffix: "node22"
+ use-sticky-disk: ${{ inputs.use-sticky-disk }}
- name: Setup Bun
if: inputs.install-bun == 'true'
uses: oven-sh/setup-bun@v2
with:
- bun-version: "1.3.9+cf6cdbbba"
+ bun-version: "1.3.9"
- name: Runtime versions
shell: bash
@@ -63,10 +72,12 @@ runs:
if command -v bun &>/dev/null; then bun -v; fi
- name: Capture node path
+ if: inputs.install-deps == 'true'
shell: bash
run: echo "NODE_BIN=$(dirname "$(node -p "process.execPath")")" >> "$GITHUB_ENV"
- name: Install dependencies
+ if: inputs.install-deps == 'true'
shell: bash
env:
CI: "true"
diff --git a/.github/actions/setup-pnpm-store-cache/action.yml b/.github/actions/setup-pnpm-store-cache/action.yml
index 8e25492ac92..e1e5a34abda 100644
--- a/.github/actions/setup-pnpm-store-cache/action.yml
+++ b/.github/actions/setup-pnpm-store-cache/action.yml
@@ -9,6 +9,18 @@ inputs:
description: Suffix appended to the cache key.
required: false
default: "node22"
+ use-sticky-disk:
+ description: Use Blacksmith sticky disks instead of actions/cache for pnpm store.
+ required: false
+ default: "false"
+ use-restore-keys:
+ description: Whether to use restore-keys fallback for actions/cache.
+ required: false
+ default: "true"
+ use-actions-cache:
+ description: Whether to restore/save pnpm store with actions/cache.
+ required: false
+ default: "true"
runs:
using: composite
steps:
@@ -38,7 +50,22 @@ runs:
shell: bash
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- - name: Restore pnpm store cache
+ - name: Mount pnpm store sticky disk
+ if: inputs.use-sticky-disk == 'true'
+ uses: useblacksmith/stickydisk@v1
+ with:
+ key: ${{ github.repository }}-pnpm-store-${{ runner.os }}-${{ inputs.cache-key-suffix }}
+ path: ${{ steps.pnpm-store.outputs.path }}
+
+ - name: Restore pnpm store cache (exact key only)
+ if: inputs.use-actions-cache == 'true' && inputs.use-sticky-disk != 'true' && inputs.use-restore-keys != 'true'
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.pnpm-store.outputs.path }}
+ key: ${{ runner.os }}-pnpm-store-${{ inputs.cache-key-suffix }}-${{ hashFiles('pnpm-lock.yaml') }}
+
+ - name: Restore pnpm store cache (with fallback keys)
+ if: inputs.use-actions-cache == 'true' && inputs.use-sticky-disk != 'true' && inputs.use-restore-keys == 'true'
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.path }}
diff --git a/.github/codeql/codeql-javascript-typescript.yml b/.github/codeql/codeql-javascript-typescript.yml
new file mode 100644
index 00000000000..5a765db5392
--- /dev/null
+++ b/.github/codeql/codeql-javascript-typescript.yml
@@ -0,0 +1,18 @@
+name: openclaw-codeql-javascript-typescript
+
+paths:
+ - src
+ - extensions
+ - ui/src
+ - skills
+
+paths-ignore:
+ - apps
+ - dist
+ - docs
+ - "**/node_modules"
+ - "**/coverage"
+ - "**/*.test.ts"
+ - "**/*.test.tsx"
+ - "**/*.e2e.test.ts"
+ - "**/*.e2e.test.tsx"
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 9b0e7f8dc4b..adf5045728a 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -87,6 +87,13 @@ What you personally verified (not just CI), and how:
- Edge cases checked:
- What you did **not** verify:
+## Review Conversations
+
+- [ ] I replied to or resolved every bot review conversation I addressed in this PR.
+- [ ] I left unresolved only the conversations that still need reviewer or maintainer judgment.
+
+If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
+
## Compatibility / Migration
- Backward compatible? (`Yes/No`)
diff --git a/.github/workflows/auto-response.yml b/.github/workflows/auto-response.yml
index 4a572db52e6..a40149b7ccb 100644
--- a/.github/workflows/auto-response.yml
+++ b/.github/workflows/auto-response.yml
@@ -35,6 +35,7 @@ jobs:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
// Labels prefixed with "r:" are auto-response triggers.
+ const activePrLimit = 10;
const rules = [
{
label: "r: skill",
@@ -48,6 +49,20 @@ jobs:
message:
"Please use [our support server](https://discord.gg/clawd) and ask in #help or #users-helping-users to resolve this, or follow the stuck FAQ at https://docs.openclaw.ai/help/faq#im-stuck-whats-the-fastest-way-to-get-unstuck.",
},
+ {
+ label: "r: no-ci-pr",
+ message:
+ "Please don't make PRs for test failures on main.\n\n" +
+ "The team is aware of those and will handle them directly on the codebase, not only fixing the tests but also investigating what the root cause is. Having to sift through test-fix-PRs (including some that have been out of date for weeks...) on top of that doesn't help. There are already way too many PRs for humans to manage; please don't make the flood worse.\n\n" +
+ "Thank you.",
+ },
+ {
+ label: "r: too-many-prs",
+ close: true,
+ message:
+ `Closing this PR because the author has more than ${activePrLimit} active PRs in this repo. ` +
+ "Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit.",
+ },
{
label: "r: testflight",
close: true,
@@ -246,6 +261,8 @@ jobs:
};
const triggerLabel = "trigger-response";
+ const activePrLimitLabel = "r: too-many-prs";
+ const activePrLimitOverrideLabel = "r: too-many-prs-override";
const target = context.payload.issue ?? context.payload.pull_request;
if (!target) {
return;
@@ -433,6 +450,10 @@ jobs:
return;
}
+ if (pullRequest && labelSet.has(activePrLimitOverrideLabel)) {
+ labelSet.delete(activePrLimitLabel);
+ }
+
const rule = rules.find((item) => labelSet.has(item.label));
if (!rule) {
return;
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 99e839abf10..1d248d5c804 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,15 +21,23 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
- fetch-depth: 0
+ fetch-depth: 1
+ fetch-tags: false
submodules: false
+ - name: Ensure docs-scope base commit
+ uses: ./.github/actions/ensure-base-commit
+ with:
+ base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
+ fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
+
- name: Detect docs-only changes
id: check
uses: ./.github/actions/detect-docs-changes
# Detect which heavy areas are touched so PRs can skip unrelated expensive jobs.
- # Push to main keeps broad coverage.
+ # Push to main keeps broad coverage, but this job still needs to run so
+ # downstream jobs that list it in `needs` are not skipped.
changed-scope:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_only != 'true'
@@ -38,13 +46,22 @@ jobs:
run_node: ${{ steps.scope.outputs.run_node }}
run_macos: ${{ steps.scope.outputs.run_macos }}
run_android: ${{ steps.scope.outputs.run_android }}
+ run_skills_python: ${{ steps.scope.outputs.run_skills_python }}
+ run_windows: ${{ steps.scope.outputs.run_windows }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
- fetch-depth: 0
+ fetch-depth: 1
+ fetch-tags: false
submodules: false
+ - name: Ensure changed-scope base commit
+ uses: ./.github/actions/ensure-base-commit
+ with:
+ base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
+ fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
+
- name: Detect changed scopes
id: scope
shell: bash
@@ -57,75 +74,11 @@ jobs:
BASE="${{ github.event.pull_request.base.sha }}"
fi
- CHANGED="$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "UNKNOWN")"
- if [ "$CHANGED" = "UNKNOWN" ] || [ -z "$CHANGED" ]; then
- # Fail-safe: run broad checks if detection fails.
- echo "run_node=true" >> "$GITHUB_OUTPUT"
- echo "run_macos=true" >> "$GITHUB_OUTPUT"
- echo "run_android=true" >> "$GITHUB_OUTPUT"
- exit 0
- fi
-
- run_node=false
- run_macos=false
- run_android=false
- has_non_docs=false
- has_non_native_non_docs=false
-
- while IFS= read -r path; do
- [ -z "$path" ] && continue
- case "$path" in
- docs/*|*.md|*.mdx)
- continue
- ;;
- *)
- has_non_docs=true
- ;;
- esac
-
- case "$path" in
- # Generated protocol models are already covered by protocol:check and
- # should not force the full native macOS lane.
- apps/macos/Sources/OpenClawProtocol/*|apps/shared/OpenClawKit/Sources/OpenClawProtocol/*)
- ;;
- apps/macos/*|apps/ios/*|apps/shared/*|Swabble/*)
- run_macos=true
- ;;
- esac
-
- case "$path" in
- apps/android/*|apps/shared/*)
- run_android=true
- ;;
- esac
-
- case "$path" in
- src/*|test/*|extensions/*|packages/*|scripts/*|ui/*|.github/*|openclaw.mjs|package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig*.json|vitest*.ts|tsdown.config.ts|.oxlintrc.json|.oxfmtrc.jsonc)
- run_node=true
- ;;
- esac
-
- case "$path" in
- apps/android/*|apps/ios/*|apps/macos/*|apps/shared/*|Swabble/*|appcast.xml)
- ;;
- *)
- has_non_native_non_docs=true
- ;;
- esac
- done <<< "$CHANGED"
-
- # If there are non-doc files outside native app trees, keep Node checks enabled.
- if [ "$run_node" = false ] && [ "$has_non_docs" = true ] && [ "$has_non_native_non_docs" = true ]; then
- run_node=true
- fi
-
- echo "run_node=${run_node}" >> "$GITHUB_OUTPUT"
- echo "run_macos=${run_macos}" >> "$GITHUB_OUTPUT"
- echo "run_android=${run_android}" >> "$GITHUB_OUTPUT"
+ node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD
# Build dist once for Node-relevant changes and share it with downstream jobs.
build-artifacts:
- needs: [docs-scope, changed-scope, check]
+ needs: [docs-scope, changed-scope]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
@@ -134,10 +87,18 @@ jobs:
with:
submodules: false
+ - name: Ensure secrets base commit (PR fast path)
+ if: github.event_name == 'pull_request'
+ uses: ./.github/actions/ensure-base-commit
+ with:
+ base-sha: ${{ github.event.pull_request.base.sha }}
+ fetch-ref: ${{ github.event.pull_request.base.ref }}
+
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
+ use-sticky-disk: "true"
- name: Build dist
run: pnpm build
@@ -164,6 +125,7 @@ jobs:
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
+ use-sticky-disk: "true"
- name: Download dist artifact
uses: actions/download-artifact@v4
@@ -175,7 +137,7 @@ jobs:
run: pnpm release:check
checks:
- needs: [docs-scope, changed-scope, check]
+ needs: [docs-scope, changed-scope]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-16vcpu-ubuntu-2404
strategy:
@@ -185,6 +147,9 @@ jobs:
- runtime: node
task: test
command: pnpm canvas:a2ui:bundle && pnpm test
+ - runtime: node
+ task: extensions
+ command: pnpm test:extensions
- runtime: node
task: protocol
command: pnpm protocol:check
@@ -207,6 +172,7 @@ jobs:
uses: ./.github/actions/setup-node-env
with:
install-bun: "${{ matrix.runtime == 'bun' }}"
+ use-sticky-disk: "true"
- name: Configure Node test resources
if: (github.event_name != 'push' || matrix.runtime != 'bun') && matrix.task == 'test' && matrix.runtime == 'node'
@@ -223,8 +189,8 @@ jobs:
# Types, lint, and format check.
check:
name: "check"
- needs: [docs-scope]
- if: needs.docs-scope.outputs.docs_only != 'true'
+ needs: [docs-scope, changed-scope]
+ if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- name: Checkout
@@ -236,52 +202,17 @@ jobs:
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
+ use-sticky-disk: "true"
- name: Check types and lint and oxfmt
run: pnpm check
+ - name: Strict TS build smoke
+ run: pnpm build:strict-smoke
+
- name: Enforce safe external URL opening policy
run: pnpm lint:ui:no-raw-window-open
- # Report-only dead-code scans. Runs after scope detection and stores machine-readable
- # results as artifacts for later triage before we enable hard gates.
- # Temporarily disabled in CI while we process initial findings.
- deadcode:
- name: dead-code report
- needs: [docs-scope, changed-scope]
- # if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
- if: false
- runs-on: blacksmith-16vcpu-ubuntu-2404
- strategy:
- fail-fast: false
- matrix:
- include:
- - tool: knip
- command: pnpm deadcode:report:ci:knip
- - tool: ts-prune
- command: pnpm deadcode:report:ci:ts-prune
- - tool: ts-unused-exports
- command: pnpm deadcode:report:ci:ts-unused
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- submodules: false
-
- - name: Setup Node environment
- uses: ./.github/actions/setup-node-env
- with:
- install-bun: "false"
-
- - name: Run ${{ matrix.tool }} dead-code scan
- run: ${{ matrix.command }}
-
- - name: Upload dead-code results
- uses: actions/upload-artifact@v4
- with:
- name: dead-code-${{ matrix.tool }}-${{ github.run_id }}
- path: .artifacts/deadcode
-
# Validate docs (format, lint, broken links) only when docs files changed.
check-docs:
needs: [docs-scope]
@@ -297,13 +228,14 @@ jobs:
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
+ use-sticky-disk: "true"
- name: Check docs
run: pnpm check:docs
skills-python:
needs: [docs-scope, changed-scope]
- if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
+ 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')
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- name: Checkout
@@ -335,26 +267,67 @@ jobs:
with:
submodules: false
+ - name: Ensure secrets base commit
+ uses: ./.github/actions/ensure-base-commit
+ with:
+ base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
+ fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
+
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
+ use-sticky-disk: "false"
+ install-deps: "false"
- name: Setup Python
+ id: setup-python
uses: actions/setup-python@v5
with:
python-version: "3.12"
+ cache: "pip"
+ cache-dependency-path: |
+ pyproject.toml
+ .pre-commit-config.yaml
+ .github/workflows/ci.yml
+
+ - name: Restore pre-commit cache
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/pre-commit
+ key: pre-commit-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install pre-commit
run: |
python -m pip install --upgrade pip
- python -m pip install pre-commit detect-secrets==1.5.0
+ python -m pip install pre-commit
- name: Detect secrets
run: |
- if ! detect-secrets scan --baseline .secrets.baseline; then
- echo "::error::Secret scanning failed. See docs/gateway/security.md#secret-scanning-detect-secrets"
- exit 1
+ set -euo pipefail
+
+ if [ "${{ github.event_name }}" = "push" ]; then
+ echo "Running full detect-secrets scan on push."
+ pre-commit run --all-files detect-secrets
+ exit 0
+ fi
+
+ BASE="${{ github.event.pull_request.base.sha }}"
+ changed_files=()
+ if git rev-parse --verify "$BASE^{commit}" >/dev/null 2>&1; then
+ while IFS= read -r path; do
+ [ -n "$path" ] || continue
+ [ -f "$path" ] || continue
+ changed_files+=("$path")
+ done < <(git diff --name-only --diff-filter=ACMR "$BASE" HEAD)
+ fi
+
+ if [ "${#changed_files[@]}" -gt 0 ]; then
+ echo "Running detect-secrets on ${#changed_files[@]} changed file(s)."
+ pre-commit run detect-secrets --files "${changed_files[@]}"
+ else
+ echo "Falling back to full detect-secrets scan."
+ pre-commit run --all-files detect-secrets
fi
- name: Detect committed private keys
@@ -382,15 +355,15 @@ jobs:
run: pre-commit run --all-files pnpm-audit-prod
checks-windows:
- needs: [docs-scope, changed-scope, build-artifacts, check]
- if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
- runs-on: blacksmith-16vcpu-windows-2025
+ needs: [docs-scope, changed-scope]
+ if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_windows == 'true')
+ runs-on: blacksmith-32vcpu-windows-2025
timeout-minutes: 45
env:
- NODE_OPTIONS: --max-old-space-size=4096
- # Keep total concurrency predictable on the 16 vCPU runner:
- # `scripts/test-parallel.mjs` runs some vitest suites in parallel processes.
- OPENCLAW_TEST_WORKERS: 2
+ NODE_OPTIONS: --max-old-space-size=6144
+ # Keep total concurrency predictable on the 32 vCPU runner.
+ # Windows shard 2 has shown intermittent instability at 2 workers.
+ OPENCLAW_TEST_WORKERS: 1
defaults:
run:
shell: bash
@@ -398,26 +371,36 @@ jobs:
fail-fast: false
matrix:
include:
- - runtime: node
- task: lint
- shard_index: 0
- shard_count: 1
- command: pnpm lint
- runtime: node
task: test
shard_index: 1
- shard_count: 2
- command: pnpm canvas:a2ui:bundle && pnpm test
+ shard_count: 6
+ command: pnpm test
- runtime: node
task: test
shard_index: 2
- shard_count: 2
- command: pnpm canvas:a2ui:bundle && pnpm test
+ shard_count: 6
+ command: pnpm test
- runtime: node
- task: protocol
- shard_index: 0
- shard_count: 1
- command: pnpm protocol:check
+ task: test
+ shard_index: 3
+ shard_count: 6
+ command: pnpm test
+ - runtime: node
+ task: test
+ shard_index: 4
+ shard_count: 6
+ command: pnpm test
+ - runtime: node
+ task: test
+ shard_index: 5
+ shard_count: 6
+ command: pnpm test
+ - runtime: node
+ task: test
+ shard_index: 6
+ shard_count: 6
+ command: pnpm test
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -443,31 +426,24 @@ jobs:
Write-Warning "Failed to apply Defender exclusions, continuing. $($_.Exception.Message)"
}
- - name: Download dist artifact (lint lane)
- if: matrix.task == 'lint'
- uses: actions/download-artifact@v4
- with:
- name: dist-build
- path: dist/
-
- - name: Verify dist artifact (lint lane)
- if: matrix.task == 'lint'
- run: |
- set -euo pipefail
- test -s dist/index.js
- test -s dist/plugin-sdk/index.js
-
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22.x
- check-latest: true
+ check-latest: false
- name: Setup pnpm + cache store
uses: ./.github/actions/setup-pnpm-store-cache
with:
pnpm-version: "10.23.0"
cache-key-suffix: "node22"
+ # Sticky disk mount currently retries/fails on every shard and adds ~50s
+ # before install while still yielding zero pnpm store reuse.
+ # Try exact-key actions/cache restores instead to recover store reuse
+ # without the sticky-disk mount penalty.
+ use-sticky-disk: "false"
+ use-restore-keys: "false"
+ use-actions-cache: "true"
- name: Runtime versions
run: |
@@ -486,7 +462,9 @@ jobs:
which node
node -v
pnpm -v
- pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
+ # Persist Windows-native postinstall outputs in the pnpm store so restored
+ # caches can skip repeated rebuild/download work on later shards/runs.
+ pnpm install --frozen-lockfile --prefer-offline --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true --config.side-effects-cache=true || pnpm install --frozen-lockfile --prefer-offline --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true --config.side-effects-cache=true
- name: Configure test shard (Windows)
if: matrix.task == 'test'
@@ -494,6 +472,10 @@ jobs:
echo "OPENCLAW_TEST_SHARDS=${{ matrix.shard_count }}" >> "$GITHUB_ENV"
echo "OPENCLAW_TEST_SHARD_INDEX=${{ matrix.shard_index }}" >> "$GITHUB_ENV"
+ - name: Build A2UI bundle (Windows)
+ if: matrix.task == 'test'
+ run: pnpm canvas:a2ui:bundle
+
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
run: ${{ matrix.command }}
@@ -735,7 +717,7 @@ jobs:
PY
android:
- needs: [docs-scope, changed-scope, check]
+ needs: [docs-scope, changed-scope]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_android == 'true')
runs-on: blacksmith-16vcpu-ubuntu-2404
strategy:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000000..9b78a3c6172
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,130 @@
+name: CodeQL
+
+on:
+ workflow_dispatch:
+
+concurrency:
+ group: codeql-${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ runs-on: ${{ matrix.runs_on }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - language: javascript-typescript
+ runs_on: blacksmith-16vcpu-ubuntu-2404
+ needs_node: true
+ needs_python: false
+ needs_java: false
+ needs_swift_tools: false
+ needs_manual_build: false
+ needs_autobuild: false
+ config_file: ./.github/codeql/codeql-javascript-typescript.yml
+ - language: actions
+ runs_on: blacksmith-16vcpu-ubuntu-2404
+ needs_node: false
+ needs_python: false
+ needs_java: false
+ needs_swift_tools: false
+ needs_manual_build: false
+ needs_autobuild: false
+ config_file: ""
+ - language: python
+ runs_on: blacksmith-16vcpu-ubuntu-2404
+ needs_node: false
+ needs_python: true
+ needs_java: false
+ needs_swift_tools: false
+ needs_manual_build: false
+ needs_autobuild: false
+ config_file: ""
+ - language: java-kotlin
+ runs_on: blacksmith-16vcpu-ubuntu-2404
+ needs_node: false
+ needs_python: false
+ needs_java: true
+ needs_swift_tools: false
+ needs_manual_build: true
+ needs_autobuild: false
+ config_file: ""
+ - language: swift
+ runs_on: macos-latest
+ needs_node: false
+ needs_python: false
+ needs_java: false
+ needs_swift_tools: true
+ needs_manual_build: true
+ needs_autobuild: false
+ config_file: ""
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: false
+
+ - name: Setup Node environment
+ if: matrix.needs_node
+ uses: ./.github/actions/setup-node-env
+ with:
+ install-bun: "false"
+ use-sticky-disk: "true"
+
+ - name: Setup Python
+ if: matrix.needs_python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Setup Java
+ if: matrix.needs_java
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: "21"
+
+ - name: Setup Swift build tools
+ if: matrix.needs_swift_tools
+ run: brew install xcodegen swiftlint swiftformat
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v4
+ with:
+ languages: ${{ matrix.language }}
+ queries: security-and-quality
+ config-file: ${{ matrix.config_file || '' }}
+
+ - name: Autobuild
+ if: matrix.needs_autobuild
+ uses: github/codeql-action/autobuild@v4
+
+ - name: Build Android for CodeQL
+ if: matrix.language == 'java-kotlin'
+ working-directory: apps/android
+ run: ./gradlew --no-daemon :app:assembleDebug
+
+ - name: Build Swift for CodeQL
+ if: matrix.language == 'swift'
+ run: |
+ set -euo pipefail
+ swift build --package-path apps/macos --configuration release
+ cd apps/ios
+ xcodegen generate
+ xcodebuild build \
+ -project OpenClaw.xcodeproj \
+ -scheme OpenClaw \
+ -destination "generic/platform=iOS Simulator" \
+ CODE_SIGNING_ALLOWED=NO
+
+ - name: Analyze
+ uses: github/codeql-action/analyze@v4
+ with:
+ category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
index eff0993b466..f991b7f8653 100644
--- a/.github/workflows/docker-release.yml
+++ b/.github/workflows/docker-release.yml
@@ -22,20 +22,21 @@ env:
IMAGE_NAME: ${{ github.repository }}
jobs:
- # Build amd64 image
+ # Build amd64 images (default + slim share the build stage cache)
build-amd64:
runs-on: blacksmith-16vcpu-ubuntu-2404
permissions:
packages: write
contents: read
outputs:
- image-digest: ${{ steps.build.outputs.digest }}
+ digest: ${{ steps.build.outputs.digest }}
+ slim-digest: ${{ steps.build-slim.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@v4
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
+ - name: Set up Docker Builder
+ uses: useblacksmith/setup-docker-builder@v1
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
@@ -52,12 +53,15 @@ jobs:
run: |
set -euo pipefail
tags=()
+ slim_tags=()
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
tags+=("${IMAGE}:main-amd64")
+ slim_tags+=("${IMAGE}:main-slim-amd64")
fi
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
version="${GITHUB_REF#refs/tags/v}"
tags+=("${IMAGE}:${version}-amd64")
+ slim_tags+=("${IMAGE}:${version}-slim-amd64")
fi
if [[ ${#tags[@]} -eq 0 ]]; then
echo "::error::No amd64 tags resolved for ref ${GITHUB_REF}"
@@ -68,33 +72,76 @@ jobs:
printf "%s\n" "${tags[@]}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
+ {
+ echo "slim<> "$GITHUB_OUTPUT"
+
+ - name: Resolve OCI labels (amd64)
+ id: labels
+ shell: bash
+ run: |
+ set -euo pipefail
+ version="${GITHUB_SHA}"
+ if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
+ version="main"
+ fi
+ if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
+ version="${GITHUB_REF#refs/tags/v}"
+ fi
+ created="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ {
+ echo "value<> "$GITHUB_OUTPUT"
- name: Build and push amd64 image
id: build
- uses: docker/build-push-action@v6
+ uses: useblacksmith/build-push-action@v2
with:
context: .
platforms: linux/amd64
tags: ${{ steps.tags.outputs.value }}
- cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:amd64
- cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:amd64,mode=max
+ labels: ${{ steps.labels.outputs.value }}
provenance: false
push: true
+ cache-from: type=gha,scope=docker-release-amd64
+ cache-to: type=gha,mode=max,scope=docker-release-amd64
- # Build arm64 image
+ - name: Build and push amd64 slim image
+ id: build-slim
+ uses: useblacksmith/build-push-action@v2
+ with:
+ context: .
+ platforms: linux/amd64
+ build-args: |
+ OPENCLAW_VARIANT=slim
+ tags: ${{ steps.tags.outputs.slim }}
+ labels: ${{ steps.labels.outputs.value }}
+ provenance: false
+ push: true
+ cache-from: type=gha,scope=docker-release-amd64
+ cache-to: type=gha,mode=max,scope=docker-release-amd64
+
+ # Build arm64 images (default + slim share the build stage cache)
build-arm64:
runs-on: blacksmith-16vcpu-ubuntu-2404-arm
permissions:
packages: write
contents: read
outputs:
- image-digest: ${{ steps.build.outputs.digest }}
+ digest: ${{ steps.build.outputs.digest }}
+ slim-digest: ${{ steps.build-slim.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@v4
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
+ - name: Set up Docker Builder
+ uses: useblacksmith/setup-docker-builder@v1
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
@@ -111,12 +158,15 @@ jobs:
run: |
set -euo pipefail
tags=()
+ slim_tags=()
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
tags+=("${IMAGE}:main-arm64")
+ slim_tags+=("${IMAGE}:main-slim-arm64")
fi
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
version="${GITHUB_REF#refs/tags/v}"
tags+=("${IMAGE}:${version}-arm64")
+ slim_tags+=("${IMAGE}:${version}-slim-arm64")
fi
if [[ ${#tags[@]} -eq 0 ]]; then
echo "::error::No arm64 tags resolved for ref ${GITHUB_REF}"
@@ -127,20 +177,62 @@ jobs:
printf "%s\n" "${tags[@]}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
+ {
+ echo "slim<> "$GITHUB_OUTPUT"
+
+ - name: Resolve OCI labels (arm64)
+ id: labels
+ shell: bash
+ run: |
+ set -euo pipefail
+ version="${GITHUB_SHA}"
+ if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
+ version="main"
+ fi
+ if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
+ version="${GITHUB_REF#refs/tags/v}"
+ fi
+ created="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ {
+ echo "value<> "$GITHUB_OUTPUT"
- name: Build and push arm64 image
id: build
- uses: docker/build-push-action@v6
+ uses: useblacksmith/build-push-action@v2
with:
context: .
platforms: linux/arm64
tags: ${{ steps.tags.outputs.value }}
- cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:arm64
- cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:arm64,mode=max
+ labels: ${{ steps.labels.outputs.value }}
provenance: false
push: true
+ cache-from: type=gha,scope=docker-release-arm64
+ cache-to: type=gha,mode=max,scope=docker-release-arm64
- # Create multi-platform manifest
+ - name: Build and push arm64 slim image
+ id: build-slim
+ uses: useblacksmith/build-push-action@v2
+ with:
+ context: .
+ platforms: linux/arm64
+ build-args: |
+ OPENCLAW_VARIANT=slim
+ tags: ${{ steps.tags.outputs.slim }}
+ labels: ${{ steps.labels.outputs.value }}
+ provenance: false
+ push: true
+ cache-from: type=gha,scope=docker-release-arm64
+ cache-to: type=gha,mode=max,scope=docker-release-arm64
+
+ # Create multi-platform manifests
create-manifest:
runs-on: blacksmith-16vcpu-ubuntu-2404
permissions:
@@ -166,14 +258,18 @@ jobs:
run: |
set -euo pipefail
tags=()
+ slim_tags=()
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
tags+=("${IMAGE}:main")
+ slim_tags+=("${IMAGE}:main-slim")
fi
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
version="${GITHUB_REF#refs/tags/v}"
tags+=("${IMAGE}:${version}")
+ slim_tags+=("${IMAGE}:${version}-slim")
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$ ]]; then
tags+=("${IMAGE}:latest")
+ slim_tags+=("${IMAGE}:slim")
fi
fi
if [[ ${#tags[@]} -eq 0 ]]; then
@@ -185,8 +281,13 @@ jobs:
printf "%s\n" "${tags[@]}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
+ {
+ echo "slim<> "$GITHUB_OUTPUT"
- - name: Create and push manifest
+ - name: Create and push default manifest
shell: bash
run: |
set -euo pipefail
@@ -197,5 +298,19 @@ jobs:
args+=("-t" "$tag")
done
docker buildx imagetools create "${args[@]}" \
- ${{ needs.build-amd64.outputs.image-digest }} \
- ${{ needs.build-arm64.outputs.image-digest }}
+ ${{ needs.build-amd64.outputs.digest }} \
+ ${{ needs.build-arm64.outputs.digest }}
+
+ - name: Create and push slim manifest
+ shell: bash
+ run: |
+ set -euo pipefail
+ mapfile -t tags <<< "${{ steps.tags.outputs.slim }}"
+ args=()
+ for tag in "${tags[@]}"; do
+ [ -z "$tag" ] && continue
+ args+=("-t" "$tag")
+ done
+ docker buildx imagetools create "${args[@]}" \
+ ${{ needs.build-amd64.outputs.slim-digest }} \
+ ${{ needs.build-arm64.outputs.slim-digest }}
diff --git a/.github/workflows/install-smoke.yml b/.github/workflows/install-smoke.yml
index fd0ac45799d..36f64d2d6ad 100644
--- a/.github/workflows/install-smoke.yml
+++ b/.github/workflows/install-smoke.yml
@@ -19,7 +19,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
- fetch-depth: 0
+ fetch-depth: 1
+ fetch-tags: false
+
+ - name: Ensure docs-scope base commit
+ uses: ./.github/actions/ensure-base-commit
+ with:
+ base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
+ fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect docs-only changes
id: check
@@ -33,32 +40,79 @@ jobs:
- name: Checkout CLI
uses: actions/checkout@v4
- - name: Setup Node.js
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- with:
- node-version: 22.x
- check-latest: true
+ - name: Set up Docker Builder
+ uses: useblacksmith/setup-docker-builder@v1
- - name: Setup pnpm + cache store
- uses: ./.github/actions/setup-pnpm-store-cache
+ - name: Build root Dockerfile smoke image
+ uses: useblacksmith/build-push-action@v2
with:
- pnpm-version: "10.23.0"
- cache-key-suffix: "node22"
-
- - name: Install pnpm deps (minimal)
- run: pnpm install --ignore-scripts --frozen-lockfile
+ context: .
+ file: ./Dockerfile
+ tags: openclaw-dockerfile-smoke:local
+ load: true
+ push: false
+ provenance: false
+ cache-from: type=gha,scope=install-smoke-root-dockerfile
+ cache-to: type=gha,mode=max,scope=install-smoke-root-dockerfile
- name: Run root Dockerfile CLI smoke
run: |
- docker build -t openclaw-dockerfile-smoke:local -f Dockerfile .
docker run --rm --entrypoint sh openclaw-dockerfile-smoke:local -lc 'which openclaw && openclaw --version'
+ # This smoke only validates that the build-arg path preinstalls selected
+ # extension deps without breaking image build or basic CLI startup. It
+ # does not exercise runtime loading/registration of diagnostics-otel.
+ - name: Build extension Dockerfile smoke image
+ uses: useblacksmith/build-push-action@v2
+ with:
+ context: .
+ file: ./Dockerfile
+ build-args: |
+ OPENCLAW_EXTENSIONS=diagnostics-otel
+ tags: openclaw-ext-smoke:local
+ load: true
+ push: false
+ provenance: false
+ cache-from: type=gha,scope=install-smoke-root-dockerfile-ext
+ cache-to: type=gha,mode=max,scope=install-smoke-root-dockerfile-ext
+
+ - name: Smoke test Dockerfile with extension build arg
+ run: |
+ docker run --rm --entrypoint sh openclaw-ext-smoke:local -lc 'which openclaw && openclaw --version'
+
+ - name: Build installer smoke image
+ uses: useblacksmith/build-push-action@v2
+ with:
+ context: ./scripts/docker
+ file: ./scripts/docker/install-sh-smoke/Dockerfile
+ tags: openclaw-install-smoke:local
+ load: true
+ push: false
+ provenance: false
+ cache-from: type=gha,scope=install-smoke-installer-root
+ cache-to: type=gha,mode=max,scope=install-smoke-installer-root
+
+ - name: Build installer non-root image
+ if: github.event_name != 'pull_request'
+ uses: useblacksmith/build-push-action@v2
+ with:
+ context: ./scripts/docker
+ file: ./scripts/docker/install-sh-nonroot/Dockerfile
+ tags: openclaw-install-nonroot:local
+ load: true
+ push: false
+ provenance: false
+ cache-from: type=gha,scope=install-smoke-installer-nonroot
+ cache-to: type=gha,mode=max,scope=install-smoke-installer-nonroot
+
- name: Run installer docker tests
env:
CLAWDBOT_INSTALL_URL: https://openclaw.ai/install.sh
CLAWDBOT_INSTALL_CLI_URL: https://openclaw.ai/install-cli.sh
CLAWDBOT_NO_ONBOARD: "1"
CLAWDBOT_INSTALL_SMOKE_SKIP_CLI: "1"
+ CLAWDBOT_INSTALL_SMOKE_SKIP_IMAGE_BUILD: "1"
+ CLAWDBOT_INSTALL_NONROOT_SKIP_IMAGE_BUILD: ${{ github.event_name == 'pull_request' && '0' || '1' }}
CLAWDBOT_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }}
CLAWDBOT_INSTALL_SMOKE_SKIP_PREVIOUS: "1"
- run: pnpm test:install:smoke
+ run: bash scripts/test-install-sh-docker.sh
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index ed86b4c67bb..8de54a416f8 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -142,10 +142,10 @@ jobs:
}
const repo = `${context.repo.owner}/${context.repo.repo}`;
- const trustedLabel = "trusted-contributor";
- const experiencedLabel = "experienced-contributor";
- const trustedThreshold = 4;
- const experiencedThreshold = 10;
+ // const trustedLabel = "trusted-contributor";
+ // const experiencedLabel = "experienced-contributor";
+ // const trustedThreshold = 4;
+ // const experiencedThreshold = 10;
let isMaintainer = false;
try {
@@ -170,36 +170,208 @@ jobs:
return;
}
- const mergedQuery = `repo:${repo} is:pr is:merged author:${login}`;
- let mergedCount = 0;
+ // trusted-contributor and experienced-contributor labels disabled.
+ // const mergedQuery = `repo:${repo} is:pr is:merged author:${login}`;
+ // let mergedCount = 0;
+ // try {
+ // const merged = await github.rest.search.issuesAndPullRequests({
+ // q: mergedQuery,
+ // per_page: 1,
+ // });
+ // mergedCount = merged?.data?.total_count ?? 0;
+ // } catch (error) {
+ // if (error?.status !== 422) {
+ // throw error;
+ // }
+ // core.warning(`Skipping merged search for ${login}; treating as 0.`);
+ // }
+ //
+ // if (mergedCount >= experiencedThreshold) {
+ // await github.rest.issues.addLabels({
+ // ...context.repo,
+ // issue_number: context.payload.pull_request.number,
+ // labels: [experiencedLabel],
+ // });
+ // return;
+ // }
+ //
+ // if (mergedCount >= trustedThreshold) {
+ // await github.rest.issues.addLabels({
+ // ...context.repo,
+ // issue_number: context.payload.pull_request.number,
+ // labels: [trustedLabel],
+ // });
+ // }
+ - name: Apply too-many-prs label
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ with:
+ github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
+ script: |
+ const pullRequest = context.payload.pull_request;
+ if (!pullRequest) {
+ return;
+ }
+
+ const activePrLimitLabel = "r: too-many-prs";
+ const activePrLimitOverrideLabel = "r: too-many-prs-override";
+ const activePrLimit = 10;
+ const labelColor = "B60205";
+ const labelDescription = `Author has more than ${activePrLimit} active PRs in this repo`;
+ const authorLogin = pullRequest.user?.login;
+ if (!authorLogin) {
+ return;
+ }
+
+ const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pullRequest.number,
+ per_page: 100,
+ });
+
+ const labelNames = new Set(
+ currentLabels
+ .map((label) => (typeof label === "string" ? label : label?.name))
+ .filter((name) => typeof name === "string"),
+ );
+
+ if (labelNames.has(activePrLimitOverrideLabel)) {
+ 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;
+ }
+ }
+ }
+ return;
+ }
+
+ const ensureLabelExists = async () => {
+ try {
+ await github.rest.issues.getLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: activePrLimitLabel,
+ });
+ } catch (error) {
+ if (error?.status !== 404) {
+ throw error;
+ }
+ await github.rest.issues.createLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: activePrLimitLabel,
+ color: labelColor,
+ description: labelDescription,
+ });
+ }
+ };
+
+ const isPrivilegedAuthor = async () => {
+ if (pullRequest.author_association === "OWNER") {
+ return true;
+ }
+
+ let isMaintainer = false;
+ try {
+ const membership = await github.rest.teams.getMembershipForUserInOrg({
+ org: context.repo.owner,
+ team_slug: "maintainer",
+ username: authorLogin,
+ });
+ isMaintainer = membership?.data?.state === "active";
+ } catch (error) {
+ if (error?.status !== 404) {
+ throw error;
+ }
+ }
+
+ if (isMaintainer) {
+ return true;
+ }
+
+ try {
+ const permission = await github.rest.repos.getCollaboratorPermissionLevel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ username: authorLogin,
+ });
+ const roleName = (permission?.data?.role_name ?? "").toLowerCase();
+ return roleName === "admin" || roleName === "maintain";
+ } catch (error) {
+ if (error?.status !== 404) {
+ throw error;
+ }
+ }
+
+ return false;
+ };
+
+ if (await isPrivilegedAuthor()) {
+ 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;
+ }
+ }
+ }
+ return;
+ }
+
+ let openPrCount = 0;
try {
- const merged = await github.rest.search.issuesAndPullRequests({
- q: mergedQuery,
+ const result = await github.rest.search.issuesAndPullRequests({
+ q: `repo:${context.repo.owner}/${context.repo.repo} is:pr is:open author:${authorLogin}`,
per_page: 1,
});
- mergedCount = merged?.data?.total_count ?? 0;
+ openPrCount = result?.data?.total_count ?? 0;
} catch (error) {
if (error?.status !== 422) {
throw error;
}
- core.warning(`Skipping merged search for ${login}; treating as 0.`);
+ core.warning(`Skipping open PR count for ${authorLogin}; treating as 0.`);
}
- if (mergedCount >= experiencedThreshold) {
- await github.rest.issues.addLabels({
- ...context.repo,
- issue_number: context.payload.pull_request.number,
- labels: [experiencedLabel],
- });
+ if (openPrCount > activePrLimit) {
+ await ensureLabelExists();
+ if (!labelNames.has(activePrLimitLabel)) {
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pullRequest.number,
+ labels: [activePrLimitLabel],
+ });
+ }
return;
}
- if (mergedCount >= trustedThreshold) {
- await github.rest.issues.addLabels({
- ...context.repo,
- issue_number: context.payload.pull_request.number,
- labels: [trustedLabel],
- });
+ 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;
+ }
+ }
}
backfill-pr-labels:
@@ -241,10 +413,10 @@ jobs:
const sizeLabels = ["size: XS", "size: S", "size: M", "size: L", "size: XL"];
const labelColor = "b76e79";
- const trustedLabel = "trusted-contributor";
- const experiencedLabel = "experienced-contributor";
- const trustedThreshold = 4;
- const experiencedThreshold = 10;
+ // const trustedLabel = "trusted-contributor";
+ // const experiencedLabel = "experienced-contributor";
+ // const trustedThreshold = 4;
+ // const experiencedThreshold = 10;
const contributorCache = new Map();
@@ -294,27 +466,28 @@ jobs:
return "maintainer";
}
- const mergedQuery = `repo:${repoFull} is:pr is:merged author:${login}`;
- let mergedCount = 0;
- try {
- const merged = await github.rest.search.issuesAndPullRequests({
- q: mergedQuery,
- per_page: 1,
- });
- mergedCount = merged?.data?.total_count ?? 0;
- } catch (error) {
- if (error?.status !== 422) {
- throw error;
- }
- core.warning(`Skipping merged search for ${login}; treating as 0.`);
- }
+ // trusted-contributor and experienced-contributor labels disabled.
+ // const mergedQuery = `repo:${repoFull} is:pr is:merged author:${login}`;
+ // let mergedCount = 0;
+ // try {
+ // const merged = await github.rest.search.issuesAndPullRequests({
+ // q: mergedQuery,
+ // per_page: 1,
+ // });
+ // mergedCount = merged?.data?.total_count ?? 0;
+ // } catch (error) {
+ // if (error?.status !== 422) {
+ // throw error;
+ // }
+ // core.warning(`Skipping merged search for ${login}; treating as 0.`);
+ // }
- let label = null;
- if (mergedCount >= experiencedThreshold) {
- label = experiencedLabel;
- } else if (mergedCount >= trustedThreshold) {
- label = trustedLabel;
- }
+ const label = null;
+ // if (mergedCount >= experiencedThreshold) {
+ // label = experiencedLabel;
+ // } else if (mergedCount >= trustedThreshold) {
+ // label = trustedLabel;
+ // }
contributorCache.set(login, label);
return label;
@@ -479,10 +652,10 @@ jobs:
}
const repo = `${context.repo.owner}/${context.repo.repo}`;
- const trustedLabel = "trusted-contributor";
- const experiencedLabel = "experienced-contributor";
- const trustedThreshold = 4;
- const experiencedThreshold = 10;
+ // const trustedLabel = "trusted-contributor";
+ // const experiencedLabel = "experienced-contributor";
+ // const trustedThreshold = 4;
+ // const experiencedThreshold = 10;
let isMaintainer = false;
try {
@@ -507,34 +680,35 @@ jobs:
return;
}
- const mergedQuery = `repo:${repo} is:pr is:merged author:${login}`;
- let mergedCount = 0;
- try {
- const merged = await github.rest.search.issuesAndPullRequests({
- q: mergedQuery,
- per_page: 1,
- });
- mergedCount = merged?.data?.total_count ?? 0;
- } catch (error) {
- if (error?.status !== 422) {
- throw error;
- }
- core.warning(`Skipping merged search for ${login}; treating as 0.`);
- }
-
- if (mergedCount >= experiencedThreshold) {
- await github.rest.issues.addLabels({
- ...context.repo,
- issue_number: context.payload.issue.number,
- labels: [experiencedLabel],
- });
- return;
- }
-
- if (mergedCount >= trustedThreshold) {
- await github.rest.issues.addLabels({
- ...context.repo,
- issue_number: context.payload.issue.number,
- labels: [trustedLabel],
- });
- }
+ // trusted-contributor and experienced-contributor labels disabled.
+ // const mergedQuery = `repo:${repo} is:pr is:merged author:${login}`;
+ // let mergedCount = 0;
+ // try {
+ // const merged = await github.rest.search.issuesAndPullRequests({
+ // q: mergedQuery,
+ // per_page: 1,
+ // });
+ // mergedCount = merged?.data?.total_count ?? 0;
+ // } catch (error) {
+ // if (error?.status !== 422) {
+ // throw error;
+ // }
+ // core.warning(`Skipping merged search for ${login}; treating as 0.`);
+ // }
+ //
+ // if (mergedCount >= experiencedThreshold) {
+ // await github.rest.issues.addLabels({
+ // ...context.repo,
+ // issue_number: context.payload.issue.number,
+ // labels: [experiencedLabel],
+ // });
+ // return;
+ // }
+ //
+ // if (mergedCount >= trustedThreshold) {
+ // await github.rest.issues.addLabels({
+ // ...context.repo,
+ // issue_number: context.payload.issue.number,
+ // labels: [trustedLabel],
+ // });
+ // }
diff --git a/.github/workflows/sandbox-common-smoke.yml b/.github/workflows/sandbox-common-smoke.yml
index 26c0dcc106f..13688bd0f25 100644
--- a/.github/workflows/sandbox-common-smoke.yml
+++ b/.github/workflows/sandbox-common-smoke.yml
@@ -26,6 +26,9 @@ jobs:
with:
submodules: false
+ - name: Set up Docker Builder
+ uses: useblacksmith/setup-docker-builder@v1
+
- name: Build minimal sandbox base (USER sandbox)
shell: bash
run: |
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 00000000000..e6feef90e6b
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,214 @@
+name: Stale
+
+on:
+ schedule:
+ - cron: "17 3 * * *"
+ workflow_dispatch:
+
+permissions: {}
+
+jobs:
+ stale:
+ permissions:
+ issues: write
+ pull-requests: write
+ runs-on: blacksmith-16vcpu-ubuntu-2404
+ steps:
+ - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
+ id: app-token
+ continue-on-error: true
+ with:
+ app-id: "2729701"
+ private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
+ - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
+ id: app-token-fallback
+ continue-on-error: true
+ with:
+ app-id: "2971289"
+ private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
+ - name: Mark stale issues and pull requests (primary)
+ id: stale-primary
+ continue-on-error: true
+ uses: actions/stale@v9
+ 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
+ stale-issue-label: stale
+ stale-pr-label: stale
+ exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
+ exempt-pr-labels: maintainer,no-stale
+ operations-per-run: 2000
+ ascending: true
+ exempt-all-assignees: true
+ remove-stale-when-updated: true
+ stale-issue-message: |
+ This issue has been automatically marked as stale due to inactivity.
+ Please add updates or it will be closed.
+ stale-pr-message: |
+ This pull request has been automatically marked as stale due to inactivity.
+ Please add updates or it will be closed.
+ close-issue-message: |
+ 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 repro steps.
+ close-issue-reason: not_planned
+ close-pr-message: |
+ Closing due to inactivity.
+ If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
+ That channel is the escape hatch for high-quality PRs that get auto-closed.
+ - name: Check stale state cache
+ id: stale-state
+ if: always()
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ with:
+ github-token: ${{ steps.app-token-fallback.outputs.token || steps.app-token.outputs.token }}
+ script: |
+ const cacheKey = "_state";
+ const { owner, repo } = context.repo;
+
+ try {
+ const { data } = await github.rest.actions.getActionsCacheList({
+ owner,
+ repo,
+ key: cacheKey,
+ });
+ const caches = data.actions_caches ?? [];
+ const hasState = caches.some(cache => cache.key === cacheKey);
+ core.setOutput("has_state", hasState ? "true" : "false");
+ } catch (error) {
+ const message = error instanceof Error ? error.message : String(error);
+ core.warning(`Failed to check stale state cache: ${message}`);
+ core.setOutput("has_state", "false");
+ }
+ - name: Mark stale issues and pull requests (fallback)
+ if: (steps.stale-primary.outcome == 'failure' || steps.stale-state.outputs.has_state == 'true') && steps.app-token-fallback.outputs.token != ''
+ uses: actions/stale@v9
+ 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
+ stale-issue-label: stale
+ stale-pr-label: stale
+ exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
+ exempt-pr-labels: maintainer,no-stale
+ operations-per-run: 2000
+ ascending: true
+ exempt-all-assignees: true
+ remove-stale-when-updated: true
+ stale-issue-message: |
+ This issue has been automatically marked as stale due to inactivity.
+ Please add updates or it will be closed.
+ stale-pr-message: |
+ This pull request has been automatically marked as stale due to inactivity.
+ Please add updates or it will be closed.
+ close-issue-message: |
+ 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 repro steps.
+ close-issue-reason: not_planned
+ close-pr-message: |
+ Closing due to inactivity.
+ If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
+ That channel is the escape hatch for high-quality PRs that get auto-closed.
+
+ lock-closed-issues:
+ permissions:
+ issues: write
+ runs-on: blacksmith-16vcpu-ubuntu-2404
+ steps:
+ - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
+ id: app-token
+ with:
+ app-id: "2729701"
+ private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
+ - name: Lock closed issues after 48h of no comments
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
+ with:
+ github-token: ${{ steps.app-token.outputs.token }}
+ script: |
+ const lockAfterHours = 48;
+ const lockAfterMs = lockAfterHours * 60 * 60 * 1000;
+ const perPage = 100;
+ const cutoffMs = Date.now() - lockAfterMs;
+ const { owner, repo } = context.repo;
+
+ let locked = 0;
+ let inspected = 0;
+
+ let page = 1;
+ while (true) {
+ const { data: issues } = await github.rest.issues.listForRepo({
+ owner,
+ repo,
+ state: "closed",
+ sort: "updated",
+ direction: "desc",
+ per_page: perPage,
+ page,
+ });
+
+ if (issues.length === 0) {
+ break;
+ }
+
+ for (const issue of issues) {
+ if (issue.pull_request) {
+ continue;
+ }
+ if (issue.locked) {
+ continue;
+ }
+ if (!issue.closed_at) {
+ continue;
+ }
+
+ inspected += 1;
+ const closedAtMs = Date.parse(issue.closed_at);
+ if (!Number.isFinite(closedAtMs)) {
+ continue;
+ }
+ if (closedAtMs > cutoffMs) {
+ continue;
+ }
+
+ let lastCommentMs = 0;
+ if (issue.comments > 0) {
+ const { data: comments } = await github.rest.issues.listComments({
+ owner,
+ repo,
+ issue_number: issue.number,
+ per_page: 1,
+ page: 1,
+ sort: "created",
+ direction: "desc",
+ });
+
+ if (comments.length > 0) {
+ lastCommentMs = Date.parse(comments[0].created_at);
+ }
+ }
+
+ const lastActivityMs = Math.max(closedAtMs, lastCommentMs || 0);
+ if (lastActivityMs > cutoffMs) {
+ continue;
+ }
+
+ await github.rest.issues.lock({
+ owner,
+ repo,
+ issue_number: issue.number,
+ lock_reason: "resolved",
+ });
+
+ locked += 1;
+ }
+
+ page += 1;
+ }
+
+ core.info(`Inspected ${inspected} closed issues; locked ${locked}.`);
diff --git a/.gitignore b/.gitignore
index b5d3257e7e6..29afb5e1261 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@ mise.toml
apps/android/.gradle/
apps/android/app/build/
apps/android/.cxx/
+apps/android/.kotlin/
# Bun build artifacts
*.bun-build
@@ -94,7 +95,7 @@ USER.md
!.agent/workflows/
/local/
package-lock.json
-.claude/settings.local.json
+.claude/
.agents/
.agents
.agent/
diff --git a/.pi/extensions/diff.ts b/.pi/extensions/diff.ts
index 037fa240afb..9f8e718e892 100644
--- a/.pi/extensions/diff.ts
+++ b/.pi/extensions/diff.ts
@@ -6,15 +6,7 @@
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { DynamicBorder } from "@mariozechner/pi-coding-agent";
-import {
- Container,
- Key,
- matchesKey,
- type SelectItem,
- SelectList,
- Text,
-} from "@mariozechner/pi-tui";
+import { showPagedSelectList } from "./ui/paged-select";
interface FileInfo {
status: string;
@@ -108,87 +100,17 @@ export default function (pi: ExtensionAPI) {
}
};
- // Show file picker with SelectList
- await ctx.ui.custom((tui, theme, _kb, done) => {
- const container = new Container();
-
- // Top border
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
-
- // Title
- container.addChild(new Text(theme.fg("accent", theme.bold(" Select file to diff")), 0, 0));
-
- // Build select items with colored status
- const items: SelectItem[] = files.map((f) => {
- let statusColor: string;
- switch (f.status) {
- case "M":
- statusColor = theme.fg("warning", f.status);
- break;
- case "A":
- statusColor = theme.fg("success", f.status);
- break;
- case "D":
- statusColor = theme.fg("error", f.status);
- break;
- case "?":
- statusColor = theme.fg("muted", f.status);
- break;
- default:
- statusColor = theme.fg("dim", f.status);
- }
- return {
- value: f,
- label: `${statusColor} ${f.file}`,
- };
- });
-
- const visibleRows = Math.min(files.length, 15);
- let currentIndex = 0;
-
- const selectList = new SelectList(items, visibleRows, {
- selectedPrefix: (t) => theme.fg("accent", t),
- selectedText: (t) => t, // Keep existing colors
- description: (t) => theme.fg("muted", t),
- scrollInfo: (t) => theme.fg("dim", t),
- noMatch: (t) => theme.fg("warning", t),
- });
- selectList.onSelect = (item) => {
+ const items = files.map((file) => ({
+ value: file,
+ label: `${file.status} ${file.file}`,
+ }));
+ await showPagedSelectList({
+ ctx,
+ title: " Select file to diff",
+ items,
+ onSelect: (item) => {
void openSelected(item.value as FileInfo);
- };
- selectList.onCancel = () => done();
- selectList.onSelectionChange = (item) => {
- currentIndex = items.indexOf(item);
- };
- container.addChild(selectList);
-
- // Help text
- container.addChild(
- new Text(theme.fg("dim", " ↑↓ navigate • ←→ page • enter open • esc close"), 0, 0),
- );
-
- // Bottom border
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
-
- return {
- render: (w) => container.render(w),
- invalidate: () => container.invalidate(),
- handleInput: (data) => {
- // Add paging with left/right
- if (matchesKey(data, Key.left)) {
- // Page up - clamp to 0
- currentIndex = Math.max(0, currentIndex - visibleRows);
- selectList.setSelectedIndex(currentIndex);
- } else if (matchesKey(data, Key.right)) {
- // Page down - clamp to last
- currentIndex = Math.min(items.length - 1, currentIndex + visibleRows);
- selectList.setSelectedIndex(currentIndex);
- } else {
- selectList.handleInput(data);
- }
- tui.requestRender();
- },
- };
+ },
});
},
});
diff --git a/.pi/extensions/files.ts b/.pi/extensions/files.ts
index bba2760d032..e1325303521 100644
--- a/.pi/extensions/files.ts
+++ b/.pi/extensions/files.ts
@@ -6,15 +6,7 @@
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { DynamicBorder } from "@mariozechner/pi-coding-agent";
-import {
- Container,
- Key,
- matchesKey,
- type SelectItem,
- SelectList,
- Text,
-} from "@mariozechner/pi-tui";
+import { showPagedSelectList } from "./ui/paged-select";
interface FileEntry {
path: string;
@@ -113,82 +105,30 @@ export default function (pi: ExtensionAPI) {
}
};
- // Show file picker with SelectList
- await ctx.ui.custom((tui, theme, _kb, done) => {
- const container = new Container();
-
- // Top border
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
-
- // Title
- container.addChild(new Text(theme.fg("accent", theme.bold(" Select file to open")), 0, 0));
-
- // Build select items with colored operations
- const items: SelectItem[] = files.map((f) => {
- const ops: string[] = [];
- if (f.operations.has("read")) {
- ops.push(theme.fg("muted", "R"));
- }
- if (f.operations.has("write")) {
- ops.push(theme.fg("success", "W"));
- }
- if (f.operations.has("edit")) {
- ops.push(theme.fg("warning", "E"));
- }
- const opsLabel = ops.join("");
- return {
- value: f,
- label: `${opsLabel} ${f.path}`,
- };
- });
-
- const visibleRows = Math.min(files.length, 15);
- let currentIndex = 0;
-
- const selectList = new SelectList(items, visibleRows, {
- selectedPrefix: (t) => theme.fg("accent", t),
- selectedText: (t) => t, // Keep existing colors
- description: (t) => theme.fg("muted", t),
- scrollInfo: (t) => theme.fg("dim", t),
- noMatch: (t) => theme.fg("warning", t),
- });
- selectList.onSelect = (item) => {
- void openSelected(item.value as FileEntry);
- };
- selectList.onCancel = () => done();
- selectList.onSelectionChange = (item) => {
- currentIndex = items.indexOf(item);
- };
- container.addChild(selectList);
-
- // Help text
- container.addChild(
- new Text(theme.fg("dim", " ↑↓ navigate • ←→ page • enter open • esc close"), 0, 0),
- );
-
- // Bottom border
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
-
+ const items = files.map((file) => {
+ const ops: string[] = [];
+ if (file.operations.has("read")) {
+ ops.push("R");
+ }
+ if (file.operations.has("write")) {
+ ops.push("W");
+ }
+ if (file.operations.has("edit")) {
+ ops.push("E");
+ }
return {
- render: (w) => container.render(w),
- invalidate: () => container.invalidate(),
- handleInput: (data) => {
- // Add paging with left/right
- if (matchesKey(data, Key.left)) {
- // Page up - clamp to 0
- currentIndex = Math.max(0, currentIndex - visibleRows);
- selectList.setSelectedIndex(currentIndex);
- } else if (matchesKey(data, Key.right)) {
- // Page down - clamp to last
- currentIndex = Math.min(items.length - 1, currentIndex + visibleRows);
- selectList.setSelectedIndex(currentIndex);
- } else {
- selectList.handleInput(data);
- }
- tui.requestRender();
- },
+ value: file,
+ label: `${ops.join("")} ${file.path}`,
};
});
+ await showPagedSelectList({
+ ctx,
+ title: " Select file to open",
+ items,
+ onSelect: (item) => {
+ void openSelected(item.value as FileEntry);
+ },
+ });
},
});
}
diff --git a/.pi/extensions/prompt-url-widget.ts b/.pi/extensions/prompt-url-widget.ts
index 2bb56b104ea..e39c7fd949b 100644
--- a/.pi/extensions/prompt-url-widget.ts
+++ b/.pi/extensions/prompt-url-widget.ts
@@ -114,6 +114,17 @@ export default function promptUrlWidgetExtension(pi: ExtensionAPI) {
}
};
+ const renderPromptMatch = (ctx: ExtensionContext, match: PromptMatch) => {
+ setWidget(ctx, match);
+ applySessionName(ctx, match);
+ void fetchGhMetadata(pi, match.kind, match.url).then((meta) => {
+ const title = meta?.title?.trim();
+ const authorText = formatAuthor(meta?.author);
+ setWidget(ctx, match, title, authorText);
+ applySessionName(ctx, match, title);
+ });
+ };
+
pi.on("before_agent_start", async (event, ctx) => {
if (!ctx.hasUI) {
return;
@@ -123,14 +134,7 @@ export default function promptUrlWidgetExtension(pi: ExtensionAPI) {
return;
}
- setWidget(ctx, match);
- applySessionName(ctx, match);
- void fetchGhMetadata(pi, match.kind, match.url).then((meta) => {
- const title = meta?.title?.trim();
- const authorText = formatAuthor(meta?.author);
- setWidget(ctx, match, title, authorText);
- applySessionName(ctx, match, title);
- });
+ renderPromptMatch(ctx, match);
});
pi.on("session_switch", async (_event, ctx) => {
@@ -177,14 +181,7 @@ export default function promptUrlWidgetExtension(pi: ExtensionAPI) {
return;
}
- setWidget(ctx, match);
- applySessionName(ctx, match);
- void fetchGhMetadata(pi, match.kind, match.url).then((meta) => {
- const title = meta?.title?.trim();
- const authorText = formatAuthor(meta?.author);
- setWidget(ctx, match, title, authorText);
- applySessionName(ctx, match, title);
- });
+ renderPromptMatch(ctx, match);
};
pi.on("session_start", async (_event, ctx) => {
diff --git a/.pi/extensions/ui/paged-select.ts b/.pi/extensions/ui/paged-select.ts
new file mode 100644
index 00000000000..a92db66bc68
--- /dev/null
+++ b/.pi/extensions/ui/paged-select.ts
@@ -0,0 +1,82 @@
+import { DynamicBorder } from "@mariozechner/pi-coding-agent";
+import {
+ Container,
+ Key,
+ matchesKey,
+ type SelectItem,
+ SelectList,
+ Text,
+} from "@mariozechner/pi-tui";
+
+type CustomUiContext = {
+ ui: {
+ custom: (
+ render: (
+ tui: { requestRender: () => void },
+ theme: {
+ fg: (tone: string, text: string) => string;
+ bold: (text: string) => string;
+ },
+ kb: unknown,
+ done: () => void,
+ ) => {
+ render: (width: number) => string;
+ invalidate: () => void;
+ handleInput: (data: string) => void;
+ },
+ ) => Promise;
+ };
+};
+
+export async function showPagedSelectList(params: {
+ ctx: CustomUiContext;
+ title: string;
+ items: SelectItem[];
+ onSelect: (item: SelectItem) => void;
+}): Promise {
+ await params.ctx.ui.custom((tui, theme, _kb, done) => {
+ const container = new Container();
+
+ container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
+ container.addChild(new Text(theme.fg("accent", theme.bold(params.title)), 0, 0));
+
+ const visibleRows = Math.min(params.items.length, 15);
+ let currentIndex = 0;
+
+ const selectList = new SelectList(params.items, visibleRows, {
+ selectedPrefix: (text) => theme.fg("accent", text),
+ selectedText: (text) => text,
+ description: (text) => theme.fg("muted", text),
+ scrollInfo: (text) => theme.fg("dim", text),
+ noMatch: (text) => theme.fg("warning", text),
+ });
+ selectList.onSelect = (item) => params.onSelect(item);
+ selectList.onCancel = () => done();
+ selectList.onSelectionChange = (item) => {
+ currentIndex = params.items.indexOf(item);
+ };
+ container.addChild(selectList);
+
+ container.addChild(
+ new Text(theme.fg("dim", " ↑↓ navigate • ←→ page • enter open • esc close"), 0, 0),
+ );
+ container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
+
+ return {
+ render: (width) => container.render(width),
+ invalidate: () => container.invalidate(),
+ handleInput: (data) => {
+ if (matchesKey(data, Key.left)) {
+ currentIndex = Math.max(0, currentIndex - visibleRows);
+ selectList.setSelectedIndex(currentIndex);
+ } else if (matchesKey(data, Key.right)) {
+ currentIndex = Math.min(params.items.length - 1, currentIndex + visibleRows);
+ selectList.setSelectedIndex(currentIndex);
+ } else {
+ selectList.handleInput(data);
+ }
+ tui.requestRender();
+ },
+ };
+ });
+}
diff --git a/.pi/prompts/landpr.md b/.pi/prompts/landpr.md
index 95e4692f3e5..2d0553a7336 100644
--- a/.pi/prompts/landpr.md
+++ b/.pi/prompts/landpr.md
@@ -9,7 +9,7 @@ Input
- If ambiguous: ask.
Do (end-to-end)
-Goal: PR must end in GitHub state = MERGED (never CLOSED). Use `gh pr merge` with `--rebase` or `--squash`.
+Goal: PR must end in GitHub state = MERGED (never CLOSED). Prefer `gh pr merge --squash`; use `--rebase` only when preserving commit history is required.
1. Assign PR to self:
- `gh pr edit --add-assignee @me`
@@ -37,8 +37,8 @@ Goal: PR must end in GitHub state = MERGED (never CLOSED). Use `gh pr merge` wit
- Implement fixes + add/adjust tests
- Update `CHANGELOG.md` and mention `#` + `@$contrib`
9. Decide merge strategy:
- - Rebase if we want to preserve commit history
- - Squash if we want a single clean commit
+ - Squash (preferred): use when we want a single clean commit
+ - Rebase: use only when we explicitly want to preserve commit history
- If unclear, ask
10. Full gate (BEFORE commit):
- `pnpm lint && pnpm build && pnpm test`
@@ -54,8 +54,8 @@ Goal: PR must end in GitHub state = MERGED (never CLOSED). Use `gh pr merge` wit
```
13. Merge PR (must show MERGED on GitHub):
- - Rebase: `gh pr merge --rebase`
- - Squash: `gh pr merge --squash`
+ - Squash (preferred): `gh pr merge --squash`
+ - Rebase (history-preserving fallback): `gh pr merge --rebase`
- Never `gh pr close` (closing is wrong)
14. Sync main:
- `git checkout main`
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 30b6363a34d..2f9d299a5b3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -30,7 +30,7 @@ repos:
- --baseline
- .secrets.baseline
- --exclude-files
- - '(^|/)(dist/|vendor/|pnpm-lock\.yaml$|\.detect-secrets\.cfg$)'
+ - '(^|/)pnpm-lock\.yaml$'
- --exclude-lines
- 'key_content\.include\?\("BEGIN PRIVATE KEY"\)'
- --exclude-lines
@@ -47,6 +47,32 @@ repos:
- '=== "string"'
- --exclude-lines
- 'typeof remote\?\.password === "string"'
+ - --exclude-lines
+ - "OPENCLAW_DOCKER_GPG_FINGERPRINT="
+ - --exclude-lines
+ - '"secretShape": "(secret_input|sibling_ref)"'
+ - --exclude-lines
+ - 'API key rotation \(provider-specific\): set `\*_API_KEYS`'
+ - --exclude-lines
+ - 'password: `OPENCLAW_GATEWAY_PASSWORD` -> `gateway\.auth\.password` -> `gateway\.remote\.password`'
+ - --exclude-lines
+ - 'password: `OPENCLAW_GATEWAY_PASSWORD` -> `gateway\.remote\.password` -> `gateway\.auth\.password`'
+ - --exclude-files
+ - '^src/gateway/client\.watchdog\.test\.ts$'
+ - --exclude-lines
+ - 'export CUSTOM_API_K[E]Y="your-key"'
+ - --exclude-lines
+ - 'grep -q ''N[O]DE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache'' ~/.bashrc \|\| cat >> ~/.bashrc <<''EOF'''
+ - --exclude-lines
+ - 'env: \{ MISTRAL_API_K[E]Y: "sk-\.\.\." \},'
+ - --exclude-lines
+ - '"ap[i]Key": "xxxxx"(,)?'
+ - --exclude-lines
+ - 'ap[i]Key: "A[I]za\.\.\.",'
+ - --exclude-lines
+ - '"ap[i]Key": "(resolved|normalized|legacy)-key"(,)?'
+ - --exclude-lines
+ - 'sparkle:edSignature="[A-Za-z0-9+/=]+"'
# Shell script linting
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.11.0
diff --git a/.secrets.baseline b/.secrets.baseline
index 089515fe250..b1f909e6ca4 100644
--- a/.secrets.baseline
+++ b/.secrets.baseline
@@ -128,7 +128,8 @@
{
"path": "detect_secrets.filters.regex.should_exclude_file",
"pattern": [
- "(^|/)pnpm-lock\\.yaml$"
+ "(^|/)pnpm-lock\\.yaml$",
+ "^src/gateway/client\\.watchdog\\.test\\.ts$"
]
},
{
@@ -141,8 +142,26 @@
"\"gateway\\.auth\\.password\"",
"\"talk\\.apiKey\"",
"=== \"string\"",
- "typeof remote\\?\\.password === \"string\""
+ "typeof remote\\?\\.password === \"string\"",
+ "OPENCLAW_DOCKER_GPG_FINGERPRINT=",
+ "\"secretShape\": \"(secret_input|sibling_ref)\"",
+ "API key rotation \\(provider-specific\\): set `\\*_API_KEYS`",
+ "password: `OPENCLAW_GATEWAY_PASSWORD` -> `gateway\\.auth\\.password` -> `gateway\\.remote\\.password`",
+ "password: `OPENCLAW_GATEWAY_PASSWORD` -> `gateway\\.remote\\.password` -> `gateway\\.auth\\.password`",
+ "export CUSTOM_API_K[E]Y=\"your-key\"",
+ "grep -q 'N[O]DE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc \\|\\| cat >> ~/.bashrc <<'EOF'",
+ "env: \\{ MISTRAL_API_K[E]Y: \"sk-\\.\\.\\.\" \\},",
+ "\"ap[i]Key\": \"xxxxx\"(,)?",
+ "ap[i]Key: \"A[I]za\\.\\.\\.\",",
+ "\"ap[i]Key\": \"(resolved|normalized|legacy)-key\"(,)?",
+ "sparkle:edSignature=\"[A-Za-z0-9+/=]+\""
]
+ },
+ {
+ "path": "src/gateway/client\\.watchdog\\.test\\.ts$",
+ "reason": "Allowlisted because this is a static PEM fixture used by the watchdog TLS fingerprint test.",
+ "min_level": 2,
+ "condition": "filename"
}
],
"results": {
@@ -152,37 +171,14 @@
"filename": ".detect-secrets.cfg",
"hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9",
"is_verified": false,
- "line_number": 17
+ "line_number": 13
},
{
"type": "Secret Keyword",
"filename": ".detect-secrets.cfg",
"hashed_secret": "fe88fceb47e040ba1bfafa4ac639366188df2f6d",
"is_verified": false,
- "line_number": 19
- }
- ],
- "appcast.xml": [
- {
- "type": "Base64 High Entropy String",
- "filename": "appcast.xml",
- "hashed_secret": "2bc43713edb8f775582c6314953b7c020d691aba",
- "is_verified": false,
- "line_number": 141
- },
- {
- "type": "Base64 High Entropy String",
- "filename": "appcast.xml",
- "hashed_secret": "2fcd83b35235522978c19dbbab2884a09aa64f35",
- "is_verified": false,
- "line_number": 209
- },
- {
- "type": "Base64 High Entropy String",
- "filename": "appcast.xml",
- "hashed_secret": "78b65f0952ed8a557e0f67b2364ff67cb6863bc8",
- "is_verified": false,
- "line_number": 310
+ "line_number": 15
}
],
"apps/android/app/src/test/java/ai/openclaw/android/node/AppUpdateHandlerTest.kt": [
@@ -194,22 +190,13 @@
"line_number": 58
}
],
- "apps/ios/Sources/Gateway/GatewaySettingsStore.swift": [
- {
- "type": "Secret Keyword",
- "filename": "apps/ios/Sources/Gateway/GatewaySettingsStore.swift",
- "hashed_secret": "5f7c0c35e552780b67fe1c0ee186764354793be3",
- "is_verified": false,
- "line_number": 28
- }
- ],
"apps/ios/Tests/DeepLinkParserTests.swift": [
{
"type": "Secret Keyword",
"filename": "apps/ios/Tests/DeepLinkParserTests.swift",
"hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073",
"is_verified": false,
- "line_number": 89
+ "line_number": 105
}
],
"apps/macos/Sources/OpenClawProtocol/GatewayModels.swift": [
@@ -218,7 +205,7 @@
"filename": "apps/macos/Sources/OpenClawProtocol/GatewayModels.swift",
"hashed_secret": "7990585255d25249fb1e6eac3d2bd6c37429b2cd",
"is_verified": false,
- "line_number": 1492
+ "line_number": 1763
}
],
"apps/macos/Tests/OpenClawIPCTests/AnthropicAuthResolverTests.swift": [
@@ -243,7 +230,7 @@
"filename": "apps/macos/Tests/OpenClawIPCTests/GatewayEndpointStoreTests.swift",
"hashed_secret": "19dad5cecb110281417d1db56b60e1b006d55bb4",
"is_verified": false,
- "line_number": 61
+ "line_number": 81
}
],
"apps/macos/Tests/OpenClawIPCTests/GatewayLaunchAgentManagerTests.swift": [
@@ -270,7 +257,7 @@
"filename": "apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayChannel.swift",
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
"is_verified": false,
- "line_number": 106
+ "line_number": 115
}
],
"apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift": [
@@ -279,7 +266,7 @@
"filename": "apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift",
"hashed_secret": "7990585255d25249fb1e6eac3d2bd6c37429b2cd",
"is_verified": false,
- "line_number": 1492
+ "line_number": 1763
}
],
"docs/.i18n/zh-CN.tm.jsonl": [
@@ -9618,7 +9605,7 @@
"filename": "docs/channels/feishu.md",
"hashed_secret": "186154712b2d5f6791d85b9a0987b98fa231779c",
"is_verified": false,
- "line_number": 435
+ "line_number": 499
}
],
"docs/channels/irc.md": [
@@ -9627,7 +9614,7 @@
"filename": "docs/channels/irc.md",
"hashed_secret": "d54831b8e4b461d85e32ea82156d2fb5ce5cb624",
"is_verified": false,
- "line_number": 191
+ "line_number": 198
}
],
"docs/channels/line.md": [
@@ -9636,7 +9623,7 @@
"filename": "docs/channels/line.md",
"hashed_secret": "83661b43df128631f891767fbfc5b049af3dce86",
"is_verified": false,
- "line_number": 61
+ "line_number": 65
}
],
"docs/channels/matrix.md": [
@@ -9697,21 +9684,21 @@
"filename": "docs/concepts/memory.md",
"hashed_secret": "39d711243bfcee9fec8299b204e1aa9c3430fa12",
"is_verified": false,
- "line_number": 281
+ "line_number": 301
},
{
"type": "Secret Keyword",
"filename": "docs/concepts/memory.md",
"hashed_secret": "1a8abbf465c52363ab4c9c6ad945b8e857cbea55",
"is_verified": false,
- "line_number": 305
+ "line_number": 325
},
{
"type": "Secret Keyword",
"filename": "docs/concepts/memory.md",
"hashed_secret": "b9f640d6095b9f6b5a65983f7b76dbbb254e0044",
"is_verified": false,
- "line_number": 706
+ "line_number": 726
}
],
"docs/concepts/model-providers.md": [
@@ -9720,21 +9707,21 @@
"filename": "docs/concepts/model-providers.md",
"hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
"is_verified": false,
- "line_number": 178
+ "line_number": 227
},
{
"type": "Secret Keyword",
"filename": "docs/concepts/model-providers.md",
"hashed_secret": "6a4a6c8f2406f4f0843a0a1aae6a320f92f9d6ae",
"is_verified": false,
- "line_number": 274
+ "line_number": 387
},
{
"type": "Secret Keyword",
"filename": "docs/concepts/model-providers.md",
"hashed_secret": "ef83ad68b9b66e008727b7c417c6a8f618b5177e",
"is_verified": false,
- "line_number": 305
+ "line_number": 418
}
],
"docs/gateway/configuration-examples.md": [
@@ -9757,21 +9744,21 @@
"filename": "docs/gateway/configuration-examples.md",
"hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27",
"is_verified": false,
- "line_number": 332
+ "line_number": 336
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-examples.md",
"hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
"is_verified": false,
- "line_number": 431
+ "line_number": 439
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-examples.md",
"hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
"is_verified": false,
- "line_number": 596
+ "line_number": 613
}
],
"docs/gateway/configuration-reference.md": [
@@ -9780,70 +9767,70 @@
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 149
+ "line_number": 199
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e",
"is_verified": false,
- "line_number": 1267
+ "line_number": 1614
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "bde4db9b4c3be4049adc3b9a69851d7c35119770",
"is_verified": false,
- "line_number": 1283
+ "line_number": 1630
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "7f8aaf142ce0552c260f2e546dda43ddd7c9aef3",
"is_verified": false,
- "line_number": 1461
+ "line_number": 1817
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27",
"is_verified": false,
- "line_number": 1603
+ "line_number": 1990
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
"is_verified": false,
- "line_number": 1631
+ "line_number": 2046
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
"is_verified": false,
- "line_number": 1862
+ "line_number": 2278
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6",
"is_verified": false,
- "line_number": 1966
+ "line_number": 2408
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
"is_verified": false,
- "line_number": 2202
+ "line_number": 2661
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration-reference.md",
"hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25",
"is_verified": false,
- "line_number": 2204
+ "line_number": 2663
}
],
"docs/gateway/configuration.md": [
@@ -9852,14 +9839,14 @@
"filename": "docs/gateway/configuration.md",
"hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
"is_verified": false,
- "line_number": 434
+ "line_number": 461
},
{
"type": "Secret Keyword",
"filename": "docs/gateway/configuration.md",
"hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25",
"is_verified": false,
- "line_number": 435
+ "line_number": 462
}
],
"docs/gateway/local-models.md": [
@@ -9884,7 +9871,7 @@
"filename": "docs/gateway/tailscale.md",
"hashed_secret": "9cb0dc5383312aa15b9dc6745645bde18ff5ade9",
"is_verified": false,
- "line_number": 81
+ "line_number": 86
}
],
"docs/help/environment.md": [
@@ -9909,35 +9896,35 @@
"filename": "docs/help/faq.md",
"hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac",
"is_verified": false,
- "line_number": 1412
+ "line_number": 1503
},
{
"type": "Secret Keyword",
"filename": "docs/help/faq.md",
"hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
"is_verified": false,
- "line_number": 1689
+ "line_number": 1780
},
{
"type": "Secret Keyword",
"filename": "docs/help/faq.md",
"hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25",
"is_verified": false,
- "line_number": 1690
+ "line_number": 1781
},
{
"type": "Secret Keyword",
"filename": "docs/help/faq.md",
"hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
"is_verified": false,
- "line_number": 2118
+ "line_number": 2209
},
{
"type": "Secret Keyword",
"filename": "docs/help/faq.md",
"hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6",
"is_verified": false,
- "line_number": 2398
+ "line_number": 2490
}
],
"docs/install/macos-vm.md": [
@@ -9964,7 +9951,7 @@
"filename": "docs/perplexity.md",
"hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8",
"is_verified": false,
- "line_number": 36
+ "line_number": 43
}
],
"docs/plugins/voice-call.md": [
@@ -9973,7 +9960,7 @@
"filename": "docs/plugins/voice-call.md",
"hashed_secret": "cb46980ce5532f18440dff4bbbe097896a8c08c8",
"is_verified": false,
- "line_number": 239
+ "line_number": 254
}
],
"docs/providers/anthropic.md": [
@@ -9991,7 +9978,7 @@
"filename": "docs/providers/claude-max-api-proxy.md",
"hashed_secret": "b5c2827eb65bf13b87130e7e3c424ba9ff07cd67",
"is_verified": false,
- "line_number": 80
+ "line_number": 86
}
],
"docs/providers/glm.md": [
@@ -10025,14 +10012,14 @@
"filename": "docs/providers/minimax.md",
"hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
"is_verified": false,
- "line_number": 71
+ "line_number": 69
},
{
"type": "Secret Keyword",
"filename": "docs/providers/minimax.md",
"hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
"is_verified": false,
- "line_number": 140
+ "line_number": 148
}
],
"docs/providers/moonshot.md": [
@@ -10041,7 +10028,7 @@
"filename": "docs/providers/moonshot.md",
"hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
"is_verified": false,
- "line_number": 43
+ "line_number": 49
}
],
"docs/providers/nvidia.md": [
@@ -10059,7 +10046,7 @@
"filename": "docs/providers/ollama.md",
"hashed_secret": "e774aaeac31c6272107ba89080295e277050fa7c",
"is_verified": false,
- "line_number": 33
+ "line_number": 37
}
],
"docs/providers/openai.md": [
@@ -10068,7 +10055,7 @@
"filename": "docs/providers/openai.md",
"hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
"is_verified": false,
- "line_number": 31
+ "line_number": 32
}
],
"docs/providers/opencode.md": [
@@ -10111,7 +10098,7 @@
"filename": "docs/providers/venice.md",
"hashed_secret": "c179fe46776696372a90218532dc0d67267f2f04",
"is_verified": false,
- "line_number": 236
+ "line_number": 251
}
],
"docs/providers/vllm.md": [
@@ -10154,7 +10141,7 @@
"filename": "docs/tools/browser.md",
"hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
"is_verified": false,
- "line_number": 140
+ "line_number": 149
}
],
"docs/tools/firecrawl.md": [
@@ -10172,7 +10159,7 @@
"filename": "docs/tools/skills-config.md",
"hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
"is_verified": false,
- "line_number": 29
+ "line_number": 31
}
],
"docs/tools/skills.md": [
@@ -10181,7 +10168,7 @@
"filename": "docs/tools/skills.md",
"hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
"is_verified": false,
- "line_number": 198
+ "line_number": 201
}
],
"docs/tools/web.md": [
@@ -10190,28 +10177,21 @@
"filename": "docs/tools/web.md",
"hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8",
"is_verified": false,
- "line_number": 62
- },
- {
- "type": "Secret Keyword",
- "filename": "docs/tools/web.md",
- "hashed_secret": "96c682c88ed551f22fe76d206c2dfb7df9221ad9",
- "is_verified": false,
- "line_number": 113
+ "line_number": 135
},
{
"type": "Secret Keyword",
"filename": "docs/tools/web.md",
"hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac",
"is_verified": false,
- "line_number": 161
+ "line_number": 228
},
{
"type": "Secret Keyword",
"filename": "docs/tools/web.md",
"hashed_secret": "674397e2c0c2faaa85961c708d2a96a7cc7af217",
"is_verified": false,
- "line_number": 235
+ "line_number": 332
}
],
"docs/tts.md": [
@@ -10227,7 +10207,7 @@
"filename": "docs/tts.md",
"hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e",
"is_verified": false,
- "line_number": 100
+ "line_number": 101
}
],
"docs/zh-CN/brave-search.md": [
@@ -10254,14 +10234,14 @@
"filename": "docs/zh-CN/channels/feishu.md",
"hashed_secret": "b60d121b438a380c343d5ec3c2037564b82ffef3",
"is_verified": false,
- "line_number": 195
+ "line_number": 191
},
{
"type": "Secret Keyword",
"filename": "docs/zh-CN/channels/feishu.md",
"hashed_secret": "186154712b2d5f6791d85b9a0987b98fa231779c",
"is_verified": false,
- "line_number": 445
+ "line_number": 505
}
],
"docs/zh-CN/channels/line.md": [
@@ -10806,37 +10786,37 @@
"filename": "extensions/bluebubbles/src/actions.test.ts",
"hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
"is_verified": false,
- "line_number": 86
+ "line_number": 54
}
],
"extensions/bluebubbles/src/attachments.test.ts": [
+ {
+ "type": "Secret Keyword",
+ "filename": "extensions/bluebubbles/src/attachments.test.ts",
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
+ "is_verified": false,
+ "line_number": 79
+ },
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/attachments.test.ts",
"hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
"is_verified": false,
- "line_number": 21
+ "line_number": 90
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/attachments.test.ts",
"hashed_secret": "db1530e1ea43af094d3d75b8dbaf19a4a182a318",
"is_verified": false,
- "line_number": 85
- },
- {
- "type": "Secret Keyword",
- "filename": "extensions/bluebubbles/src/attachments.test.ts",
- "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
- "is_verified": false,
- "line_number": 103
+ "line_number": 154
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/attachments.test.ts",
"hashed_secret": "052f076c732648ab32d2fcde9fe255319bfa0c7b",
"is_verified": false,
- "line_number": 215
+ "line_number": 260
}
],
"extensions/bluebubbles/src/chat.test.ts": [
@@ -10845,42 +10825,42 @@
"filename": "extensions/bluebubbles/src/chat.test.ts",
"hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
"is_verified": false,
- "line_number": 19
+ "line_number": 68
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/chat.test.ts",
"hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
"is_verified": false,
- "line_number": 54
+ "line_number": 93
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/chat.test.ts",
"hashed_secret": "5c5a15a8b0b3e154d77746945e563ba40100681b",
"is_verified": false,
- "line_number": 82
+ "line_number": 115
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/chat.test.ts",
"hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d",
"is_verified": false,
- "line_number": 131
+ "line_number": 158
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/chat.test.ts",
"hashed_secret": "4dcc26a1d99532846fedf1265df4f40f4e0005b8",
"is_verified": false,
- "line_number": 227
+ "line_number": 239
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/chat.test.ts",
"hashed_secret": "fd2a721f7be1ee3d691a011affcdb11d0ca365a8",
"is_verified": false,
- "line_number": 290
+ "line_number": 302
}
],
"extensions/bluebubbles/src/monitor.test.ts": [
@@ -10889,14 +10869,7 @@
"filename": "extensions/bluebubbles/src/monitor.test.ts",
"hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
"is_verified": false,
- "line_number": 278
- },
- {
- "type": "Secret Keyword",
- "filename": "extensions/bluebubbles/src/monitor.test.ts",
- "hashed_secret": "1ae0af3fe72b3ba394f9fa95a6cffc090d726c23",
- "is_verified": false,
- "line_number": 552
+ "line_number": 169
}
],
"extensions/bluebubbles/src/reactions.test.ts": [
@@ -10905,28 +10878,28 @@
"filename": "extensions/bluebubbles/src/reactions.test.ts",
"hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
"is_verified": false,
- "line_number": 37
+ "line_number": 35
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/reactions.test.ts",
"hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
"is_verified": false,
- "line_number": 178
+ "line_number": 192
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/reactions.test.ts",
"hashed_secret": "a4a05c9a6449eb9d6cdac81dd7edc49230e327e6",
"is_verified": false,
- "line_number": 209
+ "line_number": 223
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/reactions.test.ts",
"hashed_secret": "a2833da9f0a16f09994754d0a31749cecf8c8c77",
"is_verified": false,
- "line_number": 315
+ "line_number": 295
}
],
"extensions/bluebubbles/src/send.test.ts": [
@@ -10935,14 +10908,14 @@
"filename": "extensions/bluebubbles/src/send.test.ts",
"hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
"is_verified": false,
- "line_number": 55
+ "line_number": 79
},
{
"type": "Secret Keyword",
"filename": "extensions/bluebubbles/src/send.test.ts",
"hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d",
"is_verified": false,
- "line_number": 692
+ "line_number": 757
}
],
"extensions/bluebubbles/src/targets.test.ts": [
@@ -10951,16 +10924,7 @@
"filename": "extensions/bluebubbles/src/targets.test.ts",
"hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225",
"is_verified": false,
- "line_number": 61
- }
- ],
- "extensions/bluebubbles/src/targets.ts": [
- {
- "type": "Hex High Entropy String",
- "filename": "extensions/bluebubbles/src/targets.ts",
- "hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225",
- "is_verified": false,
- "line_number": 265
+ "line_number": 62
}
],
"extensions/copilot-proxy/index.ts": [
@@ -11005,7 +10969,7 @@
"filename": "extensions/feishu/src/docx.test.ts",
"hashed_secret": "f49922d511d666848f250663c4fca84074b856a8",
"is_verified": false,
- "line_number": 97
+ "line_number": 124
}
],
"extensions/feishu/src/media.test.ts": [
@@ -11014,7 +10978,7 @@
"filename": "extensions/feishu/src/media.test.ts",
"hashed_secret": "f49922d511d666848f250663c4fca84074b856a8",
"is_verified": false,
- "line_number": 45
+ "line_number": 76
}
],
"extensions/feishu/src/reply-dispatcher.test.ts": [
@@ -11023,7 +10987,7 @@
"filename": "extensions/feishu/src/reply-dispatcher.test.ts",
"hashed_secret": "f49922d511d666848f250663c4fca84074b856a8",
"is_verified": false,
- "line_number": 48
+ "line_number": 74
}
],
"extensions/google-antigravity-auth/index.ts": [
@@ -11041,7 +11005,7 @@
"filename": "extensions/google-gemini-cli-auth/oauth.test.ts",
"hashed_secret": "021343c1f561d7bcbc3b513df45cc3a6baf67b43",
"is_verified": false,
- "line_number": 30
+ "line_number": 43
}
],
"extensions/irc/src/accounts.ts": [
@@ -11050,7 +11014,7 @@
"filename": "extensions/irc/src/accounts.ts",
"hashed_secret": "920f8f5815b381ea692e9e7c2f7119f2b1aa620a",
"is_verified": false,
- "line_number": 19
+ "line_number": 23
}
],
"extensions/irc/src/client.test.ts": [
@@ -11075,7 +11039,7 @@
"filename": "extensions/line/src/channel.startup.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 103
+ "line_number": 94
}
],
"extensions/matrix/src/matrix/accounts.test.ts": [
@@ -11118,7 +11082,7 @@
"filename": "extensions/memory-lancedb/config.ts",
"hashed_secret": "ecb252044b5ea0f679ee78ec1a12904739e2904d",
"is_verified": false,
- "line_number": 101
+ "line_number": 105
}
],
"extensions/memory-lancedb/index.test.ts": [
@@ -11145,14 +11109,14 @@
"filename": "extensions/nextcloud-talk/src/accounts.ts",
"hashed_secret": "920f8f5815b381ea692e9e7c2f7119f2b1aa620a",
"is_verified": false,
- "line_number": 22
+ "line_number": 28
},
{
"type": "Secret Keyword",
"filename": "extensions/nextcloud-talk/src/accounts.ts",
"hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb",
"is_verified": false,
- "line_number": 151
+ "line_number": 147
}
],
"extensions/nextcloud-talk/src/channel.ts": [
@@ -11161,7 +11125,7 @@
"filename": "extensions/nextcloud-talk/src/channel.ts",
"hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb",
"is_verified": false,
- "line_number": 396
+ "line_number": 403
}
],
"extensions/nostr/README.md": [
@@ -11287,7 +11251,7 @@
"filename": "extensions/nostr/src/types.test.ts",
"hashed_secret": "3bee216ebc256d692260fc3adc765050508fef5e",
"is_verified": false,
- "line_number": 123
+ "line_number": 141
}
],
"extensions/open-prose/skills/prose/SKILL.md": [
@@ -11337,7 +11301,7 @@
"filename": "extensions/twitch/src/status.test.ts",
"hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511",
"is_verified": false,
- "line_number": 122
+ "line_number": 92
}
],
"extensions/voice-call/README.md": [
@@ -11355,7 +11319,7 @@
"filename": "extensions/voice-call/src/config.test.ts",
"hashed_secret": "62207a469ec2fdcfc7d66b04c2980ac1501acbf0",
"is_verified": false,
- "line_number": 129
+ "line_number": 44
}
],
"extensions/voice-call/src/providers/telnyx.test.ts": [
@@ -11376,15 +11340,6 @@
"line_number": 41
}
],
- "extensions/zalo/src/monitor.webhook.test.ts": [
- {
- "type": "Secret Keyword",
- "filename": "extensions/zalo/src/monitor.webhook.test.ts",
- "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
- "is_verified": false,
- "line_number": 40
- }
- ],
"skills/1password/references/cli-examples.md": [
{
"type": "Secret Keyword",
@@ -11496,7 +11451,7 @@
"filename": "src/agents/model-auth.ts",
"hashed_secret": "8956265d216d474a080edaa97880d37fc1386f33",
"is_verified": false,
- "line_number": 25
+ "line_number": 27
}
],
"src/agents/models-config.e2e-harness.ts": [
@@ -11505,7 +11460,7 @@
"filename": "src/agents/models-config.e2e-harness.ts",
"hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d",
"is_verified": false,
- "line_number": 110
+ "line_number": 157
}
],
"src/agents/models-config.fills-missing-provider-apikey-from-env-var.e2e.test.ts": [
@@ -11539,14 +11494,14 @@
"filename": "src/agents/models-config.providers.nvidia.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 13
+ "line_number": 14
},
{
"type": "Secret Keyword",
"filename": "src/agents/models-config.providers.nvidia.test.ts",
"hashed_secret": "be1a7be9d4d5af417882b267f4db6dddc08507bd",
"is_verified": false,
- "line_number": 27
+ "line_number": 23
}
],
"src/agents/models-config.providers.ollama.e2e.test.ts": [
@@ -11589,7 +11544,7 @@
"filename": "src/agents/openai-responses.reasoning-replay.test.ts",
"hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
"is_verified": false,
- "line_number": 55
+ "line_number": 92
}
],
"src/agents/pi-embedded-runner.e2e.test.ts": [
@@ -11598,14 +11553,7 @@
"filename": "src/agents/pi-embedded-runner.e2e.test.ts",
"hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
"is_verified": false,
- "line_number": 127
- },
- {
- "type": "Secret Keyword",
- "filename": "src/agents/pi-embedded-runner.e2e.test.ts",
- "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c",
- "is_verified": false,
- "line_number": 238
+ "line_number": 122
}
],
"src/agents/pi-embedded-runner/model.ts": [
@@ -11614,7 +11562,7 @@
"filename": "src/agents/pi-embedded-runner/model.ts",
"hashed_secret": "e774aaeac31c6272107ba89080295e277050fa7c",
"is_verified": false,
- "line_number": 118
+ "line_number": 279
}
],
"src/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.ts": [
@@ -11623,7 +11571,7 @@
"filename": "src/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 86
+ "line_number": 114
}
],
"src/agents/pi-tools.safe-bins.e2e.test.ts": [
@@ -11711,28 +11659,7 @@
"filename": "src/agents/tools/web-search.ts",
"hashed_secret": "dfba7aade0868074c2861c98e2a9a92f3178a51b",
"is_verified": false,
- "line_number": 97
- },
- {
- "type": "Secret Keyword",
- "filename": "src/agents/tools/web-search.ts",
- "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb",
- "is_verified": false,
- "line_number": 285
- },
- {
- "type": "Secret Keyword",
- "filename": "src/agents/tools/web-search.ts",
- "hashed_secret": "c4865ff9250aca23b0d98eb079dad70ebec1cced",
- "is_verified": false,
- "line_number": 295
- },
- {
- "type": "Secret Keyword",
- "filename": "src/agents/tools/web-search.ts",
- "hashed_secret": "527ee41f36386e85fa932ef09471ca017f3c95c8",
- "is_verified": false,
- "line_number": 298
+ "line_number": 292
}
],
"src/agents/tools/web-tools.enabled-defaults.e2e.test.ts": [
@@ -11798,7 +11725,7 @@
"filename": "src/auto-reply/status.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 36
+ "line_number": 37
}
],
"src/browser/bridge-server.auth.test.ts": [
@@ -11807,7 +11734,7 @@
"filename": "src/browser/bridge-server.auth.test.ts",
"hashed_secret": "6af3c121ed4a752936c297cddfb7b00394eabf10",
"is_verified": false,
- "line_number": 66
+ "line_number": 72
}
],
"src/browser/browser-utils.test.ts": [
@@ -11816,14 +11743,14 @@
"filename": "src/browser/browser-utils.test.ts",
"hashed_secret": "4e126c049580d66ca1549fa534d95a7263f27f46",
"is_verified": false,
- "line_number": 38
+ "line_number": 47
},
{
"type": "Basic Auth Credentials",
"filename": "src/browser/browser-utils.test.ts",
"hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
"is_verified": false,
- "line_number": 159
+ "line_number": 171
}
],
"src/browser/cdp.test.ts": [
@@ -11832,7 +11759,7 @@
"filename": "src/browser/cdp.test.ts",
"hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
"is_verified": false,
- "line_number": 186
+ "line_number": 318
}
],
"src/channels/plugins/plugins-channel.test.ts": [
@@ -11841,7 +11768,7 @@
"filename": "src/channels/plugins/plugins-channel.test.ts",
"hashed_secret": "99c962e8c62296bdc9a17f5caf91ce9bb4c7e0e6",
"is_verified": false,
- "line_number": 46
+ "line_number": 64
}
],
"src/cli/program.smoke.e2e.test.ts": [
@@ -11859,7 +11786,7 @@
"filename": "src/cli/update-cli.test.ts",
"hashed_secret": "e4f91dd323bac5bfc4f60a6e433787671dc2421d",
"is_verified": false,
- "line_number": 239
+ "line_number": 277
}
],
"src/commands/auth-choice.e2e.test.ts": [
@@ -11946,7 +11873,7 @@
"filename": "src/commands/doctor-memory-search.test.ts",
"hashed_secret": "2e07956ffc9bc4fd624064c40b7495c85d5f1467",
"is_verified": false,
- "line_number": 38
+ "line_number": 43
}
],
"src/commands/model-picker.e2e.test.ts": [
@@ -12001,14 +11928,14 @@
"filename": "src/commands/onboard-auth.config-minimax.ts",
"hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
"is_verified": false,
- "line_number": 36
+ "line_number": 37
},
{
"type": "Secret Keyword",
"filename": "src/commands/onboard-auth.config-minimax.ts",
"hashed_secret": "ddcb713196b974770575a9bea5a4e7d46361f8e9",
"is_verified": false,
- "line_number": 78
+ "line_number": 79
}
],
"src/commands/onboard-auth.e2e.test.ts": [
@@ -12107,7 +12034,7 @@
"filename": "src/commands/onboard-non-interactive/api-keys.ts",
"hashed_secret": "112f3a99b283a4e1788dedd8e0e5d35375c33747",
"is_verified": false,
- "line_number": 11
+ "line_number": 12
}
],
"src/commands/status.update.test.ts": [
@@ -12143,7 +12070,7 @@
"filename": "src/config/config-misc.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 62
+ "line_number": 102
}
],
"src/config/config.env-vars.test.ts": [
@@ -12152,21 +12079,21 @@
"filename": "src/config/config.env-vars.test.ts",
"hashed_secret": "a24ef9c1a27cac44823571ceef2e8262718eee36",
"is_verified": false,
- "line_number": 13
+ "line_number": 17
},
{
"type": "Secret Keyword",
"filename": "src/config/config.env-vars.test.ts",
"hashed_secret": "29d5f92e9ee44d4854d6dfaeefc3dc27d779fdf3",
"is_verified": false,
- "line_number": 19
+ "line_number": 23
},
{
"type": "Secret Keyword",
"filename": "src/config/config.env-vars.test.ts",
"hashed_secret": "1672b6a1e7956c6a70f45d699aa42a351b1f8b80",
"is_verified": false,
- "line_number": 27
+ "line_number": 31
}
],
"src/config/config.irc.test.ts": [
@@ -12193,14 +12120,14 @@
"filename": "src/config/env-preserve-io.test.ts",
"hashed_secret": "85639f0560fd9bf8704f52e01c5e764c9ed5a6aa",
"is_verified": false,
- "line_number": 59
+ "line_number": 31
},
{
"type": "Secret Keyword",
"filename": "src/config/env-preserve-io.test.ts",
"hashed_secret": "996650087ab48bdb1ca80f0842c97d4fbb6f1c71",
"is_verified": false,
- "line_number": 86
+ "line_number": 75
}
],
"src/config/env-preserve.test.ts": [
@@ -12239,28 +12166,28 @@
"filename": "src/config/env-substitution.test.ts",
"hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511",
"is_verified": false,
- "line_number": 37
+ "line_number": 85
},
{
"type": "Secret Keyword",
"filename": "src/config/env-substitution.test.ts",
"hashed_secret": "ec417f567082612f8fd6afafe1abcab831fca840",
"is_verified": false,
- "line_number": 68
+ "line_number": 105
},
{
"type": "Secret Keyword",
"filename": "src/config/env-substitution.test.ts",
"hashed_secret": "520bd69c3eb1646d9a78181ecb4c90c51fdf428d",
"is_verified": false,
- "line_number": 69
+ "line_number": 106
},
{
"type": "Secret Keyword",
"filename": "src/config/env-substitution.test.ts",
"hashed_secret": "f136444bf9b3d01a9f9b772b80ac6bf7b6a43ef0",
"is_verified": false,
- "line_number": 227
+ "line_number": 360
}
],
"src/config/io.write-config.test.ts": [
@@ -12269,7 +12196,7 @@
"filename": "src/config/io.write-config.test.ts",
"hashed_secret": "13951588fd3325e25ed1e3b116d7009fb221c85e",
"is_verified": false,
- "line_number": 65
+ "line_number": 289
}
],
"src/config/model-alias-defaults.test.ts": [
@@ -12278,107 +12205,107 @@
"filename": "src/config/model-alias-defaults.test.ts",
"hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
"is_verified": false,
- "line_number": 66
+ "line_number": 13
}
],
"src/config/redact-snapshot.test.ts": [
- {
- "type": "Base64 High Entropy String",
- "filename": "src/config/redact-snapshot.test.ts",
- "hashed_secret": "3732e17b2d11ed6c64fef02c341958007af154e7",
- "is_verified": false,
- "line_number": 77
- },
- {
- "type": "Secret Keyword",
- "filename": "src/config/redact-snapshot.test.ts",
- "hashed_secret": "3732e17b2d11ed6c64fef02c341958007af154e7",
- "is_verified": false,
- "line_number": 77
- },
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "7f413afd37447cd321d79286be0f58d7a9875d9b",
"is_verified": false,
- "line_number": 89
- },
- {
- "type": "Secret Keyword",
- "filename": "src/config/redact-snapshot.test.ts",
- "hashed_secret": "c21afa950dee2a70f3e0f6ffdfbc87f8edb90262",
- "is_verified": false,
- "line_number": 99
- },
- {
- "type": "Secret Keyword",
- "filename": "src/config/redact-snapshot.test.ts",
- "hashed_secret": "83a9937c6de261ffda22304834f30fe6c8f97926",
- "is_verified": false,
- "line_number": 110
- },
- {
- "type": "Secret Keyword",
- "filename": "src/config/redact-snapshot.test.ts",
- "hashed_secret": "87ac76dfc9cba93bead43c191e31bd099a97cc11",
- "is_verified": false,
- "line_number": 198
+ "line_number": 78
},
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "abb1aabcd0e49019c2873944a40671a80ccd64c7",
"is_verified": false,
- "line_number": 309
+ "line_number": 84
+ },
+ {
+ "type": "Secret Keyword",
+ "filename": "src/config/redact-snapshot.test.ts",
+ "hashed_secret": "83a9937c6de261ffda22304834f30fe6c8f97926",
+ "is_verified": false,
+ "line_number": 88
+ },
+ {
+ "type": "Secret Keyword",
+ "filename": "src/config/redact-snapshot.test.ts",
+ "hashed_secret": "c21afa950dee2a70f3e0f6ffdfbc87f8edb90262",
+ "is_verified": false,
+ "line_number": 91
+ },
+ {
+ "type": "Base64 High Entropy String",
+ "filename": "src/config/redact-snapshot.test.ts",
+ "hashed_secret": "3732e17b2d11ed6c64fef02c341958007af154e7",
+ "is_verified": false,
+ "line_number": 95
+ },
+ {
+ "type": "Secret Keyword",
+ "filename": "src/config/redact-snapshot.test.ts",
+ "hashed_secret": "3732e17b2d11ed6c64fef02c341958007af154e7",
+ "is_verified": false,
+ "line_number": 95
+ },
+ {
+ "type": "Secret Keyword",
+ "filename": "src/config/redact-snapshot.test.ts",
+ "hashed_secret": "87ac76dfc9cba93bead43c191e31bd099a97cc11",
+ "is_verified": false,
+ "line_number": 227
},
{
"type": "Base64 High Entropy String",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "8e22880b4e96bab354e1da6c91d2f58dabde3555",
"is_verified": false,
- "line_number": 321
+ "line_number": 397
},
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "8e22880b4e96bab354e1da6c91d2f58dabde3555",
"is_verified": false,
- "line_number": 321
+ "line_number": 397
},
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "a9c732e05044a08c760cce7f6d142cd0d35a19e5",
"is_verified": false,
- "line_number": 375
+ "line_number": 455
},
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "50843dd5651cfafbe7c5611c1eed195c63e6e3fd",
"is_verified": false,
- "line_number": 691
+ "line_number": 771
},
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "927e7cdedcb8f71af399a49fb90a381df8b8df28",
"is_verified": false,
- "line_number": 808
+ "line_number": 1007
},
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "1996cc327bd39dad69cd8feb24250dafd51e7c08",
"is_verified": false,
- "line_number": 814
+ "line_number": 1013
},
{
"type": "Secret Keyword",
"filename": "src/config/redact-snapshot.test.ts",
"hashed_secret": "a5c0a65a4fa8874a486aa5072671927ceba82a90",
"is_verified": false,
- "line_number": 838
+ "line_number": 1037
}
],
"src/config/schema.help.ts": [
@@ -12387,21 +12314,14 @@
"filename": "src/config/schema.help.ts",
"hashed_secret": "9f4cda226d3868676ac7f86f59e4190eb94bd208",
"is_verified": false,
- "line_number": 109
+ "line_number": 653
},
{
"type": "Secret Keyword",
"filename": "src/config/schema.help.ts",
"hashed_secret": "01822c8bbf6a8b136944b14182cb885100ec2eae",
"is_verified": false,
- "line_number": 130
- },
- {
- "type": "Secret Keyword",
- "filename": "src/config/schema.help.ts",
- "hashed_secret": "bb7dfd9746e660e4a4374951ec5938ef0e343255",
- "is_verified": false,
- "line_number": 187
+ "line_number": 686
}
],
"src/config/schema.irc.ts": [
@@ -12440,14 +12360,14 @@
"filename": "src/config/schema.labels.ts",
"hashed_secret": "e73c9fcad85cd4eecc74181ec4bdb31064d68439",
"is_verified": false,
- "line_number": 104
+ "line_number": 217
},
{
"type": "Secret Keyword",
"filename": "src/config/schema.labels.ts",
"hashed_secret": "2eda7cd978f39eebec3bf03e4410a40e14167fff",
"is_verified": false,
- "line_number": 145
+ "line_number": 326
}
],
"src/config/slack-http-config.test.ts": [
@@ -12483,7 +12403,7 @@
"filename": "src/gateway/auth-rate-limit.ts",
"hashed_secret": "76ed0a056aa77060de25754586440cff390791d0",
"is_verified": false,
- "line_number": 37
+ "line_number": 39
}
],
"src/gateway/auth.test.ts": [
@@ -12492,79 +12412,72 @@
"filename": "src/gateway/auth.test.ts",
"hashed_secret": "db5543cd7440bbdc4c5aaf8aa363715c31dd5a27",
"is_verified": false,
- "line_number": 32
+ "line_number": 96
},
{
"type": "Secret Keyword",
"filename": "src/gateway/auth.test.ts",
"hashed_secret": "d51f846285cbc6d1dd76677a0fd588c8df44e506",
"is_verified": false,
- "line_number": 48
+ "line_number": 113
},
{
"type": "Secret Keyword",
"filename": "src/gateway/auth.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 95
+ "line_number": 255
},
{
"type": "Secret Keyword",
"filename": "src/gateway/auth.test.ts",
"hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c",
"is_verified": false,
- "line_number": 103
+ "line_number": 263
}
],
"src/gateway/call.test.ts": [
+ {
+ "type": "Secret Keyword",
+ "filename": "src/gateway/call.test.ts",
+ "hashed_secret": "2e07956ffc9bc4fd624064c40b7495c85d5f1467",
+ "is_verified": false,
+ "line_number": 90
+ },
{
"type": "Secret Keyword",
"filename": "src/gateway/call.test.ts",
"hashed_secret": "db5543cd7440bbdc4c5aaf8aa363715c31dd5a27",
"is_verified": false,
- "line_number": 357
+ "line_number": 607
},
{
"type": "Secret Keyword",
"filename": "src/gateway/call.test.ts",
"hashed_secret": "de1c41e8ece73f5d5c259bb37eccb59a542b91dc",
"is_verified": false,
- "line_number": 361
+ "line_number": 611
},
{
"type": "Secret Keyword",
"filename": "src/gateway/call.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 398
+ "line_number": 683
},
{
"type": "Secret Keyword",
"filename": "src/gateway/call.test.ts",
"hashed_secret": "e493f561d90c6638c1f51c5a8a069c3b129b79ed",
"is_verified": false,
- "line_number": 408
- },
- {
- "type": "Secret Keyword",
- "filename": "src/gateway/call.test.ts",
- "hashed_secret": "2e07956ffc9bc4fd624064c40b7495c85d5f1467",
- "is_verified": false,
- "line_number": 413
+ "line_number": 690
},
{
"type": "Secret Keyword",
"filename": "src/gateway/call.test.ts",
"hashed_secret": "bddc29032de580fb53b3a9a0357dd409086db800",
"is_verified": false,
- "line_number": 426
- },
- {
- "type": "Secret Keyword",
- "filename": "src/gateway/call.test.ts",
- "hashed_secret": "6255675480f681df08c1704b7b3cd2c49917f0e2",
- "is_verified": false,
- "line_number": 463
+ "line_number": 704
}
],
"src/gateway/client.e2e.test.ts": [
@@ -12582,7 +12495,7 @@
"filename": "src/gateway/gateway-cli-backend.live.test.ts",
"hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825",
"is_verified": false,
- "line_number": 38
+ "line_number": 45
}
],
"src/gateway/gateway-models.profiles.live.test.ts": [
@@ -12591,7 +12504,7 @@
"filename": "src/gateway/gateway-models.profiles.live.test.ts",
"hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825",
"is_verified": false,
- "line_number": 242
+ "line_number": 384
}
],
"src/gateway/server-methods/skills.update.normalizes-api-key.test.ts": [
@@ -12609,7 +12522,7 @@
"filename": "src/gateway/server-methods/talk.ts",
"hashed_secret": "e478a5eeba4907d2f12a68761996b9de745d826d",
"is_verified": false,
- "line_number": 13
+ "line_number": 14
}
],
"src/gateway/server.auth.e2e.test.ts": [
@@ -12652,7 +12565,7 @@
"filename": "src/gateway/session-utils.test.ts",
"hashed_secret": "bb9a5d9483409d2c60b28268a0efcb93324d4cda",
"is_verified": false,
- "line_number": 280
+ "line_number": 563
}
],
"src/gateway/test-openai-responses-model.ts": [
@@ -12679,14 +12592,14 @@
"filename": "src/infra/env.test.ts",
"hashed_secret": "df98a117ddabf85991b9fe0e268214dc0e1254dc",
"is_verified": false,
- "line_number": 9
+ "line_number": 7
},
{
"type": "Secret Keyword",
"filename": "src/infra/env.test.ts",
"hashed_secret": "6d811dc1f59a55ca1a3d38b5042a062b9f79e8ec",
"is_verified": false,
- "line_number": 30
+ "line_number": 14
}
],
"src/infra/outbound/message-action-runner.test.ts": [
@@ -12695,14 +12608,14 @@
"filename": "src/infra/outbound/message-action-runner.test.ts",
"hashed_secret": "804ec071803318791b835cffd6e509c8d32239db",
"is_verified": false,
- "line_number": 129
+ "line_number": 180
},
{
"type": "Secret Keyword",
"filename": "src/infra/outbound/message-action-runner.test.ts",
"hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
"is_verified": false,
- "line_number": 435
+ "line_number": 529
}
],
"src/infra/outbound/outbound.test.ts": [
@@ -12711,7 +12624,7 @@
"filename": "src/infra/outbound/outbound.test.ts",
"hashed_secret": "804ec071803318791b835cffd6e509c8d32239db",
"is_verified": false,
- "line_number": 631
+ "line_number": 896
}
],
"src/infra/provider-usage.auth.normalizes-keys.test.ts": [
@@ -12720,21 +12633,21 @@
"filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts",
"hashed_secret": "45c7365e3b542cdb4fae6ec10c2ff149224d7656",
"is_verified": false,
- "line_number": 80
+ "line_number": 162
},
{
"type": "Secret Keyword",
"filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts",
"hashed_secret": "b67074884ab7ef7c7a8cd6a3da9565d96c792248",
"is_verified": false,
- "line_number": 81
+ "line_number": 163
},
{
"type": "Secret Keyword",
"filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts",
"hashed_secret": "d4d8027e64f9cf4180d3aecfe31ea409368022ee",
"is_verified": false,
- "line_number": 82
+ "line_number": 164
}
],
"src/infra/shell-env.test.ts": [
@@ -12743,21 +12656,21 @@
"filename": "src/infra/shell-env.test.ts",
"hashed_secret": "65c10dc3549fe07424148a8a4790a3341ecbc253",
"is_verified": false,
- "line_number": 26
+ "line_number": 133
},
{
"type": "Secret Keyword",
"filename": "src/infra/shell-env.test.ts",
"hashed_secret": "e013ffda590d2178607c16d11b1ea42f75ceb0e7",
"is_verified": false,
- "line_number": 58
+ "line_number": 165
},
{
"type": "Base64 High Entropy String",
"filename": "src/infra/shell-env.test.ts",
"hashed_secret": "be6ee9a6bf9f2dad84a5a67d6c0576a5bacc391e",
"is_verified": false,
- "line_number": 60
+ "line_number": 167
}
],
"src/line/accounts.test.ts": [
@@ -12789,7 +12702,7 @@
"filename": "src/line/bot-handlers.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 106
+ "line_number": 102
}
],
"src/line/bot-message-context.test.ts": [
@@ -12825,7 +12738,7 @@
"filename": "src/line/webhook.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 23
+ "line_number": 21
}
],
"src/logging/redact.test.ts": [
@@ -12873,7 +12786,7 @@
"filename": "src/media-understanding/providers/deepgram/audio.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 27
+ "line_number": 20
}
],
"src/media-understanding/providers/google/video.test.ts": [
@@ -12882,7 +12795,7 @@
"filename": "src/media-understanding/providers/google/video.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 64
+ "line_number": 56
}
],
"src/media-understanding/providers/openai/audio.test.ts": [
@@ -12891,7 +12804,7 @@
"filename": "src/media-understanding/providers/openai/audio.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 22
+ "line_number": 18
}
],
"src/media-understanding/runner.auto-audio.test.ts": [
@@ -12900,7 +12813,7 @@
"filename": "src/media-understanding/runner.auto-audio.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 40
+ "line_number": 23
}
],
"src/media-understanding/runner.deepgram.test.ts": [
@@ -12909,7 +12822,7 @@
"filename": "src/media-understanding/runner.deepgram.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 44
+ "line_number": 31
}
],
"src/memory/embeddings-voyage.test.ts": [
@@ -12918,14 +12831,14 @@
"filename": "src/memory/embeddings-voyage.test.ts",
"hashed_secret": "7c2020578bbe5e2e3f78d7f954eb2ad8ab5b0403",
"is_verified": false,
- "line_number": 33
+ "line_number": 24
},
{
"type": "Secret Keyword",
"filename": "src/memory/embeddings-voyage.test.ts",
"hashed_secret": "8afdb3da9b79c8957ae35978ea8f33fbc3bfdf60",
"is_verified": false,
- "line_number": 77
+ "line_number": 88
}
],
"src/memory/embeddings.test.ts": [
@@ -12934,21 +12847,21 @@
"filename": "src/memory/embeddings.test.ts",
"hashed_secret": "a47110e348a3063541fb1f1f640d635d457181a0",
"is_verified": false,
- "line_number": 45
+ "line_number": 47
},
{
"type": "Secret Keyword",
"filename": "src/memory/embeddings.test.ts",
"hashed_secret": "c734e47630dda71619c696d88381f06f7511bd78",
"is_verified": false,
- "line_number": 160
+ "line_number": 195
},
{
"type": "Secret Keyword",
"filename": "src/memory/embeddings.test.ts",
"hashed_secret": "56e1d57b8db262b08bc73c60ed08d8c92e59503f",
"is_verified": false,
- "line_number": 189
+ "line_number": 291
}
],
"src/pairing/pairing-store.ts": [
@@ -12957,7 +12870,7 @@
"filename": "src/pairing/pairing-store.ts",
"hashed_secret": "f8c6f1ff98c5ee78c27d34a3ca68f35ad79847af",
"is_verified": false,
- "line_number": 13
+ "line_number": 14
}
],
"src/pairing/setup-code.test.ts": [
@@ -12966,14 +12879,14 @@
"filename": "src/pairing/setup-code.test.ts",
"hashed_secret": "4914c103484773b5a8e18448b11919bb349cbff8",
"is_verified": false,
- "line_number": 22
+ "line_number": 31
},
{
"type": "Secret Keyword",
"filename": "src/pairing/setup-code.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 96
+ "line_number": 357
}
],
"src/security/audit.test.ts": [
@@ -12982,14 +12895,14 @@
"filename": "src/security/audit.test.ts",
"hashed_secret": "21f688ab56f76a99e5c6ed342291422f4e57e47f",
"is_verified": false,
- "line_number": 2063
+ "line_number": 3473
},
{
"type": "Secret Keyword",
"filename": "src/security/audit.test.ts",
"hashed_secret": "3dc927d80543dc0f643940b70d066bd4b4c4b78e",
"is_verified": false,
- "line_number": 2094
+ "line_number": 3486
}
],
"src/telegram/monitor.test.ts": [
@@ -12998,14 +12911,14 @@
"filename": "src/telegram/monitor.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 205
+ "line_number": 497
},
{
"type": "Secret Keyword",
"filename": "src/telegram/monitor.test.ts",
"hashed_secret": "5934c4d4a4fa5d66ddb3d3fc0bba84996c17a5b7",
"is_verified": false,
- "line_number": 233
+ "line_number": 688
}
],
"src/telegram/webhook.test.ts": [
@@ -13014,7 +12927,7 @@
"filename": "src/telegram/webhook.test.ts",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
- "line_number": 42
+ "line_number": 24
}
],
"src/tts/tts.test.ts": [
@@ -13023,35 +12936,35 @@
"filename": "src/tts/tts.test.ts",
"hashed_secret": "2e7a7ee14caebf378fc32d6cf6f557f347c96773",
"is_verified": false,
- "line_number": 36
+ "line_number": 37
},
{
"type": "Hex High Entropy String",
"filename": "src/tts/tts.test.ts",
"hashed_secret": "b214f706bb602c1cc2adc5c6165e73622305f4bb",
"is_verified": false,
- "line_number": 98
+ "line_number": 101
},
{
"type": "Secret Keyword",
"filename": "src/tts/tts.test.ts",
"hashed_secret": "75ddfb45216fe09680dfe70eda4f559a910c832c",
"is_verified": false,
- "line_number": 397
+ "line_number": 468
},
{
"type": "Secret Keyword",
"filename": "src/tts/tts.test.ts",
"hashed_secret": "e29af93630aa18cc3457cb5b13937b7ab7c99c9b",
"is_verified": false,
- "line_number": 413
+ "line_number": 478
},
{
"type": "Secret Keyword",
"filename": "src/tts/tts.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
- "line_number": 447
+ "line_number": 564
}
],
"src/tui/gateway-chat.test.ts": [
@@ -13060,7 +12973,7 @@
"filename": "src/tui/gateway-chat.test.ts",
"hashed_secret": "6255675480f681df08c1704b7b3cd2c49917f0e2",
"is_verified": false,
- "line_number": 85
+ "line_number": 121
}
],
"src/web/login.test.ts": [
@@ -13078,7 +12991,7 @@
"filename": "ui/src/i18n/locales/en.ts",
"hashed_secret": "de0ff6b974d6910aca8d6b830e1b761f076d8fe6",
"is_verified": false,
- "line_number": 60
+ "line_number": 61
}
],
"ui/src/i18n/locales/pt-BR.ts": [
@@ -13087,7 +13000,7 @@
"filename": "ui/src/i18n/locales/pt-BR.ts",
"hashed_secret": "ef7b6f95faca2d7d3a5aa5a6434c89530c6dd243",
"is_verified": false,
- "line_number": 60
+ "line_number": 61
}
],
"vendor/a2ui/README.md": [
@@ -13100,5 +13013,5 @@
}
]
},
- "generated_at": "2026-02-17T13:34:38Z"
+ "generated_at": "2026-03-09T08:37:13Z"
}
diff --git a/.swiftformat b/.swiftformat
index fd8c0e6315c..ab608a90178 100644
--- a/.swiftformat
+++ b/.swiftformat
@@ -48,4 +48,4 @@
--allman false
# Exclusions
---exclude .build,.swiftpm,DerivedData,node_modules,dist,coverage,xcuserdata,Peekaboo,Swabble,apps/android,apps/ios,apps/shared,apps/macos/Sources/MoltbotProtocol
+--exclude .build,.swiftpm,DerivedData,node_modules,dist,coverage,xcuserdata,Peekaboo,Swabble,apps/android,apps/ios,apps/shared,apps/macos/Sources/MoltbotProtocol,apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift
diff --git a/.swiftlint.yml b/.swiftlint.yml
index b5622880111..e4f925fdf20 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -19,6 +19,8 @@ excluded:
- "*.playground"
# Generated (protocol-gen-swift.ts)
- apps/macos/Sources/MoltbotProtocol/GatewayModels.swift
+ # Generated (generate-host-env-security-policy-swift.mjs)
+ - apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift
analyzer_rules:
- unused_declaration
diff --git a/AGENTS.md b/AGENTS.md
index a0eca723170..b70210cf8e3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -5,6 +5,9 @@
- GitHub issues/comments/PR comments: use literal multiline strings or `-F - <<'EOF'` (or $'...') for real newlines; never embed "\\n".
- GitHub comment footgun: never use `gh issue/pr comment -b "..."` when body contains backticks or shell chars. Always use single-quoted heredoc (`-F - <<'EOF'`) so no command substitution/escaping corruption.
- GitHub linking footgun: don’t wrap issue/PR refs like `#24643` in backticks when you want auto-linking. Use plain `#24643` (optionally add full URL).
+- PR landing comments: always make commit SHAs clickable with full commit links (both landed SHA + source SHA when present).
+- PR review conversations: if a bot leaves review conversations on your PR, address them and resolve those conversations yourself once fixed. Leave a conversation unresolved only when reviewer or maintainer judgment is still needed; do not leave bot-conversation cleanup to maintainers.
+- GitHub searching footgun: don't limit yourself to the first 500 issues or PRs when wanting to search all. Unless you're supposed to look at the most recent, keep going until you've reached the last page in the search
- Security advisory analysis: before triage/severity decisions, read `SECURITY.md` to align with OpenClaw's trust model and design boundaries.
## Project Structure & Module Organization
@@ -26,6 +29,7 @@
- Docs are hosted on Mintlify (docs.openclaw.ai).
- Internal doc links in `docs/**/*.md`: root-relative, no `.md`/`.mdx` (example: `[Config](/configuration)`).
- When working with documentation, read the mintlify skill.
+- For docs, UI copy, and picker lists, order services/providers alphabetically unless the section is explicitly describing runtime behavior (for example auto-detection or execution order).
- Section cross-references: use anchors on root-relative paths (example: `[Hooks](/configuration#hooks)`).
- Doc headings and anchors: avoid em dashes and apostrophes in headings because they break Mintlify anchor links.
- When Peter asks for links, reply with full `https://docs.openclaw.ai/...` URLs (not root-relative).
@@ -75,6 +79,8 @@
- Language: TypeScript (ESM). Prefer strict typing; avoid `any`.
- Formatting/linting via Oxlint and Oxfmt; run `pnpm check` before commits.
- Never add `@ts-nocheck` and do not disable `no-explicit-any`; fix root causes and update Oxlint/Oxfmt config only when required.
+- Dynamic import guardrail: do not mix `await import("x")` and static `import ... from "x"` for the same module in production code paths. If you need lazy loading, create a dedicated `*.runtime.ts` boundary (that re-exports from `x`) and dynamically import that boundary from lazy callers only.
+- Dynamic import verification: after refactors that touch lazy-loading/module boundaries, run `pnpm build` and check for `[INEFFECTIVE_DYNAMIC_IMPORT]` warnings before submitting.
- Never share class behavior via prototype mutation (`applyPrototypeMixins`, `Object.defineProperty` on `.prototype`, or exporting `Class.prototype` for merges). Use explicit inheritance/composition (`A extends B extends C`) or helper composition so TypeScript can typecheck.
- If this pattern is needed, stop and get explicit approval before shipping; default behavior is to split/refactor into an explicit class hierarchy and keep members strongly typed.
- In tests, prefer per-instance stubs over prototype mutation (`SomeClass.prototype.method = ...`) unless a test explicitly documents why prototype-level patching is required.
@@ -100,6 +106,8 @@
- Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
- Full kit + what’s covered: `docs/testing.md`.
- Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process).
+- Changelog placement: in the active version block, append new entries to the end of the target section (`### Changes` or `### Fixes`); do not insert new entries at the top of a section.
+- Changelog attribution: use at most one contributor mention per line; prefer `Thanks @author` and do not also add `by @author` on the same entry.
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available.
@@ -107,6 +115,7 @@
**Full maintainer PR workflow (optional):** If you want the repo's end-to-end maintainer workflow (triage order, quality bar, rebase rules, commit/changelog conventions, co-contributor policy, and the `review-pr` > `prepare-pr` > `merge-pr` pipeline), see `.agents/skills/PR_WORKFLOW.md`. Maintainers may use other workflows; when a maintainer specifies a workflow, follow that. If no workflow is specified, default to PR_WORKFLOW.
+- `/landpr` lives in the global Codex prompts (`~/.codex/prompts/landpr.md`); when landing or merging any PR, always follow that `/landpr` process.
- Create commits with `scripts/committer "" `; avoid manual `git add`/`git commit` so staging stays scoped.
- Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`).
- Group related changes; avoid bundling unrelated refactors.
@@ -213,6 +222,7 @@
## NPM + 1Password (publish/verify)
- Use the 1password skill; all `op` commands must run inside a fresh tmux session.
+- Correct 1Password path for npm release auth: `op://Private/Npmjs` (use that item; OTP stays `op://Private/Npmjs/one-time password?attribute=otp`).
- Sign in: `eval "$(op signin --account my.1password.com)"` (app unlocked + integration on).
- OTP: `op read 'op://Private/Npmjs/one-time password?attribute=otp'`.
- Publish: `npm publish --access public --otp=""` (run from the package dir).
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d19abc6835e..40fafa21920 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,97 +2,710 @@
Docs: https://docs.openclaw.ai
-## 2026.3.1
-
-### Changes
-
-- Android/Nodes parity: add `system.notify`, `photos.latest`, `contacts.search`/`contacts.add`, `calendar.events`/`calendar.add`, and `motion.activity`/`motion.pedometer`, with motion sensor-aware command gating and improved activity sampling reliability. (#29398) Thanks @obviyus.
-- Android/Nodes: add `camera.list`, `device.permissions`, `device.health`, and `notifications.actions` (`open`/`dismiss`/`reply`) on Android nodes, plus first-class node-tool actions for the new device/notification commands. (#28260) Thanks @obviyus.
-- Discord/Thread bindings: replace fixed TTL lifecycle with inactivity (`idleHours`, default 24h) plus optional hard `maxAgeHours` lifecycle controls, and add `/session idle` + `/session max-age` commands for focused thread-bound sessions. (#27845) Thanks @osolmaz.
-- Web UI/i18n: add German (`de`) locale support and auto-render language options from supported locale constants in Overview settings. (#28495) thanks @dsantoreis.
-- Web UI/Cron i18n: localize cron page labels, filters, form help text, and validation/error messaging in English and zh-CN. (#29315) Thanks @BUGKillerKing.
-- Android/Gateway capability refresh: add live Android capability integration coverage and node canvas capability refresh wiring, plus runtime hardening for A2UI readiness retries, scoped canvas URL normalization, debug diagnostics JSON, and JavaScript MIME delivery. (#28388) Thanks @obviyus.
-- Feishu/Docx tables + uploads: add `feishu_doc` actions for Docx table creation/cell writing (`create_table`, `write_table_cells`, `create_table_with_values`) and image/file uploads (`upload_image`, `upload_file`) with stricter create/upload error handling for missing `document_id` and placeholder cleanup failures. (#20304) Thanks @xuhao1.
-- Feishu/Reactions: add inbound `im.message.reaction.created_v1` handling, route verified reactions through synthetic inbound turns, and harden verification with timeout + fail-closed filtering so non-bot or unverified reactions are dropped. (#16716) Thanks @schumilin.
-- Feishu/Chat tooling: add `feishu_chat` tool actions for chat info and member queries, with configurable enablement under `channels.feishu.tools.chat`. (#14674) Thanks @liuweifly.
-- Feishu/Doc permissions: support optional owner permission grant fields on `feishu_doc` create and report permission metadata only when the grant call succeeds, with regression coverage for success/failure/omitted-owner paths. (#28295) Thanks @zhoulongchao77.
-- Memory/LanceDB: support custom OpenAI `baseUrl` and embedding dimensions for LanceDB memory. (#17874) Thanks @rish2jain and @vincentkoc.
-
-### Fixes
-
-- Agents/Thinking fallback: when providers reject unsupported thinking levels without enumerating alternatives, retry with `think=off` to avoid hard failure during model/provider fallback chains. Landed from contributor PR #31002 by @yfge. Thanks @yfge.
-- Agents/Failover reason classification: avoid false rate-limit classification from incidental `tpm` substrings by matching TPM as a standalone token/phrase and keeping auth-context errors on the auth path. Landed from contributor PR #31007 by @HOYALIM. Thanks @HOYALIM.
-- Slack/Announce target account routing: enable session-backed announce-target lookup for Slack so multi-account announces resolve the correct `accountId` instead of defaulting to bot-token context. Landed from contributor PR #31028 by @taw0002. Thanks @taw0002.
-- Tools/Edit workspace boundary errors: preserve the real `Path escapes workspace root` failure path instead of surfacing a misleading access/file-not-found error when editing outside workspace roots. Landed from contributor PR #31015 by @haosenwang1018. Thanks @haosenwang1018.
-- Auto-reply/NO_REPLY: strip `NO_REPLY` token from mixed-content messages instead of leaking raw control text to end users. Landed from contributor PR #31080 by @scoootscooob. Thanks @scoootscooob.
-- Sandbox/mkdirp boundary checks: allow directory-safe boundary validation for existing in-boundary subdirectories, preventing false `cannot create directories` failures in sandbox write mode. (#30610) Thanks @glitch418x.
-- Android/Voice screen TTS: stream assistant speech via ElevenLabs WebSocket in Talk Mode, stop cleanly on speaker mute/barge-in, and ignore stale out-of-order stream events. (#29521) Thanks @gregmousseau.
-- Web UI/Cron: include configured agent model defaults/fallbacks in cron model suggestions so scheduled-job model autocomplete reflects configured models. (#29709) Thanks @Sid-Qin.
-- Cron/Delivery: disable the agent messaging tool when `delivery.mode` is `"none"` so cron output is not sent to Telegram or other channels. (#21808) Thanks @lailoo.
-- CLI/Cron: clarify `cron list` output by renaming `Agent` to `Agent ID` and adding a `Model` column for isolated agent-turn jobs. (#26259) Thanks @openperf.
-- Feishu/Reply media attachments: send Feishu reply `mediaUrl`/`mediaUrls` payloads as attachments alongside text/streamed replies in the reply dispatcher, including legacy fallback when `mediaUrls` is empty. (#28959) Thanks @icesword0760.
-- Slack/User-token resolution: normalize Slack account user-token sourcing through resolved account metadata (`SLACK_USER_TOKEN` env + config) so monitor reads, Slack actions, directory lookups, onboarding allow-from resolution, and capabilities probing consistently use the effective user token. (#28103) Thanks @Glucksberg.
-- Agents/Sessions list transcript paths: handle missing/non-string/relative `sessions.list.path` values and per-agent `{agentId}` templates when deriving `transcriptPath`, so cross-agent session listings resolve to concrete agent session files instead of workspace-relative paths. (#24775) Thanks @martinfrancois.
-- Feishu/Reaction notifications: add `channels.feishu.reactionNotifications` (`off | own | all`, default `own`) so operators can disable reaction ingress or allow all verified reaction events (not only bot-authored message reactions). (#28529) Thanks @cowboy129.
-- Feishu/Outbound session routing: stop assuming bare `oc_` identifiers are always group chats, honor explicit `dm:`/`group:` prefixes for `oc_` chat IDs, and default ambiguous bare `oc_` targets to direct routing to avoid DM session misclassification. (#10407) Thanks @Bermudarat.
-- Feishu/Group session routing: add configurable group session scopes (`group`, `group_sender`, `group_topic`, `group_topic_sender`) with legacy `topicSessionMode=enabled` compatibility so Feishu group conversations can isolate sessions by sender/topic as configured. (#17798) Thanks @yfge.
-- Feishu/Reply-in-thread routing: add `replyInThread` config (`disabled|enabled`) for group replies, propagate `reply_in_thread` across text/card/media/streaming sends, and align topic-scoped session routing so newly created reply threads stay on the same session root. (#27325) Thanks @kcinzgg.
-- Feishu/Typing backoff: re-throw Feishu typing add/remove rate-limit and quota errors (`429`, `99991400`, `99991403`) and detect SDK non-throwing backoff responses so the typing keepalive circuit breaker can stop retries instead of looping indefinitely. (#28494) Thanks @guoqunabc.
-- Feishu/Zalo runtime logging: replace direct `console.log/error` usage in Feishu typing-indicator paths and Zalo monitor paths with runtime-gated logger calls so verbosity controls are respected while preserving typing backoff behavior. (#18841) Thanks @Clawborn.
-- Feishu/Probe status caching: cache successful `probeFeishu()` bot-info results for 10 minutes (bounded cache with per-account keying) to reduce repeated status/onboarding probe API calls, while bypassing cache for failures and exceptions. (#28907) Thanks @Glucksberg.
-- Feishu/Opus media send type: send `.opus` attachments with `msg_type: "audio"` (instead of `"media"`) so Feishu voice messages deliver correctly while `.mp4` remains `msg_type: "media"` and documents remain `msg_type: "file"`. (#28269) Thanks @Glucksberg.
-- Feishu/Mobile video media type: treat inbound `message_type: "media"` as video-equivalent for media key extraction, placeholder inference, and media download resolution so mobile-app video sends ingest correctly. (#25502) Thanks @4ier.
-- Feishu/Inbound sender fallback: fall back to `sender_id.user_id` when `sender_id.open_id` is missing on inbound events, and use ID-type-aware sender lookup so mobile-delivered messages keep stable sender identity/routing. (#26703) Thanks @NewdlDewdl.
-- Feishu/Inbound rich-text parsing: preserve `share_chat` payload summaries when available and add explicit parsing for rich-text `code`/`code_block`/`pre` tags so forwarded and code-heavy messages keep useful context in agent input. (#28591) Thanks @kevinWangSheng.
-- Feishu/Post markdown parsing: parse rich-text `post` payloads through a shared markdown-aware parser with locale-wrapper support, preserved mention/image metadata extraction, and inline/fenced code fidelity for agent input rendering. (#12755) Thanks @WilsonLiu95.
-- Feishu/Reply context metadata: include inbound `parent_id` and `root_id` as `ReplyToId`/`RootMessageId` in inbound context, and parse interactive-card quote bodies into readable text when fetching replied messages. (#18529) Thanks @qiangu.
-- Feishu/Post embedded media: extract `media` tags from inbound rich-text (`post`) messages and download embedded video/audio files alongside existing embedded-image handling, with regression coverage. (#21786) Thanks @laopuhuluwa.
-- Feishu/Local media sends: propagate `mediaLocalRoots` through Feishu outbound media sending into `loadWebMedia` so local path attachments work with post-CVE local-root enforcement. (#27884) Thanks @joelnishanth.
-- Feishu/Group sender allowlist fallback: add global `channels.feishu.groupSenderAllowFrom` sender authorization for group chats, with per-group `groups..allowFrom` precedence and regression coverage for allow/block/precedence behavior. (#29174) Thanks @1MoreBuild.
-- Feishu/Group wildcard policy fallback: honor `channels.feishu.groups["*"]` when no explicit group match exists so unmatched groups inherit wildcard reply-policy settings instead of falling back to global defaults. (#29456) Thanks @WaynePika.
-- Feishu/Docx append/write ordering: insert converted Docx blocks sequentially (single-block creates) so Feishu append/write preserves markdown block order instead of returning shuffled sections in asynchronous batch inserts. (#26172, #26022) Thanks @echoVic.
-- Feishu/Docx convert fallback chunking: recursively split oversized markdown chunks (including long no-heading sections) when `document.convert` hits content limits, while keeping fenced-code-aware split boundaries whenever possible. (#14402) Thanks @lml2468.
-- Feishu/Inbound media regression coverage: add explicit tests for message resource type mapping (`image` stays `image`, non-image maps to `file`) to prevent reintroducing unsupported Feishu `type=audio` fetches. (#16311, #8746) Thanks @Yaxuan42.
-- Feishu/API quota controls: add `typingIndicator` and `resolveSenderNames` config flags (top-level and per-account) so operators can disable typing reactions and sender-name lookup requests while keeping default behavior unchanged. (#10513) Thanks @BigUncle.
-- TTS/Voice bubbles: use opus output and enable `audioAsVoice` routing for Feishu and WhatsApp (in addition to Telegram) so supported channels receive voice-bubble playback instead of file-style audio attachments. (#27366) Thanks @smthfoxy.
-- Security/Feishu webhook ingress: bound unauthenticated webhook rate-limit state with stale-window pruning and a hard key cap to prevent unbounded pre-auth memory growth from rotating source keys. (#26050) Thanks @bmendonca3.
-- Security/Compaction audit: remove the post-compaction audit injection message. (#28507) Thanks @fuller-stack-dev and @vincentkoc.
-- Telegram/Reply media context: include replied media files in inbound context when replying to media, defer reply-media downloads to debounce flush, gate reply-media fetch behind DM authorization, and preserve replied media when non-vision sticker fallback runs (including cached-sticker paths). (#28488) Thanks @obviyus.
-- Telegram/Outbound chunking: route oversize splitting through the shared outbound pipeline (including subagents), retry Telegram sends when escaped HTML exceeds limits, and preserve boundary whitespace when retry re-splitting rendered chunks so plain-text/transcript fidelity is retained. (#29342, #27317; follow-up to #27461) Thanks @obviyus.
-- Gateway/WS: close repeated post-handshake `unauthorized role:*` request floods per connection and sample duplicate rejection logs, preventing a single misbehaving client from degrading gateway responsiveness. (#20168) Thanks @acy103, @vibecodooor, and @vincentkoc.
-- Gateway/macOS supervised restart: actively `launchctl kickstart -k` during intentional supervised restarts to bypass LaunchAgent `ThrottleInterval` delays, and fall back to in-process restart when kickstart fails. Landed from contributor PR #29078 by @cathrynlavery. Thanks @cathrynlavery.
-- Gateway/Auth: improve device-auth v2 migration diagnostics so operators get clearer guidance when legacy clients connect. (#28305) Thanks @vincentkoc.
-- Gateway/Control UI CSP: allow required Google Fonts origins in Control UI CSP. (#29279) Thanks @Glucksberg and @vincentkoc.
-- CLI/Install: add an npm-link fallback to fix CLI startup `Permission denied` failures (`exit 127`) on affected installs. (#17151) Thanks @sskyu and @vincentkoc.
-- CLI/Ollama config: allow `config set` for Ollama `apiKey` without predeclared provider config. (#29299) Thanks @vincentkoc.
-- Onboarding/Custom providers: improve verification reliability for slower local endpoints (for example Ollama) during setup. (#27380) Thanks @Sid-Qin.
-- Ollama/Autodiscovery: harden autodiscovery and warning behavior. (#29201) Thanks @marcodelpin and @vincentkoc.
-- Ollama/Context window: unify context window handling across discovery, merge, and OpenAI-compatible transport paths. (#29205) Thanks @Sid-Qin, @jimmielightner, and @vincentkoc.
-- Agents/Ollama: demote empty-discovery logging from `warn` to `debug` to reduce noisy warnings in normal edge-case discovery flows. (#26379) Thanks @byungsker.
-- Install/npm: fix npm global install deprecation warnings. (#28318) Thanks @vincentkoc.
-- fix(model): preserve reasoning in provider fallback resolution. (#29285) Fixes #25636. Thanks @vincentkoc.
-- Browser/Open & navigate: accept `url` as an alias parameter for `open` and `navigate`. (#29260) Thanks @vincentkoc.
-- Sandbox/Browser Docker: pass `OPENCLAW_BROWSER_NO_SANDBOX=1` to sandbox browser containers and bump sandbox browser security hash epoch so existing containers are recreated and pick up the env on upgrade. (#29879) Thanks @Lukavyi.
-- Codex/Usage window: label weekly usage window as `Week` instead of `Day`. (#26267) Thanks @Sid-Qin.
-- Slack/Native commands: register Slack native status as `/agentstatus` (Slack-reserved `/status`) so manifest slash command registration stays valid while text `/status` still works. Landed from contributor PR #29032 by @maloqab. Thanks @maloqab.
-- Android/Nodes reliability: reject `facing=both` when `deviceId` is set to avoid mislabeled duplicate captures, allow notification `open`/`reply` on non-clearable entries while still gating dismiss, trigger listener rebind before notification actions, and scale invoke-result ack timeout to invoke budget for large clip payloads. (#28260) Thanks @obviyus.
-- Android/Camera clip: remove `camera.clip` HTTP-upload fallback to base64 so clip transport is deterministic and fail-loud, and reject non-positive `maxWidth` values so invalid inputs fall back to the safe resize default. (#28229) Thanks @obviyus.
-- Android/Nodes notification wake flow: enable Android `system.notify` default allowlist, emit `notifications.changed` events for posted/removed notifications (excluding OpenClaw app-owned notifications), canonicalize notification session keys before enqueue/wake routing, and skip heartbeat wakes when consecutive notification summaries dedupe. (#29440) Thanks @obviyus.
-- Android/Gateway canvas capability refresh: send `node.canvas.capability.refresh` with object `params` (`{}`) from Android node runtime so gateway object-schema validation accepts refresh retries and A2UI host recovery works after scoped capability expiry. (#28413) Thanks @obviyus.
-- Daemon/macOS TLS certs: default LaunchAgent service env `NODE_EXTRA_CA_CERTS` to `/etc/ssl/cert.pem` (while preserving explicit overrides) so HTTPS clients no longer fail with local-issuer errors under launchd. (#27915) Thanks @Lukavyi.
-- Docker/Image permissions: normalize `/app/extensions`, `/app/.agent`, and `/app/.agents` to directory mode `755` and file mode `644` during image build so plugin discovery does not block inherited world-writable paths. (#30191) Fixes #30139. Thanks @edincampara.
-- Update/Global npm: fallback to `--omit=optional` when global `npm update` fails so optional dependency install failures no longer abort update flows. (#24896) Thanks @xinhuagu and @vincentkoc.
-- Plugins/NPM spec install: fix npm-spec plugin installs when `npm pack` output is empty by detecting newly created `.tgz` archives in the pack directory. (#21039) Thanks @graysurf and @vincentkoc.
-- Plugins/Install: clear stale install errors when an npm package is not found so follow-up install attempts report current state correctly. (#25073) Thanks @dalefrieswthat.
-- OpenAI Responses/Compaction: rewrite and unify the OpenAI Responses store patches to treat empty `baseUrl` as non-direct, honor `compat.supportsStore=false`, and auto-inject server-side compaction `context_management` for compatible direct OpenAI models (with per-model opt-out/threshold overrides). Landed from contributor PRs #16930 (@OiPunk), #22441 (@EdwardWu7), and #25088 (@MoerAI). Thanks @OiPunk, @EdwardWu7, and @MoerAI.
-
## Unreleased
### Changes
-- ACP/ACPX streaming: pin ACPX plugin support to `0.1.15`, add configurable ACPX command/version probing, and streamline ACP stream delivery (`final_only` default + reduced tool-event noise) with matching runtime and test updates. (#30036) Thanks @osolmaz.
-- Cron/Heartbeat light bootstrap context: add opt-in lightweight bootstrap mode for automation runs (`--light-context` for cron agent turns and `agents.*.heartbeat.lightContext` for heartbeat), keeping only `HEARTBEAT.md` for heartbeat runs and skipping bootstrap-file injection for cron lightweight runs. (#26064) Thanks @jose-velez.
+- 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.
+
+### Breaking
+
+- Cron/doctor: tighten isolated cron delivery so cron jobs can no longer notify through ad hoc agent sends or fallback main-session summaries, and add `openclaw doctor --fix` migration for legacy cron storage and legacy notify/webhook delivery metadata. (#40998) Thanks @mbelinky.
+
+### Fixes
+
+- macOS/LaunchAgent install: tighten LaunchAgent directory and plist permissions during install so launchd bootstrap does not fail when the target home path or generated plist inherited group/world-writable modes.
+- Gateway/Control UI: keep dashboard auth tokens in session-scoped browser storage so same-tab refreshes preserve remote token auth without restoring long-lived localStorage token persistence, while scoping tokens to the selected gateway URL and fragment-only bootstrap flow. (#40892) thanks @velvet-shark.
+- 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.
+- Context engine/tests: add bundled-registry regression coverage for cross-chunk resolution, plugin-sdk re-exports, and concurrent chunk registration. (#40460) thanks @dsantoreis.
+- Agents/embedded runner: bound compaction retry waiting and drain embedded runs during SIGUSR1 restart so session lanes recover instead of staying blocked behind compaction. (#40324) thanks @cgdusek.
+- ACP/sessions.patch: allow `spawnedBy` and `spawnDepth` lineage fields on ACP session keys so `sessions_spawn` with `runtime: "acp"` no longer fails during child-session setup. Fixes #40971. (#40995) thanks @xaeon2026.
+- ACP/stop reason mapping: resolve gateway chat `state: "error"` completions as ACP `end_turn` instead of `refusal` so transient backend failures are not surfaced as deliberate refusals. (#41187) thanks @pejmanjohn.
+- ACP/setSessionMode: propagate gateway `sessions.patch` failures back to ACP clients so rejected mode changes no longer return silent success. (#41185) thanks @pejmanjohn.
+- Agents/embedded logs: add structured, sanitized lifecycle and failover observation events so overload and provider failures are easier to tail and filter. (#41336) thanks @altaywtf.
+- iOS/gateway foreground recovery: reconnect immediately on foreground return after stale background sockets are torn down, so the app no longer stays disconnected until a later wake path happens. (#41384) Thanks @mbelinky.
+- Cron/subagent followup: do not misclassify empty or `NO_REPLY` cron responses as interim acknowledgements that need a rerun, so deliberately silent cron jobs are no longer retried. (#41383) thanks @jackal092927.
+- Auth/cooldowns: reset expired auth-profile cooldown error counters before computing the next backoff so stale on-disk counters do not re-escalate into long cooldown loops after expiry. (#41028) thanks @zerone0x.
+- Gateway/node pending drain followup: keep `hasMore` true when the deferred baseline status item still needs delivery, and avoid allocating empty pending-work state for drain-only nodes with no queued work. (#41429) Thanks @mbelinky.
+- ACP/bridge mode: reject unsupported per-session MCP server setup and propagate rejected session-mode changes so IDE clients see explicit bridge limitations instead of silent success. (#41424) Thanks @mbelinky.
+- ACP/session UX: replay stored user and assistant text on `loadSession`, expose Gateway-backed session controls and metadata, and emit approximate session usage updates so IDE clients restore context more faithfully. (#41425) Thanks @mbelinky.
+- ACP/tool streaming: enrich `tool_call` and `tool_call_update` events with best-effort text content and file-location hints so IDE clients can follow bridge tool activity more naturally. (#41442) Thanks @mbelinky.
+- ACP/runtime attachments: forward normalized inbound image attachments into ACP runtime turns so ACPX sessions can preserve image prompt content on the runtime path. (#41427) Thanks @mbelinky.
+- ACP/regressions: add gateway RPC coverage for ACP lineage patching, ACPX runtime coverage for image prompt serialization, and an operator smoke-test procedure for live ACP spawn verification. (#41456) Thanks @mbelinky.
+- Agents/billing recovery: probe single-provider billing cooldowns on the existing throttle so topping up credits can recover without a manual gateway restart. (#41422) thanks @altaywtf.
+- ACP/follow-up hardening: make session restore and prompt completion degrade gracefully on transcript/update failures, enforce bounded tool-location traversal, and skip non-image ACPX turns the runtime cannot serialize. (#41464) Thanks @mbelinky.
+- Agents/fallback observability: add structured, sanitized model-fallback decision and auth-profile failure-state events with correlated run IDs so cooldown probes and failover paths are easier to trace in logs. (#41337) thanks @altaywtf.
+- Protocol/Swift model sync: regenerate pending node work Swift bindings after the landed `node.pending.*` schema additions so generated protocol artifacts are consistent again. (#41477) Thanks @mbelinky.
+- Discord/reply chunking: resolve the effective `maxLinesPerMessage` config across live reply paths and preserve `chunkMode` in the fast send path so long Discord replies no longer split unexpectedly at the default 17-line limit. (#40133) thanks @rbutera.
+- Logging/probe observations: suppress structured embedded and model-fallback probe warnings on the console without hiding error or fatal output. (#41338) thanks @altaywtf.
+
+## 2026.3.8
+
+### Changes
+
+- CLI/backup: add `openclaw backup create` and `openclaw backup verify` for local state archives, including `--only-config`, `--no-include-workspace`, manifest/payload validation, and backup guidance in destructive flows. (#40163) thanks @shichangs.
+- macOS/onboarding: add a remote gateway token field for remote mode, preserve existing non-plaintext `gateway.remote.token` config values until explicitly replaced, and warn when the loaded token shape cannot be used directly from the macOS app. (#40187, supersedes #34614) Thanks @cgdusek.
+- Talk mode: add top-level `talk.silenceTimeoutMs` config so Talk waits a configurable amount of silence before auto-sending the current transcript, while keeping each platform's existing default pause window when unset. (#39607) Thanks @danodoesdesign. Fixes #17147.
+- TUI: infer the active agent from the current workspace when launched inside a configured agent workspace, while preserving explicit `agent:` session targets. (#39591) thanks @arceus77-7.
+- Tools/Brave web search: add opt-in `tools.web.search.brave.mode: "llm-context"` so `web_search` can call Brave's LLM Context endpoint and return extracted grounding snippets with source metadata, plus config/docs/test coverage. (#33383) Thanks @thirumaleshp.
+- CLI/install: include the short git commit hash in `openclaw --version` output when metadata is available, and keep installer version checks compatible with the decorated format. (#39712) thanks @sourman.
+- CLI/backup: improve archive naming for date sorting, add config-only backup mode, and harden backup planning, publication, and verification edge cases. (#40163) Thanks @gumadeiras.
+- ACP/Provenance: add optional ACP ingress provenance metadata and visible receipt injection (`openclaw acp --provenance off|meta|meta+receipt`) so OpenClaw agents can retain and report ACP-origin context with session trace IDs. (#40473) thanks @mbelinky.
+- Tools/web search: alphabetize provider ordering across runtime selection, onboarding/configure pickers, and config metadata, so provider lists stay neutral and multi-key auto-detect now prefers Grok before Kimi. (#40259) thanks @kesku.
+- Docs/Web search: restore $5/month free-credit details, replace defunct "Data for Search"/"Data for AI" plan names with current "Search" plan, and note legacy subscription validity in Brave setup docs. Follows up on #26860. (#40111) Thanks @remusao.
+- Extensions/ACPX tests: move the shared runtime fixture helper from `src/runtime-internals/` to `src/test-utils/` so the test-only helper no longer looks like shipped runtime code.
+
+### Fixes
+
+- Update/macOS launchd restart: re-enable disabled LaunchAgent services before updater bootstrap so `openclaw update` can recover from a disabled gateway service instead of leaving the restart step stuck.
+- macOS app/chat UI: route browser proxy through the local node browser service, preserve plain-text paste semantics, strip completed assistant trace/debug wrapper noise from transcripts, refresh permission state after returning from System Settings, and tolerate malformed cron rows in the macOS tab. (#39516) Thanks @Imhermes1.
+- Android/Play distribution: remove self-update, background location, `screen.record`, and background mic capture from the Android app, narrow the foreground service to `dataSync` only, and clean up the legacy `location.enabledMode=always` preference migration. (#39660) Thanks @obviyus.
+- Telegram/DM routing: dedupe inbound Telegram DMs per agent instead of per session key so the same DM cannot trigger duplicate replies when both `agent:main:main` and `agent:main:telegram:direct:` resolve for one agent. Fixes #40005. Supersedes #40116. (#40519) thanks @obviyus.
+- Cron/Telegram announce delivery: route text-only announce jobs through the real outbound adapters after finalizing descendant output so plain Telegram targets no longer report `delivered: true` when no message actually reached Telegram. (#40575) thanks @obviyus.
+- Matrix/DM routing: add safer fallback detection for broken `m.direct` homeservers, honor explicit room bindings over DM classification, and preserve room-bound agent selection for Matrix DM rooms. (#19736) Thanks @derbronko.
+- Feishu/plugin onboarding: clear the short-lived plugin discovery cache before reloading the registry after installing a channel plugin, so onboarding no longer re-prompts to download Feishu immediately after a successful install. Fixes #39642. (#39752) Thanks @GazeKingNuWu.
+- Plugins/channel onboarding: prefer bundled channel plugins over duplicate npm-installed copies during onboarding and release-channel sync, preventing bundled plugins from being shadowed by npm installs with the same plugin ID. (#40092)
+- Config/runtime snapshots: keep secrets-runtime-resolved config and auth-profile snapshots intact after config writes so follow-up reads still see file-backed secret values while picking up the persisted config update. (#37313) thanks @bbblending.
+- Gateway/Control UI: resolve bundled dashboard assets through symlinked global wrappers and auto-detected package roots, while keeping configured and custom roots on the strict hardlink boundary. (#40385) Thanks @LarytheLord.
+- Browser/extension relay: add `browser.relayBindHost` so the Chrome relay can bind to an explicit non-loopback address for WSL2 and other cross-namespace setups, while preserving loopback-only defaults. (#39364) Thanks @mvanhorn.
+- Browser/CDP: normalize loopback direct WebSocket CDP URLs back to HTTP(S) for `/json/*` tab operations so local `ws://` / `wss://` profiles can still list, focus, open, and close tabs after the new direct-WS support lands. (#31085) Thanks @shrey150.
+- Browser/CDP: rewrite wildcard `ws://0.0.0.0` and `ws://[::]` debugger URLs from remote `/json/version` responses back to the external CDP host/port, fixing Browserless-style container endpoints. (#17760) Thanks @joeharouni.
+- Browser/extension relay: wait briefly for a previously attached Chrome tab to reappear after transient relay drops before failing with `tab not found`, reducing noisy reconnect flakes. (#32461) Thanks @AaronWander.
+- macOS/Tailscale gateway discovery: keep Tailscale Serve probing alive when other remote gateways are already discovered, prefer direct transport for resolved `.ts.net` and Tailscale Serve gateways, and set `TERM=dumb` for GUI-launched Tailscale CLI discovery. (#40167) thanks @ngutman.
+- TUI/theme: detect light terminal backgrounds via `COLORFGBG` and pick a WCAG AA-compliant light palette, with `OPENCLAW_THEME=light|dark` override for terminals without auto-detection. (#38636) Thanks @ademczuk and @vincentkoc.
+- Agents/openai-codex: normalize `gpt-5.4` fallback transport back to `openai-codex-responses` on `chatgpt.com/backend-api` when config drifts to the generic OpenAI responses endpoint. (#38736) Thanks @0xsline.
+- Models/openai-codex GPT-5.4 forward-compat: use the GPT-5.4 1,050,000-token context window and 128,000 max tokens for `openai-codex/gpt-5.4` instead of inheriting stale legacy Codex limits in resolver fallbacks and model listing. (#37876) thanks @yuweuii.
+- Tools/web search: restore Perplexity OpenRouter/Sonar compatibility for legacy `OPENROUTER_API_KEY`, `sk-or-...`, and explicit `perplexity.baseUrl` / `model` setups while keeping direct Perplexity keys on the native Search API path. (#39937) Thanks @obviyus.
+- Agents/failover: detect Amazon Bedrock `Too many tokens per day` quota errors as rate limits across fallback, cron retry, and memory embeddings while keeping context-window `too many tokens per request` errors out of the rate-limit lane. (#39377) Thanks @gambletan.
+- Mattermost replies: keep `root_id` pinned to the existing thread root when an agent replies inside a thread, while still using reply-target threading for top-level posts. (#27744) thanks @hnykda.
+- Telegram/DM partial streaming: keep DM preview lanes on real message edits instead of native draft materialization so final replies no longer flash a second duplicate copy before collapsing back to one.
+- macOS overlays: fix VoiceWake, Talk, and Notify overlay exclusivity crashes by removing shared `inout` visibility mutation from `OverlayPanelFactory.present`, and add a repeated Talk overlay smoke test. (#39275, #39321) Thanks @fellanH.
+- macOS Talk Mode: set the speech recognition request `taskHint` to `.dictation` for mic capture, and add regression coverage for the request defaults. (#38445) Thanks @dmiv.
+- macOS release packaging: default `scripts/package-mac-app.sh` to universal binaries for `BUILD_CONFIG=release`, and clarify that `scripts/package-mac-dist.sh` already produces the release zip + DMG. (#33891) Thanks @cgdusek.
+- Hooks/session-memory: keep `/new` and `/reset` memory artifacts in the bound agent workspace and align saved reset session keys with that workspace when stale main-agent keys leak into the hook path. (#39875) thanks @rbutera.
+- Sessions/model switch: clear stale cached `contextTokens` when a session changes models so status and runtime paths recompute against the active model window. (#38044) thanks @yuweuii.
+- ACP/session history: persist transcripts for successful ACP child runs, preserve exact transcript text, record ACP spawned-session lineage, and keep spawn-time transcript-path persistence best-effort so history storage failures do not block execution. (#40137) thanks @mbelinky.
+- Docs/browser: add a layered WSL2 + Windows remote Chrome CDP troubleshooting guide, including Control UI origin pitfalls and extension-relay bind-address guidance. (#39407) Thanks @Owlock.
+- Context engine registry/bundled builds: share the registry state through a `globalThis` singleton so duplicated bundled module copies can resolve engines registered by each other at runtime, with regression coverage for duplicate-module imports. (#40115) thanks @jalehman.
+- Podman/setup: fix `cannot chdir: Permission denied` in `run_as_user` when `setup-podman.sh` is invoked from a directory the target user cannot access, by wrapping user-switch calls in a subshell that cd's to `/tmp` with `/` fallback. (#39435) Thanks @langdon and @jlcbk.
+- Podman/SELinux: auto-detect SELinux enforcing/permissive mode and add `:Z` relabel to bind mounts in `run-openclaw-podman.sh` and the Quadlet template, fixing `EACCES` on Fedora/RHEL hosts. Supports `OPENCLAW_BIND_MOUNT_OPTIONS` override. (#39449) Thanks @langdon and @githubbzxs.
+- Agents/context-engine plugins: bootstrap runtime plugins once at embedded-run, compaction, and subagent boundaries so plugin-provided context engines and hooks load from the active workspace before runtime resolution. (#40232)
+- Docs/Changelog: correct the contributor credit for the bundled Control UI global-install fix to @LarytheLord. (#40420) Thanks @velvet-shark.
+- Telegram/media downloads: time out only stalled body reads so polling recovers from hung file downloads without aborting slow downloads that are still streaming data. (#40098) thanks @tysoncung.
+- Docker/runtime image: prune dev dependencies, strip build-only dist metadata for smaller Docker images. (#40307) Thanks @vincentkoc.
+- Gateway/restart timeout recovery: exit non-zero when restart-triggered shutdown drains time out so launchd/systemd restart the gateway instead of treating the failed restart as a clean stop. Landed from contributor PR #40380 by @dsantoreis. Thanks @dsantoreis.
+- Gateway/config restart guard: validate config before service start/restart and keep post-SIGUSR1 startup failures from crashing the gateway process, reducing invalid-config restart loops and macOS permission loss. Landed from contributor PR #38699 by @lml2468. Thanks @lml2468.
+- Gateway/launchd respawn detection: treat `XPC_SERVICE_NAME` as a launchd supervision hint so macOS restarts exit cleanly under launchd instead of attempting detached self-respawn. Landed from contributor PR #20555 by @dimat. Thanks @dimat.
+- Telegram/poll restart cleanup: abort the in-flight Telegram API fetch when shutdown or forced polling restarts stop a runner, preventing stale `getUpdates` long polls from colliding with the replacement runner. Landed from contributor PR #23950 by @Gkinthecodeland. Thanks @Gkinthecodeland.
+- Cron/restart catch-up staggering: limit immediate missed-job replay on startup and reschedule the deferred remainder from the post-catchup clock so restart bursts do not starve the gateway or silently skip overdue recurring jobs. Landed from contributor PR #18925 by @rexlunae. Thanks @rexlunae.
+- Cron/owner-only tools: pass trusted isolated cron runs into the embedded agent with owner context so `cron`/`gateway` tooling remains available after the owner-auth hardening narrowed direct-message ownership inference.
+- Browser/SSRF: block private-network intermediate redirect hops in strict browser navigation flows and fail closed when remote tab-open paths cannot inspect redirect chains. Thanks @zpbrent.
+- MS Teams/authz: keep `groupPolicy: "allowlist"` enforcing sender allowlists even when a team/channel route allowlist is configured, so route matches no longer widen group access to every sender in that route. Thanks @zpbrent.
+- Security/system.run: bind approved `bun` and `deno run` script operands to on-disk file snapshots so post-approval script rewrites are denied before execution.
+- Skills/download installs: pin the validated per-skill tools root before writing downloaded archives, so rebinding the lexical tools path cannot redirect download writes outside the intended tools directory. Thanks @tdjackey.
+
+## 2026.3.7
+
+### Changes
+
+- Agents/context engine plugin interface: add `ContextEngine` plugin slot with full lifecycle hooks (`bootstrap`, `ingest`, `assemble`, `compact`, `afterTurn`, `prepareSubagentSpawn`, `onSubagentEnded`), slot-based registry with config-driven resolution, `LegacyContextEngine` wrapper preserving existing compaction behavior, scoped subagent runtime for plugin runtimes via `AsyncLocalStorage`, and `sessions.get` gateway method. Enables plugins like `lossless-claw` to provide alternative context management strategies without modifying core compaction logic. Zero behavior change when no context engine plugin is configured. (#22201) thanks @jalehman.
+- ACP/persistent channel bindings: add durable Discord channel and Telegram topic binding storage, routing resolution, and CLI/docs support so ACP thread targets survive restarts and can be managed consistently. (#34873) Thanks @dutifulbob.
+- Telegram/ACP topic bindings: accept Telegram Mac Unicode dash option prefixes in `/acp spawn`, support Telegram topic thread binding (`--thread here|auto`), route bound-topic follow-ups to ACP sessions, add actionable Telegram approval buttons with prefixed approval-id resolution, and pin successful bind confirmations in-topic. (#36683) Thanks @huntharo.
+- Telegram/topic agent routing: support per-topic `agentId` overrides in forum groups and DM topics so topics can route to dedicated agents with isolated sessions. (#33647; based on #31513) Thanks @kesor and @Sid-Qin.
+- Web UI/i18n: add Spanish (`es`) locale support in the Control UI, including locale detection, lazy loading, and language picker labels across supported locales. (#35038) Thanks @DaoPromociones.
+- Onboarding/web search: add provider selection step and full provider list in configure wizard, with SecretRef ref-mode support during onboarding. (#34009) Thanks @kesku and @thewilloftheshadow.
+- Tools/Web search: switch Perplexity provider to Search API with structured results plus new language/region/time filters. (#33822) Thanks @kesku.
+- Gateway: add SecretRef support for gateway.auth.token with auth-mode guardrails. (#35094) Thanks @joshavant.
+- Docker/Podman extension dependency baking: add `OPENCLAW_EXTENSIONS` so container builds can preinstall selected bundled extension npm dependencies into the image for faster and more reproducible startup in container deployments. (#32223) Thanks @sallyom.
+- Plugins/before_prompt_build system-context fields: add `prependSystemContext` and `appendSystemContext` so static plugin guidance can be placed in system prompt space for provider caching and lower repeated prompt token cost. (#35177) thanks @maweibin.
+- Plugins/hook policy: add `plugins.entries..hooks.allowPromptInjection`, validate unknown typed hook names at runtime, and preserve legacy `before_agent_start` model/provider overrides while stripping prompt-mutating fields when prompt injection is disabled. (#36567) thanks @gumadeiras.
+- Hooks/Compaction lifecycle: emit `session:compact:before` and `session:compact:after` internal events plus plugin compaction callbacks with session/count metadata, so automations can react to compaction runs consistently. (#16788) thanks @vincentkoc.
+- Agents/compaction post-context configurability: add `agents.defaults.compaction.postCompactionSections` so deployments can choose which `AGENTS.md` sections are re-injected after compaction, while preserving legacy fallback behavior when the documented default pair is configured in any order. (#34556) thanks @efe-arv.
+- TTS/OpenAI-compatible endpoints: add `messages.tts.openai.baseUrl` config support with config-over-env precedence, endpoint-aware directive validation, and OpenAI TTS request routing to the resolved base URL. (#34321) thanks @RealKai42.
+- Slack/DM typing feedback: add `channels.slack.typingReaction` so Socket Mode DMs can show reaction-based processing status even when Slack native assistant typing is unavailable. (#19816) Thanks @dalefrieswthat.
+- Discord/allowBots mention gating: add `allowBots: "mentions"` to only accept bot-authored messages that mention the bot. Thanks @thewilloftheshadow.
+- Agents/tool-result truncation: preserve important tail diagnostics by using head+tail truncation for oversized tool results while keeping configurable truncation options. (#20076) thanks @jlwestsr.
+- Cron/job snapshot persistence: skip backup during normalization persistence in `ensureLoaded` so `jobs.json.bak` keeps the pre-edit snapshot for recovery, while preserving backup creation on explicit user-driven writes. (#35234) Thanks @0xsline.
+- CLI: make read-only SecretRef status flows degrade safely (#37023) thanks @joshavant.
+- Tools/Diffs guidance: restore a short system-prompt hint for enabled diffs while keeping the detailed instructions in the companion skill, so diffs usage guidance stays out of user-prompt space. (#36904) thanks @gumadeiras.
+- Tools/Diffs guidance loading: move diffs usage guidance from unconditional prompt-hook injection to the plugin companion skill path, reducing unrelated-turn prompt noise while keeping diffs tool behavior unchanged. (#32630) thanks @sircrumpet.
+- Docs/Web search: remove outdated Brave free-tier wording and replace prescriptive AI ToS guidance with neutral compliance language in Brave setup docs. (#26860) Thanks @HenryLoenwind.
+- Config/Compaction safeguard tuning: expose `agents.defaults.compaction.recentTurnsPreserve` and quality-guard retry knobs through the validated config surface and embedded-runner wiring, with regression coverage for real config loading and schema metadata. (#25557) thanks @rodrigouroz.
+- iOS/App Store Connect release prep: align iOS bundle identifiers under `ai.openclaw.client`, refresh Watch app icons, add Fastlane metadata/screenshot automation, and support Keychain-backed ASC auth for uploads. (#38936) Thanks @ngutman.
+- Mattermost/model picker: add Telegram-style interactive provider/model browsing for `/oc_model` and `/oc_models`, fix picker callback updates, and emit a normal confirmation reply when a model is selected. (#38767) thanks @mukhtharcm.
+- Docker/multi-stage build: restructure Dockerfile as a multi-stage build to produce a minimal runtime image without build tools, source code, or Bun; add `OPENCLAW_VARIANT=slim` build arg for a bookworm-slim variant. (#38479) Thanks @sallyom.
+- Google/Gemini 3.1 Flash-Lite: add first-class `google/gemini-3.1-flash-lite-preview` support across model-id normalization, default aliases, media-understanding image lookups, Google Gemini CLI forward-compat fallback, and docs.
+- Agents/compaction model override: allow `agents.defaults.compaction.model` to route compaction summarization through a different model than the main session, and document the override across config help/reference surfaces. (#38753) thanks @starbuck100.
+
+### Breaking
+
+- **BREAKING:** Gateway auth now requires explicit `gateway.auth.mode` when both `gateway.auth.token` and `gateway.auth.password` are configured (including SecretRefs). Set `gateway.auth.mode` to `token` or `password` before upgrade to avoid startup/pairing/TUI failures. (#35094) Thanks @joshavant.
+
+### Fixes
+
+- Models/MiniMax: stop advertising removed `MiniMax-M2.5-Lightning` in built-in provider catalogs, onboarding metadata, and docs; keep the supported fast-tier model as `MiniMax-M2.5-highspeed`.
+- Models/Vercel AI Gateway: synthesize the built-in `vercel-ai-gateway` provider from `AI_GATEWAY_API_KEY` and auto-discover the live `/v1/models` catalog so `/models vercel-ai-gateway` exposes current refs including `openai/gpt-5.4`.
+- Security/Config: fail closed when `loadConfig()` hits validation or read errors so invalid configs cannot silently fall back to permissive runtime defaults. (#9040) Thanks @joetomasone.
+- Memory/Hybrid search: preserve negative FTS5 BM25 relevance ordering in `bm25RankToScore()` so stronger keyword matches rank above weaker ones instead of collapsing or reversing scores. (#33757) Thanks @lsdcc01.
+- LINE/`requireMention` group gating: align inbound and reply-stage LINE group policy resolution across raw, `group:`, and `room:` keys (including account-scoped group config), preserve plugin-backed reply-stage fallback behavior, and add regression coverage for prefixed-only group/room config plus reply-stage policy resolution. (#35847) Thanks @kirisame-wang.
+- Onboarding/local setup: default unset local `tools.profile` to `coding` instead of `messaging`, restoring file/runtime tools for fresh local installs while preserving explicit user-set profiles. (from #38241, overlap with #34958) Thanks @cgdusek.
+- Gateway/Telegram stale-socket restart guard: only apply stale-socket restarts to channels that publish event-liveness timestamps, preventing Telegram providers from being misclassified as stale solely due to long uptime and avoiding restart/pairing storms after upgrade. (openclaw#38464)
+- Onboarding/headless Linux daemon probe hardening: treat `systemctl --user is-enabled` probe failures as non-fatal during daemon install flow so onboarding no longer crashes on SSH/headless VPS environments before showing install guidance. (#37297) Thanks @acarbajal-web.
+- Memory/QMD mcporter Windows spawn hardening: when `mcporter.cmd` launch fails with `spawn EINVAL`, retry via bare `mcporter` shell resolution so QMD recall can continue instead of falling back to builtin memory search. (#27402) Thanks @i0ivi0i.
+- Tools/web_search Brave language-code validation: align `search_lang` handling with Brave-supported codes (including `zh-hans`, `zh-hant`, `en-gb`, and `pt-br`), map common alias inputs (`zh`, `ja`) to valid Brave values, and reject unsupported codes before upstream requests to prevent 422 failures. (#37260) Thanks @heyanming.
+- Models/openai-completions streaming compatibility: force `compat.supportsUsageInStreaming=false` for non-native OpenAI-compatible endpoints during model normalization, preventing usage-only stream chunks from triggering `choices[0]` parser crashes in provider streams. (#8714) Thanks @nonanon1.
+- Tools/xAI native web-search collision guard: drop OpenClaw `web_search` from tool registration when routing to xAI/Grok model providers (including OpenRouter `x-ai/*`) to avoid duplicate tool-name request failures against provider-native `web_search`. (#14749) Thanks @realsamrat.
+- TUI/token copy-safety rendering: treat long credential-like mixed alphanumeric tokens (including quoted forms) as copy-sensitive in render sanitization so formatter hard-wrap guards no longer inject visible spaces into auth-style values before display. (#26710) Thanks @jasonthane.
+- WhatsApp/self-chat response prefix fallback: stop forcing `"[openclaw]"` as the implicit outbound response prefix when no identity name or response prefix is configured, so blank/default prefix settings no longer inject branding text unexpectedly in self-chat flows. (#27962) Thanks @ecanmor.
+- Memory/QMD search result decoding: accept `qmd search` hits that only include `file` URIs (for example `qmd://collection/path.md`) without `docid`, resolve them through managed collection roots, and keep multi-collection results keyed by file fallback so valid QMD hits no longer collapse to empty `memory_search` output. (#28181) Thanks @0x76696265.
+- Memory/QMD collection-name conflict recovery: when `qmd collection add` fails because another collection already occupies the same `path + pattern`, detect the conflicting collection from `collection list`, remove it, and retry add so agent-scoped managed collections are created deterministically instead of being silently skipped; also add warning-only fallback when qmd metadata is unavailable to avoid destructive guesses. (#25496) Thanks @Ramsbaby.
+- Slack/app_mention race dedupe: when `app_mention` dispatch wins while same-`ts` `message` prepare is still in-flight, suppress the later message dispatch so near-simultaneous Slack deliveries do not produce duplicate replies; keep single-retry behavior and add regression coverage for both dropped and successful message-prepare outcomes. (#37033) Thanks @Takhoffman.
+- Gateway/chat streaming tool-boundary text retention: merge assistant delta segments into per-run chat buffers so pre-tool text is preserved in live chat deltas/finals when providers emit post-tool assistant segments as non-prefix snapshots. (#36957) Thanks @Datyedyeguy.
+- TUI/model indicator freshness: prevent stale session snapshots from overwriting freshly patched model selection (and reset per-session freshness when switching session keys) so `/model` updates reflect immediately instead of lagging by one or more commands. (#21255) Thanks @kowza.
+- TUI/final-error rendering fallback: when a chat `final` event has no renderable assistant content but includes envelope `errorMessage`, render the formatted error text instead of collapsing to `"(no output)"`, preserving actionable failure context in-session. (#14687) Thanks @Mquarmoc.
+- TUI/session-key alias event matching: treat chat events whose session keys are canonical aliases (for example `agent::main` vs `main`) as the same session while preserving cross-agent isolation, so assistant replies no longer disappear or surface in another terminal window due to strict key-form mismatch. (#33937) Thanks @yjh1412.
+- OpenAI Codex OAuth/login parity: keep `openclaw models auth login --provider openai-codex` on the built-in path even without provider plugins, preserve Pi-generated authorize URLs without local scope rewriting, and stop validating successful Codex sign-ins against the public OpenAI Responses API after callback. (#37558; follow-up to #36660 and #24720) Thanks @driesvints, @Skippy-Gunboat, and @obviyus.
+- Agents/config schema lookup: add `gateway` tool action `config.schema.lookup` so agents can inspect one config path at a time before edits without loading the full schema into prompt context. (#37266) Thanks @gumadeiras.
+- Onboarding/API key input hardening: strip non-Latin1 Unicode artifacts from normalized secret input (while preserving Latin-1 content and internal spaces) so malformed copied API keys cannot trigger HTTP header `ByteString` construction crashes; adds regression coverage for shared normalization and MiniMax auth header usage. (#24496) Thanks @fa6maalassaf.
+- Kimi Coding/Anthropic tools compatibility: normalize `anthropic-messages` tool payloads to OpenAI-style `tools[].function` + compatible `tool_choice` when targeting Kimi Coding endpoints, restoring tool-call workflows that regressed after v2026.3.2. (#37038) Thanks @mochimochimochi-hub.
+- Heartbeat/workspace-path guardrails: append explicit workspace `HEARTBEAT.md` path guidance (and `docs/heartbeat.md` avoidance) to heartbeat prompts so heartbeat runs target workspace checklists reliably across packaged install layouts. (#37037) Thanks @stofancy.
+- Subagents/kill-complete announce race: when a late `subagent-complete` lifecycle event arrives after an earlier kill marker, clear stale kill suppression/cleanup flags and re-run announce cleanup so finished runs no longer get silently swallowed. (#37024) Thanks @cmfinlan.
+- Agents/tool-result cleanup timeout hardening: on embedded runner teardown idle timeouts, clear pending tool-call state without persisting synthetic `missing tool result` entries, preventing timeout cleanups from poisoning follow-up turns; adds regression coverage for timeout clear-vs-flush behavior. (#37081) Thanks @Coyote-Den.
+- Agents/openai-completions stream timeout hardening: ensure runtime undici global dispatchers use extended streaming body/header timeouts (including env-proxy dispatcher mode) before embedded runs, reducing forced mid-stream `terminated` failures on long generations; adds regression coverage for dispatcher selection and idempotent reconfiguration. (#9708) Thanks @scottchguard.
+- Agents/fallback cooldown probe execution: thread explicit rate-limit cooldown probe intent from model fallback into embedded runner auth-profile selection so same-provider fallback attempts can actually run when all profiles are cooldowned for `rate_limit` (instead of failing pre-run as `No available auth profile`), while preserving default cooldown skip behavior and adding regression tests at both fallback and runner layers. (#13623) Thanks @asfura.
+- Cron/OpenAI Codex OAuth refresh hardening: when `openai-codex` token refresh fails specifically on account-id extraction, reuse the cached access token instead of failing the run immediately, with regression coverage to keep non-Codex and unrelated refresh failures unchanged. (#36604) Thanks @laulopezreal.
+- TUI/session isolation for `/new`: make `/new` allocate a unique `tui-` session key instead of resetting the shared agent session, so multiple TUI clients on the same agent stop receiving each other’s replies; also sanitize `/new` and `/reset` failure text before rendering in-terminal. Landed from contributor PR #39238 by @widingmarcus-cyber. Thanks @widingmarcus-cyber.
+- Synology Chat/rate-limit env parsing: honor `SYNOLOGY_RATE_LIMIT=0` as an explicit value while still falling back to the default limit for malformed env values instead of partially parsing them. Landed from contributor PR #39197 by @scoootscooob. Thanks @scoootscooob.
+- Voice-call/OpenAI Realtime STT config defaults: honor explicit `vadThreshold: 0` and `silenceDurationMs: 0` instead of silently replacing them with defaults. Landed from contributor PR #39196 by @scoootscooob. Thanks @scoootscooob.
+- Voice-call/OpenAI TTS speed config: honor explicit `speed: 0` instead of silently replacing it with the default speed. Landed from contributor PR #39318 by @ql-wade. Thanks @ql-wade.
+- launchd/runtime PID parsing: reject `pid <= 0` from `launchctl print` so the daemon state parser no longer treats kernel/non-running sentinel values as real process IDs. Landed from contributor PR #39281 by @mvanhorn. Thanks @mvanhorn.
+- Cron/file permission hardening: enforce owner-only (`0600`) cron store/backup/run-log files and harden cron store + run-log directories to `0700`, including pre-existing directories from older installs. (#36078) Thanks @aerelune.
+- Gateway/remote WS break-glass hostname support: honor `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1` for `ws://` hostname URLs (not only private IP literals) across onboarding validation and runtime gateway connection checks, while still rejecting public IP literals and non-unicast IPv6 endpoints. (#36930) Thanks @manju-rn.
+- Routing/binding lookup scalability: pre-index route bindings by channel/account and avoid full binding-list rescans on channel-account cache rollover, preventing multi-second `resolveAgentRoute` stalls in large binding configurations. (#36915) Thanks @songchenghao.
+- Browser/session cleanup: track browser tabs opened by session-scoped browser tool runs and close tracked tabs during `sessions.reset`/`sessions.delete` runtime cleanup, preventing orphaned tabs and unbounded browser memory growth after session teardown. (#36666) Thanks @Harnoor6693.
+- Plugin/hook install rollback hardening: stage installs under the canonical install base, validate and run dependency installs before publish, and restore updates by rename instead of deleting the target path, reducing partial-replace and symlink-rebind risk during install failures.
+- Slack/local file upload allowlist parity: propagate `mediaLocalRoots` through the Slack send action pipeline so workspace-rooted attachments pass `assertLocalMediaAllowed` checks while non-allowlisted paths remain blocked. (synthesis: #36656; overlap considered from #36516, #36496, #36493, #36484, #32648, #30888) Thanks @2233admin.
+- Agents/compaction safeguard pre-check: skip embedded compaction before entering the Pi SDK when a session has no real conversation messages, avoiding unnecessary LLM API calls on idle sessions. (#36451) thanks @Sid-Qin.
+- Config/schema cache key stability: build merged schema cache keys with incremental hashing to avoid large single-string serialization and prevent `RangeError: Invalid string length` on high-cardinality plugin/channel metadata. (#36603) Thanks @powermaster888.
+- iMessage/cron completion announces: strip leaked inline reply tags (for example `[[reply_to:6100]]`) from user-visible completion text so announcement deliveries do not expose threading metadata. (#24600) Thanks @vincentkoc.
+- Cron/manual run enqueue flow: queue `cron.run` requests behind the cron execution lane, return immediate `{ ok: true, enqueued: true, runId }` acknowledgements, preserve `{ ok: true, ran: false, reason }` skip responses for already-running and not-due jobs, and document the asynchronous completion flow. (#40204)
+- Control UI/iMessage duplicate reply routing: keep internal webchat turns on dispatcher delivery (instead of origin-channel reroute) so Control UI chats do not duplicate replies into iMessage, while preserving webchat-provider relayed routing for external surfaces. Fixes #33483. Thanks @alicexmolt.
+- Sessions/daily reset transcript archival: archive prior transcript files during stale-session scheduled/daily resets by capturing the previous session entry before rollover, preventing orphaned transcript files on disk. (#35493) Thanks @byungsker.
+- Feishu/group slash command detection: normalize group mention wrappers before command-authorization probing so mention-prefixed commands (for example `@Bot/model` and `@Bot /reset`) are recognized as gateway commands instead of being forwarded to the agent. (#35994) Thanks @liuxiaopai-ai.
+- Control UI/auth token separation: keep the shared gateway token in browser auth validation while reserving cached device tokens for signed device payloads, preventing false `device token mismatch` disconnects after restart/rotation. Landed from contributor PR #37382 by @FradSer. Thanks @FradSer.
+- Gateway/browser auth reconnect hardening: stop counting missing token/password submissions as auth rate-limit failures, and stop auto-reconnecting Control UI clients on non-recoverable auth errors so misconfigured browser tabs no longer lock out healthy sessions. Landed from contributor PR #38725 by @ademczuk. Thanks @ademczuk.
+- Gateway/service token drift repair: stop persisting shared auth tokens into installed gateway service units, flag stale embedded service tokens for reinstall, and treat tokenless service env as canonical so token rotation/reboot flows stay aligned with config/env resolution. Landed from contributor PR #28428 by @l0cka. Thanks @l0cka.
+- Control UI/agents-page selection: keep the edited agent selected after saving agent config changes and reloading the agents list, so `/agents` no longer snaps back to the default agent. Landed from contributor PR #39301 by @MumuTW. Thanks @MumuTW.
+- Gateway/auth follow-up hardening: preserve systemd `EnvironmentFile=` precedence/source provenance in daemon audits and doctor repairs, block shared-password override flows from piggybacking cached device tokens, and fail closed when config-first gateway SecretRefs cannot resolve. Follow-up to #39241.
+- Agents/context pruning: guard assistant thinking/text char estimation against malformed blocks (missing `thinking`/`text` strings or null entries) so pruning no longer crashes with malformed provider content. (openclaw#35146) thanks @Sid-Qin.
+- Agents/transcript policy: set `preserveSignatures` to Anthropic-only handling in `resolveTranscriptPolicy` so Anthropic thinking signatures are preserved while non-Anthropic providers remain unchanged. (#32813) thanks @Sid-Qin.
+- Agents/schema cleaning: detect Venice + Grok model IDs as xAI-proxied targets so unsupported JSON Schema keywords are stripped before requests, preventing Venice/Grok `Invalid arguments` failures. (openclaw#35355) thanks @Sid-Qin.
+- Skills/native command deduplication: centralize skill command dedupe by canonical `skillName` in `listSkillCommandsForAgents` so duplicate suffixed variants (for example `_2`) are no longer surfaced across interfaces outside Discord. (#27521) thanks @shivama205.
+- Agents/xAI tool-call argument decoding: decode HTML-entity encoded xAI/Grok tool-call argument values (`&`, `"`, `<`, `>`, numeric entities) before tool execution so commands with shell operators and quotes no longer fail with parse errors. (#35276) Thanks @Sid-Qin.
+- Linux/WSL2 daemon install hardening: add regression coverage for WSL environment detection, WSL-specific systemd guidance, and `systemctl --user is-enabled` failure paths so WSL2/headless onboarding keeps treating bus-unavailable probes as non-fatal while preserving real permission errors. Related: #36495. Thanks @vincentkoc.
+- Linux/systemd status and degraded-session handling: treat degraded-but-reachable `systemctl --user status` results as available, preserve early errors for truly unavailable user-bus cases, and report externally managed running services as running instead of `not installed`. Thanks @vincentkoc.
+- Agents/thinking-tag promotion hardening: guard `promoteThinkingTagsToBlocks` against malformed assistant content entries (`null`/`undefined`) before `block.type` reads so malformed provider payloads no longer crash session processing while preserving pass-through behavior. (#35143) thanks @Sid-Qin.
+- Gateway/Control UI version reporting: align runtime and browser client version metadata to avoid `dev` placeholders, wait for bootstrap version before first UI websocket connect, and only forward bootstrap `serverVersion` to same-origin gateway targets to prevent cross-target version leakage. (from #35230, #30928, #33928) Thanks @Sid-Qin, @joelnishanth, and @MoerAI.
+- Control UI/markdown parser crash fallback: catch `marked.parse()` failures and fall back to escaped plain-text `
` rendering so malformed recursive markdown no longer crashes Control UI session rendering on load. (#36445) Thanks @BinHPdev.
+- Control UI/markdown fallback regression coverage: add explicit regression assertions for parser-error fallback behavior so malformed markdown no longer risks reintroducing hard-crash rendering paths in future markdown/parser upgrades. (#36445) Thanks @BinHPdev.
+- Web UI/config form: treat `additionalProperties: true` object schemas as editable map entries instead of unsupported fields so Accounts-style maps stay editable in form mode. (#35380, supersedes #32072) Thanks @stakeswky and @liuxiaopai-ai.
+- Feishu/streaming card delivery synthesis: unify snapshot and delta streaming merge semantics, apply overlap-aware final merge, suppress duplicate final text delivery (including text+media final packets), prefer topic-thread `message.reply` routing when a reply target exists, and tune card print cadence to avoid duplicate incremental rendering. (from #33245, #32896, #33840) Thanks @rexl2018, @kcinzgg, and @aerelune.
+- Feishu/group mention detection: carry startup-probed bot display names through monitor dispatch so `requireMention` checks compare against current bot identity instead of stale config names, fixing missed `@bot` handling in groups while preserving multi-bot false-positive guards. (#36317, #34271) Thanks @liuxiaopai-ai.
+- Security/dependency audit: patch transitive Hono vulnerabilities by pinning `hono` to `4.12.5` and `@hono/node-server` to `1.19.10` in production resolution paths. Thanks @shakkernerd.
+- Security/dependency audit: bump `tar` to `7.5.10` (from `7.5.9`) to address the high-severity hardlink path traversal advisory (`GHSA-qffp-2rhf-9h96`). Thanks @shakkernerd.
+- Cron/announce delivery robustness: bypass pending-descendant announce guards for cron completion sends, ensure named-agent announce routes have outbound session entries, and fall back to direct delivery only when an announce send was actually attempted and failed. (from #35185, #32443, #34987) Thanks @Sid-Qin, @scoootscooob, and @bmendonca3.
+- Cron/announce best-effort fallback: run direct outbound fallback after attempted announce failures even when delivery is configured as best-effort, so Telegram cron sends are not left as attempted-but-undelivered after `cron announce delivery failed` warnings.
+- Auto-reply/system events: restore runtime system events to the message timeline (`System:` lines), preserve think-hint parsing with prepended events, and carry events into deferred followup/collect/steer-backlog prompts to keep cache behavior stable without dropping queued metadata. (#34794) Thanks @anisoptera.
+- Security/audit account handling: avoid prototype-chain account IDs in audit validation by using own-property checks for `accounts`. (#34982) Thanks @HOYALIM.
+- Cron/restart catch-up semantics: replay interrupted recurring jobs and missed immediate cron slots on startup without replaying interrupted one-shot jobs, with guarded missed-slot probing to avoid malformed-schedule startup aborts and duplicate-trigger drift after restart. (from #34466, #34896, #34625, #33206) Thanks @dunamismax, @dsantoreis, @Octane0411, and @Sid-Qin.
+- Venice/provider onboarding hardening: align per-model Venice completion-token limits with discovery metadata, clamp untrusted discovery values to safe bounds, sync the static Venice fallback catalog with current live model metadata, and disable tool wiring for Venice models that do not support function calling so default Venice setups no longer fail with `max_completion_tokens` or unsupported-tools 400s. Fixes #38168. Thanks @Sid-Qin, @powermaster888 and @vincentkoc.
+- Agents/session usage tracking: preserve accumulated usage metadata on embedded Pi runner error exits so failed turns still update session `totalTokens` from real usage instead of stale prior values. (#34275) thanks @RealKai42.
+- Slack/reaction thread context routing: carry Slack native DM channel IDs through inbound context and threading tool resolution so reaction targets resolve consistently for DM `To=user:*` sessions (including `toolContext.currentChannelId` fallback behavior). (from #34831; overlaps #34440, #34502, #34483, #32754) Thanks @dunamismax.
+- Subagents/announce completion scoping: scope nested direct-child completion aggregation to the current requester run window, harden frozen completion capture for deterministic descendant synthesis, and route completion announce delivery through parent-agent announce turns with provenance-aware internal events. (#35080) Thanks @tyler6204.
+- Nodes/system.run approval hardening: use explicit argv-mutation signaling when regenerating prepared `rawCommand`, and cover the `system.run.prepare -> system.run` handoff so direct PATH-based `nodes.run` commands no longer fail with `rawCommand does not match command`. (#33137) thanks @Sid-Qin.
+- Models/custom provider headers: propagate `models.providers..headers` across inline, fallback, and registry-found model resolution so header-authenticated proxies consistently receive configured request headers. (#27490) thanks @Sid-Qin.
+- Ollama/remote provider auth fallback: synthesize a local runtime auth key for explicitly configured `models.providers.ollama` entries that omit `apiKey`, so remote Ollama endpoints run without requiring manual dummy-key setup while preserving env/profile/config key precedence and missing-config failures. (#11283) Thanks @cpreecs.
+- Ollama/custom provider headers: forward resolved model headers into native Ollama stream requests so header-authenticated Ollama proxies receive configured request headers. (#24337) thanks @echoVic.
+- Ollama/compaction and summarization: register custom `api: "ollama"` handling for compaction, branch-style internal summarization, and TTS text summarization on current `main`, so native Ollama models no longer fail with `No API provider registered for api: ollama` outside the main run loop. Thanks @JaviLib.
+- Daemon/systemd install robustness: treat `systemctl --user is-enabled` exit-code-4 `not-found` responses as not-enabled by combining stderr/stdout detail parsing, so Ubuntu fresh installs no longer fail with `systemctl is-enabled unavailable`. (#33634) Thanks @Yuandiaodiaodiao.
+- Slack/system-event session routing: resolve reaction/member/pin/interaction system-event session keys through channel/account bindings (with sender-aware DM routing) so inbound Slack events target the correct agent session in multi-account setups instead of defaulting to `agent:main`. (#34045) Thanks @paulomcg, @daht-mad and @vincentkoc.
+- Slack/native streaming markdown conversion: stop pre-normalizing text passed to Slack native `markdown_text` in streaming start/append/stop paths to prevent Markdown style corruption from double conversion. (#34931)
+- Gateway/HTTP tools invoke media compatibility: preserve raw media payload access for direct `/tools/invoke` clients by allowing media `nodes` invoke commands only in HTTP tool context, while keeping agent-context media invoke blocking to prevent base64 prompt bloat. (#34365) Thanks @obviyus.
+- Security/archive ZIP hardening: extract ZIP entries via same-directory temp files plus atomic rename, then re-open and reject post-rename hardlink alias races outside the destination root.
+- Agents/Nodes media outputs: add dedicated `photos_latest` action handling, block media-returning `nodes invoke` commands, keep metadata-only `camera.list` invoke allowed, and normalize empty `photos_latest` results to a consistent response shape to prevent base64 context bloat. (#34332) Thanks @obviyus.
+- TUI/session-key canonicalization: normalize `openclaw tui --session` values to lowercase so uppercase session names no longer drop real-time streaming updates due to gateway/TUI key mismatches. (#33866, #34013) thanks @lynnzc.
+- iMessage/echo loop hardening: strip leaked assistant-internal scaffolding from outbound iMessage replies, drop reflected assistant-content messages before they re-enter inbound processing, extend echo-cache text retention for delayed reflections, and suppress repeated loop traffic before it amplifies into queue overflow. (#33295) Thanks @joelnishanth.
+- Skills/workspace boundary hardening: reject workspace and extra-dir skill roots or `SKILL.md` files whose realpath escapes the configured source root, and skip syncing those escaped skills into sandbox workspaces.
+- Outbound/send config threading: pass resolved SecretRef config through outbound adapters and helper send paths so send flows do not reload unresolved runtime config. (#33987) Thanks @joshavant.
+- gateway: harden shared auth resolution across systemd, discord, and node host (#39241) Thanks @joshavant.
+- Secrets/models.json persistence hardening: keep SecretRef-managed api keys + headers from persisting in generated models.json, expand audit/apply coverage, and harden marker handling/serialization. (#38955) Thanks @joshavant.
+- Sessions/subagent attachments: remove `attachments[].content.maxLength` from `sessions_spawn` schema to avoid llama.cpp GBNF repetition overflow, and preflight UTF-8 byte size before buffer allocation while keeping runtime file-size enforcement unchanged. (#33648) Thanks @anisoptera.
+- Runtime/tool-state stability: recover from dangling Anthropic `tool_use` after compaction, serialize long-running Discord handler runs without blocking new inbound events, and prevent stale busy snapshots from suppressing stuck-channel recovery. (from #33630, #33583) Thanks @kevinWangSheng and @theotarr.
+- ACP/Discord startup hardening: clean up stuck ACP worker children on gateway restart, unbind stale ACP thread bindings during Discord startup reconciliation, and add per-thread listener watchdog timeouts so wedged turns cannot block later messages. (#33699) Thanks @dutifulbob.
+- Extensions/media local-root propagation: consistently forward `mediaLocalRoots` through extension `sendMedia` adapters (Google Chat, Slack, iMessage, Signal, WhatsApp), preserving non-local media behavior while restoring local attachment resolution from configured roots. Synthesis of #33581, #33545, #33540, #33536, #33528. Thanks @bmendonca3.
+- Gateway/plugin HTTP auth hardening: require gateway auth when any overlapping matched route needs it, block mixed-auth fallthrough at dispatch, and reject mixed-auth exact/prefix route overlaps during plugin registration.
+- Feishu/video media send contract: keep mp4-like outbound payloads on `msg_type: "media"` (including reply and reply-in-thread paths) so videos render as media instead of degrading to file-link behavior, while preserving existing non-video file subtype handling. (from #33720, #33808, #33678) Thanks @polooooo, @dingjianrui, and @kevinWangSheng.
+- Gateway/security default response headers: add `Permissions-Policy: camera=(), microphone=(), geolocation=()` to baseline gateway HTTP security headers for all responses. (#30186) thanks @habakan.
+- Plugins/startup loading: lazily initialize plugin runtime, split startup-critical plugin SDK imports into `openclaw/plugin-sdk/core` and `openclaw/plugin-sdk/telegram`, and preserve `api.runtime` reflection semantics for plugin compatibility. (#28620) thanks @hmemcpy.
+- Plugins/startup performance: reduce bursty plugin discovery/manifest overhead with short in-process caches, skip importing bundled memory plugins that are disabled by slot selection, and speed legacy root `openclaw/plugin-sdk` compatibility via runtime root-alias routing while preserving backward compatibility. Thanks @gumadeiras.
+- Build/lazy runtime boundaries: replace ineffective dynamic import sites with dedicated lazy runtime boundaries across Slack slash handling, Telegram audit, CLI send deps, memory fallback, and outbound delivery paths while preserving behavior. (#33690) thanks @gumadeiras.
+- Gateway/password CLI hardening: add `openclaw gateway run --password-file`, warn when inline `--password` is used because it can leak via process listings, and document env/file-backed password input as the preferred startup path. Fixes #27948. Thanks @vibewrk and @vincentkoc.
+- Config/heartbeat legacy-path handling: auto-migrate top-level `heartbeat` into `agents.defaults.heartbeat` (with merge semantics that preserve explicit defaults), and keep startup failures on non-migratable legacy entries in the detailed invalid-config path instead of generic migration-failed errors. (#32706) thanks @xiwan.
+- Plugins/SDK subpath parity: expand plugin SDK subpaths across bundled channels/extensions (Discord, Slack, Signal, iMessage, WhatsApp, LINE, and bundled companion plugins), with build/export/type/runtime wiring so scoped imports resolve consistently in source and dist while preserving compatibility. (#33737) thanks @gumadeiras.
+- Google/Gemini Flash model selection: switch built-in `gemini-flash` defaults and docs/examples from the nonexistent `google/gemini-3.1-flash-preview` ID to the working `google/gemini-3-flash-preview`, while normalizing legacy OpenClaw config that still uses the old Flash 3.1 alias.
+- Plugins/bundled scoped-import migration: migrate bundled plugins from monolithic `openclaw/plugin-sdk` imports to scoped subpaths (or `openclaw/plugin-sdk/core`) across registration and startup-sensitive runtime files, add CI/release guardrails to prevent regressions, and keep root `openclaw/plugin-sdk` support for external/community plugins. Thanks @gumadeiras.
+- Routing/session duplicate suppression synthesis: align shared session delivery-context inheritance, channel-paired route-field merges, and reply-surface target matching so dmScope=main turns avoid cross-surface duplicate replies while thread-aware forwarding keeps intended routing semantics. (from #33629, #26889, #17337, #33250) Thanks @Yuandiaodiaodiao, @kevinwildenradt, @Glucksberg, and @bmendonca3.
+- Routing/legacy session route inheritance: preserve external route metadata inheritance for legacy channel session keys (`agent:::` and `...:thread:`) so `chat.send` does not incorrectly fall back to webchat when valid delivery context exists. Follow-up to #33786.
+- Routing/legacy route guard tightening: require legacy session-key channel hints to match the saved delivery channel before inheriting external routing metadata, preventing custom namespaced keys like `agent::work:` from inheriting stale non-webchat routes.
+- Gateway/internal client routing continuity: prevent webchat/TUI/UI turns from inheriting stale external reply routes by requiring explicit `deliver: true` for external delivery, keeping main-session external inheritance scoped to non-Webchat/UI clients, and honoring configured `session.mainKey` when identifying main-session continuity. (from #35321, #34635, #35356) Thanks @alexyyyander and @Octane0411.
+- Security/auth labels: remove token and API-key snippets from user-facing auth status labels so `/status` and `/models` do not expose credential fragments. (#33262) thanks @cu1ch3n.
+- Models/MiniMax portal vision routing: add `MiniMax-VL-01` to the `minimax-portal` provider, route portal image understanding through the MiniMax VLM endpoint, and align media auto-selection plus Telegram sticker description with the shared portal image provider path. (#33953) Thanks @tars90percent.
+- Auth/credential semantics: align profile eligibility + probe diagnostics with SecretRef/expiry rules and harden browser download atomic writes. (#33733) thanks @joshavant.
+- Security/audit denyCommands guidance: suggest likely exact node command IDs for unknown `gateway.nodes.denyCommands` entries so ineffective denylist entries are easier to correct. (#29713) thanks @liquidhorizon88-bot.
+- Agents/overload failover handling: classify overloaded provider failures separately from rate limits/status timeouts, add short overload backoff before retry/failover, record overloaded prompt/assistant failures as transient auth-profile cooldowns (with probeable same-provider fallback) instead of treating them like persistent auth/billing failures, and keep one-shot cron retry classification aligned so overloaded fallback summaries still count as transient retries.
+- Docs/security hardening guidance: document Docker `DOCKER-USER` + UFW policy and add cross-linking from Docker install docs for VPS/public-host setups. (#27613) thanks @dorukardahan.
+- Docs/security threat-model links: replace relative `.md` links with Mintlify-compatible root-relative routes in security docs to prevent broken internal navigation. (#27698) thanks @clawdoo.
+- Plugins/Update integrity drift: avoid false integrity drift prompts when updating npm-installed plugins from unpinned specs, while keeping drift checks for exact pinned versions. (#37179) Thanks @vincentkoc.
+- iOS/Voice timing safety: guard system speech start/finish callbacks to the active utterance to avoid misattributed start events during rapid stop/restart cycles. (#33304) thanks @mbelinky; original implementation direction by @ngutman.
+- Gateway/chat.send command scopes: require `operator.admin` for persistent `/config set|unset` writes routed through gateway chat clients while keeping `/config show` available to normal write-scoped operator clients, preserving messaging-channel config command behavior without widening RPC write scope into admin config mutation. Thanks @tdjackey for reporting.
+- iOS/Talk incremental speech pacing: allow long punctuation-free assistant chunks to start speaking at safe whitespace boundaries so voice responses begin sooner instead of waiting for terminal punctuation. (#33305) thanks @mbelinky; original implementation by @ngutman.
+- iOS/Watch reply reliability: make watch session activation waiters robust under concurrent requests so status/send calls no longer hang intermittently, and align delegate callbacks with Swift 6 actor safety. (#33306) thanks @mbelinky; original implementation by @Rocuts.
+- Docs/tool-loop detection config keys: align `docs/tools/loop-detection.md` examples and field names with the current `tools.loopDetection` schema to prevent copy-paste validation failures from outdated keys. (#33182) Thanks @Mylszd.
+- Gateway/session agent discovery: include disk-scanned agent IDs in `listConfiguredAgentIds` even when `agents.list` is configured, so disk-only/ACP agent sessions remain visible in gateway session aggregation and listings. (#32831) thanks @Sid-Qin.
+- Discord/inbound debouncer: skip bot-own MESSAGE_CREATE events before they reach the debounce queue to avoid self-triggered slowdowns in busy servers. Thanks @thewilloftheshadow.
+- Discord/Agent-scoped media roots: pass `mediaLocalRoots` through Discord monitor reply delivery (message + component interaction paths) so local media attachments honor per-agent workspace roots instead of falling back to default global roots. Thanks @thewilloftheshadow.
+- Discord/slash command handling: intercept text-based slash commands in channels, register plugin commands as native, and send fallback acknowledgments for empty slash runs so interactions do not hang. Thanks @thewilloftheshadow.
+- Discord/thread session lifecycle: reset thread-scoped sessions when a thread is archived so reopening a thread starts fresh without deleting transcript history. Thanks @thewilloftheshadow.
+- Discord/presence defaults: send an online presence update on ready when no custom presence is configured so bots no longer appear offline by default. Thanks @thewilloftheshadow.
+- Discord/typing cleanup: stop typing indicators after silent/NO_REPLY runs by marking the run complete before dispatch idle cleanup. Thanks @thewilloftheshadow.
+- ACP/sandbox spawn parity: block `/acp spawn` from sandboxed requester sessions with the same host-runtime guard already enforced for `sessions_spawn({ runtime: "acp" })`, preserving non-sandbox ACP flows while closing the command-path policy gap. Thanks @patte.
+- Discord/config SecretRef typing: align Discord account token config typing with SecretInput so SecretRef tokens typecheck. (#32490) Thanks @scoootscooob.
+- Discord/voice messages: request upload slots with JSON fetch calls so voice message uploads no longer fail with content-type errors. Thanks @thewilloftheshadow.
+- Discord/voice decoder fallback: drop the native Opus dependency and use opusscript for voice decoding to avoid native-opus installs. Thanks @thewilloftheshadow.
+- Discord/auto presence health signal: add runtime availability-driven presence updates plus connected-state reporting to improve health monitoring and operator visibility. (#33277) Thanks @thewilloftheshadow.
+- HEIC image inputs: accept HEIC/HEIF `input_image` sources in Gateway HTTP APIs, normalize them to JPEG before provider delivery, and document the expanded default MIME allowlist. Thanks @vincentkoc.
+- Gateway/HEIC input follow-up: keep non-HEIC `input_image` MIME handling unchanged, make HEIC tests hermetic, and enforce chat-completions `maxTotalImageBytes` against post-normalization image payload size. Thanks @vincentkoc.
+- Telegram/draft-stream boundary stability: materialize DM draft previews at assistant-message/tool boundaries, serialize lane-boundary callbacks before final delivery, and scope preview cleanup to the active preview so multi-step Telegram streams no longer lose, overwrite, or leave stale preview bubbles. (#33842) Thanks @ngutman.
+- Telegram/DM draft finalization reliability: require verified final-text draft emission before treating preview finalization as delivered, and fall back to normal payload send when final draft delivery is not confirmed (preventing missing final responses and preserving media/button delivery). (#32118) Thanks @OpenCils.
+- Telegram/DM draft final delivery: materialize text-only `sendMessageDraft` previews into one permanent final message and skip duplicate final payload sends, while preserving fallback behavior when materialization fails. (#34318) Thanks @Brotherinlaw-13.
+- Telegram/DM draft duplicate display: clear stale DM draft previews after materializing the real final message, including threadless fallback when DM topic lookup fails, so partial streaming no longer briefly shows duplicate replies. (#36746) Thanks @joelnishanth.
+- Telegram/draft preview boundary + silent-token reliability: stabilize answer-lane message boundaries across late-partial/message-start races, preserve/reset finalized preview state at the correct boundaries, and suppress `NO_REPLY` lead-fragment leaks without broad heartbeat-prefix false positives. (#33169) Thanks @obviyus.
+- Telegram/native commands `commands.allowFrom` precedence: make native Telegram commands honor `commands.allowFrom` as the command-specific authorization source, including group chats, instead of falling back to channel sender allowlists. (#28216) Thanks @toolsbybuddy and @vincentkoc.
+- Telegram/`groupAllowFrom` sender-ID validation: restore sender-only runtime validation so negative chat/group IDs remain invalid entries instead of appearing accepted while still being unable to authorize group access. (#37134) Thanks @qiuyuemartin-max and @vincentkoc.
+- Telegram/native group command auth: authorize native commands in groups and forum topics against `groupAllowFrom` and per-group/topic sender overrides, while keeping auth rejection replies in the originating topic thread. (#39267) Thanks @edwluo.
+- Telegram/named-account DMs: restore non-default-account DM routing when a named Telegram account falls back to the default agent by keeping groups fail-closed but deriving a per-account session key for DMs, including identity-link canonicalization and regression coverage for account isolation. (from #32426; fixes #32351) Thanks @chengzhichao-xydt.
+- Discord/audit wildcard warnings: ignore "\*" wildcard keys when counting unresolved guild channels so doctor/status no longer warns on allow-all configs. (#33125) Thanks @thewilloftheshadow.
+- Discord/channel resolution: default bare numeric recipients to channels, harden allowlist numeric ID handling with safe fallbacks, and avoid inbound WS heartbeat stalls. (#33142) Thanks @thewilloftheshadow.
+- Discord/chunk delivery reliability: preserve chunk ordering when using a REST client and retry chunk sends on 429/5xx using account retry settings. (#33226) Thanks @thewilloftheshadow.
+- Discord/mention handling: add id-based mention formatting + cached rewrites, resolve inbound mentions to display names, and add optional ignoreOtherMentions gating (excluding @everyone/@here). (#33224) Thanks @thewilloftheshadow.
+- Discord/media SSRF allowlist: allow Discord CDN hostnames (including wildcard domains) in inbound media SSRF policy to prevent proxy/VPN fake-ip blocks. (#33275) Thanks @thewilloftheshadow.
+- Telegram/device pairing notifications: auto-arm one-shot notify on `/pair qr`, auto-ping on new pairing requests, and add manual fallback via `/pair approve latest` if the ping does not arrive. (#33299) thanks @mbelinky.
+- Exec heartbeat routing: scope exec-triggered heartbeat wakes to agent session keys so unrelated agents are no longer awakened by exec events, while preserving legacy unscoped behavior for non-canonical session keys. (#32724) thanks @altaywtf
+- macOS/Tailscale remote gateway discovery: add a Tailscale Serve fallback peer probe path (`wss://.ts.net`) when Bonjour and wide-area DNS-SD discovery return no gateways, and refresh both discovery paths from macOS onboarding. (#32860) Thanks @ngutman.
+- iOS/Gateway keychain hardening: move gateway metadata and TLS fingerprints to device keychain storage with safer migration behavior and rollback-safe writes to reduce credential loss risk during upgrades. (#33029) thanks @mbelinky.
+- iOS/Concurrency stability: replace risky shared-state access in camera and gateway connection paths with lock-protected access patterns to reduce crash risk under load. (#33241) thanks @mbelinky.
+- iOS/Security guardrails: limit production API-key sourcing to app config and make deep-link confirmation prompts safer by coalescing queued requests instead of silently dropping them. (#33031) thanks @mbelinky.
+- iOS/TTS playback fallback: keep voice playback resilient by switching from PCM to MP3 when provider format support is unavailable, while avoiding sticky fallback on generic local playback errors. (#33032) thanks @mbelinky.
+- Plugin outbound/text-only adapter compatibility: allow direct-delivery channel plugins that only implement `sendText` (without `sendMedia`) to remain outbound-capable, gracefully fall back to text delivery for media payloads when `sendMedia` is absent, and fail explicitly for media-only payloads with no text fallback. (#32788) thanks @liuxiaopai-ai.
+- Telegram/multi-account default routing clarity: warn only for ambiguous (2+) account setups without an explicit default, add `openclaw doctor` warnings for missing/invalid multi-account defaults across channels, and document explicit-default guidance for channel routing and Telegram config. (#32544) thanks @Sid-Qin.
+- Telegram/plugin outbound hook parity: run `message_sending` + `message_sent` in Telegram reply delivery, include reply-path hook metadata (`mediaUrls`, `threadId`), and report `message_sent.success=false` when hooks blank text and no outbound message is delivered. (#32649) Thanks @KimGLee.
+- CLI/Coding-agent reliability: switch default `claude-cli` non-interactive args to `--permission-mode bypassPermissions`, auto-normalize legacy `--dangerously-skip-permissions` backend overrides to the modern permission-mode form, align coding-agent + live-test docs with the non-PTY Claude path, and emit session system-event heartbeat notices when CLI watchdog no-output timeouts terminate runs. (#28610, #31149, #34055). Thanks @niceysam, @cryptomaltese and @vincentkoc.
+- Gateway/OpenAI chat completions: parse active-turn `image_url` content parts (including parameterized data URIs and guarded URL sources), forward them as multimodal `images`, accept image-only user turns, enforce per-request image-part/byte budgets, default URL-based image fetches to disabled unless explicitly enabled by config, and redact image base64 data in cache-trace/provider payload diagnostics. (#17685) Thanks @vincentkoc
+- ACP/ACPX session bootstrap: retry with `sessions new` when `sessions ensure` returns no session identifiers so ACP spawns avoid `NO_SESSION`/`ACP_TURN_FAILED` failures on affected agents. (#28786, #31338, #34055). Thanks @Sid-Qin and @vincentkoc.
+- ACP/sessions_spawn parent stream visibility: add `streamTo: "parent"` for `runtime: "acp"` to forward initial child-run progress/no-output/completion updates back into the requester session as system events (instead of direct child delivery), and emit a tail-able session-scoped relay log (`.acp-stream.jsonl`, returned as `streamLogPath` when available), improving orchestrator visibility for blocked or long-running harness turns. (#34310, #29909; reopened from #34055). Thanks @vincentkoc.
+- Agents/bootstrap truncation warning handling: unify bootstrap budget/truncation analysis across embedded + CLI runtime, `/context`, and `openclaw doctor`; add `agents.defaults.bootstrapPromptTruncationWarning` (`off|once|always`, default `once`) and persist warning-signature metadata so truncation warnings are consistent and deduped across turns. (#32769) Thanks @gumadeiras.
+- Agents/Skills runtime loading: propagate run config into embedded attempt and compaction skill-entry loading so explicitly enabled bundled companion skills are discovered consistently when skill snapshots do not already provide resolved entries. Thanks @gumadeiras.
+- Agents/Session startup date grounding: substitute `YYYY-MM-DD` placeholders in startup/post-compaction AGENTS context and append runtime current-time lines for `/new` and `/reset` prompts so daily-memory references resolve correctly. (#32381) Thanks @chengzhichao-xydt.
+- Agents/Compaction template heading alignment: update AGENTS template section names to `Session Startup`/`Red Lines` and keep legacy `Every Session`/`Safety` fallback extraction so post-compaction context remains intact across template versions. (#25098) thanks @echoVic.
+- Agents/Compaction continuity: expand staged-summary merge instructions to preserve active task status, batch progress, latest user request, and follow-up commitments so compaction handoffs retain in-flight work context. (#8903) thanks @joetomasone.
+- Agents/Compaction safeguard structure hardening: require exact fallback summary headings, sanitize untrusted compaction instruction text before prompt embedding, and keep structured sections when preserving all turns. (#25555) thanks @rodrigouroz.
+- Gateway/status self version reporting: make Gateway self version in `openclaw status` prefer runtime `VERSION` (while preserving explicit `OPENCLAW_VERSION` override), preventing stale post-upgrade app version output. (#32655) thanks @liuxiaopai-ai.
+- Memory/QMD index isolation: set `QMD_CONFIG_DIR` alongside `XDG_CONFIG_HOME` so QMD config state stays per-agent despite upstream XDG handling bugs, preventing cross-agent collection indexing and excess disk/CPU usage. (#27028) thanks @HenryLoenwind.
+- Memory/QMD collection safety: stop destructive collection rebinds when QMD `collection list` only reports names without path metadata, preventing `memory search` from dropping existing collections if re-add fails. (#36870) Thanks @Adnannnnnnna.
+- Memory/QMD duplicate-document recovery: detect `UNIQUE constraint failed: documents.collection, documents.path` update failures, rebuild managed collections once, and retry update so periodic QMD syncs recover instead of failing every run; includes regression coverage to avoid over-matching unrelated unique constraints. (#27649) Thanks @MiscMich.
+- Memory/local embedding initialization hardening: add regression coverage for transient initialization retry and mixed `embedQuery` + `embedBatch` concurrent startup to lock single-flight initialization behavior. (#15639) thanks @SubtleSpark.
+- CLI/Coding-agent reliability: switch default `claude-cli` non-interactive args to `--permission-mode bypassPermissions`, auto-normalize legacy `--dangerously-skip-permissions` backend overrides to the modern permission-mode form, align coding-agent + live-test docs with the non-PTY Claude path, and emit session system-event heartbeat notices when CLI watchdog no-output timeouts terminate runs. Related to #28261. Landed from contributor PRs #28610 and #31149. Thanks @niceysam, @cryptomaltese and @vincentkoc.
+- ACP/ACPX session bootstrap: retry with `sessions new` when `sessions ensure` returns no session identifiers so ACP spawns avoid `NO_SESSION`/`ACP_TURN_FAILED` failures on affected agents. Related to #28786. Landed from contributor PR #31338. Thanks @Sid-Qin and @vincentkoc.
+- LINE/auth boundary hardening synthesis: enforce strict LINE webhook authn/z boundary semantics across pairing-store account scoping, DM/group allowlist separation, fail-closed webhook auth/runtime behavior, and replay/duplication controls (including in-flight replay reservation and post-success dedupe marking). (from #26701, #26683, #25978, #17593, #16619, #31990, #26047, #30584, #18777) Thanks @bmendonca3, @davidahmann, @harshang03, @haosenwang1018, @liuxiaopai-ai, @coygeek, and @Takhoffman.
+- LINE/media download synthesis: fix file-media download handling and M4A audio classification across overlapping LINE regressions. (from #26386, #27761, #27787, #29509, #29755, #29776, #29785, #32240) Thanks @kevinWangSheng, @loiie45e, @carrotRakko, @Sid-Qin, @codeafridi, and @bmendonca3.
+- LINE/context and routing synthesis: fix group/room peer routing and command-authorization context propagation, and keep processing later events in mixed-success webhook batches. (from #21955, #24475, #27035, #28286) Thanks @lailoo, @mcaxtr, @jervyclaw, @Glucksberg, and @Takhoffman.
+- LINE/status/config/webhook synthesis: fix status false positives from snapshot/config state and accept LINE webhook HEAD probes for compatibility. (from #10487, #25726, #27537, #27908, #31387) Thanks @BlueBirdBack, @stakeswky, @loiie45e, @puritysb, and @mcaxtr.
+- LINE cleanup/test follow-ups: fold cleanup/test learnings into the synthesis review path while keeping runtime changes focused on regression fixes. (from #17630, #17289) Thanks @Clawborn and @davidahmann.
+- Mattermost/interactive buttons: add interactive button send/callback support with directory-based channel/user target resolution, and harden callbacks via account-scoped HMAC verification plus sender-scoped DM routing. (#19957) thanks @tonydehnke.
+- Feishu/groupPolicy legacy alias compatibility: treat legacy `groupPolicy: "allowall"` as `open` in both schema parsing and runtime policy checks so intended open-group configs no longer silently drop group messages when `groupAllowFrom` is empty. (from #36358) Thanks @Sid-Qin.
+- Mattermost/plugin SDK import policy: replace remaining monolithic `openclaw/plugin-sdk` imports in Mattermost mention-gating paths/tests with scoped subpaths (`openclaw/plugin-sdk/compat` and `openclaw/plugin-sdk/mattermost`) so `pnpm check` passes `lint:plugins:no-monolithic-plugin-sdk-entry-imports` on baseline. (#36480) Thanks @Takhoffman.
+- Telegram/polls: add Telegram poll action support to channel action discovery and tool/CLI poll flows, with multi-account discoverability gated to accounts that can actually execute polls (`sendMessage` + `poll`). (#36547) thanks @gumadeiras.
+- Agents/failover cooldown classification: stop treating generic `cooling down` text as provider `rate_limit` so healthy models no longer show false global cooldown/rate-limit warnings while explicit `model_cooldown` markers still trigger failover. (#32972) thanks @stakeswky.
+- Agents/failover service-unavailable handling: stop treating bare proxy/CDN `service unavailable` errors as provider overload while keeping them retryable via the timeout/failover path, so transient outages no longer show false rate-limit warnings or block fallback. (#36646) thanks @jnMetaCode.
+- Plugins/HTTP route migration diagnostics: rewrite legacy `api.registerHttpHandler(...)` loader failures into actionable migration guidance so doctor/plugin diagnostics point operators to `api.registerHttpRoute(...)` or `registerPluginHttpRoute(...)`. (#36794) Thanks @vincentkoc
+- Doctor/Heartbeat upgrade diagnostics: warn when heartbeat delivery is configured with an implicit `directPolicy` so upgrades pin direct/DM behavior explicitly instead of relying on the current default. (#36789) Thanks @vincentkoc.
+- Agents/current-time UTC anchor: append a machine-readable UTC suffix alongside local `Current time:` lines in shared cron-style prompt contexts so agents can compare UTC-stamped workspace timestamps without doing timezone math. (#32423) thanks @jriff.
+- Ollama/local model handling: preserve explicit lower `contextWindow` / `maxTokens` overrides during merge refresh, and keep native Ollama streamed replies from surfacing fallback `thinking` / `reasoning` text once real content starts streaming. (#39292) Thanks @vincentkoc.
+- TUI/webchat command-owner scope alignment: treat internal-channel gateway sessions with `operator.admin` as owner-authorized in command auth, restoring cron/gateway/connector tool access for affected TUI/webchat sessions while keeping external channels on identity-based owner checks. (from #35666, #35673, #35704) Thanks @Naylenv, @Octane0411, and @Sid-Qin.
+- Discord/inbound timeout isolation: separate inbound worker timeout tracking from listener timeout budgets so queued Discord replies are no longer dropped when listener watchdog windows expire mid-run. (#36602) Thanks @dutifulbob.
+- Memory/doctor SecretRef handling: treat SecretRef-backed memory-search API keys as configured, and fail embedding setup with explicit unresolved-secret errors instead of crashing. (#36835) Thanks @joshavant.
+- Memory/flush default prompt: ban timestamped variant filenames during default memory flush runs so durable notes stay in the canonical daily `memory/YYYY-MM-DD.md` file. (#34951) thanks @zerone0x.
+- Agents/reply delivery timing: flush embedded Pi block replies before waiting on compaction retries so already-generated assistant replies reach channels before compaction wait completes. (#35489) thanks @Sid-Qin.
+- Agents/gateway config guidance: stop exposing `config.schema` through the agent `gateway` tool, remove prompt/docs guidance that told agents to call it, and keep agents on `config.get` plus `config.patch`/`config.apply` for config changes. (#7382) thanks @kakuteki.
+- Provider/KiloCode: Keep duplicate models after malformed discovery rows, and strip legacy `reasoning_effort` when proxy reasoning injection is skipped. (#32352) Thanks @pandemicsyn and @vincentkoc.
+- Agents/failover: classify periodic provider limit exhaustion text (for example `Weekly/Monthly Limit Exhausted`) as `rate_limit` while keeping explicit `402 Payment Required` variants in billing, so failover continues without misclassifying billing-wrapped quota errors. (#33813) thanks @zhouhe-xydt.
+- Mattermost/interactive button callbacks: allow external callback base URLs and stop requiring loopback-origin requests so button clicks work when Mattermost reaches the gateway over Tailscale, LAN, or a reverse proxy. (#37543) thanks @mukhtharcm.
+- Gateway/chat.send route inheritance: keep explicit external delivery for channel-scoped sessions while preventing shared-main and other channel-agnostic webchat sessions from inheriting stale external routes, so Control UI replies stay on webchat without breaking selected channel-target sessions. (#34669) Thanks @vincentkoc.
+- Telegram/Discord media upload caps: make outbound uploads honor channel `mediaMaxMb` config, raise Telegram's default media cap to 100MB, and remove MIME fallback limits that kept some Telegram uploads at 16MB. Thanks @vincentkoc.
+- Skills/nano-banana-pro resolution override: respect explicit `--resolution` values during image editing and only auto-detect output size from input images when the flag is omitted. (#36880) Thanks @shuofengzhang and @vincentkoc.
+- Skills/openai-image-gen CLI validation: validate `--background` and `--style` inputs early, normalize supported values, and warn when those flags are ignored for incompatible models. (#36762) Thanks @shuofengzhang and @vincentkoc.
+- Skills/openai-image-gen output formats: validate `--output-format` values early, normalize aliases like `jpg -> jpeg`, and warn when the flag is ignored for incompatible models. (#36648) Thanks @shuofengzhang and @vincentkoc.
+- ACP/skill env isolation: strip skill-injected API keys from ACP harness child-process environments so tools like Codex CLI keep their own auth flow instead of inheriting billed provider keys from active skills. (#36316) Thanks @taw0002 and @vincentkoc.
+- WhatsApp media upload caps: make outbound media sends and auto-replies honor `channels.whatsapp.mediaMaxMb` with per-account overrides so inbound and outbound limits use the same channel config. Thanks @vincentkoc.
+- Windows/Plugin install: when OpenClaw runs on Windows via Bun and `npm-cli.js` is not colocated with the runtime binary, fall back to `npm.cmd`/`npx.cmd` through the existing `cmd.exe` wrapper so `openclaw plugins install` no longer fails with `spawn EINVAL`. (#38056) Thanks @0xlin2023.
+- Telegram/send retry classification: retry grammY `Network request ... failed after N attempts` envelopes in send flows without reclassifying plain `Network request ... failed!` wrappers as transient, restoring the intended retry path while keeping broad send-context message matching tight. (#38056) Thanks @0xlin2023.
+- Gateway/probes: keep `/health`, `/healthz`, `/ready`, and `/readyz` reachable when the Control UI is mounted at `/`, preserve plugin-owned route precedence on those paths, and make `/ready` and `/readyz` report channel-backed readiness with startup grace plus `503` on disconnected managed channels, while `/health` and `/healthz` stay shallow liveness probes. (#18446) Thanks @vibecodooor, @mahsumaktas, and @vincentkoc.
+- Feishu/media downloads: drop invalid timeout fields from SDK method calls now that client-level `httpTimeoutMs` applies to requests. (#38267) Thanks @ant1eicher and @thewilloftheshadow.
+- PI embedded runner/Feishu docs: propagate sender identity into embedded attempts so Feishu doc auto-grant restores requester access for embedded-runner executions. (#32915) thanks @cszhouwei.
+- Agents/usage normalization: normalize missing or partial assistant usage snapshots before compaction accounting so `openclaw agent --json` no longer crashes when provider payloads omit `totalTokens` or related usage fields. (#34977) thanks @sp-hk2ldn.
+- Venice/default model refresh: switch the built-in Venice default to `kimi-k2-5`, update onboarding aliasing, and refresh Venice provider docs/recommendations to match the current private and anonymized catalog. (from #12964) Fixes #20156. Thanks @sabrinaaquino and @vincentkoc.
+- Agents/skill API write pacing: add a global prompt guardrail that treats skill-driven external API writes as rate-limited by default, so runners prefer batched writes, avoid tight request loops, and respect `429`/`Retry-After`. Thanks @vincentkoc.
+- Google Chat/multi-account webhook auth fallback: when `channels.googlechat.accounts.default` carries shared webhook audience/path settings (for example after config normalization), inherit those defaults for named accounts while preserving top-level and per-account overrides, so inbound webhook verification no longer fails silently for named accounts missing duplicated audience fields. Fixes #38369.
+- Models/tool probing: raise the tool-capability probe budget from 32 to 256 tokens so reasoning models that spend tokens on thinking before returning a required tool call are less likely to be misclassified as not supporting tools. (#7521) Thanks @jakobdylanc.
+- Gateway/transient network classification: treat wrapped `...: fetch failed` transport messages as transient while avoiding broad matches like `Web fetch failed (404): ...`, preventing Discord reconnect wrappers from crashing the gateway without suppressing non-network tool failures. (#38530) Thanks @xinhuagu.
+- ACP/console silent reply suppression: filter ACP `NO_REPLY` lead fragments and silent-only finals before `openclaw agent` logging/delivery so console-backed ACP sessions no longer leak `NO`/`NO_REPLY` placeholders. (#38436) Thanks @ql-wade.
+- Feishu/reply delivery reliability: disable block streaming in Feishu reply options so plain-text auto-render replies are no longer silently dropped before final delivery. (#38258) Thanks @xinhuagu.
+- Agents/reply MEDIA delivery: normalize local assistant `MEDIA:` paths before block/final delivery, keep media dedupe aligned with message-tool sends, and contain malformed media normalization failures so generated files send reliably instead of falling back to empty responses. (#38572) Thanks @obviyus.
+- Sessions/bootstrap cache rollover invalidation: clear cached workspace bootstrap snapshots whenever an existing `sessionKey` rolls to a new `sessionId` across auto-reply, command, and isolated cron session resolvers, so `AGENTS.md`/`MEMORY.md`/`USER.md` updates are reloaded after daily, idle, or forced session resets instead of staying stale until gateway restart. (#38494) Thanks @LivingInDrm.
+- Gateway/Telegram polling health monitor: skip stale-socket restarts for Telegram long-polling channels and thread channel identity through shared health evaluation so polling connections are not restarted on the WebSocket stale-socket heuristic. (#38395) Thanks @ql-wade and @Takhoffman.
+- Daemon/systemd fresh-install probe: check for OpenClaw's managed user unit before running `systemctl --user is-enabled`, so first-time Linux installs no longer fail on generic missing-unit probe errors. (#38819) Thanks @adaHubble.
+- Gateway/container lifecycle: allow `openclaw gateway stop` to SIGTERM unmanaged gateway listeners and `openclaw gateway restart` to SIGUSR1 a single unmanaged listener when no service manager is installed, so container and supervisor-based deployments are no longer blocked by `service disabled` no-op responses. Fixes #36137. Thanks @vincentkoc.
+- Gateway/Windows restart supervision: relaunch task-managed gateways through Scheduled Task with quoted helper-script command paths, distinguish restart-capable supervisors per platform, and stop orphaned Windows gateway children during self-restart. (#38825) Thanks @obviyus.
+- Telegram/native topic command routing: resolve forum-topic native commands through the same conversation route as inbound messages so topic `agentId` overrides and bound topic sessions target the active session instead of the default topic-parent session. (#38871) Thanks @obviyus.
+- Markdown/assistant image hardening: flatten remote markdown images to plain text across the Control UI, exported HTML, and shared Swift chat while keeping inline `data:image/...` markdown renderable, so model output no longer triggers automatic remote image fetches. (#38895) Thanks @obviyus.
+- Config/compaction safeguard settings: regression-test `agents.defaults.compaction.recentTurnsPreserve` through `loadConfig()` and cover the new help metadata entry so the exposed preserve knob stays wired through schema validation and config UX. (#25557) thanks @rodrigouroz.
+- iOS/Quick Setup presentation: skip automatic Quick Setup when a gateway is already configured (active connect config, last-known connection, preferred gateway, or manual host), so reconnecting installs no longer get prompted to connect again. (#38964) Thanks @ngutman.
+- CLI/Docs memory help accuracy: clarify `openclaw memory status --deep` behavior and align memory command examples/docs with the current search options. (#31803) Thanks @JasonOA888 and @Avi974.
+- Auto-reply/allowlist store account scoping: keep `/allowlist ... --store` writes scoped to the selected account and clear legacy unscoped entries when removing default-account store access, preventing cross-account default allowlist bleed-through from legacy pairing-store reads. Thanks @tdjackey for reporting and @vincentkoc for the fix.
+- Security/Nostr: harden profile mutation/import loopback guards by failing closed on non-loopback forwarded client headers (`x-forwarded-for` / `x-real-ip`) and rejecting `sec-fetch-site: cross-site`; adds regression coverage for proxy-forwarded and browser cross-site mutation attempts.
+- CLI/bootstrap Node version hint maintenance: replace hardcoded nvm `22` instructions in `openclaw.mjs` with `MIN_NODE_MAJOR` interpolation so future minimum-Node bumps keep startup guidance in sync automatically. (#39056) Thanks @onstash.
+- Discord/native slash command auth: honor `commands.allowFrom.discord` (and `commands.allowFrom["*"]`) in guild slash-command pre-dispatch authorization so allowlisted senders are no longer incorrectly rejected as unauthorized. (#38794) Thanks @jskoiz and @thewilloftheshadow.
+- Outbound/message target normalization: ignore empty legacy `to`/`channelId` fields when explicit `target` is provided so valid target-based sends no longer fail legacy-param validation; includes regression coverage. (#38944) Thanks @Narcooo.
+- Models/auth token prompts: guard cancelled manual token prompts so `Symbol(clack:cancel)` values cannot be persisted into auth profiles; adds regression coverage for cancelled `models auth paste-token`. (#38951) Thanks @MumuTW.
+- Gateway/loopback announce URLs: treat `http://` and `https://` aliases with the same loopback/private-network policy as websocket URLs so loopback cron announce delivery no longer fails secure URL validation. (#39064) Thanks @Narcooo.
+- Models/default provider fallback: when the hardcoded default provider is removed from `models.providers`, resolve defaults from configured providers instead of reporting stale removed-provider defaults in status output. (#38947) Thanks @davidemanuelDEV.
+- Agents/cache-trace stability: guard stable stringify against circular references in trace payloads so near-limit payloads no longer crash with `Maximum call stack size exceeded`; adds regression coverage. (#38935) Thanks @MumuTW.
+- Extensions/diffs CI stability: add `headers` to the `localReq` test helper in `extensions/diffs/index.test.ts` so forwarding-hint checks no longer crash with `req.headers` undefined. (supersedes #39063) Thanks @Shennng.
+- Agents/compaction thresholding: apply `agents.defaults.contextTokens` cap to the model passed into embedded run and `/compact` session creation so auto-compaction thresholds use the effective context window, not native model max context. (#39099) Thanks @MumuTW.
+- Models/merge mode provider precedence: when `models.mode: "merge"` is active and config explicitly sets a provider `baseUrl`, keep config as source of truth instead of preserving stale runtime `models.json` `baseUrl` values; includes normalized provider-key coverage. (#39103) Thanks @BigUncle.
+- UI/Control chat tool streaming: render tool events live in webchat without requiring refresh by enabling `tool-events` capability, fixing stream/event correlation, and resetting/reloading stream state around tool results and terminal events. (#39104) Thanks @jakepresent.
+- Models/provider apiKey persistence hardening: when a provider `apiKey` value equals a known provider env var value, persist the canonical env var name into `models.json` instead of resolved plaintext secrets. (#38889) Thanks @gambletan.
+- Discord/model picker persistence check: add a short post-dispatch settle delay before reading back session model state so picker confirmations stop reporting false mismatch warnings after successful model switches. (#39105) Thanks @akropp.
+- Agents/OpenAI WS compat store flag: omit `store` from `response.create` payloads when model compat sets `supportsStore: false`, preventing strict OpenAI-compatible providers from rejecting websocket requests with unknown-field errors. (#39113) Thanks @scoootscooob.
+- Config/validation log sanitization: sanitize config-validation issue paths/messages before logging so control characters and ANSI escape sequences cannot inject misleading terminal output from crafted config content. (#39116) Thanks @powermaster888.
+- Agents/compaction counter accuracy: count successful overflow-triggered auto-compactions (`willRetry=true`) in the compaction counter while still excluding aborted/no-result events, so `/status` reflects actual safeguard compaction activity. (#39123) Thanks @MumuTW.
+- Gateway/chat delta ordering: flush buffered assistant deltas before emitting tool `start` events so pre-tool text is delivered to Control UI before tool cards, avoiding transient text/tool ordering artifacts in streaming. (#39128) Thanks @0xtangping.
+- Voice-call plugin schema parity: add missing manifest `configSchema` fields (`webhookSecurity`, `streaming.preStartTimeoutMs|maxPendingConnections|maxPendingConnectionsPerIp|maxConnections`, `staleCallReaperSeconds`) so gateway AJV validation accepts already-supported runtime config instead of failing with `additionalProperties` errors. (#38892) Thanks @giumex.
+- Agents/OpenAI WS reconnect retry accounting: avoid double retry scheduling when reconnect failures emit both `error` and `close`, so retry budgets track actual reconnect attempts instead of exhausting early. (#39133) Thanks @scoootscooob.
+- Daemon/Windows schtasks runtime detection: use locale-invariant `Last Run Result` running codes (`0x41301`/`267009`) as the primary running signal so `openclaw node status` no longer misreports active tasks as stopped on non-English Windows locales. (#39076) Thanks @ademczuk.
+- Usage/token count formatting: round near-million token counts to millions (`1.0m`) instead of `1000k`, with explicit boundary coverage for `999_499` and `999_500`. (#39129) Thanks @CurryMessi.
+- Gateway/session bootstrap cache invalidation ordering: clear bootstrap snapshots only after active embedded-run shutdown wait completes, preventing dying runs from repopulating stale cache between `/new`/`sessions.reset` turns. (#38873) Thanks @MumuTW.
+- Browser/dispatcher error clarity: preserve dispatcher-side failure context in browser fetch errors while still appending operator guidance and explicit no-retry model hints, preventing misleading `"Can't reach service"` wrapping and avoiding LLM retry loops. (#39090) Thanks @NewdlDewdl.
+- Telegram/polling offset safety: confirm persisted offsets before polling startup while validating stored `lastUpdateId` values as non-negative safe integers (with overflow guards) so malformed offset state cannot cause update skipping/dropping. (#39111) Thanks @MumuTW.
+- Telegram/status SecretRef read-only resolution: resolve env-backed bot-token SecretRefs in config-only/status inspection while respecting provider source/defaults and env allowlists, so status no longer crashes or reports false-ready tokens for disallowed providers. (#39130) Thanks @neocody.
+- Agents/OpenAI WS max-token zero forwarding: treat `maxTokens: 0` as an explicit value in websocket `response.create` payloads (instead of dropping it as falsy), with regression coverage for zero-token forwarding. (#39148) Thanks @scoootscooob.
+- Podman/.env gateway bind precedence: evaluate `OPENCLAW_GATEWAY_BIND` after sourcing `.env` in `run-openclaw-podman.sh` so env-file overrides are honored. (#38785) Thanks @majinyu666.
+- Models/default alias refresh: bump `gpt` to `openai/gpt-5.4` and Gemini defaults to `gemini-3.1` preview aliases (including normalization/default wiring) to track current model IDs. (#38638) Thanks @ademczuk.
+- Config/env substitution degraded mode: convert missing `${VAR}` resolution in config reads from hard-fail to warning-backed degraded behavior, while preventing unresolved placeholders from being accepted as gateway credentials. (#39050) Thanks @akz142857.
+- Discord inbound listener non-blocking dispatch: make `MESSAGE_CREATE` listener handoff asynchronous (no per-listener queue blocking), so long runs no longer stall unrelated incoming events. (#39154) Thanks @yaseenkadlemakki.
+- Daemon/Windows PATH freeze fix: stop persisting install-time `PATH` snapshots into Scheduled Task scripts so runtime tool lookup follows current host PATH updates; also refresh local TUI history on silent local finals. (#39139) Thanks @Narcooo.
+- Gateway/systemd service restart hardening: clear stale gateway listeners by explicit run-port before service bind, add restart stale-pid port-override support, tune systemd start/stop/exit handling, and disable detached child mode only in service-managed runtime so cgroup stop semantics clean up descendants reliably. (#38463) Thanks @spirittechie.
+- Discord/plugin native command aliases: let plugins declare provider-specific slash names so native Discord registration can avoid built-in command collisions; the bundled Talk voice plugin now uses `/talkvoice` natively on Discord while keeping text `/voice`.
+- Daemon/Windows schtasks status normalization: derive runtime state from locale-neutral numeric `Last Run Result` codes only (without language string matching) and surface unknown when numeric result data is unavailable, preventing locale-specific misclassification drift. (#39153) Thanks @scoootscooob.
+- Telegram/polling conflict recovery: reset the polling `webhookCleared` latch on `getUpdates` 409 conflicts so webhook cleanup re-runs on restart cycles and polling avoids infinite conflict loops. (#39205) Thanks @amittell.
+- Heartbeat/requests-in-flight scheduling: stop advancing `nextDueMs` and avoid immediate `scheduleNext()` timer overrides on requests-in-flight skips, so wake-layer retry cooldowns are honored and heartbeat cadence no longer drifts under sustained contention. (#39182) Thanks @MumuTW.
+- Memory/SQLite contention resilience: re-apply `PRAGMA busy_timeout` on every sync-store and QMD connection open so process restarts/reopens no longer revert to immediate `SQLITE_BUSY` failures under lock contention. (#39183) Thanks @MumuTW.
+- Gateway/webchat route safety: block webchat/control-ui clients from inheriting stored external delivery routes on channel-scoped sessions (while preserving route inheritance for UI/TUI clients), preventing cross-channel leakage from scoped chats. (#39175) Thanks @widingmarcus-cyber.
+- Telegram error-surface resilience: return a user-visible fallback reply when dispatch/debounce processing fails instead of going silent, while preserving draft-stream cleanup and best-effort thread-scoped fallback delivery. (#39209) Thanks @riftzen-bit.
+- Gateway/password auth startup diagnostics: detect unresolved provider-reference objects in `gateway.auth.password` and fail with a specific bootstrap-secrets error message instead of generic misconfiguration output. (#39230) Thanks @ademczuk.
+- Agents/OpenAI-responses compatibility: strip unsupported `store` payload fields when `supportsStore=false` (including OpenAI-compatible non-OpenAI providers) while preserving server-compaction payload behavior. (#39219) Thanks @ademczuk.
+- Agents/model fallback visibility: warn when configured model IDs cannot be resolved and fallback is applied, with log-safe sanitization of model text to prevent control-sequence injection in warning output. (#39215) Thanks @ademczuk.
+- Outbound delivery replay safety: use two-phase delivery ACK markers (`.json` -> `.delivered` -> unlink) and startup marker cleanup so crash windows between send and cleanup do not replay already-delivered messages. (#38668) Thanks @Gundam98.
+- Nodes/system.run approval binding: carry prepared approval plans through gateway forwarding and bind interpreter-style script operands across approval to execution, so post-approval script rewrites are denied while unchanged approved script runs keep working. Thanks @tdjackey for reporting.
+- Nodes/system.run PowerShell wrapper parsing: treat `pwsh`/`powershell` `-EncodedCommand` forms as shell-wrapper payloads so allowlist mode still requires approval instead of falling back to plain argv analysis. Thanks @tdjackey for reporting.
+- Control UI/auth error reporting: map generic browser `Fetch failed` websocket close errors back to actionable gateway auth messages (`gateway token mismatch`, `authentication failed`, `retry later`) so dashboard disconnects stop hiding credential problems. Landed from contributor PR #28608 by @KimGLee. Thanks @KimGLee.
+- Media/mime unknown-kind handling: return `undefined` (not `"unknown"`) for missing/unrecognized MIME kinds and use document-size fallback caps for unknown remote media, preventing phantom `` Signal events from being treated as real messages. (#39199) Thanks @nicolasgrasset.
+- Nodes/system.run allow-always persistence: honor shell comment semantics during allowlist analysis so `#`-tailed payloads that never execute are not persisted as trusted follow-up commands. Thanks @tdjackey for reporting.
+- Signal/inbound attachment fan-in: forward all successfully fetched inbound attachments through `MediaPaths`/`MediaUrls`/`MediaTypes` (instead of only the first), and improve multi-attachment placeholder summaries in mention-gated pending history. (#39212) Thanks @joeykrug.
+- Nodes/system.run dispatch-wrapper boundary: keep shell-wrapper approval classification active at the depth boundary so `env` wrapper stacks cannot reach `/bin/sh -c` execution without the expected approval gate. Thanks @tdjackey for reporting.
+- Docker/token persistence on reconfigure: reuse the existing `.env` gateway token during `docker-setup.sh` reruns and align compose token env defaults, so Docker installs stop silently rotating tokens and breaking existing dashboard sessions. Landed from contributor PR #33097 by @chengzhichao-xydt. Thanks @chengzhichao-xydt.
+- Agents/strict OpenAI turn ordering: apply assistant-first transcript bootstrap sanitization to strict OpenAI-compatible providers (for example vLLM/Gemma via `openai-completions`) without adding Google-specific session markers, preventing assistant-first history rejections. (#39252) Thanks @scoootscooob.
+- Discord/exec approvals gateway auth: pass resolved shared gateway credentials into the Discord exec-approvals gateway client so token-auth installs stop failing approvals with `gateway token mismatch`. Related to #38179. Thanks @0riginal-claw for the adjacent PR #35147 investigation.
+- Subagents/workspace inheritance: propagate parent workspace directory to spawned subagent runs so child sessions reliably inherit workspace-scoped instructions (`AGENTS.md`, `SOUL.md`, etc.) without exposing workspace override through tool-call arguments. (#39247) Thanks @jasonQin6.
+- Exec approvals/gateway-node policy: honor explicit `ask=off` from `exec-approvals.json` even when runtime defaults are stricter, so trusted full/off setups stop re-prompting on gateway and node exec paths. Landed from contributor PR #26789 by @pandego. Thanks @pandego.
+- Exec approvals/config fallback: inherit `ask` from `exec-approvals.json` when `tools.exec.ask` is unset, so local full/off defaults no longer fall back to `on-miss` for exec tool and `nodes run`. Landed from contributor PR #29187 by @Bartok9. Thanks @Bartok9.
+- Exec approvals/allow-always shell scripts: persist and match script paths for wrapper invocations like `bash scripts/foo.sh` while still blocking `-c`/`-s` wrapper bypasses. Landed from contributor PR #35137 by @yuweuii. Thanks @yuweuii.
+- Queue/followup dedupe across drain restarts: dedupe queued redelivery `message_id` values after queue recreation so busy-session followups no longer duplicate on replayed inbound events. Landed from contributor PR #33168 by @rylena. Thanks @rylena.
+- Telegram/preview-final edit idempotence: treat `message is not modified` errors during preview finalization as delivered so partial-stream final replies do not fall back to duplicate sends. Landed from contributor PR #34983 by @HOYALIM. Thanks @HOYALIM.
+- Telegram/DM streaming transport parity: use message preview transport for all DM streaming lanes so final delivery can edit the active preview instead of sending duplicate finals. Landed from contributor PR #38906 by @gambletan. Thanks @gambletan.
+- Telegram/DM draft streaming restoration: restore native `sendMessageDraft` preview transport for DM answer streaming while keeping reasoning on message transport, with regression coverage to keep draft finalization from sending duplicate finals. (#39398) Thanks @obviyus.
+- Telegram/send retry safety: retry non-idempotent send paths only for pre-connect failures and make custom retry predicates strict, preventing ambiguous reconnect retries from sending duplicate messages. Landed from contributor PR #34238 by @hal-crackbot. Thanks @hal-crackbot.
+- ACP/run spawn delivery bootstrap: stop reusing requester inline delivery targets for one-shot `mode: "run"` ACP spawns, so fresh run-mode workers bootstrap in isolation instead of inheriting thread-bound session delivery behavior. (#39014) Thanks @lidamao633.
+- Discord/DM session-key normalization: rewrite legacy `discord:dm:*` and phantom direct-message `discord:channel:` session keys to `discord:direct:*` when the sender matches, so multi-agent Discord DMs stop falling into empty channel-shaped sessions and resume replying correctly.
+- Discord/native slash session fallback: treat empty configured bound-session keys as missing so `/status` and other native commands fall back to the routed slash session and routed channel session instead of blanking Discord session keys in normal channel bindings.
+- Agents/tool-call dispatch normalization: normalize provider-prefixed tool names before dispatch across `toolCall`, `toolUse`, and `functionCall` blocks, while preserving multi-segment tool suffixes when stripping provider wrappers so malformed-but-recoverable tool names no longer fail with `Tool not found`. (#39328) Thanks @vincentkoc.
+- Agents/parallel tool-call compatibility: honor `parallel_tool_calls` / `parallelToolCalls` extra params only for `openai-completions` and `openai-responses` payloads, preserve higher-precedence alias overrides across config and runtime layers, and ignore invalid non-boolean values so single-tool-call providers like NVIDIA-hosted Kimi stop failing on forced parallel tool-call payloads. (#37048) Thanks @vincentkoc.
+- Config/invalid-load fail-closed: stop converting `INVALID_CONFIG` into an empty runtime config, keep valid settings available only through explicit best-effort diagnostic reads, and route read-only CLI diagnostics through that path so unknown keys no longer silently drop security-sensitive config. (#28140) Thanks @bobsahur-robot and @vincentkoc.
+- Agents/codex-cli sandbox defaults: switch the built-in Codex backend from `read-only` to `workspace-write` so spawned coding runs can edit files out of the box. Landed from contributor PR #39336 by @0xtangping. Thanks @0xtangping.
+- Gateway/health-monitor restart reason labeling: report `disconnected` instead of `stuck` for clean channel disconnect restarts, so operator logs distinguish socket drops from genuinely stuck channels. (#36436) Thanks @Sid-Qin.
+- Control UI/agents-page overrides: auto-create minimal per-agent config entries when editing inherited agents, so model/tool/skill changes enable Save and inherited model fallbacks can be cleared by writing a primary-only override. Landed from contributor PR #39326 by @dunamismax. Thanks @dunamismax.
+- Gateway/Telegram webhook-mode recovery: add `webhookCertPath` to re-upload self-signed certificates during webhook registration and skip stale-socket detection for webhook-mode channels, so Telegram webhook setups survive health-monitor restarts. Landed from contributor PR #39313 by @fellanH. Thanks @fellanH.
+- Discord/config schema parity: add `channels.discord.agentComponents` to the strict Zod config schema so valid `agentComponents.enabled` settings (root and account-scoped) no longer fail with unrecognized-key validation errors. Landed from contributor PR #39378 by @gambletan. Thanks @gambletan and @thewilloftheshadow.
+- ACPX/MCP session bootstrap: inject configured MCP servers into ACP `session/new` and `session/load` for acpx-backed sessions, restoring Canva and other external MCP tools. Landed from contributor PR #39337. Thanks @goodspeed-apps.
+- Control UI/Telegram sender labels: preserve inbound sender labels in sanitized chat history so dashboard user-message groups split correctly and show real group-member names instead of `You`. (#39414) Thanks @obviyus.
+- Agents/failover 402 recovery: keep temporary spend-limit `402` payloads retryable, preserve explicit insufficient-credit billing detection even in long provider payloads, and allow throttled billing-cooldown probes so single-provider setups can recover instead of staying locked out. (#38533) Thanks @xialonglee.
+- Browser/config schema: accept `browser.profiles.*.driver: "openclaw"` while preserving legacy `"clawd"` compatibility in validated config. (#39374; based on #35621) Thanks @gambletan and @ingyukoh.
+
+## 2026.3.2
+
+### Changes
+
+- Secrets/SecretRef coverage: expand SecretRef support across the full supported user-supplied credential surface (64 targets total), including runtime collectors, `openclaw secrets` planning/apply/audit flows, onboarding SecretInput UX, and related docs; unresolved refs now fail fast on active surfaces while inactive surfaces report non-blocking diagnostics. (#29580) Thanks @joshavant.
+- Tools/PDF analysis: add a first-class `pdf` tool with native Anthropic and Google PDF provider support, extraction fallback for non-native models, configurable defaults (`agents.defaults.pdfModel`, `pdfMaxBytesMb`, `pdfMaxPages`), and docs/tests covering routing, validation, and registration. (#31319) Thanks @tyler6204.
+- Outbound adapters/plugins: add shared `sendPayload` support across direct-text-media, Discord, Slack, WhatsApp, Zalo, and Zalouser with multi-media iteration and chunk-aware text fallback. (#30144) Thanks @nohat.
+- Models/MiniMax: add first-class `MiniMax-M2.5-highspeed` support across built-in provider catalogs, onboarding flows, and MiniMax OAuth plugin defaults, while keeping legacy `MiniMax-M2.5-Lightning` compatibility for existing configs.
+- Sessions/Attachments: add inline file attachment support for `sessions_spawn` (subagent runtime only) with base64/utf8 encoding, transcript content redaction, lifecycle cleanup, and configurable limits via `tools.sessions_spawn.attachments`. (#16761) Thanks @napetrov.
+- Telegram/Streaming defaults: default `channels.telegram.streaming` to `partial` (from `off`) so new Telegram setups get live preview streaming out of the box, with runtime fallback to message-edit preview when native drafts are unavailable.
+- Telegram/DM streaming: use `sendMessageDraft` for private preview streaming, keep reasoning/answer preview lanes separated in DM reasoning-stream mode. (#31824) Thanks @obviyus.
+- Telegram/voice mention gating: add optional `disableAudioPreflight` on group/topic config to skip mention-detection preflight transcription for inbound voice notes where operators want text-only mention checks. (#23067) Thanks @yangnim21029.
+- CLI/Config validation: add `openclaw config validate` (with `--json`) to validate config files before gateway startup, and include detailed invalid-key paths in startup invalid-config errors. (#31220) thanks @Sid-Qin.
+- Tools/Diffs: add PDF file output support and rendering quality customization controls (`fileQuality`, `fileScale`, `fileMaxWidth`) for generated diff artifacts, and document PDF as the preferred option when messaging channels compress images. (#31342) Thanks @gumadeiras.
+- Memory/Ollama embeddings: add `memorySearch.provider = "ollama"` and `memorySearch.fallback = "ollama"` support, honor `models.providers.ollama` settings for memory embedding requests, and document Ollama embedding usage. (#26349) Thanks @nico-hoff.
+- Zalo Personal plugin (`@openclaw/zalouser`): rebuilt channel runtime to use native `zca-js` integration in-process, removing external CLI transport usage and keeping QR/login + send/listen flows fully inside OpenClaw.
+- Plugin SDK/channel extensibility: expose `channelRuntime` on `ChannelGatewayContext` so external channel plugins can access shared runtime helpers (reply/routing/session/text/media/commands) without internal imports. (#25462) Thanks @guxiaobo.
+- Plugin runtime/STT: add `api.runtime.stt.transcribeAudioFile(...)` so extensions can transcribe local audio files through OpenClaw's configured media-understanding audio providers. (#22402) Thanks @benthecarman.
+- Plugin hooks/session lifecycle: include `sessionKey` in `session_start`/`session_end` hook events and contexts so plugins can correlate lifecycle callbacks with routing identity. (#26394) Thanks @tempeste.
+- Hooks/message lifecycle: add internal hook events `message:transcribed` and `message:preprocessed`, plus richer outbound `message:sent` context (`isGroup`, `groupId`) for group-conversation correlation and post-transcription automations. (#9859) Thanks @Drickon.
+- Media understanding/audio echo: add optional `tools.media.audio.echoTranscript` + `echoFormat` to send a pre-agent transcript confirmation message to the originating chat, with echo disabled by default. (#32150) Thanks @AytuncYildizli.
+- Plugin runtime/system: expose `runtime.system.requestHeartbeatNow(...)` so extensions can wake targeted sessions immediately after enqueueing system events. (#19464) Thanks @AustinEral.
+- Plugin runtime/events: expose `runtime.events.onAgentEvent` and `runtime.events.onSessionTranscriptUpdate` for extension-side subscriptions, and isolate transcript-listener failures so one faulty listener cannot break the entire update fanout. (#16044) Thanks @scifantastic.
+- CLI/Banner taglines: add `cli.banner.taglineMode` (`random` | `default` | `off`) to control funny tagline behavior in startup output, with docs + FAQ guidance and regression tests for config override behavior.
+- Agents/compaction safeguard quality-audit rollout: keep summary quality audits disabled by default unless `agents.defaults.compaction.qualityGuard` is explicitly enabled, and add config plumbing for bounded retry control. (#25556) thanks @rodrigouroz.
+- Gateway/input_image MIME validation: sniff uploaded image bytes before MIME allowlist enforcement again so declared image types cannot mask concrete non-image payloads, while keeping HEIC/HEIF normalization behavior scoped to actual HEIC inputs. Thanks @vincentkoc.
+- Zalo Personal plugin (`@openclaw/zalouser`): keep canonical DM routing while preserving legacy DM session continuity on upgrade, and preserve provider-native `g-`/`u-` target ids in outbound send and directory flows so #33992 lands without breaking existing sessions or stored targets. (#33992) Thanks @darkamenosa.
+
+### Breaking
+
+- **BREAKING:** Onboarding now defaults `tools.profile` to `messaging` for new local installs (interactive + non-interactive). New setups no longer start with broad coding/system tools unless explicitly configured.
+- **BREAKING:** ACP dispatch now defaults to enabled unless explicitly disabled (`acp.dispatch.enabled=false`). If you need to pause ACP turn routing while keeping `/acp` controls, set `acp.dispatch.enabled=false`. Docs: https://docs.openclaw.ai/tools/acp-agents
+- **BREAKING:** Plugin SDK removed `api.registerHttpHandler(...)`. Plugins must register explicit HTTP routes via `api.registerHttpRoute({ path, auth, match, handler })`, and dynamic webhook lifecycles should use `registerPluginHttpRoute(...)`.
+- **BREAKING:** Zalo Personal plugin (`@openclaw/zalouser`) no longer depends on external `zca`-compatible CLI binaries (`openzca`, `zca-cli`) for runtime send/listen/login; operators should use `openclaw channels login --channel zalouser` after upgrade to refresh sessions in the new JS-native path.
+
+### Fixes
+
+- Feishu/Outbound render mode: respect Feishu account `renderMode` in outbound sends so card mode (and auto-detected markdown tables/code blocks) uses markdown card delivery instead of always sending plain text. (#31562) Thanks @arkyu2077.
+- Plugin command/runtime hardening: validate and normalize plugin command name/description at registration boundaries, and guard Telegram native menu normalization paths so malformed plugin command specs cannot crash startup (`trim` on undefined). (#31997) Fixes #31944. Thanks @liuxiaopai-ai.
+- Telegram: guard duplicate-token checks and gateway startup token normalization when account tokens are missing, preventing `token.trim()` crashes during status/start flows. (#31973) Thanks @ningding97.
+- Discord/lifecycle startup status: push an immediate `connected` status snapshot when the gateway is already connected before lifecycle debug listeners attach, with abort-guarding to avoid contradictory status flips during pre-aborted startup. (#32336) Thanks @mitchmcalister.
+- Feishu/inbound mention normalization: preserve all inbound mention semantics by normalizing Feishu mention placeholders into explicit `name` tags (instead of stripping them), improving multi-mention context fidelity in agent prompts while retaining bot/self mention disambiguation. (#30252) Thanks @Lanfei.
+- Feishu/multi-app mention routing: guard mention detection in multi-bot groups by validating mention display name alongside bot `open_id`, preventing false-positive self-mentions from Feishu WebSocket remapping so only the actually mentioned bot responds under `requireMention`. (#30315) Thanks @teaguexiao.
+- Feishu/session-memory hook parity: trigger the shared `before_reset` session-memory hook path when Feishu `/new` and `/reset` commands execute so reset flows preserve memory behavior consistent with other channels. (#31437) Thanks @Linux2010.
+- Feishu/LINE group system prompts: forward per-group `systemPrompt` config into inbound context `GroupSystemPrompt` for Feishu and LINE group/room events so configured group-specific behavior actually applies at dispatch time. (#31713) Thanks @whiskyboy.
+- Mentions/Slack formatting hardening: add null-safe guards for runtime text normalization paths so malformed/undefined text payloads do not crash mention stripping or mrkdwn conversion. (#31865) Thanks @stone-jin.
+- Feishu/Plugin sdk compatibility: add safe webhook default fallbacks when loading Feishu monitor state so mixed-version installs no longer crash if older `openclaw/plugin-sdk` builds omit webhook default constants. (#31606)
+- Feishu/group broadcast dispatch: add configurable multi-agent group broadcast dispatch with observer-session isolation, cross-account dedupe safeguards, and non-mention history buffering rules that avoid duplicate replay in broadcast/topic workflows. (#29575) Thanks @ohmyskyhigh.
+- Gateway/Subagent TLS pairing: allow authenticated local `gateway-client` backend self-connections to skip device pairing while still requiring pairing for non-local/direct-host paths, restoring `sessions_spawn` with `gateway.tls.enabled=true` in Docker/LAN setups. Fixes #30740. Thanks @Sid-Qin and @vincentkoc.
+- Browser/CDP startup diagnostics: include Chrome stderr output and a Linux no-sandbox hint in startup timeout errors so failed launches are easier to diagnose. (#29312) Thanks @veast.
+- Synology Chat/webhook ingress hardening: enforce bounded body reads (size + timeout) via shared request-body guards to prevent unauthenticated slow-body hangs before token validation. (#25831) Thanks @bmendonca3.
+- Feishu/Dedup restart resilience: warm persistent dedup state into memory on monitor startup so retry events after gateway restart stay suppressed without requiring initial on-disk probe misses. (#31605)
+- Voice-call/runtime lifecycle: prevent `EADDRINUSE` loops by resetting failed runtime promises, making webhook `start()` idempotent with the actual bound port, and fully cleaning up webhook/tunnel/tailscale resources after startup failures. (#32395) Thanks @scoootscooob.
+- Gateway/Security hardening: tie loopback-origin dev allowance to actual local socket clients (not Host header claims), add explicit warnings/metrics when `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback` accepts websocket origins, harden safe-regex detection for quantified ambiguous alternation patterns (for example `(a|aa)+`), and bound large regex-evaluation inputs for session-filter and log-redaction paths.
+- Gateway/Plugin HTTP hardening: require explicit `auth` for plugin route registration, add route ownership guards for duplicate `path+match` registrations, centralize plugin path matching/auth logic into dedicated modules, and share webhook target-route lifecycle wiring across channel monitors to avoid stale or conflicting registrations. Thanks @tdjackey for reporting.
+- Browser/Profile defaults: prefer `openclaw` profile over `chrome` in headless/no-sandbox environments unless an explicit `defaultProfile` is configured. (#14944) Thanks @BenediktSchackenberg.
+- Gateway/WS security: keep plaintext `ws://` loopback-only by default, with explicit break-glass private-network opt-in via `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1`; align onboarding/client/call validation and tests to this strict-default policy. (#28670) Thanks @dashed, @vincentkoc.
+- OpenAI Codex OAuth/TLS prerequisites: add an OAuth TLS cert-chain preflight with actionable remediation for cert trust failures, and gate doctor TLS prerequisite probing to OpenAI Codex OAuth-configured installs (or explicit `doctor --deep`) to avoid unconditional outbound probe latency. (#32051) Thanks @alexfilatov.
+- Security/Webhook request hardening: enforce auth-before-body parsing for BlueBubbles and Google Chat webhook handlers, add strict pre-auth body/time budgets for webhook auth paths (including LINE signature verification), and add shared in-flight/request guardrails plus regression tests/lint checks to prevent reintroducing unauthenticated slow-body DoS patterns. Thanks @GCXWLP for reporting.
+- CLI/Config validation and routing hardening: dedupe `openclaw config validate` failures to a single authoritative report, expose allowed-values metadata/hints across core Zod and plugin AJV validation (including `--json` fields), sanitize terminal-rendered validation text, and make command-path parsing root-option-aware across preaction/route/lazy registration (including routed `config get/unset` with split root options). Thanks @gumadeiras.
+- Browser/Extension relay reconnect tolerance: keep `/json/version` and `/cdp` reachable during short MV3 worker disconnects when attached targets still exist, and retain clients across reconnect grace windows. (#30232) Thanks @Sid-Qin.
+- CLI/Browser start timeout: honor `openclaw browser --timeout start` and stop by removing the fixed 15000ms override so slower Chrome startups can use caller-provided timeouts. (#22412, #23427) Thanks @vincentkoc.
+- Synology Chat/gateway lifecycle: keep `startAccount` pending until abort for inactive and active account paths to prevent webhook route restart loops under gateway supervision. (#23074) Thanks @druide67.
+- Exec approvals/allowlist matching: escape regex metacharacters in path-pattern literals (while preserving glob wildcards), preventing crashes on allowlisted executables like `/usr/bin/g++` and correctly matching mixed wildcard/literal token paths. (#32162) Thanks @stakeswky.
+- Synology Chat/webhook compatibility: accept JSON and alias payload fields, allow token resolution from body/query/header sources, and ACK webhook requests with `204` to avoid persistent `Processing...` states in Synology Chat clients. (#26635) Thanks @memphislee09-source.
+- Voice-call/Twilio signature verification: retry signature validation across deterministic URL port variants (with/without port) to handle mixed Twilio signing behavior behind reverse proxies and non-standard ports. (#25140) Thanks @drvoss.
+- Slack/Bolt startup compatibility: remove invalid `message.channels` and `message.groups` event registrations so Slack providers no longer crash on startup with Bolt 4.6+; channel/group traffic continues through the unified `message` handler (`channel_type`). (#32033) Thanks @mahopan.
+- Slack/socket auth failure handling: fail fast on non-recoverable auth errors (`account_inactive`, `invalid_auth`, etc.) during startup and reconnect instead of retry-looping indefinitely, including `unable_to_socket_mode_start` error payload propagation. (#32377) Thanks @scoootscooob.
+- Gateway/macOS LaunchAgent hardening: write `Umask=077` in generated gateway LaunchAgent plists so npm upgrades preserve owner-only default file permissions for gateway-created state files. (#31919) Fixes #31905. Thanks @liuxiaopai-ai.
+- macOS/LaunchAgent security defaults: write `Umask=63` (octal `077`) into generated gateway launchd plists so post-update service reinstalls keep owner-only file permissions by default instead of falling back to system `022`. (#32022) Fixes #31905. Thanks @liuxiaopai-ai.
+- Media understanding/provider HTTP proxy routing: pass a proxy-aware fetch function from `HTTPS_PROXY`/`HTTP_PROXY` env vars into audio/video provider calls (with graceful malformed-proxy fallback) so transcription/video requests honor configured outbound proxies. (#27093) Thanks @mcaxtr.
+- Sandbox/workspace mount permissions: make primary `/workspace` bind mounts read-only whenever `workspaceAccess` is not `rw` (including `none`) across both core sandbox container and sandbox browser create flows. (#32227) Thanks @guanyu-zhang.
+- Tools/fsPolicy propagation: honor `tools.fs.workspaceOnly` for image/pdf local-root allowlists so non-sandbox media paths outside workspace are rejected when workspace-only mode is enabled. (#31882) Thanks @justinhuangcode.
+- Daemon/Homebrew runtime pinning: resolve Homebrew Cellar Node paths to stable Homebrew-managed symlinks (including versioned formulas like `node@22`) so gateway installs keep the intended runtime across brew upgrades. (#32185) Thanks @scoootscooob.
+- Browser/Security output boundary hardening: replace check-then-rename output commits with root-bound fd-verified writes, unify install/skills canonical path-boundary checks, and add regression coverage for symlink-rebind race paths across browser output and shared fs-safe write flows. Thanks @tdjackey for reporting.
+- Gateway/Security canonicalization hardening: decode plugin route path variants to canonical fixpoint (with bounded depth), fail closed on canonicalization anomalies, and enforce gateway auth for deeply encoded `/api/channels/*` variants to prevent alternate-path auth bypass through plugin handlers. Thanks @tdjackey for reporting.
+- Browser/Gateway hardening: preserve env credentials for `OPENCLAW_GATEWAY_URL` / `CLAWDBOT_GATEWAY_URL` while treating explicit `--url` as override-only auth, and make container browser hardening flags optional with safer defaults for Docker/LXC stability. (#31504) Thanks @vincentkoc.
+- Gateway/Control UI basePath webhook passthrough: let non-read methods under configured `controlUiBasePath` fall through to plugin routes (instead of returning Control UI 405), restoring webhook handlers behind basePath mounts. (#32311) Thanks @ademczuk.
+- Gateway/Webchat streaming finalization: flush throttled trailing assistant text before `final` chat events so streaming consumers do not miss tail content, while preserving duplicate suppression and heartbeat/silent lead-fragment guards. (#24856) Thanks @visionik and @vincentkoc.
+- Control UI/Legacy browser compatibility: replace `toSorted`-dependent cron suggestion sorting in `app-render` with a compatibility helper so older browsers without `Array.prototype.toSorted` no longer white-screen. (#31775) Thanks @liuxiaopai-ai.
+- macOS/PeekabooBridge: add compatibility socket symlinks for legacy `clawdbot`, `clawdis`, and `moltbot` Application Support socket paths so pre-rename clients can still connect. (#6033) Thanks @lumpinif and @vincentkoc.
+- Gateway/message tool reliability: avoid false `Unknown channel` failures when `message.*` actions receive platform-specific channel ids by falling back to `toolContext.currentChannelProvider`, and prevent health-monitor restart thrash for channels that just (re)started by adding a per-channel startup-connect grace window. (from #32367) Thanks @MunemHashmi.
+- Windows/Spawn canonicalization: unify non-core Windows spawn handling across ACP client, QMD/mcporter memory paths, and sandbox Docker execution using the shared wrapper-resolution policy, with targeted regression coverage for `.cmd` shim unwrapping and shell fallback behavior. (#31750) Thanks @Takhoffman.
+- Security/ACP sandbox inheritance: enforce fail-closed runtime guardrails for `sessions_spawn` with `runtime="acp"` by rejecting ACP spawns from sandboxed requester sessions and rejecting `sandbox="require"` for ACP runtime, preventing sandbox-boundary bypass via host-side ACP initialization. (#32254) Thanks @tdjackey for reporting, and @dutifulbob for the fix.
+- Security/Web tools SSRF guard: keep DNS pinning for untrusted `web_fetch` and citation-redirect URL checks when proxy env vars are set, and require explicit dangerous opt-in before env-proxy routing can bypass pinned dispatch for trusted/operator-controlled endpoints. Thanks @tdjackey for reporting.
+- Gemini schema sanitization: coerce malformed JSON Schema `properties` values (`null`, arrays, primitives) to `{}` before provider validation, preventing downstream strict-validator crashes on invalid plugin/tool schemas. (#32332) Thanks @webdevtodayjason.
+- Media understanding/malformed attachment guards: harden attachment selection and decision summary formatting against non-array or malformed attachment payloads to prevent runtime crashes on invalid inbound metadata shapes. (#28024) Thanks @claw9267.
+- Browser/Extension navigation reattach: preserve debugger re-attachment when relay is temporarily disconnected by deferring relay attach events until reconnect/re-announce, reducing post-navigation tab loss. (#28725) Thanks @stone-jin.
+- Browser/Extension relay stale tabs: evict stale cached targets from `/json/list` when extension targets are destroyed/crashed or commands fail with missing target/session errors. (#6175) Thanks @vincentkoc.
+- Browser/CDP startup readiness: wait for CDP websocket readiness after launching Chrome and cleanly stop/reset when readiness never arrives, reducing follow-up `PortInUseError` races after `browser start`/`open`. (#29538) Thanks @AaronWander.
+- OpenAI/Responses WebSocket tool-call id hygiene: normalize blank/whitespace streamed tool-call ids before persistence, and block empty `function_call_output.call_id` payloads in the WS conversion path to avoid OpenAI 400 errors (`Invalid 'input[n].call_id': empty string`), with regression coverage for both inbound stream normalization and outbound payload guards.
+- Security/Nodes camera URL downloads: bind node `camera.snap`/`camera.clip` URL payload downloads to the resolved node host, enforce fail-closed behavior when node `remoteIp` is unavailable, and use SSRF-guarded fetch with redirect host/protocol checks to prevent off-node fetch pivots. Thanks @tdjackey for reporting.
+- Config/backups hardening: enforce owner-only (`0600`) permissions on rotated config backups and clean orphan `.bak.*` files outside the managed backup ring, reducing credential leakage risk from stale or permissive backup artifacts. (#31718) Thanks @YUJIE2002.
+- Telegram/inbound media filenames: preserve original `file_name` metadata for document/audio/video/animation downloads (with fetch/path fallbacks), so saved inbound attachments keep sender-provided names instead of opaque Telegram file paths. (#31837) Thanks @Kay-051.
+- Gateway/OpenAI chat completions: honor `x-openclaw-message-channel` when building `agentCommand` input for `/v1/chat/completions`, preserving caller channel identity instead of forcing `webchat`. (#30462) Thanks @bmendonca3.
+- Plugin SDK/runtime hardening: add package export verification in CI/release checks to catch missing runtime exports before publish-time regressions. (#28575) Thanks @bmendonca3.
+- Media/MIME normalization: normalize parameterized/case-variant MIME strings in `kindFromMime` (for example `Audio/Ogg; codecs=opus`) so WhatsApp voice notes are classified as audio and routed through transcription correctly. (#32280) Thanks @Lucenx9.
+- Discord/audio preflight mentions: detect audio attachments via Discord `content_type` and gate preflight transcription on typed text (not media placeholders), so guild voice-note mentions are transcribed and matched correctly. (#32136) Thanks @jnMetaCode.
+- Discord/acp inline actions: prefer autocomplete for `/acp` action inline values and ignore bound-thread bot system messages to prevent ACP loops. (#33136) Thanks @thewilloftheshadow.
+- Feishu/topic session routing: use `thread_id` as topic session scope fallback when `root_id` is absent, keep first-turn topic keys stable across thread creation, and force thread replies when inbound events already carry topic/thread context. (#29788) Thanks @songyaolun.
+- Gateway/Webchat NO_REPLY streaming: suppress assistant lead-fragment deltas that are prefixes of `NO_REPLY` and keep final-message buffering in sync, preventing partial `NO` leaks on silent-response runs while preserving legitimate short replies. (#32073) Thanks @liuxiaopai-ai.
+- Telegram/models picker callbacks: keep long model buttons selectable by falling back to compact callback payloads and resolving provider ids on selection (with provider re-prompt on ambiguity), avoiding Telegram 64-byte callback truncation failures. (#31857) Thanks @bmendonca3.
+- Context-window metadata warmup: add exponential config-load retry backoff (1s -> 2s -> 4s, capped at 60s) so transient startup failures recover automatically without hot-loop retries.
+- Voice-call/Twilio external outbound: auto-register webhook-first `outbound-api` calls (initiated outside OpenClaw) so media streams are accepted and call direction metadata stays accurate. (#31181) Thanks @scoootscooob.
+- Feishu/topic root replies: prefer `root_id` as outbound `replyTargetMessageId` when present, and parse millisecond `message_create_time` values correctly so topic replies anchor to the root message in grouped thread flows. (#29968) Thanks @bmendonca3.
+- Feishu/DM pairing reply target: send pairing challenge replies to `chat:` instead of `user:` so Lark/Feishu private chats with user-id-only sender payloads receive pairing messages reliably. (#31403) Thanks @stakeswky.
+- Feishu/Lark private DM routing: treat inbound `chat_type: "private"` as direct-message context for pairing/mention-forward/reaction synthetic handling so Lark private chats behave like Feishu p2p DMs. (#31400) Thanks @stakeswky.
+- Feishu/streaming card transport error handling: check `response.ok` before parsing JSON in token and card create requests so non-JSON HTTP error responses surface deterministic status failures. (#35628) Thanks @Sid-Qin.
+- Signal/message actions: allow `react` to fall back to `toolContext.currentMessageId` when `messageId` is omitted, matching Telegram behavior and unblocking agent-initiated reactions on inbound turns. (#32217) Thanks @dunamismax.
+- Discord/message actions: allow `react` to fall back to `toolContext.currentMessageId` when `messageId` is omitted, matching Telegram/Signal reaction ergonomics in inbound turns.
+- Synology Chat/reply delivery: resolve webhook usernames to Chat API `user_id` values for outbound chatbot replies, avoiding mismatches between webhook user IDs and `method=chatbot` recipient IDs in multi-account setups. (#23709) Thanks @druide67.
+- Slack/thread context payloads: only inject thread starter/history text on first thread turn for new sessions while preserving thread metadata, reducing repeated context-token bloat on long-lived thread sessions. (#32133) Thanks @sourman.
+- Slack/session routing: keep top-level channel messages in one shared session when `replyToMode=off`, while preserving thread-scoped keys for true thread replies and non-off modes. (#32193) Thanks @bmendonca3.
+- Slack/app_mention dedupe race handling: keep seen-message dedupe to prevent duplicate replies while allowing a one-time app_mention retry when the paired message event was dropped pre-dispatch, so requireMention channels do not lose mentions under Slack event reordering. (#34937) Thanks @littleben.
+- Voice-call/webhook routing: require exact webhook path matches (instead of prefix matches) so lookalike paths cannot reach provider verification/dispatch logic. (#31930) Thanks @afurm.
+- Zalo/Pairing auth tests: add webhook regression coverage asserting DM pairing-store reads/writes remain account-scoped, preventing cross-account authorization bleed in multi-account setups. (#26121) Thanks @bmendonca3.
+- Zalouser/Pairing auth tests: add account-scoped DM pairing-store regression coverage (`monitor.account-scope.test.ts`) to prevent cross-account allowlist bleed in multi-account setups. (#26672) Thanks @bmendonca3.
+- Feishu/Send target prefixes: normalize explicit `group:`/`dm:` send targets and preserve explicit receive-id routing hints when resolving outbound Feishu targets. (#31594) Thanks @liuxiaopai-ai.
+- Webchat/Feishu session continuation: preserve routable `OriginatingChannel`/`OriginatingTo` metadata from session delivery context in `chat.send`, and prefer provider-normalized channel when deciding cross-channel route dispatch so Webchat replies continue on the selected Feishu session instead of falling back to main/internal session routing. (#31573)
+- Telegram/implicit mention forum handling: exclude Telegram forum system service messages (`forum_topic_*`, `general_forum_topic_*`) from reply-chain implicit mention detection so `requireMention` does not get bypassed inside bot-created topic lifecycle events. (#32262) Thanks @scoootscooob.
+- Slack/inbound debounce routing: isolate top-level non-DM message debounce keys by message timestamp to avoid cross-thread collisions, preserve DM batching, and flush pending top-level buffers before immediate non-debounce follow-ups to keep ordering stable. (#31951) Thanks @scoootscooob.
+- Feishu/Duplicate replies: suppress same-target reply dispatch when message-tool sends use generic provider metadata (`provider: "message"`) and normalize `lark`/`feishu` provider aliases during duplicate-target checks, preventing double-delivery in Feishu sessions. (#31526)
+- Webchat/silent token leak: filter assistant `NO_REPLY`-only transcript entries from `chat.history` responses and add client-side defense-in-depth guards in the chat controller so internal silent tokens never render as visible chat bubbles. (#32015) Consolidates overlap from #32183, #32082, #32045, #32052, #32172, and #32112. Thanks @ademczuk, @liuxiaopai-ai, @ningding97, @bmendonca3, and @x4v13r1120.
+- Doctor/local memory provider checks: stop false-positive local-provider warnings when `provider=local` and no explicit `modelPath` is set by honoring default local model fallback while still warning when gateway probe reports local embeddings not ready. (#32014) Fixes #31998. Thanks @adhishthite.
+- Media understanding/parakeet CLI output parsing: read `parakeet-mlx` transcripts from `--output-dir/.txt` when txt output is requested (or default), with stdout fallback for non-txt formats. (#9177) Thanks @mac-110.
+- Media understanding/audio transcription guard: skip tiny/empty audio files (<1024 bytes) before provider/CLI transcription to avoid noisy invalid-audio failures and preserve clean fallback behavior. (#8388) Thanks @Glucksberg.
+- Gateway/Plugin HTTP route precedence: run explicit plugin HTTP routes before the Control UI SPA catch-all so registered plugin webhook/custom paths remain reachable, while unmatched paths still fall through to Control UI handling. (#31885) Thanks @Sid-Qin.
+- Gateway/Node browser proxy routing: honor `profile` from `browser.request` JSON body when query params omit it, while preserving query-profile precedence when both are present. (#28852) Thanks @Sid-Qin.
+- Gateway/Control UI basePath POST handling: return 405 for `POST` on exact basePath routes (for example `/openclaw`) instead of redirecting, and add end-to-end regression coverage that root-mounted webhook POST paths still pass through to plugin handlers. (#31349) Thanks @Sid-Qin.
+- Browser/default profile selection: default `browser.defaultProfile` behavior now prefers `openclaw` (managed standalone CDP) when no explicit default is configured, while still auto-provisioning the `chrome` relay profile for explicit opt-in use. (#32031) Fixes #31907. Thanks @liuxiaopai-ai.
+- Sandbox/mkdirp boundary checks: allow existing in-boundary directories to pass mkdirp boundary validation when directory open probes return platform-specific I/O errors, with regression coverage for directory-safe fallback behavior. (#31547) Thanks @stakeswky.
+- Models/config env propagation: apply `config.env.vars` before implicit provider discovery in models bootstrap so config-scoped credentials are visible to implicit provider resolution paths. (#32295) Thanks @hsiaoa.
+- Models/Codex usage labels: infer weekly secondary usage windows from reset cadence when API window seconds are ambiguously reported as 24h, so `openclaw models status` no longer mislabels weekly limits as daily. (#31938) Thanks @bmendonca3.
+- Gateway/Heartbeat model reload: treat `models.*` and `agents.defaults.model` config updates as heartbeat hot-reload triggers so heartbeat picks up model changes without a full gateway restart. (#32046) Thanks @stakeswky.
+- Memory/LanceDB embeddings: forward configured `embedding.dimensions` into OpenAI embeddings requests so vector size and API output dimensions stay aligned when dimensions are explicitly configured. (#32036) Thanks @scotthuang.
+- Gateway/Control UI method guard: allow POST requests to non-UI routes to fall through when no base path is configured, and add POST regression coverage for fallthrough and base-path 405 behavior. (#23970) Thanks @tyler6204.
+- Browser/CDP status accuracy: require a successful `Browser.getVersion` response over the CDP websocket (not just socket-open) before reporting `cdpReady`, so stale idle command channels are surfaced as unhealthy. (#23427) Thanks @vincentkoc.
+- Daemon/systemd checks in containers: treat missing `systemctl` invocations (including `spawn systemctl ENOENT`/`EACCES`) as unavailable service state during `is-enabled` checks, preventing container flows from failing with `Gateway service check failed` before install/status handling can continue. (#26089) Thanks @sahilsatralkar and @vincentkoc.
+- Security/Node exec approvals: revalidate approval-bound `cwd` identity immediately before execution/forwarding and fail closed with an explicit denial when `cwd` drifts after approval hardening.
+- Security audit/skills workspace hardening: add `skills.workspace.symlink_escape` warning in `openclaw security audit` when workspace `skills/**/SKILL.md` resolves outside the workspace root (for example symlink-chain drift), plus docs coverage in the security glossary.
+- Security/Node exec approvals: preserve shell/dispatch-wrapper argv semantics during approval hardening so approved wrapper commands (for example `env sh -c ...`) cannot drift into a different runtime command shape, and add regression coverage for both approval-plan generation and approved runtime execution paths. Thanks @tdjackey for reporting.
+- Security/fs-safe write hardening: make `writeFileWithinRoot` use same-directory temp writes plus atomic rename, add post-write inode/hardlink revalidation with security warnings on boundary drift, and avoid truncating existing targets when final rename fails.
+- Security/Skills archive extraction: unify tar extraction safety checks across tar.gz and tar.bz2 install flows, enforce tar compressed-size limits, and fail closed if tar.bz2 archives change between preflight and extraction to prevent bypasses of entry-type/size guardrails. Thanks @GCXWLP for reporting.
+- Security/Prompt spoofing hardening: stop injecting queued runtime events into user-role prompt text, route them through trusted system-prompt context, and neutralize inbound spoof markers like `[System Message]` and line-leading `System:` in untrusted message content. (#30448)
+- Sandbox/Docker setup command parsing: accept `agents.*.sandbox.docker.setupCommand` as either a string or a string array, and normalize arrays to newline-delimited shell scripts so multi-step setup commands no longer concatenate without separators. (#31953) Thanks @liuxiaopai-ai.
+- Sandbox/Bootstrap context boundary hardening: reject symlink/hardlink alias bootstrap seed files that resolve outside the source workspace and switch post-compaction `AGENTS.md` context reads to boundary-verified file opens, preventing host file content from being injected via workspace aliasing. Thanks @tdjackey for reporting.
+- Agents/Sandbox workdir mapping: map container workdir paths (for example `/workspace`) back to the host workspace before sandbox path validation so exec requests keep the intended directory in containerized runs instead of falling back to an unavailable host path. (#31841) Thanks @liuxiaopai-ai.
+- Docker/Sandbox bootstrap hardening: make `OPENCLAW_SANDBOX` opt-in parsing explicit (`1|true|yes|on`), support custom Docker socket paths via `OPENCLAW_DOCKER_SOCKET`, defer docker.sock exposure until sandbox prerequisites pass, and reset/roll back persisted sandbox mode to `off` when setup is skipped or partially fails to avoid stale broken sandbox state. (#29974) Thanks @jamtujest and @vincentkoc.
+- Hooks/webhook ACK compatibility: return `200` (instead of `202`) for successful `/hooks/agent` requests so providers that require `200` (for example Forward Email) accept dispatched agent hook deliveries. (#28204) Thanks @AIflow-Labs.
+- Feishu/Run channel fallback: prefer `Provider` over `Surface` when inferring queued run `messageProvider` fallback (when `OriginatingChannel` is missing), preventing Feishu turns from being mislabeled as `webchat` in mixed relay metadata contexts. (#31880) Fixes #31859. Thanks @liuxiaopai-ai.
+- Skills/sherpa-onnx-tts: run the `sherpa-onnx-tts` bin under ESM (replace CommonJS `require` imports) and add regression coverage to prevent `require is not defined in ES module scope` startup crashes. (#31965) Thanks @bmendonca3.
+- Inbound metadata/direct relay context: restore direct-channel conversation metadata blocks for external channels (for example WhatsApp) while preserving webchat-direct suppression, so relay agents recover sender/message identifiers without reintroducing internal webchat metadata noise. (#31969) Fixes #29972. Thanks @Lucenx9.
+- Slack/Channel message subscriptions: register explicit `message.channels` and `message.groups` monitor handlers (alongside generic `message`) so channel/group event subscriptions are consumed even when Slack dispatches typed message event names. Fixes #31674.
+- Hooks/session-scoped memory context: expose ephemeral `sessionId` in embedded plugin tool contexts and `before_tool_call`/`after_tool_call` hook contexts (including compaction and client-tool wiring) so plugins can isolate per-conversation state across `/new` and `/reset`. Related #31253 and #31304. Thanks @Sid-Qin and @Servo-AIpex.
+- Voice-call/Twilio inbound greeting: run answered-call initial notify greeting for Twilio instead of skipping the manager speak path, with regression coverage for both Twilio and Plivo notify flows. (#29121) Thanks @xinhuagu.
+- Voice-call/stale call hydration: verify active calls with the provider before loading persisted in-progress calls so stale locally persisted records do not block or misroute new call handling after restarts. (#4325) Thanks @garnetlyx.
+- Feishu/File upload filenames: percent-encode non-ASCII/special-character `file_name` values in Feishu multipart uploads so Chinese/symbol-heavy filenames are sent as proper attachments instead of plain text links. (#31179) Thanks @Kay-051.
+- Media/MIME channel parity: route Telegram/Signal/iMessage media-kind checks through normalized `kindFromMime` so mixed-case/parameterized MIME values classify consistently across message channels.
+- WhatsApp/inbound self-message context: propagate inbound `fromMe` through the web inbox pipeline and annotate direct self messages as `(self)` in envelopes so agents can distinguish owner-authored turns from contact turns. (#32167) Thanks @scoootscooob.
+- Webchat/stream finalization: persist streamed assistant text when final events omit `message`, while keeping final payload precedence and skipping empty stream buffers to prevent disappearing replies after tool turns. (#31920) Thanks @Sid-Qin.
+- Feishu/Inbound ordering: serialize message handling per chat while preserving cross-chat concurrency to avoid same-chat race drops under bursty inbound traffic. (#31807)
+- Feishu/Typing notification suppression: skip typing keepalive reaction re-adds when the indicator is already active, preventing duplicate notification pings from repeated identical emoji adds. (#31580)
+- Feishu/Probe failure backoff: cache API and timeout probe failures for one minute per account key while preserving abort-aware probe timeouts, reducing repeated health-check retries during transient credential/network outages. (#29970)
+- Feishu/Streaming block fallback: preserve markdown block stream text as final streaming-card content when final payload text is missing, while still suppressing non-card internal block chunk delivery. (#30663)
+- Feishu/Bitable API errors: unify Feishu Bitable tool error handling with structured `LarkApiError` responses and consistent API/context attribution across wiki/base metadata, field, and record operations. (#31450)
+- Feishu/Missing-scope grant URL fix: rewrite known invalid scope aliases (`contact:contact.base:readonly`) to valid scope names in permission grant links, so remediation URLs open with correct Feishu consent scopes. (#31943)
+- BlueBubbles/Message metadata: harden send response ID extraction, include sender identity in DM context, and normalize inbound `message_id` selection to avoid duplicate ID metadata. (#23970) Thanks @tyler6204.
+- WebChat/markdown tables: ensure GitHub-flavored markdown table parsing is explicitly enabled at render time and add horizontal overflow handling for wide tables, with regression coverage for table-only and mixed text+table content. (#32365) Thanks @BlueBirdBack.
+- Feishu/default account resolution: always honor explicit `channels.feishu.defaultAccount` during outbound account selection (including top-level-credential setups where the preferred id is not present in `accounts`), instead of silently falling back to another account id. (#32253) Thanks @bmendonca3.
+- Feishu/Sender lookup permissions: suppress user-facing grant prompts for stale non-existent scope errors (`contact:contact.base:readonly`) during best-effort sender-name resolution so inbound messages continue without repeated false permission notices. (#31761)
+- Discord/dispatch + Slack formatting: restore parallel outbound dispatch across Discord channels with per-channel queues while preserving in-channel ordering, and run Slack preview/stream update text through mrkdwn normalization for consistent formatting. (#31927) Thanks @Sid-Qin.
+- Feishu/Inbound debounce: debounce rapid same-chat sender bursts into one ordered dispatch turn, skip already-processed retries when composing merged text, and preserve bot-mention intent across merged entries to reduce duplicate or late inbound handling. (#31548)
+- Tests/Sandbox + archive portability: use junction-compatible directory-link setup on Windows and explicit file-symlink platform guards in symlink escape tests where unprivileged file symlinks are unavailable, reducing false Windows CI failures while preserving traversal checks on supported paths. (#28747) Thanks @arosstale.
+- Browser/Extension re-announce reliability: keep relay state in `connecting` when re-announce forwarding fails and extend debugger re-attach retries after navigation to reduce false attached states and post-nav disconnect loops. (#27630) Thanks @markmusson.
+- Browser/Act request compatibility: accept legacy flattened `action="act"` params (`kind/ref/text/...`) in addition to `request={...}` so browser act calls no longer fail with `request required`. (#15120) Thanks @vincentkoc.
+- OpenRouter/x-ai compatibility: skip `reasoning.effort` injection for `x-ai/*` models (for example Grok) so OpenRouter requests no longer fail with invalid-arguments errors on unsupported reasoning params. (#32054) Thanks @scoootscooob.
+- Models/openai-completions developer-role compatibility: force `supportsDeveloperRole=false` for non-native endpoints, treat unparseable `baseUrl` values as non-native, and add regression coverage for empty/malformed baseUrl plus explicit-true override behavior. (#29479) thanks @akramcodez.
+- Browser/Profile attach-only override: support `browser.profiles..attachOnly` (fallback to global `browser.attachOnly`) so loopback proxy profiles can skip local launch/port-ownership checks without forcing attach-only mode for every profile. (#20595) Thanks @unblockedgamesstudio and @vincentkoc.
+- Sessions/Lock recovery: detect recycled Linux PIDs by comparing lock-file `starttime` with `/proc//stat` starttime, so stale `.jsonl.lock` files are reclaimed immediately in containerized PID-reuse scenarios while preserving compatibility for older lock files. (#26443) Fixes #27252. Thanks @HirokiKobayashi-R and @vincentkoc.
+- Cron/isolated delivery target fallback: remove early unresolved-target return so cron delivery can flow through shared outbound target resolution (including per-channel `resolveDefaultTo` fallback) when `delivery.to` is omitted. (#32364) Thanks @hclsys.
+- OpenAI media capabilities: include `audio` in the OpenAI provider capability list so audio transcription models are eligible in media-understanding provider selection. (#12717) Thanks @openjay.
+- Browser/Managed tab cap: limit loopback managed `openclaw` page tabs to 8 via best-effort cleanup after tab opens to reduce long-running renderer buildup while preserving attach-only and remote profile behavior. (#29724) Thanks @pandego.
+- Docker/Image health checks: add Dockerfile `HEALTHCHECK` that probes gateway `GET /healthz` so container runtimes can mark unhealthy instances without requiring auth credentials in the probe command. (#11478) Thanks @U-C4N and @vincentkoc.
+- Gateway/Node dangerous-command parity: include `sms.send` in default onboarding node `denyCommands`, share onboarding deny defaults with the gateway dangerous-command source of truth, and include `sms.send` in phone-control `/phone arm writes` handling so SMS follows the same break-glass flow as other dangerous node commands. Thanks @zpbrent.
+- Pairing/AllowFrom account fallback: handle omitted `accountId` values in `readChannelAllowFromStore` and `readChannelAllowFromStoreSync` as `default`, while preserving legacy unscoped allowFrom merges for default-account flows. Thanks @Sid-Qin and @vincentkoc.
+- Browser/Remote CDP ownership checks: skip local-process ownership errors for non-loopback remote CDP profiles when HTTP is reachable but the websocket handshake fails, and surface the remote websocket attach/retry path instead. (#15582) Landed from contributor (#28780) Thanks @stubbi, @bsormagec, @unblockedgamesstudio and @vincentkoc.
+- Browser/CDP proxy bypass: force direct loopback agent paths and scoped `NO_PROXY` expansion for localhost CDP HTTP/WS connections when proxy env vars are set, so browser relay/control still works behind global proxy settings. (#31469) Thanks @widingmarcus-cyber.
+- Sessions/idle reset correctness: preserve existing `updatedAt` during inbound metadata-only writes so idle-reset boundaries are not unintentionally refreshed before actual user turns. (#32379) Thanks @romeodiaz.
+- Sessions/lock recovery: reclaim orphan legacy same-PID lock files missing `starttime` when no in-process lock ownership exists, avoiding false lock timeouts after PID reuse while preserving active lock safety checks. (#32081) Thanks @bmendonca3.
+- Sessions/store cache invalidation: reload cached session stores when file size changes within the same mtime tick by keying cache validation on a single file-stat snapshot (`mtimeMs` + `sizeBytes`), with regression coverage for same-tick rewrites. (#32191) Thanks @jalehman.
+- Agents/Subagents `sessions_spawn`: reject malformed `agentId` inputs before normalization (for example error-message/path-like strings) to prevent unintended synthetic agent IDs and ghost workspace/session paths; includes strict validation regression coverage. (#31381) Thanks @openperf.
+- CLI/installer Node preflight: enforce Node.js `v22.12+` consistently in both `openclaw.mjs` runtime bootstrap and installer active-shell checks, with actionable nvm recovery guidance for mismatched shell PATH/defaults. (#32356) Thanks @jasonhargrove.
+- Web UI/config form: support SecretInput string-or-secret-ref unions in map `additionalProperties`, so provider API key fields stay editable instead of being marked unsupported. (#31866) Thanks @ningding97.
+- Auto-reply/inline command cleanup: preserve newline structure when stripping inline `/status` and extracting inline slash commands by collapsing only horizontal whitespace, preventing paragraph flattening in multi-line replies. (#32224) Thanks @scoootscooob.
+- Config/raw redaction safety: preserve non-sensitive literals during raw redaction round-trips, scope SecretRef redaction to secret IDs (not structural fields like `source`/`provider`), and fall back to structured raw redaction when text replacement cannot restore the original config shape. (#32174) Thanks @bmendonca3.
+- Hooks/runtime stability: keep the internal hook handler registry on a `globalThis` singleton so hook registration/dispatch remains consistent when bundling emits duplicate module copies. (#32292) Thanks @Drickon.
+- Hooks/after_tool_call: include embedded session context (`sessionKey`, `agentId`) and fire the hook exactly once per tool execution by removing duplicate adapter-path dispatch in embedded runs. (#32201) Thanks @jbeno, @scoootscooob, @vincentkoc.
+- Hooks/tool-call correlation: include `runId` and `toolCallId` in plugin tool hook payloads/context and scope tool start/adjusted-param tracking by run to prevent cross-run collisions in `before_tool_call` and `after_tool_call`. (#32360) Thanks @vincentkoc.
+- Plugins/install diagnostics: reject legacy plugin package shapes without `openclaw.extensions` and return an explicit upgrade hint with troubleshooting docs for repackaging. (#32055) Thanks @liuxiaopai-ai.
+- Hooks/plugin context parity: ensure `llm_input` hooks in embedded attempts receive the same `trigger` and `channelId`-aware `hookCtx` used by the other hook phases, preserving channel/trigger-scoped plugin behavior. (#28623) Thanks @davidrudduck and @vincentkoc.
+- Plugins/hardlink install compatibility: allow bundled plugin manifests and entry files to load when installed via hardlink-based package managers (`pnpm`, `bun`) while keeping hardlink rejection enabled for non-bundled plugin sources. (#32119) Fixes #28175, #28404, #29455. Thanks @markfietje.
+- Cron/session reaper reliability: move cron session reaper sweeps into `onTimer` `finally` and keep pruning active even when timer ticks fail early (for example cron store parse failures), preventing stale isolated run sessions from accumulating indefinitely. (#31996) Fixes #31946. Thanks @scoootscooob.
+- Cron/HEARTBEAT_OK summary leak: suppress fallback main-session enqueue for heartbeat/internal ack summaries in isolated announce mode so `HEARTBEAT_OK` noise never appears in user chat while real summaries still forward. (#32093) Thanks @scoootscooob.
+- Authentication: classify `permission_error` as `auth_permanent` for profile fallback. (#31324) Thanks @Sid-Qin.
+- Agents/host edit reliability: treat host edit-tool throws as success only when on-disk post-check confirms replacement likely happened (`newText` present and `oldText` absent), preventing false failure reports while avoiding pre-write false positives. (#32383) Thanks @polooooo.
+- Plugins/install fallback safety: resolve bare install specs to bundled plugin ids before npm lookup (for example `diffs` -> bundled `@openclaw/diffs`), keep npm fallback limited to true package-not-found errors, and continue rejecting non-plugin npm packages that fail manifest validation. (#32096) Thanks @scoootscooob.
+- Web UI/inline code copy fidelity: disable forced mid-token wraps on inline `` spans so copied UUID/hash/token strings preserve exact content instead of inserting line-break spaces. (#32346) Thanks @hclsys.
+- Restart sentinel formatting: avoid duplicate `Reason:` lines when restart message text already matches `stats.reason`, keeping restart notifications concise for users and downstream parsers. (#32083) Thanks @velamints2.
+- Auto-reply/followup queue: avoid stale callback reuse across idle-window restarts by caching the followup runner only when a drain actually starts, preserving enqueue ordering after empty-finalize paths. (#31902) Thanks @Lanfei.
+- Agents/tool-result guard: always clear pending tool-call state on interruptions even when synthetic tool results are disabled, preventing orphaned tool-use transcripts that cause follow-up provider request failures. (#32120) Thanks @jnMetaCode.
+- Failover/error classification: treat HTTP `529` (provider overloaded, common with Anthropic-compatible APIs) as `rate_limit` so model failover can engage instead of misclassifying the error path. (#31854) Thanks @bugkill3r.
+- Logging: use local time for logged timestamps instead of UTC, aligning log output with documented local timezone behavior and avoiding confusion during local diagnostics. (#28434) Thanks @liuy.
+- Agents/Subagent announce cleanup: keep completion-message runs pending while descendants settle, add a 30 minute hard-expiry backstop to avoid indefinite pending state, and keep retry bookkeeping resumable across deferred wakes. (#23970) Thanks @tyler6204.
+- Secrets/exec resolver timeout defaults: use provider `timeoutMs` as the default inactivity (`noOutputTimeoutMs`) watchdog for exec secret providers, preventing premature no-output kills for resolvers that start producing output after 2s. (#32235) Thanks @bmendonca3.
+- Auto-reply/reminder guard note suppression: when a turn makes reminder-like commitments but schedules no new cron jobs, suppress the unscheduled-reminder warning note only if an enabled cron already exists for the same session; keep warnings for unrelated sessions, disabled jobs, or unreadable cron store paths. (#32255) Thanks @scoootscooob.
+- Cron/isolated announce heartbeat suppression: treat multi-payload runs as skippable when any payload is a heartbeat ack token and no payload has media, preventing internal narration + trailing `HEARTBEAT_OK` from being delivered to users. (#32131) Thanks @adhishthite.
+- Cron/store migration: normalize legacy cron jobs with string `schedule` and top-level `command`/`timeout` fields into canonical schedule/payload/session-target shape on load, preventing schedule-error loops on old persisted stores. (#31926) Thanks @bmendonca3.
+- Tests/Windows backup rotation: skip chmod-only backup permission assertions on Windows while retaining compose/rotation/prune coverage across platforms to avoid false CI failures from Windows non-POSIX mode semantics. (#32286) Thanks @jalehman.
+- Tests/Subagent announce: set `OPENCLAW_TEST_FAST=1` before importing `subagent-announce` format suites so module-level fast-mode constants are captured deterministically on Windows CI, preventing timeout flakes in nested completion announce coverage. (#31370) Thanks @zwffff.
+- Control UI/markdown recursion fallback: catch markdown parser failures and safely render escaped plain-text fallback instead of crashing the Control UI on pathological markdown history payloads. (#36445, fixes #36213) Thanks @BinHPdev.
+
+## 2026.3.1
+
+### Changes
+
- OpenAI/Streaming transport: make `openai` Responses WebSocket-first by default (`transport: "auto"` with SSE fallback), add shared OpenAI WS stream/connection runtime wiring with per-session cleanup, and preserve server-side compaction payload mutation (`store` + `context_management`) on the WS path.
+- Gateway/Container probes: add built-in HTTP liveness/readiness endpoints (`/health`, `/healthz`, `/ready`, `/readyz`) for Docker/Kubernetes health checks, with fallback routing so existing handlers on those paths are not shadowed. (#31272) Thanks @vincentkoc.
+- Android/Nodes: add `camera.list`, `device.permissions`, `device.health`, and `notifications.actions` (`open`/`dismiss`/`reply`) on Android nodes, plus first-class node-tool actions for the new device/notification commands. (#28260) Thanks @obviyus.
+- Discord/Thread bindings: replace fixed TTL lifecycle with inactivity (`idleHours`, default 24h) plus optional hard `maxAgeHours` lifecycle controls, and add `/session idle` + `/session max-age` commands for focused thread-bound sessions. (#27845) Thanks @osolmaz.
+- Telegram/DM topics: add per-DM `direct` + topic config (allowlists, `dmPolicy`, `skills`, `systemPrompt`, `requireTopic`), route DM topics as distinct inbound/outbound sessions, and enforce topic-aware authorization/debounce for messages, callbacks, commands, and reactions. Landed from contributor PR #30579 by @kesor. Thanks @kesor.
+- Android/Gateway capability refresh: add live Android capability integration coverage and node canvas capability refresh wiring, plus runtime hardening for A2UI readiness retries, scoped canvas URL normalization, debug diagnostics JSON, and JavaScript MIME delivery. (#28388) Thanks @obviyus.
+- Android/Nodes parity: add `system.notify`, `photos.latest`, `contacts.search`/`contacts.add`, `calendar.events`/`calendar.add`, and `motion.activity`/`motion.pedometer`, with motion sensor-aware command gating and improved activity sampling reliability. (#29398) Thanks @obviyus.
+- Agents/Thinking defaults: set `adaptive` as the default thinking level for Anthropic Claude 4.6 models (including Bedrock Claude 4.6 refs) while keeping other reasoning-capable models at `low` unless explicitly configured.
+- Web UI/Cron i18n: localize cron page labels, filters, form help text, and validation/error messaging in English and zh-CN. (#29315) Thanks @BUGKillerKing.
+- CLI/Config: add `openclaw config file` to print the active config file path resolved from `OPENCLAW_CONFIG_PATH` or the default location. (#26256) thanks @cyb1278588254.
+- Feishu/Docx tables + uploads: add `feishu_doc` actions for Docx table creation/cell writing (`create_table`, `write_table_cells`, `create_table_with_values`) and image/file uploads (`upload_image`, `upload_file`) with stricter create/upload error handling for missing `document_id` and placeholder cleanup failures. (#20304) Thanks @xuhao1.
+- Feishu/Reactions: add inbound `im.message.reaction.created_v1` handling, route verified reactions through synthetic inbound turns, and harden verification with timeout + fail-closed filtering so non-bot or unverified reactions are dropped. (#16716) Thanks @schumilin.
+- Feishu/Chat tooling: add `feishu_chat` tool actions for chat info and member queries, with configurable enablement under `channels.feishu.tools.chat`. (#14674) Thanks @liuweifly.
+- Feishu/Doc permissions: support optional owner permission grant fields on `feishu_doc` create and report permission metadata only when the grant call succeeds, with regression coverage for success/failure/omitted-owner paths. (#28295) Thanks @zhoulongchao77.
+- Web UI/i18n: add German (`de`) locale support and auto-render language options from supported locale constants in Overview settings. (#28495) thanks @dsantoreis.
+- Tools/Diffs: add a new optional `diffs` plugin tool for read-only diff rendering from before/after text or unified patches, with gateway viewer URLs for canvas and PNG image output. Thanks @gumadeiras.
+- Memory/LanceDB: support custom OpenAI `baseUrl` and embedding dimensions for LanceDB memory. (#17874) Thanks @rish2jain and @vincentkoc.
+- ACP/ACPX streaming: pin ACPX plugin support to `0.1.15`, add configurable ACPX command/version probing, and streamline ACP stream delivery (`final_only` default + reduced tool-event noise) with matching runtime and test updates. (#30036) Thanks @osolmaz.
+- Shell env markers: set `OPENCLAW_SHELL` across shell-like runtimes (`exec`, `acp`, `acp-client`, `tui-local`) so shell startup/config rules can target OpenClaw contexts consistently, and document the markers in env/exec/acp/TUI docs. Thanks @vincentkoc.
+- Cron/Heartbeat light bootstrap context: add opt-in lightweight bootstrap mode for automation runs (`--light-context` for cron agent turns and `agents.*.heartbeat.lightContext` for heartbeat), keeping only `HEARTBEAT.md` for heartbeat runs and skipping bootstrap-file injection for cron lightweight runs. (#26064) Thanks @jose-velez.
- OpenAI/WebSocket warm-up: add optional OpenAI Responses WebSocket warm-up (`response.create` with `generate:false`), enable it by default for `openai/*`, and expose `params.openaiWsWarmup` for per-model enable/disable control.
- Agents/Subagents runtime events: replace ad-hoc subagent completion system-message handoff with typed internal completion events (`task_completion`) that are rendered consistently across direct and queued announce paths, with gateway/CLI plumbing for structured `internalEvents`.
@@ -103,114 +716,257 @@ Docs: https://docs.openclaw.ai
### Fixes
-- Agents/Model fallback: classify additional network transport errors (`ECONNREFUSED`, `ENETUNREACH`, `EHOSTUNREACH`, `ENETRESET`, `EAI_AGAIN`) as failover-worthy so fallback chains advance when primary providers are unreachable. Landed from contributor PR #19077 by @ayanesakura. Thanks @ayanesakura.
-- Agents/Copilot token refresh: refresh GitHub Copilot runtime API tokens after auth-expiry failures and re-run with the renewed token so long-running embedded/subagent turns do not fail on mid-session 401 expiry. Landed from contributor PR #8805 by @Arthur742Ramos. Thanks @Arthur742Ramos.
-- Discord/Allowlist diagnostics: add debug logs for guild/channel allowlist drops so operators can quickly identify ignored inbound messages and required allowlist entries. Landed from contributor PR #30966 by @haosenwang1018. Thanks @haosenwang1018.
-- Discord/Ack reactions: add Discord-account-level `ackReactionScope` override and support explicit `off`/`none` values in shared config schemas to disable ack reactions per account. Landed from contributor PR #30400 by @BlueBirdBack. Thanks @BlueBirdBack.
-- Discord/Forum thread tags: support `appliedTags` on Discord thread-create actions and map to `applied_tags` for forum/media starter posts, with targeted thread-creation regression coverage. Landed from contributor PR #30358 by @pushkarsingh32. Thanks @pushkarsingh32.
-- Discord/Application ID fallback: parse bot application IDs from token prefixes without numeric precision loss and use token fallback only on transport/timeout failures when probing `/oauth2/applications/@me`. Landed from contributor PR #29695 by @dhananjai1729. Thanks @dhananjai1729.
-- Discord/EventQueue timeout config: expose per-account `channels.discord.accounts..eventQueue.listenerTimeout` (and related queue options) so long-running handlers can avoid Carbon listener timeout drops. Landed from contributor PR #28945 by @Glucksberg. Thanks @Glucksberg.
-- CLI/Cron run exit code: return exit code `0` only when `cron run` reports `{ ok: true, ran: true }`, and `1` for non-run/error outcomes so scripting/debugging reflects actual execution status. Landed from contributor PR #31121 by @Sid-Qin. Thanks @Sid-Qin.
-- Nodes/Screen recording guardrails: cap `nodes` tool `screen_record` `durationMs` to 5 minutes at both schema-validation and runtime invocation layers to prevent long-running blocking captures from unbounded durations. Landed from contributor PR #31106 by @BlueBirdBack. Thanks @BlueBirdBack.
-- Telegram/Empty final replies: skip outbound send for null/undefined final text payloads without media so Telegram typing indicators do not linger on `text must be non-empty` errors. Landed from contributor PR #30969 by @haosenwang1018. Thanks @haosenwang1018.
-- Routing/Binding peer-kind parity: treat `peer.kind` `group` and `channel` as equivalent for binding scope matching (while keeping `direct` separate) so Slack/public channel bindings do not silently fall through. Landed from contributor PR #31135 by @Sid-Qin. Thanks @Sid-Qin.
-- Cron/Store EBUSY fallback: retry `rename` on `EBUSY` and use `copyFile` fallback on Windows when replacing cron store files so busy-file contention no longer causes false write failures. (#16932) Thanks @sudhanva-chakra.
-- Agents/FS workspace default: honor documented host file-tool default `tools.fs.workspaceOnly=false` when unset so host `write`/`edit` calls are not incorrectly workspace-restricted unless explicitly enabled. Landed from contributor PR #31128 by @SaucePackets. Thanks @SaucePackets.
-- Cron/Timer hot-loop guard: enforce a minimum timer re-arm delay when stale past-due jobs would otherwise trigger repeated `setTimeout(0)` loops, preventing event-loop saturation and log-flood behavior. (#29853) Thanks @FlamesCN.
-- Gateway/CLI session recovery: handle expired CLI session IDs gracefully by clearing stale session state and retrying without crashing gateway runs. Landed from contributor PR #31090 by @frankekn. Thanks @frankekn.
-- Slack/Subagent completion delivery: stop forcing bound conversation IDs into `threadId` so Slack completion announces do not send invalid `thread_ts` for DMs/top-level channels. Landed from contributor PR #31105 by @stakeswky. Thanks @stakeswky.
-- Signal/Loop protection: evaluate own-account detection before sync-message filtering (including UUID-only `accountUuid` configs) so `sentTranscript` sync events cannot bypass loop protection and self-reply loops. Landed from contributor PR #31093 by @kevinWangSheng. Thanks @kevinWangSheng.
-- Gateway/Control UI origins: support wildcard `"*"` in `gateway.controlUi.allowedOrigins` for trusted remote access setups. Landed from contributor PR #31088 by @frankekn. Thanks @frankekn.
-- Cron/Isolated CLI timeout ratio: avoid reusing persisted CLI session IDs on fresh isolated cron runs so the fresh watchdog profile is used and jobs do not abort at roughly one-third of configured `timeoutSeconds`. (#30140) Thanks @ningding97.
-- Cron/Session target guardrail: reject creating or patching `sessionTarget: "main"` cron jobs when `agentId` is not the default agent, preventing invalid cross-agent main-session bindings at write time. (#30217) Thanks @liaosvcaf.
+- Feishu/Streaming card text fidelity: merge throttled/fragmented partial updates without dropping content and avoid newline injection when stitching chunk-style deltas so card-stream output matches final reply text. (#29616) Thanks @HaoHuaqing.
+- Security/Feishu webhook ingress: bound unauthenticated webhook rate-limit state with stale-window pruning and a hard key cap to prevent unbounded pre-auth memory growth from rotating source keys. (#26050) Thanks @bmendonca3.
+- Security/Compaction audit: remove the post-compaction audit injection message. (#28507) Thanks @fuller-stack-dev and @vincentkoc.
+- Web tools/RFC2544 fake-IP compatibility: allow RFC2544 benchmark range (`198.18.0.0/15`) for trusted web-tool fetch endpoints so proxy fake-IP networking modes do not trigger false SSRF blocks. Landed from contributor PR #31176 by @sunkinux. Thanks @sunkinux.
+- Feishu/Sessions announce group targets: normalize `group:` and `channel:` Feishu targets to `chat_id` routing so `sessions_send` announce delivery no longer sends group chat IDs via `user_id` API params. Fixes #31426.
+- Windows/Plugin install: avoid `spawn EINVAL` on Windows npm/npx invocations by resolving to `node` + npm CLI scripts instead of spawning `.cmd` directly. Landed from contributor PR #31147 by @codertony. Thanks @codertony.
+- Web UI/Cron: include configured agent model defaults/fallbacks in cron model suggestions so scheduled-job model autocomplete reflects configured models. (#29709) Thanks @Sid-Qin.
+- Cron/Delivery: disable the agent messaging tool when `delivery.mode` is `"none"` so cron output is not sent to Telegram or other channels. (#21808) Thanks @lailoo.
+- CLI/Cron: clarify `cron list` output by renaming `Agent` to `Agent ID` and adding a `Model` column for isolated agent-turn jobs. (#26259) Thanks @openperf.
+- Gateway/Control UI origins: honor `gateway.controlUi.allowedOrigins: ["*"]` wildcard entries (including trimmed values) and lock behavior with regression tests. Landed from contributor PR #31058 by @byungsker. Thanks @byungsker.
+- Agents/Sessions list transcript paths: handle missing/non-string/relative `sessions.list.path` values and per-agent `{agentId}` templates when deriving `transcriptPath`, so cross-agent session listings resolve to concrete agent session files instead of workspace-relative paths. (#24775) Thanks @martinfrancois.
+- Gateway/Control UI CSP: allow required Google Fonts origins in Control UI CSP. (#29279) Thanks @vincentkoc.
+- CLI/Install: add an npm-link fallback to fix CLI startup `Permission denied` failures (`exit 127`) on affected installs. (#17151) Thanks @sskyu and @vincentkoc.
+- Plugins/NPM spec install: fix npm-spec plugin installs when `npm pack` output is empty by detecting newly created `.tgz` archives in the pack directory. (#21039) Thanks @graysurf and @vincentkoc.
+- Plugins/Install: clear stale install errors when an npm package is not found so follow-up install attempts report current state correctly. (#25073) Thanks @dalefrieswthat.
+- Gateway/macOS supervised restart: actively `launchctl kickstart -k` during intentional supervised restarts to bypass LaunchAgent `ThrottleInterval` delays, and fall back to in-process restart when kickstart fails. Landed from contributor PR #29078 by @cathrynlavery. Thanks @cathrynlavery.
+- Sessions/Internal routing: preserve established external `lastTo`/`lastChannel` routes for internal/non-deliverable turns, with added coverage for no-fallback internal routing behavior. Landed from contributor PR #30941 by @graysurf. Thanks @graysurf.
+- Auto-reply/NO_REPLY: strip `NO_REPLY` token from mixed-content messages instead of leaking raw control text to end users. Landed from contributor PR #31080 by @scoootscooob. Thanks @scoootscooob.
+- Inbound metadata/Multi-account routing: include `account_id` in trusted inbound metadata so multi-account channel sessions can reliably disambiguate the receiving account in prompt context. Landed from contributor PR #30984 by @Stxle2. Thanks @Stxle2.
+- Cron/Delivery mode none: send explicit `delivery: { mode: "none" }` from cron editor for both add and update flows so previous announce delivery is actually cleared. Landed from contributor PR #31145 by @byungsker. Thanks @byungsker.
+- Cron editor viewport: make the sticky cron edit form independently scrollable with viewport-bounded height so lower fields/actions are reachable on shorter screens. Landed from contributor PR #31133 by @Sid-Qin. Thanks @Sid-Qin.
+- Agents/Thinking fallback: when providers reject unsupported thinking levels without enumerating alternatives, retry with `think=off` to avoid hard failure during model/provider fallback chains. Landed from contributor PR #31002 by @yfge. Thanks @yfge.
+- Agents/Failover reason classification: avoid false rate-limit classification from incidental `tpm` substrings by matching TPM as a standalone token/phrase and keeping auth-context errors on the auth path. Landed from contributor PR #31007 by @HOYALIM. Thanks @HOYALIM.
+- Gateway/WS: close repeated post-handshake `unauthorized role:*` request floods per connection and sample duplicate rejection logs, preventing a single misbehaving client from degrading gateway responsiveness. (#20168) Thanks @acy103, @vibecodooor, and @vincentkoc.
+- Gateway/Auth: improve device-auth v2 migration diagnostics so operators get clearer guidance when legacy clients connect. (#28305) Thanks @vincentkoc.
+- CLI/Ollama config: allow `config set` for Ollama `apiKey` without predeclared provider config. (#29299) Thanks @vincentkoc.
+- Agents/Ollama: demote empty-discovery logging from `warn` to `debug` to reduce noisy warnings in normal edge-case discovery flows. (#26379) Thanks @byungsker.
+- Sandbox/Browser Docker: pass `OPENCLAW_BROWSER_NO_SANDBOX=1` to sandbox browser containers and bump sandbox browser security hash epoch so existing containers are recreated and pick up the env on upgrade. (#29879) Thanks @Lukavyi.
+- Tools/Edit workspace boundary errors: preserve the real `Path escapes workspace root` failure path instead of surfacing a misleading access/file-not-found error when editing outside workspace roots. Landed from contributor PR #31015 by @haosenwang1018. Thanks @haosenwang1018.
+- Browser/Open & navigate: accept `url` as an alias parameter for `open` and `navigate`. (#29260) Thanks @vincentkoc.
+- Sandbox/mkdirp boundary checks: allow directory-safe boundary validation for existing in-boundary subdirectories, preventing false `cannot create directories` failures in sandbox write mode. (#30610) Thanks @glitch418x.
+- Android/Nodes reliability: reject `facing=both` when `deviceId` is set to avoid mislabeled duplicate captures, allow notification `open`/`reply` on non-clearable entries while still gating dismiss, trigger listener rebind before notification actions, and scale invoke-result ack timeout to invoke budget for large clip payloads. (#28260) Thanks @obviyus.
+- LINE/Voice transcription: classify M4A voice media as `audio/mp4` (not `video/mp4`) by checking the MPEG-4 `ftyp` major brand (`M4A ` / `M4B `), restoring voice transcription for LINE voice messages. Landed from contributor PR #31151 by @scoootscooob. Thanks @scoootscooob.
+- Slack/Announce target account routing: enable session-backed announce-target lookup for Slack so multi-account announces resolve the correct `accountId` instead of defaulting to bot-token context. Landed from contributor PR #31028 by @taw0002. Thanks @taw0002.
+- Android/Voice screen TTS: stream assistant speech via ElevenLabs WebSocket in Talk Mode, stop cleanly on speaker mute/barge-in, and ignore stale out-of-order stream events. (#29521) Thanks @gregmousseau.
+- Android/Photos permissions: declare Android 14+ selected-photo access permission (`READ_MEDIA_VISUAL_USER_SELECTED`) and align Android permission/settings paths with current minSdk behavior for more reliable permission state handling.
+- Feishu/Reply media attachments: send Feishu reply `mediaUrl`/`mediaUrls` payloads as attachments alongside text/streamed replies in the reply dispatcher, including legacy fallback when `mediaUrls` is empty. (#28959) Thanks @icesword0760.
+- Slack/User-token resolution: normalize Slack account user-token sourcing through resolved account metadata (`SLACK_USER_TOKEN` env + config) so monitor reads, Slack actions, directory lookups, onboarding allow-from resolution, and capabilities probing consistently use the effective user token. (#28103) Thanks @chilu18.
+- Feishu/Outbound session routing: stop assuming bare `oc_` identifiers are always group chats, honor explicit `dm:`/`group:` prefixes for `oc_` chat IDs, and default ambiguous bare `oc_` targets to direct routing to avoid DM session misclassification. (#10407) Thanks @Bermudarat.
+- Feishu/Group session routing: add configurable group session scopes (`group`, `group_sender`, `group_topic`, `group_topic_sender`) with legacy `topicSessionMode=enabled` compatibility so Feishu group conversations can isolate sessions by sender/topic as configured. (#17798) Thanks @yfge.
+- Feishu/Reply-in-thread routing: add `replyInThread` config (`disabled|enabled`) for group replies, propagate `reply_in_thread` across text/card/media/streaming sends, and align topic-scoped session routing so newly created reply threads stay on the same session root. (#27325) Thanks @kcinzgg.
+- Feishu/Probe status caching: cache successful `probeFeishu()` bot-info results for 10 minutes (bounded cache with per-account keying) to reduce repeated status/onboarding probe API calls, while bypassing cache for failures and exceptions. (#28907) Thanks @hou-rong.
+- Feishu/Opus media send type: send `.opus` attachments with `msg_type: "audio"` (instead of `"media"`) so Feishu voice messages deliver correctly while `.mp4` remains `msg_type: "media"` and documents remain `msg_type: "file"`. (#28269) Thanks @PinoHouse.
+- Feishu/Mobile video media type: treat inbound `message_type: "media"` as video-equivalent for media key extraction, placeholder inference, and media download resolution so mobile-app video sends ingest correctly. (#25502) Thanks @4ier.
+- Feishu/Inbound sender fallback: fall back to `sender_id.user_id` when `sender_id.open_id` is missing on inbound events, and use ID-type-aware sender lookup so mobile-delivered messages keep stable sender identity/routing. (#26703) Thanks @NewdlDewdl.
+- Feishu/Reply context metadata: include inbound `parent_id` and `root_id` as `ReplyToId`/`RootMessageId` in inbound context, and parse interactive-card quote bodies into readable text when fetching replied messages. (#18529) Thanks @qiangu.
+- Feishu/Post embedded media: extract `media` tags from inbound rich-text (`post`) messages and download embedded video/audio files alongside existing embedded-image handling, with regression coverage. (#21786) Thanks @laopuhuluwa.
+- Feishu/Local media sends: propagate `mediaLocalRoots` through Feishu outbound media sending into `loadWebMedia` so local path attachments work with post-CVE local-root enforcement. (#27884) Thanks @joelnishanth.
+- Feishu/Group wildcard policy fallback: honor `channels.feishu.groups["*"]` when no explicit group match exists so unmatched groups inherit wildcard reply-policy settings instead of falling back to global defaults. (#29456) Thanks @WaynePika.
+- Feishu/Inbound media regression coverage: add explicit tests for message resource type mapping (`image` stays `image`, non-image maps to `file`) to prevent reintroducing unsupported Feishu `type=audio` fetches. (#16311, #8746) Thanks @Yaxuan42.
+- TTS/Voice bubbles: use opus output and enable `audioAsVoice` routing for Feishu and WhatsApp (in addition to Telegram) so supported channels receive voice-bubble playback instead of file-style audio attachments. (#27366) Thanks @smthfoxy.
+- Telegram/Reply media context: include replied media files in inbound context when replying to media, defer reply-media downloads to debounce flush, gate reply-media fetch behind DM authorization, and preserve replied media when non-vision sticker fallback runs (including cached-sticker paths). (#28488) Thanks @obviyus.
+- Android/Nodes notification wake flow: enable Android `system.notify` default allowlist, emit `notifications.changed` events for posted/removed notifications (excluding OpenClaw app-owned notifications), canonicalize notification session keys before enqueue/wake routing, and skip heartbeat wakes when consecutive notification summaries dedupe. (#29440) Thanks @obviyus.
+- Telegram/Voice fallback reply chunking: apply reply reference, quote text, and inline buttons only to the first fallback text chunk when voice delivery is blocked, preventing over-quoted multi-chunk replies. Landed from contributor PR #31067 by @xdanger. Thanks @xdanger.
+- Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #29610, #30432, #30331, and #29501. Thanks @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff.
+- Feishu/Inbound rich-text parsing: preserve `share_chat` payload summaries when available and add explicit parsing for rich-text `code`/`code_block`/`pre` tags so forwarded and code-heavy messages keep useful context in agent input. (#28591) Thanks @kevinWangSheng.
+- Feishu/Post markdown parsing: parse rich-text `post` payloads through a shared markdown-aware parser with locale-wrapper support, preserved mention/image metadata extraction, and inline/fenced code fidelity for agent input rendering. (#12755) Thanks @WilsonLiu95.
+- Telegram/Outbound chunking: route oversize splitting through the shared outbound pipeline (including subagents), retry Telegram sends when escaped HTML exceeds limits, and preserve boundary whitespace when retry re-splitting rendered chunks so plain-text/transcript fidelity is retained. (#29342, #27317; follow-up to #27461) Thanks @obviyus.
+- Slack/Native commands: register Slack native status as `/agentstatus` (Slack-reserved `/status`) so manifest slash command registration stays valid while text `/status` still works. Landed from contributor PR #29032 by @maloqab. Thanks @maloqab.
+- Android/Camera clip: remove `camera.clip` HTTP-upload fallback to base64 so clip transport is deterministic and fail-loud, and reject non-positive `maxWidth` values so invalid inputs fall back to the safe resize default. (#28229) Thanks @obviyus.
+- Android/Gateway canvas capability refresh: send `node.canvas.capability.refresh` with object `params` (`{}`) from Android node runtime so gateway object-schema validation accepts refresh retries and A2UI host recovery works after scoped capability expiry. (#28413) Thanks @obviyus.
+- Onboarding/Custom providers: improve verification reliability for slower local endpoints (for example Ollama) during setup. (#27380) Thanks @Sid-Qin.
+- Daemon/macOS TLS certs: default LaunchAgent service env `NODE_EXTRA_CA_CERTS` to `/etc/ssl/cert.pem` (while preserving explicit overrides) so HTTPS clients no longer fail with local-issuer errors under launchd. (#27915) Thanks @Lukavyi.
+- Daemon/Linux systemd user-bus fallback: when `systemctl --user` cannot reach the user bus due missing session env, fall back to `systemctl --machine @ --user` so daemon checks/install continue in headless SSH/server sessions. (#34884) Thanks @vincentkoc.
+- Gateway/Linux restart health: reduce false `openclaw gateway restart` timeouts by falling back to `ss -ltnp` when `lsof` is missing, confirming ambiguous busy-port cases via local gateway probe, and targeting the original `SUDO_USER` systemd user scope for restart commands. (#34874) Thanks @vincentkoc.
+- Discord/Components wildcard handlers: use distinct internal registration sentinel IDs and parse those sentinels as wildcard keys so select/user/role/channel/mentionable/modal interactions are not dropped by raw customId dedupe paths. Landed from contributor PR #29459 by @Sid-Qin. Thanks @Sid-Qin.
+- Feishu/Reaction notifications: add `channels.feishu.reactionNotifications` (`off | own | all`, default `own`) so operators can disable reaction ingress or allow all verified reaction events (not only bot-authored message reactions). (#28529) Thanks @cowboy129.
+- Feishu/Typing backoff: re-throw Feishu typing add/remove rate-limit and quota errors (`429`, `99991400`, `99991403`) and detect SDK non-throwing backoff responses so the typing keepalive circuit breaker can stop retries instead of looping indefinitely. (#28494) Thanks @guoqunabc.
+- Feishu/Zalo runtime logging: replace direct `console.log/error` usage in Feishu typing-indicator paths and Zalo monitor paths with runtime-gated logger calls so verbosity controls are respected while preserving typing backoff behavior. (#18841) Thanks @Clawborn.
+- Feishu/Group sender allowlist fallback: add global `channels.feishu.groupSenderAllowFrom` sender authorization for group chats, with per-group `groups..allowFrom` precedence and regression coverage for allow/block/precedence behavior. (#29174) Thanks @1MoreBuild.
+- Feishu/Docx append/write ordering: insert converted Docx blocks sequentially (single-block creates) so Feishu append/write preserves markdown block order instead of returning shuffled sections in asynchronous batch inserts. (#26172, #26022) Thanks @echoVic.
+- Feishu/Docx convert fallback chunking: recursively split oversized markdown chunks (including long no-heading sections) when `document.convert` hits content limits, while keeping fenced-code-aware split boundaries whenever possible. (#14402) Thanks @lml2468.
+- Feishu/API quota controls: add `typingIndicator` and `resolveSenderNames` config flags (top-level and per-account) so operators can disable typing reactions and sender-name lookup requests while keeping default behavior unchanged. (#10513) Thanks @BigUncle.
+- Feishu/System preview prompt leakage: stop enqueuing inbound Feishu message previews as system events so user preview text is not injected into later turns as trusted `System:` context. Landed from contributor PR #31209 by @stakeswky. Thanks @stakeswky.
+- Feishu/Typing replay suppression: skip typing indicators for stale replayed inbound messages after compaction using message-age checks with second/millisecond timestamp normalization, preventing old-message reaction floods while preserving typing for fresh messages. Landed from contributor PR #30709 by @arkyu2077. Thanks @arkyu2077.
+- Control UI/Debug log layout: render Debug Event Log payloads at full width to prevent payload JSON from being squeezed into a narrow side column. Landed from contributor PR #30978 by @stozo04. Thanks @stozo04.
+- Install/npm: fix npm global install deprecation warnings. (#28318) Thanks @vincentkoc.
+- Update/Global npm: fallback to `--omit=optional` when global `npm update` fails so optional dependency install failures no longer abort update flows. (#24896) Thanks @xinhuagu and @vincentkoc.
+- Model directives/Auth profiles: split `/model` profile suffixes at the first `@` after the last slash so email-based auth profile IDs (for example OAuth profile IDs) resolve correctly. Landed from contributor PR #30932 by @haosenwang1018. Thanks @haosenwang1018.
+- Ollama/Embedded runner base URL precedence: prioritize configured provider `baseUrl` over model defaults for embedded Ollama runs so Docker and remote-host setups avoid localhost fetch failures. (#30964) Thanks @stakeswky.
+- Ollama/Autodiscovery: harden autodiscovery and warning behavior. (#29201) Thanks @marcodelpin and @vincentkoc.
+- Ollama/Context window: unify context window handling across discovery, merge, and OpenAI-compatible transport paths. (#29205) Thanks @Sid-Qin, @jimmielightner, and @vincentkoc.
+- fix(model): preserve reasoning in provider fallback resolution. (#29285) Fixes #25636. Thanks @vincentkoc.
+- Docker/Image permissions: normalize `/app/extensions`, `/app/.agent`, and `/app/.agents` to directory mode `755` and file mode `644` during image build so plugin discovery does not block inherited world-writable paths. (#30191) Fixes #30139. Thanks @edincampara.
+- OpenAI Responses/Compaction: rewrite and unify the OpenAI Responses store patches to treat empty `baseUrl` as non-direct, honor `compat.supportsStore=false`, and auto-inject server-side compaction `context_management` for compatible direct OpenAI models (with per-model opt-out/threshold overrides). Landed from contributor PRs #16930 (@OiPunk), #22441 (@EdwardWu7), and #25088 (@MoerAI). Thanks @OiPunk, @EdwardWu7, and @MoerAI.
+- Agents/Compaction safeguard: preserve recent turns verbatim with stable user/assistant pairing, keep multimodal and tool-result hints in preserved tails, and avoid empty-history fallback text in compacted output. (#25554) thanks @rodrigouroz.
+- Usage normalization: clamp negative prompt/input token values to zero (including `prompt_tokens` alias inputs) so `/usage` and TUI usage displays cannot show nonsensical negative counts. Landed from contributor PR #31211 by @scoootscooob. Thanks @scoootscooob.
+- Secrets/Auth profiles: normalize inline SecretRef `token`/`key` values to canonical `tokenRef`/`keyRef` before persistence, and keep explicit `keyRef` precedence when inline refs are also present. Landed from contributor PR #31047 by @minupla. Thanks @minupla.
+- Codex/Usage window: label weekly usage window as `Week` instead of `Day`. (#26267) Thanks @Sid-Qin.
+- Signal/Sync message null-handling: treat `syncMessage` presence (including `null`) as sync envelope traffic so replayed sentTranscript payloads cannot bypass loop guards after daemon restart. Landed from contributor PR #31138 by @Sid-Qin. Thanks @Sid-Qin.
+- Infra/fs-safe: sanitize directory-read failures so raw `EISDIR` text never leaks to messaging surfaces, with regression tests for both root-scoped and direct safe reads. Landed from contributor PR #31205 by @polooooo. Thanks @polooooo.
+
+## Unreleased
+
+### Changes
+
+- Models/OpenAI forward compat: add support for `openai/gpt-5.4`, `openai/gpt-5.4-pro`, and `openai-codex/gpt-5.4`, including direct OpenAI Responses `serviceTier` passthrough safeguards for valid values. (#36590) Thanks @dorukardahan.
+- Android/Play package ID: rename the Android app package to `ai.openclaw.app`, including matching benchmark and Android tooling references for Play publishing. (#38712) Thanks @obviyus.
+
+### Fixes
+
+- Gateway/macOS restart: remove self-issued `launchctl kickstart -k` from launchd supervised restart path to prevent race with launchd's async bootout state machine that permanently unloads the LaunchAgent. With `ThrottleInterval=1` (current default), `exit(0)` + `KeepAlive=true` restarts the service within ~1s without the race condition. (#39760) Landed from contributor PR #39763 by @daymade. Thanks @daymade.
+- Plugin SDK/bundled subpath contracts: add regression coverage for newly routed bundled-plugin SDK exports so BlueBubbles, Mattermost, Nextcloud Talk, and Twitch subpath symbols stay pinned during future plugin-sdk cleanup. (#39638)
+- Exec/system.run env sanitization: block dangerous override-only env pivots such as `GIT_SSH_COMMAND`, editor/pager hooks, and `GIT_CONFIG_` / `NPM_CONFIG_` override prefixes so allowlisted tools cannot smuggle helper command execution through subprocess environment overrides. Thanks @tdjackey and @SnailSploit for reporting.
+- Network/fetch guard redirect auth stripping: switch cross-origin redirect handling in `fetchWithSsrFGuard` from a narrow sensitive-header denylist to a safe-header allowlist so custom auth headers like `X-Api-Key` and `Private-Token` no longer leak on origin changes. Thanks @Rickidevs for reporting.
- Security/Sandbox media reads: eliminate sandbox media TOCTOU symlink-retarget escapes by enforcing root-scoped boundary-safe reads at attachment/image load time and consolidating shared safe-read helpers across sandbox media callsites. This ships in the next npm release. Thanks @tdjackey for reporting.
-- Node host/service auth env: include `OPENCLAW_GATEWAY_TOKEN` in `openclaw node install` service environments (with `CLAWDBOT_GATEWAY_TOKEN` compatibility fallback) so installed node services keep remote gateway token auth across restart/reboot. Fixes #31041. Thanks @OneStepAt4time for reporting, @byungsker, @liuxiaopai-ai, and @vincentkoc.
+- Security/Sandbox media staging: block destination symlink escapes in `stageSandboxMedia` by replacing direct destination copies with root-scoped safe writes for both local and SCP-staged attachments, preventing out-of-workspace file overwrite through `media/inbound` alias traversal. This ships in the next npm release (`2026.3.2`). Thanks @tdjackey for reporting.
+- Security/Sandbox fs bridge: harden sandbox `readFile`, `mkdirp`, `remove`, and `rename` operations by pinning reads to boundary-opened file descriptors and anchoring filesystem changes to verified canonical parent directories plus basenames instead of passing mutable full path strings to `mkdir -p`, `rm`, and `mv`, reducing TOCTOU race exposure in sandbox file operations. This ships in the next npm release. Thanks @tdjackey for reporting.
+- Security/Workspace safe writes: harden `writeFileWithinRoot` against symlink-retarget TOCTOU races by opening existing files without truncation, creating missing files with exclusive create, deferring truncation until post-open identity+boundary validation, and removing out-of-root create artifacts on blocked races; added regression tests for truncate/create race paths. This ships in the next npm release (`2026.3.2`). Thanks @tdjackey for reporting.
- Security/Subagents sandbox inheritance: block sandboxed sessions from spawning cross-agent subagents that would run unsandboxed, preventing runtime sandbox downgrade via `sessions_spawn agentId`. Thanks @tdjackey for reporting.
-- Security/Workspace safe writes: harden `writeFileWithinRoot` against symlink-retarget TOCTOU races by opening existing files without truncation, creating missing files with exclusive create, deferring truncation until post-open identity+boundary validation, and removing out-of-root create artifacts on blocked races; added regression tests for truncate/create race paths. This ships in the next npm release (`2026.3.1`). Thanks @tdjackey for reporting.
-- Control UI/Cron editor: include `{ mode: "none" }` in `cron.update` patches when editing an existing job and selecting “Result delivery = None (internal)”, so saved jobs no longer keep stale announce delivery mode. Fixes #31075.
-- Telegram/Restart polling teardown: stop the Telegram bot instance when a polling cycle exits so in-process SIGUSR1 restarts fully tear down old long-poll loops before restart, reducing post-restart `getUpdates` 409 conflict storms. Fixes #31107.
-- Security/Node metadata policy: harden node platform classification against Unicode confusables and switch unknown platform defaults to a conservative allowlist that excludes `system.run`/`system.which` unless explicitly allowlisted, preventing metadata canonicalization drift from broadening node command permissions. Thanks @tdjackey for reporting.
-- Plugins/Discovery precedence: load bundled plugins before auto-discovered global extensions so bundled channel plugins win duplicate-ID resolution by default (explicit `plugins.load.paths` overrides remain highest precedence), with loader regression coverage. Landed from contributor PR #29710 by @Sid-Qin. Thanks @Sid-Qin.
-- Discord/Reconnect integrity: release Discord message listener lane immediately while preserving serialized handler execution, add HELLO-stall resume-first recovery with bounded fresh-identify fallback after repeated stalls, and extend lifecycle/listener regression coverage for forced reconnect scenarios. Landed from contributor PR #29508 by @cgdusek. Thanks @cgdusek.
-- Matrix/Conduit compatibility: avoid blocking startup on non-resolving Matrix sync start, preserve startup error propagation, prevent duplicate monitor listener registration, remove unreliable 2-member DM heuristics, accept `!room` IDs without alias resolution, and add matrix monitor/client regression coverage. Landed from contributor PR #31023 by @efe-arv. Thanks @efe-arv.
-- Discord/Reconnect watchdog: add a shared armable transport stall-watchdog and wire Discord gateway lifecycle force-stop semantics for silent close/reconnect zombies, with gateway/lifecycle watchdog regression coverage and runtime status liveness updates. Follow-up to contributor PR #31025 by @theotarr and PR #30530 by @liuxiaopai-ai. Thanks @theotarr and @liuxiaopai-ai.
-- Security/Skills: harden skill installer metadata parsing by rejecting unsafe installer specs (brew/node/go/uv/download) and constrain plugin-declared skill directories to the plugin root (including symlink-escape checks), with regression coverage.
-- Discord/DM command auth: unify DM allowlist + pairing-store authorization across message preflight and native command interactions so DM command gating is consistent for `open`/`pairing`/`allowlist` policies.
-- Sessions/Usage accounting: persist `cacheRead`/`cacheWrite` from the latest call snapshot (`lastCallUsage`) instead of accumulated multi-call totals, preventing inflated token/cost reporting in long tool/compaction runs. (#31005)
-- Sessions/Followup queue: always schedule followup drain even when unexpected runtime exceptions escape `runReplyAgent`, preventing silent stuck followup backlogs after failed turns. (#30627)
-- Sessions/DM scope migration: when `session.dmScope` is non-`main`, retire stale `agent:*:main` delivery routing metadata once the matching direct-chat peer session is active, preventing duplicate Telegram/DM announce deliveries from legacy main sessions after scope migration. (#31010)
-- Sessions/Compaction safety: add transcript-size forced pre-compaction memory flush (`agents.defaults.compaction.memoryFlush.forceFlushTranscriptBytes`, default 2MB) so long sessions recover without manual transcript deletion when token snapshots are stale. (#30655)
-- Diagnostics/Stuck session signal: add configurable stuck-session warning threshold via `diagnostics.stuckSessionWarnMs` (default 120000ms) to reduce false-positive warnings on long multi-tool turns. (#31032)
-- ACP/Harness thread spawn routing: force ACP harness thread creation through `sessions_spawn` (`runtime: "acp"`, `thread: true`) and explicitly forbid `message action=thread-create` for ACP harness requests, avoiding misrouted `Unknown channel` errors. (#30957) Thanks @dutifulbob.
-- Docs/ACP permissions: document the correct `permissionMode` default (`approve-reads`) and clarify non-interactive permission failure behavior/troubleshooting guidance. (#31044) Thanks @barronlroth.
-- Security/Logging utility hardening: remove `eval`-based command execution from `scripts/clawlog.sh`, switch to argv-safe command construction, and escape predicate literals for user-supplied search/category filters to block local command/predicate injection paths.
-- Security/ACPX Windows spawn hardening: resolve `.cmd/.bat` wrappers via PATH/PATHEXT and execute unwrapped Node/EXE entrypoints without shell parsing when possible, and enable strict fail-closed handling (`strictWindowsCmdWrapper`) by default for unresolvable wrappers on Windows (with explicit opt-out for compatibility). This ships in the next npm release. Thanks @tdjackey for reporting.
-- Security/Inbound metadata stripping: tighten sentinel matching and JSON-fence validation for inbound metadata stripping so user-authored lookalike lines no longer trigger unintended metadata removal.
-- Security/Zalo webhook memory hardening: bound webhook security tracking state and normalize security keying to matched webhook paths (excluding attacker query-string churn) to prevent unauthenticated memory growth pressure on reachable webhook endpoints. Thanks @Somet2mes.
-- Security/Web search citation redirects: enforce strict SSRF defaults for Gemini citation redirect resolution so redirects to localhost/private/internal targets are blocked. Thanks @tdjackey for reporting.
-- Channels/Command parsing parity: align command-body parsing fields with channel command-gating text for Slack, Signal, Microsoft Teams, Mattermost, and BlueBubbles to avoid mention-strip mismatches and inconsistent command detection.
-- CLI/Startup (Raspberry Pi + small hosts): speed up startup by avoiding unnecessary plugin preload on fast routes, adding root `--version` fast-path bootstrap bypass, parallelizing status JSON/non-JSON scans where safe, and enabling Node compile cache at startup with env override compatibility (`NODE_COMPILE_CACHE`, `NODE_DISABLE_COMPILE_CACHE`). (#5871) Thanks @BookCatKid and @vincentkoc for raising startup reports, and @lupuletic for related startup work in #27973.
-- Doctor/macOS state-dir safety: warn when OpenClaw state resolves inside iCloud Drive (`~/Library/Mobile Documents/com~apple~CloudDocs/...`) or `~/Library/CloudStorage/...`, because sync-backed paths can cause slower I/O and lock/sync races. (#31004) Thanks @vincentkoc.
-- Doctor/Linux state-dir safety: warn when OpenClaw state resolves to an `mmcblk*` mount source (SD or eMMC), because random I/O can be slower and media wear can increase under session and credential writes. (#31033) Thanks @vincentkoc.
-- CLI/Startup follow-up: add root `--help` fast-path bootstrap bypass with strict root-only matching, lazily resolve CLI channel options only when commands need them, merge build-time startup metadata (`dist/cli-startup-metadata.json`) with runtime catalog discovery so dynamic catalogs are preserved, and add low-power Linux doctor hints for compile-cache placement and respawn tuning. (#30975) Thanks @vincentkoc.
-- Docker/Compose gateway targeting: run `openclaw-cli` in the `openclaw-gateway` service network namespace, require gateway startup ordering, pin Docker setup to `gateway.mode=local`, sync `gateway.bind` from `OPENCLAW_GATEWAY_BIND`, default optional `CLAUDE_*` compose vars to empty values to reduce automation warning noise, and harden `openclaw-cli` with `cap_drop` (`NET_RAW`, `NET_ADMIN`) + `no-new-privileges`. Docs now call out the shared trust boundary explicitly. (#12504) Thanks @bvanderdrift and @vincentkoc.
-- Telegram/Outbound API proxy env: keep the Node 22 `autoSelectFamily` global-dispatcher workaround while restoring env-proxy support by using `EnvHttpProxyAgent` so `HTTP_PROXY`/`HTTPS_PROXY` continue to apply to outbound requests. (#26207) Thanks @qsysbio-cjw for reporting and @rylena and @vincentkoc for work.
- Browser/Security: fail closed on browser-control auth bootstrap errors; if auto-auth setup fails and no explicit token/password exists, browser control server startup now aborts instead of starting unauthenticated. This ships in the next npm release. Thanks @ijxpwastaken.
+- Security/ACPX Windows spawn hardening: resolve `.cmd/.bat` wrappers via PATH/PATHEXT and execute unwrapped Node/EXE entrypoints without shell parsing when possible, and enable strict fail-closed handling (`strictWindowsCmdWrapper`) by default for unresolvable wrappers on Windows (with explicit opt-out for compatibility). This ships in the next npm release. Thanks @tdjackey for reporting.
+- Security/Web search citation redirects: enforce strict SSRF defaults for Gemini citation redirect resolution so redirects to localhost/private/internal targets are blocked. Thanks @tdjackey for reporting.
+- Security/Node metadata policy: harden node platform classification against Unicode confusables and switch unknown platform defaults to a conservative allowlist that excludes `system.run`/`system.which` unless explicitly allowlisted, preventing metadata canonicalization drift from broadening node command permissions. Thanks @tdjackey for reporting.
+- Security/Skills: harden skill installer metadata parsing by rejecting unsafe installer specs (brew/node/go/uv/download) and constrain plugin-declared skill directories to the plugin root (including symlink-escape checks), with regression coverage.
- Sandbox/noVNC hardening: increase observer password entropy, shorten observer token lifetime, and replace noVNC token redirect with a bootstrap page that keeps credentials out of `Location` query strings and adds strict no-cache/no-referrer headers.
-- Security/External content marker folding: expand Unicode angle-bracket homoglyph normalization in marker sanitization so additional guillemet, double-angle, tortoise-shell, flattened-parenthesis, and ornamental variants are folded before boundary replacement. (#30951) Thanks @benediktjohannes.
-- Docs/Slack manifest scopes: add missing DM/group-DM bot scopes (`im:read`, `im:write`, `mpim:read`, `mpim:write`) to the Slack app manifest example so DM setup guidance is complete. (#29999) Thanks @JcMinarro.
-- Slack/Onboarding token help: update setup text to include the “From manifest” app-creation path and current install wording for obtaining the `xoxb-` bot token. (#30846) Thanks @yzhong52.
-- Slack/Bot attachment-only messages: when `allowBots: true`, bot messages with empty `text` now include non-forwarded attachment `text`/`fallback` content so webhook alerts are not silently dropped. (#27616) Thanks @lailoo.
-- Slack/Inbound media auth + HTML guard: keep Slack auth headers on forwarded shared attachment image downloads, and reject login/error HTML payloads (while allowing expected `.html` uploads) when resolving Slack media so auth failures do not silently pass as files. (#18642) Thanks @tumf.
+- Security/Logging utility hardening: remove `eval`-based command execution from `scripts/clawlog.sh`, switch to argv-safe command construction, and escape predicate literals for user-supplied search/category filters to block local command/predicate injection paths.
- Slack/Security ingress mismatch guard: drop slash-command and interaction payloads when app/team identifiers do not match the active Slack account context (including nested `team.id` interaction payloads), preventing cross-app or cross-workspace payload injection into system-event handling. (#29091) Thanks @Solvely-Colin.
-- Cron/Failure alerts: add configurable repeated-failure alerting with per-job overrides and Web UI cron editor support (`inherit|disabled|custom` with threshold/cooldown/channel/target fields). (#24789) Thanks @0xbrak.
-- Cron/Isolated model defaults: resolve isolated cron `subagents.model` (including object-form `primary`) through allowlist-aware model selection so isolated cron runs honor subagent model defaults unless explicitly overridden by job payload model. (#11474) Thanks @AnonO6.
-- Cron/Isolated sessions list: persist the intended pre-run model/provider on isolated cron session entries so `sessions_list` reflects payload/session model overrides even when runs fail before post-run telemetry persistence. (#21279) Thanks @altaywtf.
-- Cron tool/update flat params: recover top-level update patch fields when models omit the `patch` wrapper, and allow flattened update keys through tool input schema validation so `cron.update` no longer fails with `patch required` for valid flat payloads. (#23221)
-- Agents/Message tool scoping: include other configured channels in scoped `message` tool action enum + description so isolated/cron runs can discover and invoke cross-channel actions without schema validation failures. Landed from contributor PR #20840 by @altaywtf. Thanks @altaywtf.
-- Web UI/Chat sessions: add a cron-session visibility toggle in the session selector, fix cron-key detection across `cron:*` and `agent:*:cron:*` formats, and localize the new control labels/tooltips. (#26976) Thanks @ianderrington.
-- Web UI/Cron jobs: add schedule-kind and last-run-status filters to the Jobs list, with reset control and client-side filtering over loaded results. (#9510) Thanks @guxu11.
-- Web UI/Control UI WebSocket defaults: include normalized `gateway.controlUi.basePath` (or inferred nested route base path) in the default `gatewayUrl` so first-load dashboard connections work behind path-based reverse proxies. (#30228) Thanks @gittb.
-- Gateway/Control UI API routing: when `gateway.controlUi.basePath` is unset (default), stop serving Control UI SPA HTML for `/api` and `/api/*` so API paths fall through to normal gateway handlers/404 responses instead of `index.html`. (#30333) Fixes #30295. thanks @Sid-Qin.
-- Cron/One-shot reliability: retry transient one-shot failures with bounded backoff and configurable retry policy before disabling. (#24435) Thanks @hugenshen.
-- Gateway/Cron auditability: add gateway info logs for successful cron create, update, and remove operations. (#25090) Thanks @MoerAI.
-- Gateway/Tailscale onboarding origin allowlist: auto-add the detected Tailnet HTTPS origin during interactive configure/onboarding flows (including IPv6-safe origin formatting and binary-path reuse), so Tailscale serve/funnel Control UI access works without manual `allowedOrigins` edits. Landed from contributor PR #28960 by @Glucksberg. Thanks @Glucksberg.
-- Gateway/Upgrade migration for Control UI origins: seed `gateway.controlUi.allowedOrigins` on startup for legacy non-loopback configs (`lan`/`tailnet`/`custom`) when origins are missing or blank, preventing post-upgrade crash loops while preserving explicit existing policy. Landed from contributor PR #29394 by @synchronic1. Thanks @synchronic1.
-- Gateway/Plugin HTTP auth hardening: require gateway auth for protected plugin paths and explicit `registerHttpRoute` paths (while preserving wildcard-handler behavior for signature-auth webhooks), and run plugin handlers after built-in handlers for deterministic route precedence. Landed from contributor PR #29198 by @Mariana-Codebase. Thanks @Mariana-Codebase.
-- Gateway/Config patch guard: reject `config.patch` updates that set non-loopback `gateway.bind` while `gateway.tailscale.mode` is `serve`/`funnel`, preventing restart crash loops from invalid bind/tailscale combinations. Landed from contributor PR #30910 by @liuxiaopai-ai. Thanks @liuxiaopai-ai.
-- Cron/Schedule errors: notify users when a job is auto-disabled after repeated schedule computation failures. (#29098) Thanks @ningding97.
-- Cron/Schedule errors: notify users when a job is auto-disabled after repeated schedule computation failures. (#29098) Thanks @ningding97.
-- File tools/tilde paths: expand `~/...` against the user home directory before workspace-root checks in host file read/write/edit paths, while preserving root-boundary enforcement so outside-root targets remain blocked. (#29779) Thanks @Glucksberg.
-- Slack/HTTP mode startup: treat Slack HTTP accounts as configured when `botToken` + `signingSecret` are present (without requiring `appToken`) in channel config/runtime status so webhook mode is not silently skipped. (#30567) Thanks @liuxiaopai-ai.
-- Slack/Transient request errors: classify Slack request-error messages like `Client network socket disconnected before secure TLS connection was established` as transient in unhandled-rejection fatal detection, preventing temporary network drops from crash-looping the gateway. (#23169) Thanks @graysurf.
-- Slack/Usage footer formatting: wrap session keys in inline code in full response-usage footers so Slack does not parse colon-delimited session segments as emoji shortcodes. (#30258) Thanks @pushkarsingh32.
-- Slack/Thread session isolation: route channel/group top-level messages into thread-scoped sessions (`:thread:`) and read inbound `previousTimestamp` from the resolved thread session key, preventing cross-thread context bleed and stale timestamp lookups. (#10686) Thanks @pablohrcarvalho.
-- Slack/Socket Mode slash startup: treat `app.options()` registration as best-effort and fall back to static arg menus when listener registration fails, preventing Slack monitor startup crash loops on receiver init edge cases. (#21715) Thanks @Glucksberg.
-- Slack/Legacy streaming config: map boolean `channels.slack.streaming=false` to unified streaming mode `off` (with `nativeStreaming=false`) so legacy configs correctly disable draft preview/native streaming instead of defaulting to `partial`. (#25990) Thanks @chilu18.
-- Slack/Socket reconnect reliability: reconnect Socket Mode after disconnect/start failures using bounded exponential backoff with abort-aware waits, while preserving clean shutdown behavior and adding disconnect/error helper tests. (#27232) Thanks @pandego.
-- Memory/QMD update+embed output cap: discard captured stdout for `qmd update` and `qmd embed` runs (while keeping stderr diagnostics) so large index progress output no longer fails sync with `produced too much output` during boot/refresh. (#28900) Thanks @Glucksberg.
-- Onboarding/Custom providers: raise default custom-provider model context window to the runtime hard minimum (16k) and auto-heal existing custom model entries below that threshold during reconfiguration, preventing immediate `Model context window too small (4096 tokens)` failures. (#21653) Thanks @r4jiv007.
+- Security/Inbound metadata stripping: tighten sentinel matching and JSON-fence validation for inbound metadata stripping so user-authored lookalike lines no longer trigger unintended metadata removal.
+- Security/External content marker folding: expand Unicode angle-bracket homoglyph normalization in marker sanitization so additional guillemet, double-angle, tortoise-shell, flattened-parenthesis, and ornamental variants are folded before boundary replacement. (#30951) Thanks @benediktjohannes.
+- Security/Zalo webhook memory hardening: bound webhook security tracking state and normalize security keying to matched webhook paths (excluding attacker query-string churn) to prevent unauthenticated memory growth pressure on reachable webhook endpoints. Thanks @Somet2mes.
+- Security/Audit: flag `gateway.controlUi.allowedOrigins=["*"]` as a high-risk configuration (severity based on bind exposure), and add a Feishu doc-tool warning that `owner_open_id` on `feishu_doc` create can grant document permissions.
+- Hooks/auth throttling: reject non-`POST` `/hooks/*` requests before auth-failure accounting so unsupported methods can no longer burn the hook auth lockout budget and block legitimate webhook delivery. Thanks @JNX03 for reporting.
+- Feishu/Doc create permissions: remove caller-controlled owner fields from `feishu_doc` create and bind optional grant behavior to trusted Feishu requester context (`grant_to_requester`), preventing principal selection via tool arguments. (#31184) Thanks @Takhoffman.
+- Dashboard/macOS auth handling: switch the macOS “Open Dashboard” flow from query-string token injection to URL fragments, stop persisting Control UI gateway tokens in browser localStorage, and scrub legacy stored tokens on load. Thanks @JNX03 for reporting.
+- Gateway/Plugin HTTP auth hardening: require gateway auth for protected plugin paths and explicit `registerHttpRoute` paths (while preserving wildcard-handler behavior for signature-auth webhooks), and run plugin handlers after built-in handlers for deterministic route precedence. Landed from contributor PR #29198. Thanks @Mariana-Codebase.
+- Gateway/Upgrade migration for Control UI origins: seed `gateway.controlUi.allowedOrigins` on startup for legacy non-loopback configs (`lan`/`tailnet`/`custom`) when origins are missing or blank, preventing post-upgrade crash loops while preserving explicit existing policy. Landed from contributor PR #29394. Thanks @synchronic1.
+- Gateway/Config patch guard: reject `config.patch` updates that set non-loopback `gateway.bind` while `gateway.tailscale.mode` is `serve`/`funnel`, preventing restart crash loops from invalid bind/tailscale combinations. Landed from contributor PR #30910. Thanks @liuxiaopai-ai.
+- Gateway/Tailscale onboarding origin allowlist: auto-add the detected Tailnet HTTPS origin during interactive configure/onboarding flows (including IPv6-safe origin formatting and binary-path reuse), so Tailscale serve/funnel Control UI access works without manual `allowedOrigins` edits. Landed from contributor PR #26157. Thanks @stakeswky.
- Web UI/Assistant text: strip internal `...` scaffolding from rendered assistant messages (while preserving code-fence literals), preventing memory-context leakage in chat output for models that echo internal blocks. (#29851) Thanks @Valkster70.
- Dashboard/Sessions: allow authenticated Control UI clients to delete and patch sessions while still blocking regular webchat clients from session mutation RPCs, fixing Dashboard session delete failures. (#21264) Thanks @jskoiz.
-- TUI/Session model status: clear stale runtime model identity when model overrides change so `/model` updates are reflected immediately in `sessions.patch` responses and `sessions.list` status surfaces. (#28619) Thanks @lejean2000.
-- Agents/Session status: read thinking/verbose/reasoning levels from persisted session state in `session_status` output when resolved levels are not provided, so status reflects runtime toggles correctly. (#30129) Thanks @YuzuruS.
-- Agents/Tool-name recovery chain: normalize streamed alias/case tool names against the allowed set, preserve whitespace-only streamed placeholders to avoid collapsing to empty names, and repair/guard persisted blank `toolResult.toolName` values from matching tool calls to reduce repeated `Tool not found` loops in long sessions. Landed from contributor PRs #30620 and #30735 by @Sid-Qin, plus #30881 by @liuxiaopai-ai. Thanks @Sid-Qin and @liuxiaopai-ai.
-- TUI/SIGTERM shutdown: ignore `setRawMode EBADF` teardown errors during `SIGTERM` exit so long-running TUI sessions do not crash on terminal shutdown races, while still rethrowing unrelated stop errors. (#29430) Thanks @Cormazabal.
-- Memory/Hybrid recall: when strict hybrid scoring yields no hits, preserve keyword-backed matches using a text-weight floor so freshly indexed lexical canaries no longer disappear behind `minScore` filtering. (#29112) Thanks @ceo-nada.
-- Android/Notifications auth race: return `NOT_AUTHORIZED` when `POST_NOTIFICATIONS` is revoked between authorization precheck and delivery, instead of returning success while dropping the notification. (#30726) Thanks @obviyus.
+- Web UI/Control UI WebSocket defaults: include normalized `gateway.controlUi.basePath` (or inferred nested route base path) in the default `gatewayUrl` so first-load dashboard connections work behind path-based reverse proxies. (#30228) Thanks @gittb.
+- Gateway/Control UI API routing: when `gateway.controlUi.basePath` is unset (default), stop serving Control UI SPA HTML for `/api` and `/api/*` so API paths fall through to normal gateway handlers/404 responses instead of `index.html`. (#30333) Fixes #30295. thanks @Sid-Qin.
+- Node host/service auth env: include `OPENCLAW_GATEWAY_TOKEN` in `openclaw node install` service environments (with `CLAWDBOT_GATEWAY_TOKEN` compatibility fallback) so installed node services keep remote gateway token auth across restart/reboot. Fixes #31041. Thanks @OneStepAt4time for reporting, @byungsker, @liuxiaopai-ai, and @vincentkoc.
+- Gateway/Control UI origins: support wildcard `"*"` in `gateway.controlUi.allowedOrigins` for trusted remote access setups. Landed from contributor PR #31088. Thanks @frankekn.
+- Gateway/Cron auditability: add gateway info logs for successful cron create, update, and remove operations. (#25090) Thanks @MoerAI.
+- Control UI/Cron editor: include `{ mode: "none" }` in `cron.update` patches when editing an existing job and selecting “Result delivery = None (internal)”, so saved jobs no longer keep stale announce delivery mode. Fixes #31075.
+- Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, prevent inbound preview text from leaking into prompt system events, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #31209, #29610, #30432, #30331, and #29501. Thanks @stakeswky, @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff.
+- Feishu/Target routing + replies + dedupe: normalize provider-prefixed targets (`feishu:`/`lark:`), prefer configured `channels.feishu.defaultAccount` for tool execution, honor Feishu outbound `renderMode` in adapter text/caption sends, fall back to normal send when reply targets are withdrawn/deleted, and add synchronous in-memory dedupe guard for concurrent duplicate inbound events. Landed from contributor PRs #30428, #30438, #29958, #30444, and #29463. Thanks @bmendonca3 and @Yaxuan42.
+- Channels/Multi-account default routing: add optional `channels..defaultAccount` default-selection support across message channels so omitted `accountId` routes to an explicit configured account instead of relying on implicit first-entry ordering (fallback behavior unchanged when unset).
+- Telegram/Multi-account fallback isolation: fail closed for non-default Telegram accounts when route resolution falls back to `matchedBy=default`, preventing cross-account DM/session contamination without explicit account bindings. (#31110)
+- Telegram/DM topic session isolation: scope DM topic thread session keys by chat ID (`:`) and parse scoped thread IDs in outbound recovery so parallel DMs cannot collide on shared topic IDs. Landed from contributor PR #31064. Thanks @0xble.
+- Telegram/Multi-account group isolation: prevent channel-level `groups` config from leaking across Telegram accounts in multi-account setups, avoiding cross-account group routing drops. Landed from contributor PR #30677. Thanks @YUJIE2002.
+- Telegram/Group allowlist ordering: evaluate chat allowlist before sender allowlist enforcement so explicitly allowlisted groups are not fail-closed by empty sender allowlists. Landed from contributor PR #30680. Thanks @openperf.
+- Telegram/Empty final replies: skip outbound send for null/undefined final text payloads without media so Telegram typing indicators do not linger on `text must be non-empty` errors, with added regression coverage for undefined final payload dispatch. Landed from contributor PRs #30969 and #30746. Thanks @haosenwang1018 and @rylena.
+- Telegram/Voice caption overflow fallback: recover from `sendVoice` caption length errors by re-sending voice without caption and delivering text separately so replies are not lost. Landed from contributor PR #31131. Thanks @Sid-Qin.
+- Telegram/Reply `first` chunking: apply `replyToMode: "first"` reply targets only to the first Telegram text/media/fallback chunk, avoiding multi-chunk over-quoting in split replies. Landed from contributor PR #31077. Thanks @scoootscooob.
+- Telegram/Proxy dispatcher preservation: preserve proxy-aware global undici dispatcher behavior in Telegram network workarounds so proxy-backed Telegram + model traffic is not broken by dispatcher replacement. Landed from contributor PR #30367. Thanks @Phineas1500.
+- Telegram/Media fetch IPv4 fallback: retry Telegram media fetches once with IPv4-first dispatcher settings when dual-stack connect errors (`ETIMEDOUT`/`ENETUNREACH`/`EHOSTUNREACH`) occur, improving reliability on broken IPv6 routes. Landed from contributor PR #30554. Thanks @bosuksh.
+- Telegram/Restart polling teardown: stop the Telegram bot instance when a polling cycle exits so in-process SIGUSR1 restarts fully tear down old long-poll loops before restart, reducing post-restart `getUpdates` 409 conflict storms. Fixes #31107. Landed from contributor PR #31141. Thanks @liuxiaopai-ai.
+- Google Chat/Thread replies: set `messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD` on threaded sends so replies attach to existing threads instead of silently failing thread placement. Landed from contributor PR #30965. Thanks @novan.
+- Mattermost/Private channel policy routing: map Mattermost private channel type `P` to group chat type so `groupPolicy`/`groupAllowFrom` gates apply correctly instead of being treated as open public channels. Landed from contributor PR #30891. Thanks @BlueBirdBack.
+- Discord/Agent component interactions: accept Components v2 `cid` payloads alongside legacy `componentId`, and safely decode percent-encoded IDs without throwing on malformed `%` sequences. Landed from contributor PR #29013. Thanks @Jacky1n7.
+- Discord/Inbound media fallback: preserve attachment and sticker metadata when Discord CDN fetch/save fails by keeping URL-based media entries in context, with regression coverage for save failures and mixed success/failure ordering. Landed from contributor PR #28906. Thanks @Sid-Qin.
+- Matrix/Directory room IDs: preserve original room-ID casing for direct `!roomId` group lookups (without `:server`) so allowlist checks do not fail on case-sensitive IDs. Landed from contributor PR #31201. Thanks @williamos-dev.
+- Slack/Subagent completion delivery: stop forcing bound conversation IDs into `threadId` so Slack completion announces do not send invalid `thread_ts` for DMs/top-level channels. Landed from contributor PR #31105. Thanks @stakeswky.
+- Signal/Loop protection: evaluate own-account detection before sync-message filtering (including UUID-only `accountUuid` configs) so `sentTranscript` sync events cannot bypass loop protection and self-reply loops. Landed from contributor PR #31093. Thanks @kevinWangSheng.
+- Discord/DM command auth: unify DM allowlist + pairing-store authorization across message preflight and native command interactions so DM command gating is consistent for `open`/`pairing`/`allowlist` policies.
+- Slack/download-file scoping: thread/channel-aware `download-file` actions now propagate optional scope context and reject downloads when Slack metadata definitively shows the file is outside the requested channel/thread, while preserving legacy behavior when share metadata is unavailable.
+- Routing/Binding peer-kind parity: treat `peer.kind` `group` and `channel` as equivalent for binding scope matching (while keeping `direct` separate) so Slack/public channel bindings do not silently fall through. Landed from contributor PR #31135. Thanks @Sid-Qin.
+- Discord/Reconnect integrity: release Discord message listener lane immediately while preserving serialized handler execution, add HELLO-stall resume-first recovery with bounded fresh-identify fallback after repeated stalls, and extend lifecycle/listener regression coverage for forced reconnect scenarios. Landed from contributor PR #29508. Thanks @cgdusek.
+- Discord/Reconnect watchdog: add a shared armable transport stall-watchdog and wire Discord gateway lifecycle force-stop semantics for silent close/reconnect zombies, with gateway/lifecycle watchdog regression coverage and runtime status liveness updates. Follow-up to contributor PR #31025 by @theotarr and PR #30530 by @liuxiaopai-ai. Thanks @theotarr and @liuxiaopai-ai.
+- Matrix/Conduit compatibility: avoid blocking startup on non-resolving Matrix sync start, preserve startup error propagation, prevent duplicate monitor listener registration, remove unreliable 2-member DM heuristics, accept `!room` IDs without alias resolution, and add matrix monitor/client regression coverage. Landed from contributor PR #31023. Thanks @efe-arv.
+- Slack/HTTP mode startup: treat Slack HTTP accounts as configured when `botToken` + `signingSecret` are present (without requiring `appToken`) in channel config/runtime status so webhook mode is not silently skipped. (#30567) Thanks @liuxiaopai-ai.
+- Slack/Socket reconnect reliability: reconnect Socket Mode after disconnect/start failures using bounded exponential backoff with abort-aware waits, while preserving clean shutdown behavior and adding disconnect/error helper tests. (#27232) Thanks @pandego.
+- Slack/Thread session isolation: route channel/group top-level messages into thread-scoped sessions (`:thread:`) and read inbound `previousTimestamp` from the resolved thread session key, preventing cross-thread context bleed and stale timestamp lookups. (#10686) Thanks @pablohrcarvalho.
+- Slack/Transient request errors: classify Slack request-error messages like `Client network socket disconnected before secure TLS connection was established` as transient in unhandled-rejection fatal detection, preventing temporary network drops from crash-looping the gateway. (#23169) Thanks @graysurf.
+- Slack/Disabled channel startup: skip Slack monitor socket startup entirely when `channels.slack.enabled=false` (including configs that still contain valid tokens), preventing disabled accounts from opening websocket connections. (#30586) Thanks @liuxiaopai-ai.
+- Telegram/Outbound API proxy env: keep the Node 22 `autoSelectFamily` global-dispatcher workaround while restoring env-proxy support by using `EnvHttpProxyAgent` so `HTTP_PROXY`/`HTTPS_PROXY` continue to apply to outbound requests. (#26207) Thanks @qsysbio-cjw for reporting and @rylena and @vincentkoc for work.
+- Telegram/Thread fallback safety: when Telegram returns `message thread not found`, retry without `message_thread_id` only for DM-thread sends (not forum topics), and suppress first-attempt danger logs when retry succeeds. Landed from contributor PR #30892. Thanks @liuxiaopai-ai.
+- Slack/Inbound media auth + HTML guard: keep Slack auth headers on forwarded shared attachment image downloads, and reject login/error HTML payloads (while allowing expected `.html` uploads) when resolving Slack media so auth failures do not silently pass as files. (#18642) Thanks @tumf.
+- Slack/Bot attachment-only messages: when `allowBots: true`, bot messages with empty `text` now include non-forwarded attachment `text`/`fallback` content so webhook alerts are not silently dropped. (#27616) Thanks @lailoo.
+- Slack/Onboarding token help: update setup text to include the “From manifest” app-creation path and current install wording for obtaining the `xoxb-` bot token. (#30846) Thanks @yzhong52.
+- Feishu/Docx editing tools: add `feishu_doc` positional insert, table row/column operations, table-cell merge, and color-text updates; switch markdown write/append/insert to Descendant API insertion with large-document batching; and harden image uploads for data URI/base64/local-path inputs with strict validation and routing-safe upload metadata. (#29411) Thanks @Elarwei001.
+- Discord/Allowlist diagnostics: add debug logs for guild/channel allowlist drops so operators can quickly identify ignored inbound messages and required allowlist entries. Landed from contributor PR #30966. Thanks @haosenwang1018.
+- Discord/Ack reactions: add Discord-account-level `ackReactionScope` override and support explicit `off`/`none` values in shared config schemas to disable ack reactions per account. Landed from contributor PR #30400. Thanks @BlueBirdBack.
+- Discord/Forum thread tags: support `appliedTags` on Discord thread-create actions and map to `applied_tags` for forum/media starter posts, with targeted thread-creation regression coverage. Landed from contributor PR #30358. Thanks @pushkarsingh32.
+- Discord/Application ID fallback: parse bot application IDs from token prefixes without numeric precision loss and use token fallback only on transport/timeout failures when probing `/oauth2/applications/@me`. Landed from contributor PR #29695. Thanks @dhananjai1729.
+- Discord/EventQueue timeout config: expose per-account `channels.discord.accounts..eventQueue.listenerTimeout` (and related queue options) so long-running handlers can avoid Carbon listener timeout drops. Landed from contributor PR #24270. Thanks @pdd-cli.
+- Slack/Usage footer formatting: wrap session keys in inline code in full response-usage footers so Slack does not parse colon-delimited session segments as emoji shortcodes. (#30258) Thanks @pushkarsingh32.
+- Slack/Socket Mode slash startup: treat `app.options()` registration as best-effort and fall back to static arg menus when listener registration fails, preventing Slack monitor startup crash loops on receiver init edge cases. (#21715) Thanks @AIflow-Labs.
+- Slack/Legacy streaming config: map boolean `channels.slack.streaming=false` to unified streaming mode `off` (with `nativeStreaming=false`) so legacy configs correctly disable draft preview/native streaming instead of defaulting to `partial`. (#25990) Thanks @chilu18.
+- Cron/Failure delivery routing: add `failureAlert.mode` (`announce|webhook`) and `failureAlert.accountId` support, plus `cron.failureDestination` and per-job `delivery.failureDestination` routing with duplicate-target suppression, best-effort skip behavior, and global+job merge semantics. Landed from contributor PR #31059. Thanks @kesor.
+- Cron/announce delivery: stop duplicate completion announces when cron early-return paths already handled delivery, and replace descendant followup polling with push-based waits so cron summaries arrive without the old busy-loop fallback. (#39089) Thanks @tyler6204.
+- Cron/Failure alerts: add configurable repeated-failure alerting with per-job overrides and Web UI cron editor support (`inherit|disabled|custom` with threshold/cooldown/channel/target fields). (#24789) Thanks @0xbrak.
+- Cron/Isolated model defaults: resolve isolated cron `subagents.model` (including object-form `primary`) through allowlist-aware model selection so isolated cron runs honor subagent model defaults unless explicitly overridden by job payload model. (#11474) Thanks @AnonO6.
+- Cron/Announce delivery status: keep isolated cron runs in `ok` state when execution succeeds but announce delivery fails (for example transient `pairing required`), while preserving `delivered=false` and delivery error context for visibility. (#31082) Thanks @YuzuruS.
+- Cron/One-shot reliability: retry transient one-shot failures with bounded backoff and configurable retry policy before disabling. (#24435) Thanks @hugenshen.
+- Cron/Schedule errors: notify users when a job is auto-disabled after repeated schedule computation failures. (#29098) Thanks @ningding97.
+- Cron/One-shot reschedule re-arm: allow completed `at` jobs to run again when rescheduled to a later time than `lastRunAtMs`, while keeping completed non-rescheduled one-shot jobs inactive. (#28915) Thanks @arosstale.
+- Cron/Store EBUSY fallback: retry `rename` on `EBUSY` and use `copyFile` fallback on Windows when replacing cron store files so busy-file contention no longer causes false write failures. (#16932) Thanks @sudhanva-chakra.
+- Cron/Isolated payload selection: ignore `isError` payloads when deriving summary/output/delivery payload fallbacks, while preserving error-only fallback behavior when no non-error payload exists. (#21454) Thanks @Diaspar4u.
+- Cron/Isolated CLI timeout ratio: avoid reusing persisted CLI session IDs on fresh isolated cron runs so the fresh watchdog profile is used and jobs do not abort at roughly one-third of configured `timeoutSeconds`. (#30140) Thanks @ningding97.
+- Cron/Session target guardrail: reject creating or patching `sessionTarget: "main"` cron jobs when `agentId` is not the default agent, preventing invalid cross-agent main-session bindings at write time. (#30217) Thanks @liaosvcaf.
- Cron/Reminder session routing: preserve `job.sessionKey` for `sessionTarget="main"` runs so queued reminders wake and deliver in the originating scoped session/channel instead of being forced to the agent main session.
- Cron/Timezone regression guard: add explicit schedule coverage for `0 8 * * *` with `Asia/Shanghai` to ensure `nextRunAtMs` never rolls back to a past year and always advances to the next valid occurrence. (#30351)
+- Cron/Isolated sessions list: persist the intended pre-run model/provider on isolated cron session entries so `sessions_list` reflects payload/session model overrides even when runs fail before post-run telemetry persistence. (#21279) Thanks @altaywtf.
+- Cron tool/update flat params: recover top-level update patch fields when models omit the `patch` wrapper, and allow flattened update keys through tool input schema validation so `cron.update` no longer fails with `patch required` for valid flat payloads. (#23221)
+- Web UI/Cron jobs: add schedule-kind and last-run-status filters to the Jobs list, with reset control and client-side filtering over loaded results. (#9510) Thanks @guxu11.
+- Web UI/Chat sessions: add a cron-session visibility toggle in the session selector, fix cron-key detection across `cron:*` and `agent:*:cron:*` formats, and localize the new control labels/tooltips. (#26976) Thanks @ianderrington.
+- Cron/Timer hot-loop guard: enforce a minimum timer re-arm delay when stale past-due jobs would otherwise trigger repeated `setTimeout(0)` loops, preventing event-loop saturation and log-flood behavior. (#29853) Thanks @FlamesCN.
+- Models/provider config precedence: prefer exact `models.providers.` matches before normalized provider aliases in embedded model resolution, preventing alias/canonical key collisions from applying the wrong provider `api`, `baseUrl`, or headers. (#35934) thanks @RealKai42.
+- Models/Custom provider keys: trim custom provider map keys during normalization so image-capable models remain discoverable when provider keys are configured with leading/trailing whitespace. Landed from contributor PR #31202. Thanks @stakeswky.
+- Agents/Model fallback: classify additional network transport errors (`ECONNREFUSED`, `ENETUNREACH`, `EHOSTUNREACH`, `ENETRESET`, `EAI_AGAIN`) as failover-worthy so fallback chains advance when primary providers are unreachable. Landed from contributor PR #19077. Thanks @ayanesakura.
+- Agents/Copilot token refresh: refresh GitHub Copilot runtime API tokens after auth-expiry failures and re-run with the renewed token so long-running embedded/subagent turns do not fail on mid-session 401 expiry. Landed from contributor PR #8805. Thanks @Arthur742Ramos.
+- Agents/Subagents delivery params: reject unsupported `sessions_spawn` channel-delivery params (`target`, `channel`, `to`, `threadId`, `replyTo`, `transport`) with explicit input errors so delivery intent does not silently leak output to the parent conversation. (#31000)
+- Agents/FS workspace default: honor documented host file-tool default `tools.fs.workspaceOnly=false` when unset so host `write`/`edit` calls are not incorrectly workspace-restricted unless explicitly enabled. Landed from contributor PR #31128. Thanks @SaucePackets.
+- Sessions/Followup queue: always schedule followup drain even when unexpected runtime exceptions escape `runReplyAgent`, preventing silent stuck followup backlogs after failed turns. (#30627)
+- Sessions/Compaction safety: add transcript-size forced pre-compaction memory flush (`agents.defaults.compaction.memoryFlush.forceFlushTranscriptBytes`, default 2MB) so long sessions recover without manual transcript deletion when token snapshots are stale. (#30655)
+- Sessions/Usage accounting: persist `cacheRead`/`cacheWrite` from the latest call snapshot (`lastCallUsage`) instead of accumulated multi-call totals, preventing inflated token/cost reporting in long tool/compaction runs. (#31005)
+- Sessions/DM scope migration: when `session.dmScope` is non-`main`, retire stale `agent:*:main` delivery routing metadata once the matching direct-chat peer session is active, preventing duplicate Telegram/DM announce deliveries from legacy main sessions after scope migration. (#31010)
+- Agents/Session status: read thinking/verbose/reasoning levels from persisted session state in `session_status` output when resolved levels are not provided, so status reflects runtime toggles correctly. (#30129) Thanks @YuzuruS.
+- Agents/Tool-name recovery chain: normalize streamed alias/case tool names against the allowed set, preserve whitespace-only streamed placeholders to avoid collapsing to empty names, and repair/guard persisted blank `toolResult.toolName` values from matching tool calls to reduce repeated `Tool not found` loops in long sessions. Landed from contributor PRs #30620 and #30735, plus #30881. Thanks @Sid-Qin and @liuxiaopai-ai.
- Agents/Sessions list transcript paths: resolve `sessions_list` `transcriptPath` via agent-aware session path options and ignore combined-store sentinel paths (`(multiple)`) so listed transcript paths always point to the state directory. (#28379) Thanks @fafuzuoluo.
-- Podman/Quadlet setup: fix `sed` escaping and UID mismatch in Podman Quadlet setup. (#26414) Thanks @KnHack and @vincentkoc.
-- Browser/Navigate: resolve the correct `targetId` in navigate responses after renderer swaps. (#25326) Thanks @stone-jin and @vincentkoc.
- Agents/Ollama discovery: skip Ollama discovery when explicit models are configured. (#28827) Thanks @Kansodata and @vincentkoc.
-- Issues/triage labeling: consolidate bug intake to a single bug issue form with required bug-type classification (regression/crash/behavior), auto-apply matching subtype labels from issue form content, and retire the separate regression template to reduce misfiled issue types and improve queue filtering. Thanks @vincentkoc.
-- Android/Onboarding + voice reliability: request per-toggle onboarding permissions, update pairing guidance to `openclaw devices list/approve`, restore assistant speech playback in mic capture flow, cancel superseded in-flight speech (mute + per-reply token rotation), and keep `talk.config` loads retryable after transient failures. (#29796) Thanks @obviyus.
-- FS/Sandbox workspace boundaries: add a dedicated `outside-workspace` safe-open error code for root-escape checks, and propagate specific outside-workspace messages across edit/browser/media consumers instead of generic not-found/invalid-path fallbacks. (#29715) Thanks @YuzuruS.
-- Config/Doctor group allowlist diagnostics: align `groupPolicy: "allowlist"` warnings with per-channel runtime semantics by excluding Google Chat sender-list checks and by warning when no-fallback channels (for example iMessage) omit `groupAllowFrom`, with regression coverage. (#28477) Thanks @tonydehnke.
-- Slack/Disabled channel startup: skip Slack monitor socket startup entirely when `channels.slack.enabled=false` (including configs that still contain valid tokens), preventing disabled accounts from opening websocket connections. (#30586) Thanks @liuxiaopai-ai.
+- Onboarding/Custom providers: raise default custom-provider model context window to the runtime hard minimum (16k) and auto-heal existing custom model entries below that threshold during reconfiguration, preventing immediate `Model context window too small (4096 tokens)` failures. (#21653) Thanks @r4jiv007.
- Onboarding/Custom providers: use Azure OpenAI-specific verification auth/payload shape (`api-key`, deployment-path chat completions payload) when probing Azure endpoints so valid Azure custom-provider setup no longer fails preflight. (#29421) Thanks @kunalk16.
-- Feishu/Docx editing tools: add `feishu_doc` positional insert, table row/column operations, table-cell merge, and color-text updates; switch markdown write/append/insert to Descendant API insertion with large-document batching; and harden image uploads for data URI/base64/local-path inputs with strict validation and routing-safe upload metadata. (#29411) Thanks @Elarwei001.
+- Feishu/Onboarding SecretRef guards: avoid direct `.trim()` calls on object-form `appId`/`appSecret` in onboarding credential checks, keep status semantics strict when an account explicitly sets empty `appId` (no fallback to top-level `appId`), recognize env SecretRef `appId`/`appSecret` as configured so readiness is accurate, and preserve unresolved SecretRef errors in default account resolution for actionable diagnostics. (#30903) Thanks @LiaoyuanNing.
+- Memory/Hybrid recall: when strict hybrid scoring yields no hits, preserve keyword-backed matches using a text-weight floor so freshly indexed lexical canaries no longer disappear behind `minScore` filtering. (#29112) Thanks @ceo-nada.
+- Feishu/Startup probes: serialize multi-account bot-info probes during monitor startup so large Feishu account sets do not burst `/open-apis/bot/v3/info`, bound startup probe latency/abort handling to avoid head-of-line stalls, and avoid triggering rate limits. (#26685, #29941) Thanks @bmendonca3.
+- Android/Onboarding + voice reliability: request per-toggle onboarding permissions, update pairing guidance to `openclaw devices list/approve`, restore assistant speech playback in mic capture flow, cancel superseded in-flight speech (mute + per-reply token rotation), and keep `talk.config` loads retryable after transient failures. (#29796) Thanks @obviyus.
+- Android/Notifications auth race: return `NOT_AUTHORIZED` when `POST_NOTIFICATIONS` is revoked between authorization precheck and delivery, instead of returning success while dropping the notification. (#30726) Thanks @obviyus.
+- Commands/Owner-only tools: treat identified direct-chat senders as owners when no owner allowlist is configured, while preserving internal `operator.admin` owner sessions. (#26331) thanks @widingmarcus-cyber
+- ACP/Harness thread spawn routing: force ACP harness thread creation through `sessions_spawn` (`runtime: "acp"`, `thread: true`) and explicitly forbid `message action=thread-create` for ACP harness requests, avoiding misrouted `Unknown channel` errors. (#30957) Thanks @dutifulbob.
+- Agents/Message tool scoping: include other configured channels in scoped `message` tool action enum + description so isolated/cron runs can discover and invoke cross-channel actions without schema validation failures. Landed from contributor PR #20840. Thanks @altaywtf.
+- Plugins/Discovery precedence: load bundled plugins before auto-discovered global extensions so bundled channel plugins win duplicate-ID resolution by default (explicit `plugins.load.paths` overrides remain highest precedence), with loader regression coverage. Landed from contributor PR #29710. Thanks @Sid-Qin.
+- CLI/Startup (Raspberry Pi + small hosts): speed up startup by avoiding unnecessary plugin preload on fast routes, adding root `--version` fast-path bootstrap bypass, parallelizing status JSON/non-JSON scans where safe, and enabling Node compile cache at startup with env override compatibility (`NODE_COMPILE_CACHE`, `NODE_DISABLE_COMPILE_CACHE`). (#5871) Thanks @BookCatKid and @vincentkoc for raising startup reports, and @lupuletic for related startup work in #27973.
+- CLI/Startup follow-up: add root `--help` fast-path bootstrap bypass with strict root-only matching, lazily resolve CLI channel options only when commands need them, merge build-time startup metadata (`dist/cli-startup-metadata.json`) with runtime catalog discovery so dynamic catalogs are preserved, and add low-power Linux doctor hints for compile-cache placement and respawn tuning. (#30975) Thanks @vincentkoc.
+- Docker/Compose gateway targeting: run `openclaw-cli` in the `openclaw-gateway` service network namespace, require gateway startup ordering, pin Docker setup to `gateway.mode=local`, sync `gateway.bind` from `OPENCLAW_GATEWAY_BIND`, default optional `CLAUDE_*` compose vars to empty values to reduce automation warning noise, and harden `openclaw-cli` with `cap_drop` (`NET_RAW`, `NET_ADMIN`) + `no-new-privileges`. Docs now call out the shared trust boundary explicitly. (#12504) Thanks @bvanderdrift and @vincentkoc.
+- Docker/Image base annotations: add OCI labels for base image plus source/documentation/license metadata, include revision/version/created labels in Docker release builds, and document annotation keys/release context in install docs. Fixes #27945. Thanks @vincentkoc.
+- Config/Legacy gateway bind aliases: normalize host-style `gateway.bind` values (`0.0.0.0`/`::`/`127.0.0.1`/`localhost`) to supported bind modes (`lan`/`loopback`) during legacy migration so older configs recover without manual edits. (#30080) Thanks @liuxiaopai-ai and @vincentkoc.
+- Podman/Quadlet setup: fix `sed` escaping and UID mismatch in Podman Quadlet setup. (#26414) Thanks @KnHack and @vincentkoc.
+- Doctor/macOS state-dir safety: warn when OpenClaw state resolves inside iCloud Drive (`~/Library/Mobile Documents/com~apple~CloudDocs/...`) or `~/Library/CloudStorage/...`, because sync-backed paths can cause slower I/O and lock/sync races. (#31004) Thanks @vincentkoc.
+- Doctor/Linux state-dir safety: warn when OpenClaw state resolves to an `mmcblk*` mount source (SD or eMMC), because random I/O can be slower and media wear can increase under session and credential writes. (#31033) Thanks @vincentkoc.
+- CLI/Cron run exit code: return exit code `0` only when `cron run` reports `{ ok: true, ran: true }`, and `1` for non-run/error outcomes so scripting/debugging reflects actual execution status. Landed from contributor PR #31121. Thanks @Sid-Qin.
+- CLI/JSON preflight output: keep `--json` command stdout machine-readable by suppressing doctor preflight note output while still running legacy migration/config doctor flow. (#24368) Thanks @altaywtf.
+- Issues/triage labeling: consolidate bug intake to a single bug issue form with required bug-type classification (regression/crash/behavior), auto-apply matching subtype labels from issue form content, and retire the separate regression template to reduce misfiled issue types and improve queue filtering. Thanks @vincentkoc.
+- Logging/Subsystem console timestamps: route subsystem console timestamp rendering through `formatConsoleTimestamp(...)` so `pretty` and timestamp-prefix output use local timezone formatting consistently instead of inline UTC `toISOString()` paths. (#25970) Thanks @openperf.
+- Auto-reply/Block reply timeout path: normalize `onBlockReply(...)` execution through `Promise.resolve(...)` before timeout wrapping so mixed sync/async callbacks keep deterministic timeout behavior across strict TypeScript build paths. (#19779) Thanks @dalefrieswthat and @vincentkoc.
+- Nodes/Screen recording guardrails: cap `nodes` tool `screen_record` `durationMs` to 5 minutes at both schema-validation and runtime invocation layers to prevent long-running blocking captures from unbounded durations. Landed from contributor PR #31106. Thanks @BlueBirdBack.
+- Gateway/CLI session recovery: handle expired CLI session IDs gracefully by clearing stale session state and retrying without crashing gateway runs. Landed from contributor PR #31090. Thanks @frankekn.
+- Onboarding/Docker token parity: use `OPENCLAW_GATEWAY_TOKEN` as the default gateway token in interactive and non-interactive onboarding when `--gateway-token` is not provided, so `docker-setup.sh` token env/config values stay aligned. (#22658) Fixes #22638. Thanks @Clawborn and @vincentkoc.
+- Channels/Command parsing parity: align command-body parsing fields with channel command-gating text for Slack, Signal, Microsoft Teams, Mattermost, and BlueBubbles to avoid mention-strip mismatches and inconsistent command detection.
+- File tools/tilde paths: expand `~/...` against the user home directory before workspace-root checks in host file read/write/edit paths, while preserving root-boundary enforcement so outside-root targets remain blocked. (#29779) Thanks @Glucksberg.
+- Memory/QMD update+embed output cap: discard captured stdout for `qmd update` and `qmd embed` runs (while keeping stderr diagnostics) so large index progress output no longer fails sync with `produced too much output` during boot/refresh. (#28900; landed from contributor PR #23311 by @haitao-sjsu) Thanks @haitao-sjsu.
+- Config/Doctor group allowlist diagnostics: align `groupPolicy: "allowlist"` warnings with per-channel runtime semantics by excluding Google Chat sender-list checks and by warning when no-fallback channels (for example iMessage) omit `groupAllowFrom`, with regression coverage. (#28477) Thanks @tonydehnke.
+- TUI/Session model status: clear stale runtime model identity when model overrides change so `/model` updates are reflected immediately in `sessions.patch` responses and `sessions.list` status surfaces. (#28619) Thanks @lejean2000.
+- TUI/SIGTERM shutdown: ignore `setRawMode EBADF` teardown errors during `SIGTERM` exit so long-running TUI sessions do not crash on terminal shutdown races, while still rethrowing unrelated stop errors. (#29430) Thanks @Cormazabal.
+- Browser/Navigate: resolve the correct `targetId` in navigate responses after renderer swaps. (#25326) Thanks @stone-jin and @vincentkoc.
+- FS/Sandbox workspace boundaries: add a dedicated `outside-workspace` safe-open error code for root-escape checks, and propagate specific outside-workspace messages across edit/browser/media consumers instead of generic not-found/invalid-path fallbacks. (#29715) Thanks @YuzuruS.
+- Diagnostics/Stuck session signal: add configurable stuck-session warning threshold via `diagnostics.stuckSessionWarnMs` (default 120000ms) to reduce false-positive warnings on long multi-tool turns. (#31032)
## 2026.2.26
@@ -224,16 +980,15 @@ Docs: https://docs.openclaw.ai
- Auth/Onboarding: add an explicit account-risk warning and confirmation gate before starting Gemini CLI OAuth, and document the caution in provider docs and the Gemini CLI auth plugin README. (#16683) Thanks @vincentkoc.
- Android/Nodes: add Android `device` capability plus `device.status` and `device.info` node commands, including runtime handler wiring and protocol/registry coverage for device status/info payloads. (#27664) Thanks @obviyus.
- Android/Nodes: add `notifications.list` support on Android nodes and expose `nodes notifications_list` in agent tooling for listing active device notifications. (#27344) thanks @obviyus.
-- Docs/Contributing: add Nimrod Gutman to the maintainer roster in `CONTRIBUTING.md`. (#27840) Thanks @ngutman.
### Fixes
- FS tools/workspaceOnly: honor `tools.fs.workspaceOnly=false` for host write and edit operations so FS tools can access paths outside the workspace when sandbox is off. (#28822) thanks @lailoo. Fixes #28763. Thanks @cjscld for reporting.
- Telegram/DM allowlist runtime inheritance: enforce `dmPolicy: "allowlist"` `allowFrom` requirements using effective account-plus-parent config across account-capable channels (Telegram, Discord, Slack, Signal, iMessage, IRC, BlueBubbles, WhatsApp), and align `openclaw doctor` checks to the same inheritance logic so DM traffic is not silently dropped after upgrades. (#27936) Thanks @widingmarcus-cyber.
-- Delivery queue/recovery backoff: prevent retry starvation by persisting `lastAttemptAt` on failed sends and deferring recovery retries until each entry's `lastAttemptAt + backoff` window is eligible, while continuing to recover ready entries behind deferred ones. Landed from contributor PR #27710 by @Jimmy-xuzimo. Thanks @Jimmy-xuzimo.
+- Delivery queue/recovery backoff: prevent retry starvation by persisting `lastAttemptAt` on failed sends and deferring recovery retries until each entry's `lastAttemptAt + backoff` window is eligible, while continuing to recover ready entries behind deferred ones. Landed from contributor PR #27710. Thanks @Jimmy-xuzimo.
- Gemini OAuth/Auth flow: align OAuth project discovery metadata and endpoint fallback handling for Gemini CLI auth, including fallback coverage for environment-provided project IDs. (#16684) Thanks @vincentkoc.
- Google Chat/Lifecycle: keep Google Chat `startAccount` pending until abort in webhook mode so startup is no longer interpreted as immediate exit, preventing auto-restart loops and webhook-target churn. (#27384) thanks @junsuwhy.
-- Temp dirs/Linux umask: force `0700` permissions after temp-dir creation and self-heal existing writable temp dirs before trust checks so `umask 0002` installs no longer crash-loop on startup. Landed from contributor PR #27860 by @stakeswky. (#27853) Thanks @stakeswky.
+- Temp dirs/Linux umask: force `0700` permissions after temp-dir creation and self-heal existing writable temp dirs before trust checks so `umask 0002` installs no longer crash-loop on startup. Landed from contributor PR #27860. (#27853) Thanks @stakeswky.
- Nextcloud Talk/Lifecycle: keep `startAccount` pending until abort and stop the webhook monitor on shutdown, preventing `EADDRINUSE` restart loops when the gateway manages account lifecycle. (#27897) Thanks @steipete.
- Microsoft Teams/File uploads: acknowledge `fileConsent/invoke` immediately (`invokeResponse` before upload + file card send) so Teams no longer shows false "Something went wrong" timeout banners while upload completion continues asynchronously; includes updated async regression coverage. Landed from contributor PR #27641 by @scz2011.
- Queue/Drain/Cron reliability: harden lane draining with guaranteed `draining` flag reset on synchronous pump failures, reject new queue enqueues during gateway restart drain windows (instead of silently killing accepted tasks), add `/stop` queued-backlog cutoff metadata with stale-message skipping (while avoiding cross-session native-stop cutoff bleed), and raise isolated cron `agentTurn` outer safety timeout to avoid false 10-minute timeout races against longer agent session timeouts. (#27407, #27332, #27427)
@@ -245,12 +1000,12 @@ Docs: https://docs.openclaw.ai
- Config/Doctor allowlist safety: reject `dmPolicy: "allowlist"` configs with empty `allowFrom`, add Telegram account-level inheritance-aware validation, and teach `openclaw doctor --fix` to restore missing `allowFrom` entries from pairing-store files when present, preventing silent DM drops after upgrades. (#27936) Thanks @widingmarcus-cyber.
- Browser/Chrome extension handshake: bind relay WS message handling before `onopen` and add non-blocking `connect.challenge` response handling for gateway-style handshake frames, avoiding stuck `…` badge states when challenge frames arrive immediately on connect. Landed from contributor PR #22571 by @pandego. (#22553)
- Browser/Extension relay init: dedupe concurrent same-port relay startup with shared in-flight initialization promises so callers await one startup lifecycle and receive consistent success/failure results. Landed from contributor PR #21277 by @HOYALIM. (Related #20688)
-- Browser/Fill relay + CLI parity: accept `act.fill` fields without explicit `type` by defaulting missing/empty `type` to `text` in both browser relay route parsing and `openclaw browser fill` CLI field parsing, so relay calls no longer fail when the model omits field type metadata. Landed from contributor PR #27662 by @Uface11. (#27296) Thanks @Uface11.
+- Browser/Fill relay + CLI parity: accept `act.fill` fields without explicit `type` by defaulting missing/empty `type` to `text` in both browser relay route parsing and `openclaw browser fill` CLI field parsing, so relay calls no longer fail when the model omits field type metadata. Landed from contributor PR #27662. (#27296) Thanks @Uface11.
- Feishu/Permission error dispatch: merge sender-name permission notices into the main inbound dispatch so one user message produces one agent turn/reply (instead of a duplicate permission-notice turn), with regression coverage. (#27381) thanks @byungsker.
- Feishu/Merged forward parsing: expand inbound `merge_forward` messages by fetching and formatting API sub-messages in order, so merged forwards provide usable content context instead of only a placeholder line. (#28707) Thanks @tsu-builds.
-- Agents/Canvas default node resolution: when multiple connected canvas-capable nodes exist and no single `mac-*` candidate is selected, default to the first connected candidate instead of failing with `node required` for implicit-node canvas tool calls. Landed from contributor PR #27444 by @carbaj03. Thanks @carbaj03.
+- Agents/Canvas default node resolution: when multiple connected canvas-capable nodes exist and no single `mac-*` candidate is selected, default to the first connected candidate instead of failing with `node required` for implicit-node canvas tool calls. Landed from contributor PR #27444. Thanks @carbaj03.
- TUI/stream assembly: preserve streamed text across real tool-boundary drops without keeping stale streamed text when non-text blocks appear only in the final payload. Landed from contributor PR #27711 by @scz2011. (#27674)
-- Hooks/Internal `message:sent`: forward `sessionKey` on outbound sends from agent delivery, cron isolated delivery, gateway receipt acks, heartbeat sends, session-maintenance warnings, and restart-sentinel recovery so internal `message:sent` hooks consistently dispatch with session context, including `openclaw agent --deliver` runs resumed via `--session-id` (without explicit `--session-key`). Landed from contributor PR #27584 by @qualiobra. Thanks @qualiobra.
+- Hooks/Internal `message:sent`: forward `sessionKey` on outbound sends from agent delivery, cron isolated delivery, gateway receipt acks, heartbeat sends, session-maintenance warnings, and restart-sentinel recovery so internal `message:sent` hooks consistently dispatch with session context, including `openclaw agent --deliver` runs resumed via `--session-id` (without explicit `--session-key`). Landed from contributor PR #27584. Thanks @qualiobra.
- Pi image-token usage: stop re-injecting history image blocks each turn, process image references from the current prompt only, and prune already-answered user-image blocks in stored history to prevent runaway token growth. (#27602) Thanks @steipete.
- BlueBubbles/SSRF: auto-allowlist the configured `serverUrl` hostname for attachment fetches so localhost/private-IP BlueBubbles setups are no longer false-blocked by default SSRF checks. Landed from contributor PR #27648 by @lailoo. (#27599) Thanks @taylorhou for reporting.
- Agents/Compaction + onboarding safety: prevent destructive double-compaction by stripping stale assistant usage around compaction boundaries, skipping post-compaction custom metadata writes in the same attempt, and cancelling safeguard compaction when there are no real conversation messages to summarize; harden workspace/bootstrap detection for memory-backed workspaces; and change `openclaw onboard --reset` default scope to `config+creds+sessions` (workspace deletion now requires `--reset-scope full`). (#26458, #27314) Thanks @jaden-clovervnd, @Sid-Qin, and @widingmarcus-cyber for fix direction in #26502, #26529, and #27492.
@@ -288,7 +1043,7 @@ Docs: https://docs.openclaw.ai
- Auth/Auth profiles: normalize `auth-profiles.json` alias fields (`mode -> type`, `apiKey -> key`) before credential validation so entries copied from `openclaw.json` auth examples are no longer silently dropped. (#26950) thanks @byungsker.
- Models/Google Gemini: treat `google` (Gemini API key auth profile) as a reasoning-tag provider to prevent `` leakage, and add forward-compat model fallback for `google-gemini-cli` `gemini-3.1-pro*` / `gemini-3.1-flash*` IDs to avoid false unknown-model errors. (#26551, #26524) Thanks @byungsker.
- Models/Profile suffix parsing: centralize trailing `@profile` parsing and only treat `@` as a profile separator when it appears after the final `/`, preserving model IDs like `openai/@cf/...` and `openrouter/@preset/...` across `/model` directive parsing and allowlist model resolution, with regression coverage.
-- Models/OpenAI Codex config schema parity: accept `openai-codex-responses` in the config model API schema and TypeScript `ModelApi` union, with regression coverage for config validation. Landed from contributor PR #27501 by @AytuncYildizli. Thanks @AytuncYildizli.
+- Models/OpenAI Codex config schema parity: accept `openai-codex-responses` in the config model API schema and TypeScript `ModelApi` union, with regression coverage for config validation. Landed from contributor PR #27501. Thanks @AytuncYildizli.
- Agents/Models config: preserve agent-level provider `apiKey` and `baseUrl` during merge-mode `models.json` updates when agent values are present. (#27293) thanks @Sid-Qin.
- Azure OpenAI Responses: force `store=true` for `azure-openai-responses` direct responses API calls to avoid multi-turn 400 failures. Landed from contributor PR #27499 by @polarbear-Yang. (#27497)
- Security/Node exec approvals: require structured `commandArgv` approvals for `host=node`, enforce `systemRunBinding` matching for argv/cwd/session/agent/env context with fail-closed behavior on missing/mismatched bindings, and add `GIT_EXTERNAL_DIFF` to blocked host env keys. This ships in the next npm release (`2026.2.26`). Thanks @tdjackey for reporting.
@@ -312,7 +1067,7 @@ Docs: https://docs.openclaw.ai
- Cron/Hooks isolated routing: preserve canonical `agent:*` session keys in isolated runs so already-qualified keys are not double-prefixed (for example `agent:main:main` no longer becomes `agent:main:agent:main:main`). Landed from contributor PR #27333 by @MaheshBhushan. (#27289, #27282)
- Channels/Multi-account config: when adding a non-default channel account to a single-account top-level channel setup, move existing account-scoped top-level single-account values into `channels..accounts.default` before writing the new account so the original account keeps working without duplicated account values at channel root; `openclaw doctor --fix` now repairs previously mixed channel account shapes the same way. (#27334) thanks @gumadeiras.
- iOS/Talk mode: stop injecting the voice directive hint into iOS Talk prompts and remove the Voice Directive Hint setting, reducing model bias toward tool-style TTS directives and keeping relay responses text-first by default. (#27543) thanks @ngutman.
-- CI/Windows: shard the Windows `checks-windows` test lane into two matrix jobs and honor explicit shard index overrides in `scripts/test-parallel.mjs` to reduce CI critical-path wall time. (#27234) Thanks @joshavant.
+- Mattermost/mention gating: honor `chatmode: "onmessage"` account override in inbound group/channel mention-gate resolution, while preserving explicit group `requireMention` config precedence and adding verbose drop diagnostics for skipped inbound posts. (#27160) thanks @turian.
## 2026.2.25
@@ -429,7 +1184,7 @@ Docs: https://docs.openclaw.ai
- WhatsApp/Web reconnect: treat close status `440` as non-retryable (including string-form status values), stop reconnect loops immediately, and emit operator guidance to relink after resolving session conflicts. (#25858) Thanks @markmusson.
- WhatsApp/Reasoning safety: suppress outbound payloads marked as reasoning and hard-drop text payloads that begin with `Reasoning:` before WhatsApp delivery, preventing hidden thinking blocks from leaking to end users through final-message paths. (#25804, #25214, #24328)
- Matrix/Read receipts: send read receipts as soon as Matrix messages arrive (before handler pipeline work), so clients no longer show long-lived unread/sent states while replies are processing. (#25841, #25840) Thanks @joshjhall.
-- Telegram/Replies: when markdown formatting renders to empty HTML (for example syntax-only chunks in threaded replies), retry delivery with plain text, and fail loud when both formatted and plain payloads are empty to avoid false delivered states. (#25096, #25091) Thanks @Glucksberg.
+- Telegram/Replies: when markdown formatting renders to empty HTML (for example syntax-only chunks in threaded replies), retry delivery with plain text, and fail loud when both formatted and plain payloads are empty to avoid false delivered states. (#25096, #25091) Thanks @ArsalanShakil.
- Telegram/Media fetch: prioritize IPv4 before IPv6 in SSRF pinned DNS address ordering so media downloads still work on hosts with broken IPv6 routing. (#24295, #23975) Thanks @Glucksberg.
- Telegram/Outbound API: replace Node 22's global undici dispatcher when applying Telegram `autoSelectFamily` decisions so outbound `fetch` calls inherit IPv4 fallback instead of staying pinned to stale dispatcher settings. (#25682, #25676) Thanks @lairtonlelis.
- Onboarding/Telegram: keep core-channel onboarding available when plugin registry population is missing by falling back to built-in adapters and continuing wizard setup with actionable recovery guidance. (#25803) Thanks @Suko.
@@ -484,28 +1239,8 @@ Docs: https://docs.openclaw.ai
- Security/Exec companion host: forward canonical `system.run` display text (not payload-only shell snippets) to the macOS exec host, and enforce rawCommand/argv consistency there for shell-wrapper positional-argv carriers and env-modifier preludes, preventing companion-side approval/display drift. Thanks @tdjackey for reporting.
- Security/Exec approvals: fail closed when transparent dispatch-wrapper unwrapping exceeds the depth cap, so nested `/usr/bin/env` chains cannot bypass shell-wrapper approval gating in `allowlist` + `ask=on-miss` mode. Thanks @tdjackey for reporting.
- Security/Exec: limit default safe-bin trusted directories to immutable system paths (`/bin`, `/usr/bin`) and require explicit opt-in (`tools.exec.safeBinTrustedDirs`) for package-manager/user bin paths (for example Homebrew), add security-audit findings for risky trusted-dir choices, warn at runtime when explicitly trusted dirs are group/world writable, and add doctor hints when configured `safeBins` resolve outside trusted dirs. Thanks @tdjackey for reporting.
-- Telegram/Media fetch: prioritize IPv4 before IPv6 in SSRF pinned DNS address ordering so media downloads still work on hosts with broken IPv6 routing. (#24295, #23975) Thanks @Glucksberg.
-- Telegram/Outbound API: replace Node 22's global undici dispatcher when applying Telegram `autoSelectFamily` decisions so outbound `fetch` calls inherit IPv4 fallback instead of staying pinned to stale dispatcher settings. (#25682, #25676) Thanks @lairtonlelis.
-- Agents/Billing classification: prevent long assistant/user-facing text from being rewritten as billing failures while preserving explicit `status/code/http 402` detection for oversized structured error payloads. (#25680, #25661) Thanks @lairtonlelis.
-- Telegram/Replies: when markdown formatting renders to empty HTML (for example syntax-only chunks in threaded replies), retry delivery with plain text, and fail loud when both formatted and plain payloads are empty to avoid false delivered states. (#25096, #25091) Thanks @Glucksberg.
-- Sessions/Tool-result guard: avoid generating synthetic `toolResult` entries for assistant turns that ended with `stopReason: "aborted"` or `"error"`, preventing orphaned tool-use IDs from triggering downstream API validation errors. (#25429) Thanks @mikaeldiakhate-cell.
- Gateway/Sessions: preserve `modelProvider` on `sessions.reset` and avoid incorrect provider prefixes for legacy session models. (#25874) Thanks @lbo728.
-- Usage accounting: parse Moonshot/Kimi `cached_tokens` fields (including `prompt_tokens_details.cached_tokens`) into normalized cache-read usage metrics. (#25436) Thanks @Elarwei001.
-- Doctor/Sandbox: when sandbox mode is enabled but Docker is unavailable, surface a clear actionable warning (including failure impact and remediation) instead of a mild “skip checks” note. (#25438) Thanks @mcaxtr.
-- Config/Meta: accept numeric `meta.lastTouchedAt` timestamps and coerce them to ISO strings, preserving compatibility with agent edits that write `Date.now()` values. (#25491) Thanks @mcaxtr.
-- Auto-reply/Reset hooks: guarantee native `/new` and `/reset` flows emit command/reset hooks even on early-return command paths, with dedupe protection to avoid double hook emission. (#25459) Thanks @chilu18.
-- Hooks/Slug generator: resolve session slug model from the agent’s effective model (including defaults/fallback resolution) instead of raw agent-primary config only. (#25485) Thanks @SudeepMalipeddi.
-- Slack/DM routing: treat `D*` channel IDs as direct messages even when Slack sends an incorrect `channel_type`, preventing DM traffic from being misclassified as channel/group chats. (#25479) Thanks @mcaxtr.
-- Models/Providers: preserve explicit user `reasoning` overrides when merging provider model config with built-in catalog metadata, so `reasoning: false` is no longer overwritten by catalog defaults. (#25314) Thanks @lbo728.
-- Exec approvals: treat bare allowlist `*` as a true wildcard for parsed executables, including unresolved PATH lookups, so global opt-in allowlists work as configured. (#25250) Thanks @widingmarcus-cyber.
-- Gateway/Auth: allow trusted-proxy authenticated Control UI websocket sessions to skip device pairing when device identity is absent, preventing false `pairing required` failures behind trusted reverse proxies. (#25428) Thanks @SidQin-cyber.
-- Agents/Tool dispatch: await block-reply flush before tool execution starts so buffered block replies preserve message ordering around tool calls. (#25427) Thanks @SidQin-cyber.
- Agents/Compaction: harden summarization prompts to preserve opaque identifiers verbatim (UUIDs, IDs, tokens, host/IP/port, URLs), reducing post-compaction identifier drift and hallucinated identifier reconstruction.
-- iOS/Signing: improve `scripts/ios-team-id.sh` for Xcode 16+ by falling back to Xcode-managed provisioning profiles, add actionable guidance when an Apple account exists but no Team ID can be resolved, and ignore Xcode `xcodebuild` output directories (`apps/ios/build`, `apps/shared/OpenClawKit/build`, `Swabble/build`). (#22773) Thanks @brianleach.
-- macOS/Menu bar: stop reusing the injector delegate for the "Usage cost (30 days)" submenu to prevent recursive submenu injection loops when opening cost history. (#25341) Thanks @yingchunbai.
-- Control UI/Chat images: route image-click opens through a shared safe-open helper (allowing only safe URL schemes) and open new tabs with opener isolation to block tabnabbing. (#18685, #25444, #25847) Thanks @Mariana-Codebase and @shakkernerd.
-- CLI/Doctor: correct stale recovery hints to use valid commands (`openclaw gateway status --deep` and `openclaw configure --section model`). (#24485) Thanks @chilu18.
-- CLI/Memory search: accept `--query ` for `openclaw memory search` (while keeping positional query support), and emit a clear error when neither form is provided. (#25904, #25857) Thanks @niceysam and @stakeswky.
- Security/Sandbox: canonicalize bind-mount source paths via existing-ancestor realpath so symlink-parent + non-existent-leaf paths cannot bypass allowed-source-roots or blocked-path checks. Thanks @tdjackey.
## 2026.2.23
@@ -540,11 +1275,11 @@ Docs: https://docs.openclaw.ai
- Agents/Reasoning: when model-default thinking is active (for example `thinking=low`), keep auto-reasoning disabled unless explicitly enabled, preventing `Reasoning:` thinking-block leakage in channel replies. (#24335, #24290) thanks @Kay-051.
- Agents/Reasoning: avoid classifying provider reasoning-required errors as context overflows so these failures no longer trigger compaction-style overflow recovery. (#24593) Thanks @vincentkoc.
- Agents/Models: codify `agents.defaults.model` / `agents.defaults.imageModel` config-boundary input as `string | {primary,fallbacks}`, split explicit vs effective model resolution, and fix `models status --agent` source attribution so defaults-inherited agents are labeled as `defaults` while runtime selection still honors defaults fallback. (#24210) thanks @bianbiandashen.
-- Agents/Compaction: pass `agentDir` into manual `/compact` command runs so compaction auth/profile resolution stays scoped to the active agent. (#24133) thanks @Glucksberg.
+- Agents/Compaction: pass `agentDir` into manual `/compact` command runs so compaction auth/profile resolution stays scoped to the active agent. (#24133) thanks @miloudbelarebia.
- Agents/Compaction: pass model metadata through the embedded runtime so safeguard summarization can run when `ctx.model` is unavailable, avoiding repeated `"Summary unavailable due to context limits"` fallback summaries. (#3479) Thanks @battman21, @hanxiao and @vincentkoc.
- Agents/Compaction: cancel safeguard compaction when summary generation cannot run (missing model/API key or summarization failure), preserving history instead of truncating to fallback `"Summary unavailable"` text. (#10711) Thanks @DukeDeSouth and @vincentkoc.
- Agents/Tools: make `session_status` read transcript-derived usage mid-turn and tail-read session logs for cache-aware context reporting without full-log scans. (#22387) Thanks @1ucian.
-- Agents/Overflow: detect additional provider context-overflow error shapes (including `input length` + `max_tokens` exceed-context variants) so failures route through compaction/recovery paths instead of leaking raw provider errors to users. (#9951) Thanks @echoVic and @Glucksberg.
+- Agents/Overflow: detect additional provider context-overflow error shapes (including `input length` + `max_tokens` exceed-context variants) so failures route through compaction/recovery paths instead of leaking raw provider errors to users. (#9951) Thanks @echoVic.
- Agents/Overflow: add Chinese context-overflow pattern detection in `isContextOverflowError` so localized provider errors route through overflow recovery paths. (#22855) Thanks @Clawborn.
- Agents/Failover: treat HTTP 502/503/504 errors as failover-eligible transient timeouts so fallback chains can switch providers/models during upstream outages instead of retrying the same failing target. (#20999) Thanks @taw0002 and @vincentkoc.
- Auto-reply/Inbound metadata: hide direct-chat `message_id`/`message_id_full` and sender metadata only from normalized chat type (not sender-id sentinels), preserving group metadata visibility and preventing sender-id spoofed direct-mode classification. (#24373) thanks @jd316.
@@ -559,7 +1294,6 @@ Docs: https://docs.openclaw.ai
- Plugins/Install: when npm install returns 404 for bundled channel npm specs, fallback to bundled channel sources and complete install/enable persistence instead of failing plugin install. (#12849) Thanks @vincentkoc.
- Gemini OAuth/Auth: resolve npm global shim install layouts while discovering Gemini CLI credentials, preventing false "Gemini CLI not found" onboarding/auth failures when shim paths are on `PATH`. (#27585) Thanks @ehgamemo and @vincentkoc.
- Providers/Groq: avoid classifying Groq TPM limit errors as context overflow so throttling paths no longer trigger overflow recovery logic. (#16176) Thanks @dddabtc.
-- Gateway/WS: close repeated post-handshake `unauthorized role:*` request floods per connection and sample duplicate rejection logs, preventing a single misbehaving client from degrading gateway responsiveness. (#20168) Thanks @acy103, @vibecodooor, and @vincentkoc.
- Gateway/Restart: treat child listener PIDs as owned by the service runtime PID during restart health checks to avoid false stale-process kills and restart timeouts on launchd/systemd. (#24696) Thanks @gumadeiras.
- Config/Write: apply `unsetPaths` with immutable path-copy updates so config writes never mutate caller-provided objects, and harden `openclaw config get/set/unset` path traversal by rejecting prototype-key segments and inherited-property traversal. (#24134) thanks @frankekn.
- Channels/WhatsApp: accept `channels.whatsapp.enabled` in config validation to match built-in channel auto-enable behavior, preventing `Unrecognized key: "enabled"` failures during channel setup. (#24263) Thanks @steipete.
@@ -614,7 +1348,7 @@ Docs: https://docs.openclaw.ai
- Agents/Kimi: classify Moonshot `Your request exceeded model token limit` failures as context overflows so auto-compaction and user-facing overflow recovery trigger correctly instead of surfacing raw invalid-request errors. (#9562) Thanks @danilofalcao.
- Providers/Moonshot: mark Kimi K2.5 as image-capable in implicit + onboarding model definitions, and refresh stale explicit provider capability fields (`input`/`reasoning`/context limits) from implicit catalogs so existing configs pick up Moonshot vision support without manual model rewrites. (#13135, #4459) Thanks @manikv12.
- Agents/Transcript: enable consecutive-user turn merging for strict non-OpenAI `openai-completions` providers (for example Moonshot/Kimi), reducing `roles must alternate` ordering failures on OpenAI-compatible endpoints while preserving current OpenRouter/Opencode behavior. (#7693) Thanks @steipete.
-- Install/Discord Voice: make `@discordjs/opus` an optional dependency so `openclaw` install/update no longer hard-fails when native Opus builds fail, while keeping `opusscript` as the runtime fallback decoder for Discord voice flows. (#23737, #23733, #23703) Thanks @jeadland, @Sheetaa, and @Breakyman.
+- Install/Discord Voice: make the native Opus decoder optional so `openclaw` install/update no longer hard-fails when native builds fail, while keeping `opusscript` as the runtime fallback decoder for Discord voice flows. (#23737, #23733, #23703) Thanks @jeadland, @Sheetaa, and @Breakyman.
- Docker/Setup: precreate `$OPENCLAW_CONFIG_DIR/identity` during `docker-setup.sh` so CLI commands that need device identity (for example `devices list`) avoid `EACCES ... /home/node/.openclaw/identity` failures on restrictive bind mounts. (#23948) Thanks @ackson-beep.
- Exec/Background: stop applying the default exec timeout to background sessions (`background: true` or explicit `yieldMs`) when no explicit timeout is set, so long-running background jobs are no longer terminated at the default timeout boundary. (#23303) Thanks @steipete.
- Slack/Threading: sessions: keep parent-session forking and thread-history context active beyond first turn by removing first-turn-only gates in session init, thread-history fetch, and reply prompt context injection. (#23843, #23090) Thanks @vincentkoc and @Taskle.
@@ -691,7 +1425,7 @@ Docs: https://docs.openclaw.ai
- Config/Memory: allow `"mistral"` in `agents.defaults.memorySearch.provider` and `agents.defaults.memorySearch.fallback` schema validation. (#14934) Thanks @ThomsenDrake.
- Feishu/Commands: in group chats, command authorization now falls back to top-level `channels.feishu.allowFrom` when per-group `allowFrom` is not set, so `/command` no longer gets blocked by an unintended empty allowlist. (#23756) Thanks @steipete.
- Dev tooling: prevent `CLAUDE.md` symlink target regressions by excluding CLAUDE symlink sentinels from `oxfmt` and marking them `-text` in `.gitattributes`, so formatter/EOL normalization cannot reintroduce trailing-newline targets. Thanks @vincentkoc.
-- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349) Thanks @Glucksberg.
+- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349; landed from contributor PR #5005 by @Diaspar4u) Thanks @Diaspar4u.
- Feishu/Media: for inbound video messages that include both `file_key` (video) and `image_key` (thumbnail), prefer `file_key` when downloading media so video attachments are saved instead of silently failing on thumbnail keys. (#23633) Thanks @steipete.
- Hooks/Loader: avoid redundant hook-module recompilation on gateway restart by skipping cache-busting for bundled hooks and using stable file metadata keys (`mtime+size`) for mutable workspace/managed/plugin hook imports. (#16953) Thanks @mudrii.
- Hooks/Cron: suppress duplicate main-session events for delivered hook turns and mark `SILENT_REPLY_TOKEN` (`NO_REPLY`) early exits as delivered to prevent hook context pollution. (#20678) Thanks @JonathanWorks.
@@ -816,6 +1550,8 @@ Docs: https://docs.openclaw.ai
- Security/Control UI avatars: harden `/avatar/:agentId` local avatar serving by rejecting symlink paths and requiring fd-level file identity + size checks before reads. Thanks @tdjackey for reporting.
- Security/MSTeams media: enforce allowlist checks for SharePoint reference attachment URLs and redirect targets during Graph-backed media fetches so redirect chains cannot escape configured media host boundaries. Thanks @tdjackey for reporting.
- Security/MSTeams media: route attachment auth-retry and Graph SharePoint download redirects through shared `safeFetch` so each hop is validated with allowlist + DNS/IP checks across the full redirect chain. (#23598) Thanks @Asm3r96 and @lewiswigmore.
+- Security/MSTeams auth redirect scoping: strip bearer auth on redirect hops outside `authAllowHosts` and gate SharePoint Graph auth-header injection by auth allowlist to prevent token bleed across redirect targets. (#25045) Thanks @bmendonca3.
+- MSTeams/reply reliability: when Bot Framework revokes thread turn-context proxies (for example debounced flush paths), fall back to proactive messaging/typing and continue pending sends without duplicating already delivered messages. (#27224) Thanks @openperf.
- Security/macOS discovery: fail closed for unresolved discovery endpoints by clearing stale remote selection values, use resolved service host only for SSH target derivation, and keep remote URL config aligned with resolved endpoint availability. (#21618) Thanks @bmendonca3.
- Chat/Usage/TUI: strip synthetic inbound metadata blocks (including `Conversation info` and trailing `Untrusted context` channel metadata wrappers) from displayed conversation history so internal prompt context no longer leaks into user-visible logs.
- CI/Tests: fix TypeScript case-table typing and lint assertion regressions so `pnpm check` passes again after Synology Chat landing. (#23012) Thanks @druide67.
@@ -921,8 +1657,6 @@ Docs: https://docs.openclaw.ai
- Gateway/Config: allow `gateway.customBindHost` in strict config validation when `gateway.bind="custom"` so valid custom bind-host configurations no longer fail startup. (#20318, fixes #20289) Thanks @MisterGuy420.
- Gateway/Pairing: tolerate legacy paired devices missing `roles`/`scopes` metadata in websocket upgrade checks and backfill metadata on reconnect. (#21447, fixes #21236) Thanks @joshavant.
- Gateway/Pairing/CLI: align read-scope compatibility in pairing/device-token checks and add local `openclaw devices` fallback recovery for loopback `pairing required` deadlocks, with explicit fallback notice to unblock approval bootstrap flows. (#21616) Thanks @shakkernerd.
-- Cron: honor `cron.maxConcurrentRuns` in the timer loop so due jobs can execute up to the configured parallelism instead of always running serially. (#11595) Thanks @Takhoffman.
-- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349) Thanks @Glucksberg.
- Agents/Subagents: restore announce-chain delivery to agent injection, defer nested announce output until descendant follow-up content is ready, and prevent descendant deferrals from consuming announce retry budget so deep chains do not drop final completions. (#22223) Thanks @tyler6204.
- Agents/System Prompt: label allowlisted senders as authorized senders to avoid implying ownership. Thanks @thewilloftheshadow.
- Agents/Tool display: fix exec cwd suffix inference so `pushd ... && popd ... && ` does not keep stale `(in )` context in summaries. (#21925) Thanks @Lukavyi.
@@ -982,6 +1716,8 @@ Docs: https://docs.openclaw.ai
- iOS/Watch: add an Apple Watch companion MVP with watch inbox UI, watch notification relay handling, and gateway command surfaces for watch status/send flows. (#20054) Thanks @mbelinky.
- iOS/Gateway: wake disconnected iOS nodes via APNs before `nodes.invoke` and auto-reconnect gateway sessions on silent push wake to reduce invoke failures while the app is backgrounded. (#20332) Thanks @mbelinky.
- Gateway/CLI: add paired-device hygiene flows with `device.pair.remove`, plus `openclaw devices remove` and guarded `openclaw devices clear --yes [--pending]` commands for removing paired entries and optionally rejecting pending requests. (#20057) Thanks @mbelinky.
+- Mattermost: add opt-in native slash command support with registration lifecycle, callback route/token validation, multi-account token routing, and callback URL/path configuration (`channels.mattermost.commands.*`). (#16515) Thanks @echo931.
+- Mattermost: harden native slash callback auth-bypass behavior for configurable callback paths, add callback validation coverage, and clarify callback reachability/allowlist docs. (#32467) Thanks @mukhtharcm and @echo931.
- iOS/APNs: add push registration and notification-signing configuration for node delivery. (#20308) Thanks @mbelinky.
- Gateway/APNs: add a push-test pipeline for APNs delivery validation in gateway flows. (#20307) Thanks @mbelinky.
- Security/Audit: add `gateway.http.no_auth` findings when `gateway.auth.mode="none"` leaves Gateway HTTP APIs reachable, with loopback warning and remote-exposure critical severity, plus regression coverage and docs updates.
@@ -1295,7 +2031,6 @@ Docs: https://docs.openclaw.ai
- Browser/Agents: when browser control service is unavailable, return explicit non-retry guidance (instead of "try again") so models do not loop on repeated browser tool calls until timeout. (#17673) Thanks @austenstone.
- Subagents: use child-run-based deterministic announce idempotency keys across direct and queued delivery paths (with legacy queued-item fallback) to prevent duplicate announce retries without collapsing distinct same-millisecond announces. (#17150) Thanks @widingmarcus-cyber.
- Subagents/Models: preserve `agents.defaults.model.fallbacks` when subagent sessions carry a model override, so subagent runs fail over to configured fallback models instead of retrying only the overridden primary model.
-- Agents/Tools: scope the `message` tool schema to the active channel so Telegram uses `buttons` and Discord uses `components`. (#18215) Thanks @obviyus.
- Telegram: omit `message_thread_id` for DM sends/draft previews and keep forum-topic handling (`id=1` general omitted, non-general kept), preventing DM failures with `400 Bad Request: message thread not found`. (#10942) Thanks @garnetlyx.
- Telegram: replace inbound `` placeholder with successful preflight voice transcript in message body context, preventing placeholder-only prompt bodies for mention-gated voice messages. (#16789) Thanks @Limitless2023.
- Telegram: retry inbound media `getFile` calls (3 attempts with backoff) and gracefully fall back to placeholder-only processing when retries fail, preventing dropped voice/media messages on transient Telegram network errors. (#16154) Thanks @yinghaosang.
@@ -1305,7 +2040,6 @@ Docs: https://docs.openclaw.ai
- Discord: ensure role allowlist matching uses raw role IDs for message routing authorization. Thanks @xinhuagu.
- Discord: skip text-based exec approval forwarding in favor of Discord's component-based approval UI. Thanks @thewilloftheshadow.
- Web UI/Agents: hide `BOOTSTRAP.md` in the Agents Files list after onboarding is completed, avoiding confusing missing-file warnings for completed workspaces. (#17491) Thanks @gumadeiras.
-- Memory/QMD: scope managed collection names per agent and precreate glob-backed collection directories before registration, preventing cross-agent collection clobbering and startup ENOENT failures in fresh workspaces. (#17194) Thanks @jonathanadams96.
- Gateway/Memory: initialize QMD startup sync for every configured agent (not just the default agent), so `memory.qmd.update.onBoot` is effective across multi-agent setups. (#17663) Thanks @HenryLoenwind.
- Auto-reply/WhatsApp/TUI/Web: when a final assistant message is `NO_REPLY` and a messaging tool send succeeded, mirror the delivered messaging-tool text into session-visible assistant output so TUI/Web no longer show `NO_REPLY` placeholders. (#7010) Thanks @Morrowind-Xie.
- Cron: infer `payload.kind="agentTurn"` for model-only `cron.update` payload patches, so partial agent-turn updates do not fail validation when `kind` is omitted. (#15664) Thanks @rodrigouroz.
@@ -1383,7 +2117,7 @@ Docs: https://docs.openclaw.ai
- Agents: treat `read` tool `file_path` arguments as valid in tool-start diagnostics to avoid false “read tool called without path” warnings when alias parameters are used. (#16717) Thanks @Stache73.
- Agents/Transcript: drop malformed tool-call blocks with blank required fields (`id`/`name` or missing `input`/`arguments`) during session transcript repair to prevent persistent tool-call corruption on future turns. (#15485) Thanks @mike-zachariades.
- Tools/Write/Edit: normalize structured text-block arguments for `content`/`oldText`/`newText` before filesystem edits, preventing JSON-like file corruption and false “exact text not found” misses from block-form params. (#16778) Thanks @danielpipernz.
-- Ollama/Agents: avoid forcing `` tag enforcement for Ollama models, which could suppress all output as `(no output)`. (#16191) Thanks @Glucksberg.
+- Ollama/Agents: avoid forcing `` tag enforcement for Ollama models, which could suppress all output as `(no output)`. (#16191) Thanks @briancolinger.
- Plugins: suppress false duplicate plugin id warnings when the same extension is discovered via multiple paths (config/workspace/global vs bundled), while still warning on genuine duplicates. (#16222) Thanks @shadril238.
- Agents/Process: supervise PTY/child process lifecycles with explicit ownership, cancellation, timeouts, and deterministic cleanup, preventing Codex/Pi PTY sessions from dying or stalling on resume. (#14257) Thanks @onutc.
- Skills: watch `SKILL.md` only when refreshing skills snapshot to avoid file-descriptor exhaustion in large data trees. (#11325) Thanks @household-bard.
@@ -1706,7 +2440,7 @@ Docs: https://docs.openclaw.ai
- Cron: prevent one-shot `at` jobs from re-firing on gateway restart when previously skipped or errored. (#13845)
- Discord: add exec approval cleanup option to delete DMs after approval/denial/timeout. (#13205) Thanks @thewilloftheshadow.
-- Sessions: prune stale entries, cap session store size, rotate large stores, accept duration/size thresholds, default to warn-only maintenance, and prune cron run sessions after retention windows. (#13083) Thanks @skyfallsin, @Glucksberg, @gumadeiras.
+- Sessions: prune stale entries, cap session store size, rotate large stores, accept duration/size thresholds, default to warn-only maintenance, and prune cron run sessions after retention windows. (#13083) Thanks @skyfallsin, @gumadeiras.
- CI: Implement pipeline and workflow order. Thanks @quotentiroler.
- WhatsApp: preserve original filenames for inbound documents. (#12691) Thanks @akramcodez.
- Telegram: harden quote parsing; preserve quote context; avoid QUOTE_TEXT_INVALID; avoid nested reply quote misclassification. (#12156) Thanks @rybnikov.
@@ -1796,9 +2530,6 @@ Docs: https://docs.openclaw.ai
- TTS: add missing OpenAI voices (ballad, cedar, juniper, marin, verse) to the allowlist so they are recognized instead of silently falling back to Edge TTS. (#2393)
- Cron: scheduler reliability (timer drift, restart catch-up, lock contention, stale running markers). (#10776) Thanks @tyler6204.
- Cron: store migration hardening (legacy field migration, parse error handling, explicit delivery mode persistence). (#10776) Thanks @tyler6204.
-- Memory: set Voyage embeddings `input_type` for improved retrieval. (#10818) Thanks @mcinteerj.
-- Memory/QMD: run boot refresh in background by default, add configurable QMD maintenance timeouts, retry QMD after fallback failures, and scope QMD queries to OpenClaw-managed collections. (#9690, #9705, #10042) Thanks @vignesh07.
-- Media understanding: recognize `.caf` audio attachments for transcription. (#10982) Thanks @succ985.
- Telegram: auto-inject DM topic threadId in message tool + subagent announce. (#7235) Thanks @Lukavyi.
- Security: require auth for Gateway canvas host and A2UI assets. (#9518) Thanks @coygeek.
- Cron: fix scheduling and reminder delivery regressions; harden next-run recompute + timer re-arming + legacy schedule fields. (#9733, #9823, #9948, #9932) Thanks @tyler6204, @pycckuu, @j2h4u, @fujiwara-tofu-shop.
@@ -1859,7 +2590,7 @@ Docs: https://docs.openclaw.ai
- Cron: accept epoch timestamps and 0ms durations in CLI `--at` parsing.
- Cron: reload store data when the store file is recreated or mtime changes.
- Cron: deliver announce runs directly, honor delivery mode, and respect wakeMode for summaries. (#8540) Thanks @tyler6204.
-- Telegram: include forward_from_chat metadata in forwarded messages and harden cron delivery target checks. (#8392) Thanks @Glucksberg.
+- Telegram: include forward_from_chat metadata in forwarded messages and harden cron delivery target checks. (#8392) Thanks @sleontenko.
- macOS: fix cron payload summary rendering and ISO 8601 formatter concurrency safety.
- Discord: enforce DM allowlists for agent components (buttons/select menus), honoring pairing store approvals and tag matches. (#11254) Thanks @thedudeabidesai.
@@ -1916,7 +2647,6 @@ Docs: https://docs.openclaw.ai
- Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).
- Security/Gateway: require `operator.approvals` for in-chat `/approve` when invoked from gateway clients. Thanks @yueyueL.
- Security: harden Windows exec allowlist; block cmd.exe bypass via single &. Thanks @simecek.
-- Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.
- Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.
- fix(voice-call): harden inbound allowlist; reject anonymous callers; require Telnyx publicKey for allowlist; token-gate Twilio media streams; cap webhook body size (thanks @simecek)
- Onboarding: keep TUI flow exclusive (skip completion prompt + background Web UI seed); completion prompt now handled by install/update.
@@ -1986,62 +2716,10 @@ Docs: https://docs.openclaw.ai
## 2026.1.31
-### Changes
-
-- Docs: onboarding/install/i18n/exec-approvals/Control UI/exe.dev/cacheRetention updates + misc nav/typos. (#3050, #3461, #4064, #4675, #4729, #4763, #5003, #5402, #5446, #5474, #5663, #5689, #5694, #5967, #6270, #6300, #6311, #6416, #6487, #6550, #6789)
-- Telegram: use shared pairing store. (#6127) Thanks @obviyus.
-- Agents: add OpenRouter app attribution headers. Thanks @alexanderatallah.
-- Agents: add system prompt safety guardrails. (#5445) Thanks @joshp123.
-- Agents: update pi-ai to 0.50.9 and rename cacheControlTtl -> cacheRetention (with back-compat mapping).
-- Agents: extend CreateAgentSessionOptions with systemPrompt/skills/contextFiles.
-- Agents: add tool policy conformance snapshot (no runtime behavior change). (#6011)
-- Auth: update MiniMax OAuth hint + portal auth note copy.
-- Discord: inherit thread parent bindings for routing. (#3892) Thanks @aerolalit.
-- Gateway: inject timestamps into agent and chat.send messages. (#3705) Thanks @conroywhitney, @CashWilliams.
-- Gateway: require TLS 1.3 minimum for TLS listeners. (#5970) Thanks @loganaden.
-- Web UI: refine chat layout + extend session active duration.
-- CI: add formal conformance + alias consistency checks. (#5723, #5807)
-
### Fixes
-- Security: guard remote media fetches with SSRF protections (block private/localhost, DNS pinning).
-- Updates: clean stale global install rename dirs and extend gateway update timeouts to avoid npm ENOTEMPTY failures.
- Plugins: validate plugin/hook install paths and reject traversal-like names.
-- Telegram: add download timeouts for file fetches. (#6914) Thanks @hclsys.
-- Telegram: enforce thread specs for DM vs forum sends. (#6833) Thanks @obviyus.
-- Streaming: flush block streaming on paragraph boundaries for newline chunking. (#7014)
-- Streaming: stabilize partial streaming filters.
-- Auto-reply: avoid referencing workspace files in /new greeting prompt. (#5706) Thanks @bravostation.
-- Tools: align tool execute adapters/signatures (legacy + parameter order + arg normalization).
- Tools: treat `"*"` tool allowlist entries as valid to avoid spurious unknown-entry warnings.
-- Skills: update session-logs paths from .clawdbot to .openclaw. (#4502)
-- Slack: harden media fetch limits and Slack file URL validation. (#6639) Thanks @davidiach.
-- Lint: satisfy curly rule after import sorting. (#6310)
-- Process: resolve Windows `spawn()` failures for npm-family CLIs by appending `.cmd` when needed. (#5815) Thanks @thejhinvirtuoso.
-- Discord: resolve PluralKit proxied senders for allowlists and labels. (#5838) Thanks @thewilloftheshadow.
-- Tlon: add timeout to SSE client fetch calls (CWE-400). (#5926)
-- Memory search: L2-normalize local embedding vectors to fix semantic search. (#5332)
-- Agents: align embedded runner + typings with pi-coding-agent API updates (pi 0.51.0).
-- Agents: ensure OpenRouter attribution headers apply in the embedded runner.
-- Agents: cap context window resolution for compaction safeguard. (#6187) Thanks @iamEvanYT.
-- System prompt: resolve overrides and hint using session_status for current date/time. (#1897, #1928, #2108, #3677)
-- Agents: fix Pi prompt template argument syntax. (#6543)
-- Subagents: fix announce failover race (always emit lifecycle end; timeout=0 means no-timeout). (#6621)
-- Teams: gate media auth retries.
-- Telegram: restore draft streaming partials. (#5543) Thanks @obviyus.
-- Onboarding: friendlier Windows onboarding message. (#6242) Thanks @shanselman.
-- TUI: prevent crash when searching with digits in the model selector.
-- Agents: wire before_tool_call plugin hook into tool execution. (#6570, #6660) Thanks @ryancnelson.
-- Browser: secure Chrome extension relay CDP sessions.
-- Docker: use container port for gateway command instead of host port. (#5110) Thanks @mise42.
-- Docker: start gateway CMD by default for container deployments. (#6635) Thanks @kaizen403.
-- fix(lobster): block arbitrary exec via lobsterPath/cwd injection (GHSA-4mhr-g7xj-cg8j). (#5335) Thanks @vignesh07.
-- Security: sanitize WhatsApp accountId to prevent path traversal. (#4610)
-- Security: restrict MEDIA path extraction to prevent LFI. (#4930)
-- Security: validate message-tool filePath/path against sandbox root. (#6398)
-- Security: block LD*/DYLD* env overrides for host exec. (#4896) Thanks @HassanFleyah.
-- Security: harden web tool content wrapping + file parsing safeguards. (#4058) Thanks @VACInc.
-- Security: enforce Twitch `allowFrom` allowlist gating (deny non-allowlisted senders). Thanks @MegaManSec.
## 2026.1.30
@@ -2242,7 +2920,7 @@ Docs: https://docs.openclaw.ai
- Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.openclaw.ai/tools/web
- UI: refresh Control UI dashboard design system (colors, icons, typography). (#1745, #1786) Thanks @EnzeD, @mousberg.
- Exec approvals: forward approval prompts to chat with `/approve` for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.openclaw.ai/tools/exec-approvals https://docs.openclaw.ai/tools/slash-commands
-- Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg.
+- Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @steipete.
- Diagnostics: add diagnostic flags for targeted debug logs (config + env override). https://docs.openclaw.ai/diagnostics/flags
- Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround).
- Docs: add verbose installer troubleshooting guidance.
@@ -2255,7 +2933,7 @@ Docs: https://docs.openclaw.ai
- Web UI: fix config/debug layout overflow, scrolling, and code block sizing. (#1715) Thanks @saipreetham589.
- Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.
-- Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg.
+- Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @steipete.
- Web UI: hide internal `message_id` hints in chat bubbles.
- Gateway: allow Control UI token-only auth to skip device pairing even when device identity is present (`gateway.controlUi.allowInsecureAuth`). (#1679) Thanks @steipete.
- Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47.
@@ -2457,6 +3135,7 @@ Docs: https://docs.openclaw.ai
- BlueBubbles: resolve short message IDs safely and expose full IDs in templates. (#1387) Thanks @tyler6204.
- Infra: preserve fetch helper methods when wrapping abort signals. (#1387)
- macOS: default distribution packaging to universal binaries. (#1396) Thanks @JustYannicc.
+- Embedded runner: forward sender identity into attempt execution so Feishu doc auto-grant receives requester context again. (#32915) Thanks @cszhouwei.
## 2026.1.20
@@ -2772,7 +3451,6 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic
- **BREAKING:** iOS minimum version is now 18.0 to support Textual markdown rendering in native chat. (#702)
- **BREAKING:** Microsoft Teams is now a plugin; install `@openclaw/msteams` via `openclaw plugins install @openclaw/msteams`.
-- **BREAKING:** Channel auth now prefers config over env for Discord/Telegram/Matrix (env is fallback only). (#1040) — thanks @thewilloftheshadow.
### Changes
@@ -2781,7 +3459,6 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic
- CLI/macOS: sync remote SSH target/identity to config and let `gateway status` auto-infer SSH targets (ssh-config aware).
- Telegram: scope inline buttons with allowlist default + callback gating in DMs/groups.
- Telegram: default reaction notifications to own.
-- Tools: improve `web_fetch` extraction using Readability (with fallback).
- Heartbeat: tighten prompt guidance + suppress duplicate alerts for 24h. (#980) — thanks @voidserf.
- Repo: ignore local identity files to avoid accidental commits. (#1001) — thanks @gerardward2007.
- Sessions/Security: add `session.dmScope` for multi-user DM isolation and audit warnings. (#948) — thanks @Alphonse-arianee.
@@ -2821,7 +3498,6 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic
- Sessions: keep per-session overrides when `/new` resets compaction counters. (#1050) — thanks @YuriNachos.
- Skills: allow OpenAI image-gen helper to handle URL or base64 responses. (#1050) — thanks @YuriNachos.
- WhatsApp: default response prefix only for self-chat, using identity name when set.
-- Signal/iMessage: bound transport readiness waits to 30s with periodic logging. (#1014) — thanks @Szpadel.
- iMessage: treat missing `imsg rpc` support as fatal to avoid restart loops.
- Auth: merge main auth profiles into per-agent stores for sub-agents and document inheritance. (#1013) — thanks @marcmarg.
- Agents: avoid JSON Schema `format` collisions in tool params by renaming snapshot format fields. (#1013) — thanks @marcmarg.
@@ -2918,13 +3594,7 @@ Thanks @AlexMikhalev, @CoreyH, @John-Rood, @KrauseFx, @MaudeBot, @Nachx639, @Nic
- Agents: make user time zone and 24-hour time explicit in the system prompt. (#859) — thanks @CashWilliams.
- Agents: strip downgraded tool call text without eating adjacent replies and filter thinking-tag leaks. (#905) — thanks @erikpr1994.
- Agents: cap tool call IDs for OpenAI/OpenRouter to avoid request rejections. (#875) — thanks @j1philli.
-- Agents: scrub tuple `items` schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06.
-- Agents: stabilize sub-agent announce status from runtime outcomes and normalize Result/Notes. (#835) — thanks @roshanasingh4.
-- Auth: normalize Claude Code CLI profile mode to oauth and auto-migrate config. (#855) — thanks @sebslight.
-- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06.
-- Logging: tolerate `EIO` from console writes to avoid gateway crashes. (#925, fixes #878) — thanks @grp06.
- Sandbox: restore `docker.binds` config validation and preserve configured PATH for `docker exec`. (#873) — thanks @akonyer.
-- Google: downgrade unsigned thinking blocks before send to avoid missing signature errors.
#### macOS / Apps
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 35a37f44e39..1127d7dc791 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -15,7 +15,7 @@ Welcome to the lobster tank! 🦞
- GitHub: [@steipete](https://github.com/steipete) · X: [@steipete](https://x.com/steipete)
- **Shadow** - Discord subsystem, Discord admin, Clawhub, all community moderation
- - GitHub: [@thewilloftheshadow](https://github.com/thewilloftheshadow) · X: [@4shad0wed](https://x.com/4shad0wed)
+ - GitHub: [@thewilloftheshadow](https://github.com/thewilloftheshadow) · X: [@4shadowed](https://x.com/4shadowed)
- **Vignesh** - Memory (QMD), formal modeling, TUI, IRC, and Lobster
- GitHub: [@vignesh07](https://github.com/vignesh07) · X: [@\_vgnsh](https://x.com/_vgnsh)
@@ -57,9 +57,21 @@ Welcome to the lobster tank! 🦞
- GitHub: [@joshavant](https://github.com/joshavant) · X: [@joshavant](https://x.com/joshavant)
- **Jonathan Taylor** - ACP subsystem, Gateway features/bugs, Gog/Mog/Sog CLI's, SEDMAT
- - Github [@visionik](https://github.com/visionik) · X: [@visionik](https://x.com/visionik)
+ - GitHub [@visionik](https://github.com/visionik) · X: [@visionik](https://x.com/visionik)
- **Josh Lehman** - Compaction, Tlon/Urbit subsystem
- - Github [@jalehman](https://github.com/jalehman) · X: [@jlehman\_](https://x.com/jlehman_)
+ - GitHub [@jalehman](https://github.com/jalehman) · X: [@jlehman\_](https://x.com/jlehman_)
+
+- **Radek Sienkiewicz** - Control UI + WebChat correctness
+ - GitHub [@velvet-shark](https://github.com/velvet-shark) · X: [@velvet_shark](https://twitter.com/velvet_shark)
+
+- **Muhammed Mukhthar** - Mattermost, CLI
+ - GitHub [@mukhtharcm](https://github.com/mukhtharcm) · X: [@mukhtharcm](https://x.com/mukhtharcm)
+
+- **Altay** - Agents, CLI, error handling
+ - GitHub [@altaywtf](https://github.com/altaywtf) · X: [@altaywtf](https://x.com/altaywtf)
+
+- **Robin Waslander** - Security, PR triage, bug fixes
+ - GitHub: [@hydro13](https://github.com/hydro13) · X: [@Robin_waslander](https://x.com/Robin_waslander)
## How to Contribute
@@ -74,6 +86,18 @@ Welcome to the lobster tank! 🦞
- Ensure CI checks pass
- Keep PRs focused (one thing per PR; do not mix unrelated concerns)
- Describe what & why
+- Reply to or resolve bot review conversations you addressed before asking for review again
+- **Include screenshots** — one showing the problem/before, one showing the fix/after (for UI or visual changes)
+
+## Review Conversations Are Author-Owned
+
+If a review bot leaves review conversations on your PR, you are expected to handle the follow-through:
+
+- Resolve the conversation yourself once the code or explanation fully addresses the bot's concern
+- Reply and leave it open only when you need maintainer or reviewer judgment
+- Do not leave "fixed" bot review conversations for maintainers to clean up for you
+
+This applies to both human-authored and AI-assisted PRs.
## Control UI Decorators
@@ -100,8 +124,9 @@ Please include in your PR:
- [ ] Note the degree of testing (untested / lightly tested / fully tested)
- [ ] Include prompts or session logs if possible (super helpful!)
- [ ] Confirm you understand what the code does
+- [ ] Resolve or reply to bot review conversations after you address them
-AI PRs are first-class citizens here. We just want transparency so reviewers know what to look for.
+AI PRs are first-class citizens here. We just want transparency so reviewers know what to look for. If you are using an LLM coding agent, instruct it to resolve bot review conversations it has addressed instead of leaving them for maintainers.
## Current Focus & Roadmap 🗺
diff --git a/Dockerfile b/Dockerfile
index 48d4baf6a06..d6923365b4b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,43 @@
-FROM node:22-bookworm@sha256:cd7bcd2e7a1e6f72052feb023c7f6b722205d3fcab7bbcbd2d1bfdab10b1e935
+# syntax=docker/dockerfile:1.7
+
+# Opt-in extension dependencies at build time (space-separated directory names).
+# Example: docker build --build-arg OPENCLAW_EXTENSIONS="diagnostics-otel matrix" .
+#
+# Multi-stage build produces a minimal runtime image without build tools,
+# source code, or Bun. Works with Docker, Buildx, and Podman.
+# The ext-deps stage extracts only the package.json files we need from
+# extensions/, so the main build layer is not invalidated by unrelated
+# extension source changes.
+#
+# Two runtime variants:
+# Default (bookworm): docker build .
+# Slim (bookworm-slim): docker build --build-arg OPENCLAW_VARIANT=slim .
+ARG OPENCLAW_EXTENSIONS=""
+ARG OPENCLAW_VARIANT=default
+ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:22-bookworm@sha256:b501c082306a4f528bc4038cbf2fbb58095d583d0419a259b2114b5ac53d12e9"
+ARG OPENCLAW_NODE_BOOKWORM_DIGEST="sha256:b501c082306a4f528bc4038cbf2fbb58095d583d0419a259b2114b5ac53d12e9"
+ARG OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE="node:22-bookworm-slim@sha256:9c2c405e3ff9b9afb2873232d24bb06367d649aa3e6259cbe314da59578e81e9"
+ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:9c2c405e3ff9b9afb2873232d24bb06367d649aa3e6259cbe314da59578e81e9"
+
+# Base images are pinned to SHA256 digests for reproducible builds.
+# Trade-off: digests must be updated manually when upstream tags move.
+# To update, run: docker manifest inspect node:22-bookworm (or podman)
+# and replace the digest below with the current multi-arch manifest list entry.
+
+FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps
+ARG OPENCLAW_EXTENSIONS
+COPY extensions /tmp/extensions
+# Copy package.json for opted-in extensions so pnpm resolves their deps.
+RUN mkdir -p /out && \
+ for ext in $OPENCLAW_EXTENSIONS; do \
+ if [ -f "/tmp/extensions/$ext/package.json" ]; then \
+ mkdir -p "/out/$ext" && \
+ cp "/tmp/extensions/$ext/package.json" "/out/$ext/package.json"; \
+ fi; \
+ done
+
+# ── Stage 2: Build ──────────────────────────────────────────────
+FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build
# Install Bun (required for build scripts)
RUN curl -fsSL https://bun.sh/install | bash
@@ -7,60 +46,163 @@ ENV PATH="/root/.bun/bin:${PATH}"
RUN corepack enable
WORKDIR /app
-RUN chown node:node /app
-ARG OPENCLAW_DOCKER_APT_PACKAGES=""
-RUN if [ -n "$OPENCLAW_DOCKER_APT_PACKAGES" ]; then \
- apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $OPENCLAW_DOCKER_APT_PACKAGES && \
- apt-get clean && \
- rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \
- fi
+COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
+COPY ui/package.json ./ui/package.json
+COPY patches ./patches
-COPY --chown=node:node package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
-COPY --chown=node:node ui/package.json ./ui/package.json
-COPY --chown=node:node patches ./patches
-COPY --chown=node:node scripts ./scripts
+COPY --from=ext-deps /out/ ./extensions/
-USER node
# Reduce OOM risk on low-memory hosts during dependency installation.
# Docker builds on small VMs may otherwise fail with "Killed" (exit 137).
-RUN NODE_OPTIONS=--max-old-space-size=2048 pnpm install --frozen-lockfile
+RUN --mount=type=cache,id=openclaw-pnpm-store,target=/root/.local/share/pnpm/store,sharing=locked \
+ NODE_OPTIONS=--max-old-space-size=2048 pnpm install --frozen-lockfile
-# Optionally install Chromium and Xvfb for browser automation.
-# Build with: docker build --build-arg OPENCLAW_INSTALL_BROWSER=1 ...
-# Adds ~300MB but eliminates the 60-90s Playwright install on every container start.
-# Must run after pnpm install so playwright-core is available in node_modules.
-USER root
-ARG OPENCLAW_INSTALL_BROWSER=""
-RUN if [ -n "$OPENCLAW_INSTALL_BROWSER" ]; then \
- apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xvfb && \
- mkdir -p /home/node/.cache/ms-playwright && \
- PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright \
- node /app/node_modules/playwright-core/cli.js install --with-deps chromium && \
- chown -R node:node /home/node/.cache/ms-playwright && \
- apt-get clean && \
- rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \
- fi
+COPY . .
-USER node
-COPY --chown=node:node . .
-# Normalize copied plugin/agent paths so plugin safety checks do not reject
-# world-writable directories inherited from source file modes.
+# Normalize extension paths now so runtime COPY preserves safe modes
+# without adding a second full extensions layer.
RUN for dir in /app/extensions /app/.agent /app/.agents; do \
if [ -d "$dir" ]; then \
find "$dir" -type d -exec chmod 755 {} +; \
find "$dir" -type f -exec chmod 644 {} +; \
fi; \
done
-RUN pnpm build
+
+# A2UI bundle may fail under QEMU cross-compilation (e.g. building amd64
+# on Apple Silicon). CI builds natively per-arch so this is a no-op there.
+# Stub it so local cross-arch builds still succeed.
+RUN pnpm canvas:a2ui:bundle || \
+ (echo "A2UI bundle: creating stub (non-fatal)" && \
+ mkdir -p src/canvas-host/a2ui && \
+ echo "/* A2UI bundle unavailable in this build */" > src/canvas-host/a2ui/a2ui.bundle.js && \
+ echo "stub" > src/canvas-host/a2ui/.bundle.hash && \
+ rm -rf vendor/a2ui apps/shared/OpenClawKit/Tools/CanvasA2UI)
+RUN pnpm build:docker
# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
ENV OPENCLAW_PREFER_PNPM=1
RUN pnpm ui:build
+# Prune dev dependencies and strip build-only metadata before copying
+# runtime assets into the final image.
+FROM build AS runtime-assets
+RUN CI=true pnpm prune --prod && \
+ find dist -type f \( -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' -o -name '*.map' \) -delete
+
+# ── Runtime base images ─────────────────────────────────────────
+FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS base-default
+ARG OPENCLAW_NODE_BOOKWORM_DIGEST
+LABEL org.opencontainers.image.base.name="docker.io/library/node:22-bookworm" \
+ org.opencontainers.image.base.digest="${OPENCLAW_NODE_BOOKWORM_DIGEST}"
+
+FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-slim
+ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST
+LABEL org.opencontainers.image.base.name="docker.io/library/node:22-bookworm-slim" \
+ org.opencontainers.image.base.digest="${OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST}"
+
+# ── Stage 3: Runtime ────────────────────────────────────────────
+FROM base-${OPENCLAW_VARIANT}
+ARG OPENCLAW_VARIANT
+
+# OCI base-image metadata for downstream image consumers.
+# If you change these annotations, also update:
+# - docs/install/docker.md ("Base image metadata" section)
+# - https://docs.openclaw.ai/install/docker
+LABEL org.opencontainers.image.source="https://github.com/openclaw/openclaw" \
+ org.opencontainers.image.url="https://openclaw.ai" \
+ org.opencontainers.image.documentation="https://docs.openclaw.ai/install/docker" \
+ org.opencontainers.image.licenses="MIT" \
+ org.opencontainers.image.title="OpenClaw" \
+ org.opencontainers.image.description="OpenClaw gateway and CLI runtime container image"
+
+WORKDIR /app
+
+# Install system utilities present in bookworm but missing in bookworm-slim.
+# On the full bookworm image these are already installed (apt-get is a no-op).
+RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \
+ --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 \
+ procps hostname curl git openssl
+
+RUN chown node:node /app
+
+COPY --from=runtime-assets --chown=node:node /app/dist ./dist
+COPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modules
+COPY --from=runtime-assets --chown=node:node /app/package.json .
+COPY --from=runtime-assets --chown=node:node /app/openclaw.mjs .
+COPY --from=runtime-assets --chown=node:node /app/extensions ./extensions
+COPY --from=runtime-assets --chown=node:node /app/skills ./skills
+COPY --from=runtime-assets --chown=node:node /app/docs ./docs
+
+# Keep pnpm available in the runtime image for container-local workflows.
+# Use a shared Corepack home so the non-root `node` user does not need a
+# first-run network fetch when invoking pnpm.
+ENV COREPACK_HOME=/usr/local/share/corepack
+RUN install -d -m 0755 "$COREPACK_HOME" && \
+ corepack enable && \
+ corepack prepare "$(node -p "require('./package.json').packageManager")" --activate && \
+ chmod -R a+rX "$COREPACK_HOME"
+
+# Install additional system packages needed by your skills or extensions.
+# Example: docker build --build-arg OPENCLAW_DOCKER_APT_PACKAGES="python3 wget" .
+ARG OPENCLAW_DOCKER_APT_PACKAGES=""
+RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
+ if [ -n "$OPENCLAW_DOCKER_APT_PACKAGES" ]; then \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $OPENCLAW_DOCKER_APT_PACKAGES; \
+ fi
+
+# Optionally install Chromium and Xvfb for browser automation.
+# Build with: docker build --build-arg OPENCLAW_INSTALL_BROWSER=1 ...
+# Adds ~300MB but eliminates the 60-90s Playwright install on every container start.
+# Must run after node_modules COPY so playwright-core is available.
+ARG OPENCLAW_INSTALL_BROWSER=""
+RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
+ if [ -n "$OPENCLAW_INSTALL_BROWSER" ]; then \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xvfb && \
+ mkdir -p /home/node/.cache/ms-playwright && \
+ PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright \
+ node /app/node_modules/playwright-core/cli.js install --with-deps chromium && \
+ chown -R node:node /home/node/.cache/ms-playwright; \
+ fi
+
+# Optionally install Docker CLI for sandbox container management.
+# Build with: docker build --build-arg OPENCLAW_INSTALL_DOCKER_CLI=1 ...
+# Adds ~50MB. Only the CLI is installed — no Docker daemon.
+# Required for agents.defaults.sandbox to function in Docker deployments.
+ARG OPENCLAW_INSTALL_DOCKER_CLI=""
+ARG OPENCLAW_DOCKER_GPG_FINGERPRINT="9DC858229FC7DD38854AE2D88D81803C0EBFCD88"
+RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
+ if [ -n "$OPENCLAW_INSTALL_DOCKER_CLI" ]; then \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ ca-certificates curl gnupg && \
+ install -m 0755 -d /etc/apt/keyrings && \
+ # Verify Docker apt signing key fingerprint before trusting it as a root key.
+ # 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:]')" && \
+ 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:-})" >&2; \
+ exit 1; \
+ fi && \
+ gpg --dearmor -o /etc/apt/keyrings/docker.gpg /tmp/docker.gpg.asc && \
+ rm -f /tmp/docker.gpg.asc && \
+ chmod a+r /etc/apt/keyrings/docker.gpg && \
+ printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable\n' \
+ "$(dpkg --print-architecture)" > /etc/apt/sources.list.d/docker.list && \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ docker-ce-cli docker-compose-plugin; \
+ fi
+
# Expose the CLI binary without requiring npm global writes as non-root.
-USER root
RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw \
&& chmod 755 /app/openclaw.mjs
@@ -74,7 +216,15 @@ USER node
# Start gateway server with default config.
# Binds to loopback (127.0.0.1) by default for security.
#
-# For container platforms requiring external health checks:
-# 1. Set OPENCLAW_GATEWAY_TOKEN or OPENCLAW_GATEWAY_PASSWORD env var
-# 2. Override CMD: ["node","openclaw.mjs","gateway","--allow-unconfigured","--bind","lan"]
+# IMPORTANT: With Docker bridge networking (-p 18789:18789), loopback bind
+# makes the gateway unreachable from the host. Either:
+# - Use --network host, OR
+# - Override --bind to "lan" (0.0.0.0) and set auth credentials
+#
+# Built-in probe endpoints for container health checks:
+# - GET /healthz (liveness) and GET /readyz (readiness)
+# - aliases: /health and /ready
+# For external access from host/ingress, override bind to "lan" and set auth.
+HEALTHCHECK --interval=3m --timeout=10s --start-period=15s --retries=3 \
+ CMD node -e "fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"]
diff --git a/Dockerfile.sandbox b/Dockerfile.sandbox
index a463d4a1020..8b50c7a6745 100644
--- a/Dockerfile.sandbox
+++ b/Dockerfile.sandbox
@@ -1,8 +1,12 @@
+# syntax=docker/dockerfile:1.7
+
FROM debian:bookworm-slim@sha256:98f4b71de414932439ac6ac690d7060df1f27161073c5036a7553723881bffbe
ENV DEBIAN_FRONTEND=noninteractive
-RUN apt-get update \
+RUN --mount=type=cache,id=openclaw-sandbox-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,id=openclaw-sandbox-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
+ apt-get update \
&& apt-get install -y --no-install-recommends \
bash \
ca-certificates \
@@ -10,8 +14,7 @@ RUN apt-get update \
git \
jq \
python3 \
- ripgrep \
- && rm -rf /var/lib/apt/lists/*
+ ripgrep
RUN useradd --create-home --shell /bin/bash sandbox
USER sandbox
diff --git a/Dockerfile.sandbox-browser b/Dockerfile.sandbox-browser
index ec9faf71113..f04e4a82a62 100644
--- a/Dockerfile.sandbox-browser
+++ b/Dockerfile.sandbox-browser
@@ -1,8 +1,12 @@
+# syntax=docker/dockerfile:1.7
+
FROM debian:bookworm-slim@sha256:98f4b71de414932439ac6ac690d7060df1f27161073c5036a7553723881bffbe
ENV DEBIAN_FRONTEND=noninteractive
-RUN apt-get update \
+RUN --mount=type=cache,id=openclaw-sandbox-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,id=openclaw-sandbox-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
+ apt-get update \
&& apt-get install -y --no-install-recommends \
bash \
ca-certificates \
@@ -17,11 +21,9 @@ RUN apt-get update \
socat \
websockify \
x11vnc \
- xvfb \
- && rm -rf /var/lib/apt/lists/*
+ xvfb
-COPY scripts/sandbox-browser-entrypoint.sh /usr/local/bin/openclaw-sandbox-browser
-RUN chmod +x /usr/local/bin/openclaw-sandbox-browser
+COPY --chmod=755 scripts/sandbox-browser-entrypoint.sh /usr/local/bin/openclaw-sandbox-browser
RUN useradd --create-home --shell /bin/bash sandbox
USER sandbox
diff --git a/Dockerfile.sandbox-common b/Dockerfile.sandbox-common
index 71f80070adf..39eaa3692b4 100644
--- a/Dockerfile.sandbox-common
+++ b/Dockerfile.sandbox-common
@@ -1,3 +1,5 @@
+# syntax=docker/dockerfile:1.7
+
ARG BASE_IMAGE=openclaw-sandbox:bookworm-slim
FROM ${BASE_IMAGE}
@@ -19,9 +21,10 @@ ENV HOMEBREW_CELLAR=${BREW_INSTALL_DIR}/Cellar
ENV HOMEBREW_REPOSITORY=${BREW_INSTALL_DIR}/Homebrew
ENV PATH=${BUN_INSTALL_DIR}/bin:${BREW_INSTALL_DIR}/bin:${BREW_INSTALL_DIR}/sbin:${PATH}
-RUN apt-get update \
- && apt-get install -y --no-install-recommends ${PACKAGES} \
- && rm -rf /var/lib/apt/lists/*
+RUN --mount=type=cache,id=openclaw-sandbox-common-apt-cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,id=openclaw-sandbox-common-apt-lists,target=/var/lib/apt,sharing=locked \
+ apt-get update \
+ && apt-get install -y --no-install-recommends ${PACKAGES}
RUN if [ "${INSTALL_PNPM}" = "1" ]; then npm install -g pnpm; fi
@@ -42,4 +45,3 @@ fi
# Default is sandbox, but allow BASE_IMAGE overrides to select another final user.
USER ${FINAL_USER}
-
diff --git a/README.md b/README.md
index b15cabfbbe9..767f4bc2141 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
**OpenClaw** is a _personal AI assistant_ you run on your own devices.
-It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
+It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WebChat). It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
@@ -32,15 +32,15 @@ New install? Start here: [Getting started](https://docs.openclaw.ai/start/gettin
## Sponsors
-| OpenAI | Blacksmith | Convex |
-| ----------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------- |
-| [](https://openai.com/) | [](https://blacksmith.sh/) | [](https://www.convex.dev/) |
+| OpenAI | Vercel | Blacksmith | Convex |
+| ----------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------- |
+| [](https://openai.com/) | [](https://vercel.com/) | [](https://blacksmith.sh/) | [](https://www.convex.dev/) |
**Subscriptions (OAuth):**
- **[OpenAI](https://openai.com/)** (ChatGPT/Codex)
-Model note: while any model is supported, I strongly recommend **Anthropic Pro/Max (100/200) + Opus 4.6** for long‑context strength and better prompt‑injection resistance. See [Onboarding](https://docs.openclaw.ai/start/onboarding).
+Model note: while many providers/models are supported, for the best experience and lower prompt-injection risk use the strongest latest-generation model available to you. See [Onboarding](https://docs.openclaw.ai/start/onboarding).
## Models (selection + auth)
@@ -74,7 +74,7 @@ openclaw gateway --port 18789 --verbose
# Send a message
openclaw message send --to +1234567890 --message "Hello from OpenClaw"
-# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat)
+# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/IRC/Microsoft Teams/Matrix/Feishu/LINE/Mattermost/Nextcloud Talk/Nostr/Synology Chat/Tlon/Twitch/Zalo/Zalo Personal/WebChat)
openclaw agent --message "Ship checklist" --thinking high
```
@@ -126,9 +126,9 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
## Highlights
- **[Local-first Gateway](https://docs.openclaw.ai/gateway)** — single control plane for sessions, channels, tools, and events.
-- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
+- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
- **[Multi-agent routing](https://docs.openclaw.ai/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
-- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs.
+- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — wake words on macOS/iOS and continuous voice on Android (ElevenLabs + system TTS fallback).
- **[Live Canvas](https://docs.openclaw.ai/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
- **[First-class tools](https://docs.openclaw.ai/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions.
- **[Companion apps](https://docs.openclaw.ai/platforms/macos)** — macOS menu bar app + iOS/Android [nodes](https://docs.openclaw.ai/nodes).
@@ -150,14 +150,14 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
### Channels
-- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (iMessage, recommended), [iMessage](https://docs.openclaw.ai/channels/imessage) (legacy imsg), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams) (extension), [Matrix](https://docs.openclaw.ai/channels/matrix) (extension), [Zalo](https://docs.openclaw.ai/channels/zalo) (extension), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser) (extension), [WebChat](https://docs.openclaw.ai/web/webchat).
+- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (iMessage, recommended), [iMessage](https://docs.openclaw.ai/channels/imessage) (legacy imsg), [IRC](https://docs.openclaw.ai/channels/irc), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams), [Matrix](https://docs.openclaw.ai/channels/matrix), [Feishu](https://docs.openclaw.ai/channels/feishu), [LINE](https://docs.openclaw.ai/channels/line), [Mattermost](https://docs.openclaw.ai/channels/mattermost), [Nextcloud Talk](https://docs.openclaw.ai/channels/nextcloud-talk), [Nostr](https://docs.openclaw.ai/channels/nostr), [Synology Chat](https://docs.openclaw.ai/channels/synology-chat), [Tlon](https://docs.openclaw.ai/channels/tlon), [Twitch](https://docs.openclaw.ai/channels/twitch), [Zalo](https://docs.openclaw.ai/channels/zalo), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser), [WebChat](https://docs.openclaw.ai/web/webchat).
- [Group routing](https://docs.openclaw.ai/channels/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.openclaw.ai/channels).
### Apps + nodes
- [macOS app](https://docs.openclaw.ai/platforms/macos): menu bar control plane, [Voice Wake](https://docs.openclaw.ai/nodes/voicewake)/PTT, [Talk Mode](https://docs.openclaw.ai/nodes/talk) overlay, [WebChat](https://docs.openclaw.ai/web/webchat), debug tools, [remote gateway](https://docs.openclaw.ai/gateway/remote) control.
-- [iOS node](https://docs.openclaw.ai/platforms/ios): [Canvas](https://docs.openclaw.ai/platforms/mac/canvas), [Voice Wake](https://docs.openclaw.ai/nodes/voicewake), [Talk Mode](https://docs.openclaw.ai/nodes/talk), camera, screen recording, Bonjour pairing.
-- [Android node](https://docs.openclaw.ai/platforms/android): [Canvas](https://docs.openclaw.ai/platforms/mac/canvas), [Talk Mode](https://docs.openclaw.ai/nodes/talk), camera, screen recording, optional SMS.
+- [iOS node](https://docs.openclaw.ai/platforms/ios): [Canvas](https://docs.openclaw.ai/platforms/mac/canvas), [Voice Wake](https://docs.openclaw.ai/nodes/voicewake), [Talk Mode](https://docs.openclaw.ai/nodes/talk), camera, screen recording, Bonjour + device pairing.
+- [Android node](https://docs.openclaw.ai/platforms/android): Connect tab (setup code/manual), chat sessions, voice tab, [Canvas](https://docs.openclaw.ai/platforms/mac/canvas), camera/screen recording, and Android device commands (notifications/location/SMS/photos/contacts/calendar/motion/app update).
- [macOS node mode](https://docs.openclaw.ai/nodes): system.run/notify + canvas/camera exposure.
### Tools + automation
@@ -185,7 +185,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
## How it works (short)
```
-WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBubbles / Microsoft Teams / Matrix / Zalo / Zalo Personal / WebChat
+WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBubbles / IRC / Microsoft Teams / Matrix / Feishu / LINE / Mattermost / Nextcloud Talk / Nostr / Synology Chat / Tlon / Twitch / Zalo / Zalo Personal / WebChat
│
▼
┌───────────────────────────────┐
@@ -207,7 +207,7 @@ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBu
- **[Tailscale exposure](https://docs.openclaw.ai/gateway/tailscale)** — Serve/Funnel for the Gateway dashboard + WS (remote access: [Remote](https://docs.openclaw.ai/gateway/remote)).
- **[Browser control](https://docs.openclaw.ai/tools/browser)** — openclaw‑managed Chrome/Chromium with CDP control.
- **[Canvas + A2UI](https://docs.openclaw.ai/platforms/mac/canvas)** — agent‑driven visual workspace (A2UI host: [Canvas/A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui)).
-- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — always‑on speech and continuous conversation.
+- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — wake words on macOS/iOS plus continuous voice on Android.
- **[Nodes](https://docs.openclaw.ai/nodes)** — Canvas, camera snap/clip, screen record, `location.get`, notifications, plus macOS‑only `system.run`/`system.notify`.
## Tailscale access (Gateway dashboard)
@@ -297,7 +297,7 @@ Note: signed builds required for macOS permissions to stick across rebuilds (see
### iOS node (optional)
-- Pairs as a node via the Bridge.
+- Pairs as a node over the Gateway WebSocket (device pairing).
- Voice trigger forwarding + Canvas surface.
- Controlled via `openclaw nodes …`.
@@ -305,8 +305,8 @@ Runbook: [iOS connect](https://docs.openclaw.ai/platforms/ios).
### Android node (optional)
-- Pairs via the same Bridge + pairing flow as iOS.
-- Exposes Canvas, Camera, and Screen capture commands.
+- Pairs as a WS node via device pairing (`openclaw devices ...`).
+- Exposes Connect/Chat/Voice tabs plus Canvas, Camera, Screen capture, and Android device command families.
- Runbook: [Android connect](https://docs.openclaw.ai/platforms/android).
## Agent workspace + skills
@@ -502,54 +502,58 @@ Special thanks to Adam Doppelt for lobster.bot.
Thanks to all clawtributors:
diff --git a/SECURITY.md b/SECURITY.md
index 1dc51369f9a..5f1e8f0cb9e 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -51,12 +51,15 @@ These are frequently reported but are typically closed with no code change:
- Prompt-injection-only chains without a boundary bypass (prompt injection is out of scope).
- Operator-intended local features (for example TUI local `!` shell) presented as remote injection.
+- Reports that treat explicit operator-control surfaces (for example `canvas.eval`, browser evaluate/script execution, or direct `node.invoke` execution primitives) as vulnerabilities without demonstrating an auth/policy/sandbox boundary bypass. These capabilities are intentional when enabled and are trusted-operator features, not standalone security bugs.
- Authorized user-triggered local actions presented as privilege escalation. Example: an allowlisted/owner sender running `/export-session /absolute/path.html` to write on the host. In this trust model, authorized user actions are trusted host actions unless you demonstrate an auth/sandbox/boundary bypass.
- Reports that only show a malicious plugin executing privileged actions after a trusted operator installs/enables it.
- Reports that assume per-user multi-tenant authorization on a shared gateway host/config.
- Reports that 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.
- 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.
+- Reports that depend on pre-existing symlinked skill/workspace filesystem state (for example symlink chains involving `skills/*/SKILL.md`) without showing an untrusted path that can create/control that state.
- Missing HSTS findings on default local/loopback deployments.
- Slack webhook signature findings when HTTP mode already uses signing-secret verification.
- Discord inbound webhook signature findings for paths not used by this repo's Discord integration.
@@ -114,7 +117,10 @@ Plugins/extensions are part of OpenClaw's trusted computing base for a gateway.
- Prompt-injection-only attacks (without a policy/auth/sandbox boundary bypass)
- Reports that require write access to trusted local state (`~/.openclaw`, workspace files like `MEMORY.md` / `memory/*.md`)
- Reports where exploitability depends on attacker-controlled pre-existing symlink/hardlink filesystem state in trusted local paths (for example extraction/install target trees) unless a separate untrusted boundary bypass is shown that creates that state.
+- Reports whose only claim is sandbox/workspace read expansion through trusted local skill/workspace symlink state (for example `skills/*/SKILL.md` symlink chains) unless a separate untrusted boundary bypass is shown that creates/controls that state.
+- Reports whose only claim is post-approval executable identity drift on a trusted host via same-path file replacement/rewrite unless a separate untrusted boundary bypass is shown for that host write primitive.
- Reports where the only demonstrated impact is an already-authorized sender intentionally invoking a local-action command (for example `/export-session` writing to an absolute host path) without bypassing auth, sandbox, or another documented boundary
+- Reports whose only claim is use of an explicit trusted-operator control surface (for example `canvas.eval`, browser evaluate/script execution, or direct `node.invoke` execution) without demonstrating an auth, policy, allowlist, approval, or sandbox bypass.
- Reports where the only claim is that a trusted-installed/enabled plugin can execute with gateway/host privileges (documented trust model behavior).
- Any report whose only claim is that an operator-enabled `dangerous*`/`dangerously*` config option weakens defaults (these are explicit break-glass tradeoffs by design)
- Reports that depend on trusted operator-supplied configuration values to trigger availability impact (for example custom regex patterns). These may still be fixed as defense-in-depth hardening, but are not security-boundary bypasses.
@@ -149,6 +155,8 @@ OpenClaw's security model is "personal assistant" (one trusted operator, potenti
- 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.
- Prompt injection by itself is not a vulnerability report unless it crosses one of those boundaries.
+- 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
diff --git a/appcast.xml b/appcast.xml
index 5a43fbfc41e..4bceb205614 100644
--- a/appcast.xml
+++ b/appcast.xml
@@ -3,312 +3,660 @@
OpenClaw
- 2026.2.14
- Sun, 15 Feb 2026 04:24:34 +0100
+ 2026.3.8-beta.1
+ Mon, 09 Mar 2026 07:19:57 +0000
https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
- 202602140
- 2026.2.14
+ 2026030801
+ 2026.3.8-beta.115.0
- OpenClaw 2026.2.14
+ OpenClaw 2026.3.8-beta.1
Slack/Discord: add dmPolicy + allowFrom config aliases for DM access control; legacy dm.policy + dm.allowFrom keys remain supported and openclaw doctor --fix can migrate them.
-
Discord: allow exec approval prompts to target channels or both DM+channel via channels.discord.execApprovals.target. (#16051) Thanks @leonnardo.
-
Sandbox: add sandbox.browser.binds to configure browser-container bind mounts separately from exec containers. (#16230) Thanks @seheepeak.
-
Discord: add debug logging for message routing decisions to improve --debug tracing. (#16202) Thanks @jayleekr.
+
CLI/backup: add openclaw backup create and openclaw backup verify for local state archives, including --only-config, --no-include-workspace, manifest/payload validation, and backup guidance in destructive flows. (#40163) thanks @shichangs.
+
macOS/onboarding: add a remote gateway token field for remote mode, preserve existing non-plaintext gateway.remote.token config values until explicitly replaced, and warn when the loaded token shape cannot be used directly from the macOS app. (#40187, supersedes #34614) Thanks @cgdusek.
+
Talk mode: add top-level talk.silenceTimeoutMs config so Talk waits a configurable amount of silence before auto-sending the current transcript, while keeping each platform's existing default pause window when unset. (#39607) Thanks @danodoesdesign. Fixes #17147.
+
TUI: infer the active agent from the current workspace when launched inside a configured agent workspace, while preserving explicit agent: session targets. (#39591) thanks @arceus77-7.
+
Tools/Brave web search: add opt-in tools.web.search.brave.mode: "llm-context" so web_search can call Brave's LLM Context endpoint and return extracted grounding snippets with source metadata, plus config/docs/test coverage. (#33383) Thanks @thirumaleshp.
+
CLI/install: include the short git commit hash in openclaw --version output when metadata is available, and keep installer version checks compatible with the decorated format. (#39712) thanks @sourman.
+
CLI/backup: improve archive naming for date sorting, add config-only backup mode, and harden backup planning, publication, and verification edge cases. (#40163) Thanks @gumadeiras.
+
ACP/Provenance: add optional ACP ingress provenance metadata and visible receipt injection (openclaw acp --provenance off|meta|meta+receipt) so OpenClaw agents can retain and report ACP-origin context with session trace IDs. (#40473) thanks @mbelinky.
+
Tools/web search: alphabetize provider ordering across runtime selection, onboarding/configure pickers, and config metadata, so provider lists stay neutral and multi-key auto-detect now prefers Grok before Kimi. (#40259) thanks @kesku.
+
Docs/Web search: restore $5/month free-credit details, replace defunct "Data for Search"/"Data for AI" plan names with current "Search" plan, and note legacy subscription validity in Brave setup docs. Follows up on #26860. (#40111) Thanks @remusao.
+
Extensions/ACPX tests: move the shared runtime fixture helper from src/runtime-internals/ to src/test-utils/ so the test-only helper no longer looks like shipped runtime code.
Fixes
-
CLI/Plugins: ensure openclaw message send exits after successful delivery across plugin-backed channels so one-shot sends do not hang. (#16491) Thanks @yinghaosang.
-
CLI/Plugins: run registered plugin gateway_stop hooks before openclaw message exits (success and failure paths), so plugin-backed channels can clean up one-shot CLI resources. (#16580) Thanks @gumadeiras.
-
WhatsApp: honor per-account dmPolicy overrides (account-level settings now take precedence over channel defaults for inbound DMs). (#10082) Thanks @mcaxtr.
-
Telegram: when channels.telegram.commands.native is false, exclude plugin commands from setMyCommands menu registration while keeping plugin slash handlers callable. (#15132) Thanks @Glucksberg.
-
LINE: return 200 OK for Developers Console "Verify" requests ({"events":[]}) without X-Line-Signature, while still requiring signatures for real deliveries. (#16582) Thanks @arosstale.
-
Cron: deliver text-only output directly when delivery.to is set so cron recipients get full output instead of summaries. (#16360) Thanks @thewilloftheshadow.
-
Cron/Slack: preserve agent identity (name and icon) when cron jobs deliver outbound messages. (#16242) Thanks @robbyczgw-cla.
-
Media: accept MEDIA:-prefixed paths (lenient whitespace) when loading outbound media to prevent ENOENT for tool-returned local media paths. (#13107) Thanks @mcaxtr.
-
Agents: deliver tool result media (screenshots, images, audio) to channels regardless of verbose level. (#11735) Thanks @strelov1.
-
Agents/Image tool: allow workspace-local image paths by including the active workspace directory in local media allowlists, and trust sandbox-validated paths in image loaders to prevent false "not under an allowed directory" rejections. (#15541)
-
Agents/Image tool: propagate the effective workspace root into tool wiring so workspace-local image paths are accepted by default when running without an explicit workspaceDir. (#16722)
-
BlueBubbles: include sender identity in group chat envelopes and pass clean message text to the agent prompt, aligning with iMessage/Signal formatting. (#16210) Thanks @zerone0x.
-
CLI: fix lazy core command registration so top-level maintenance commands (doctor, dashboard, reset, uninstall) resolve correctly instead of exposing a non-functional maintenance placeholder command.
-
CLI/Dashboard: when gateway.bind=lan, generate localhost dashboard URLs to satisfy browser secure-context requirements while preserving non-LAN bind behavior. (#16434) Thanks @BinHPdev.
-
TUI/Gateway: resolve local gateway target URL from gateway.bind mode (tailnet/lan) instead of hardcoded localhost so openclaw tui connects when gateway is non-loopback. (#16299) Thanks @cortexuvula.
-
TUI: honor explicit --session in openclaw tui even when session.scope is global, so named sessions no longer collapse into shared global history. (#16575) Thanks @cinqu.
-
TUI: use available terminal width for session name display in searchable select lists. (#16238) Thanks @robbyczgw-cla.
-
TUI: refactor searchable select list description layout and add regression coverage for ANSI-highlight width bounds.
-
TUI: preserve in-flight streaming replies when a different run finalizes concurrently (avoid clearing active run or reloading history mid-stream). (#10704) Thanks @axschr73.
-
TUI: keep pre-tool streamed text visible when later tool-boundary deltas temporarily omit earlier text blocks. (#6958) Thanks @KrisKind75.
-
TUI: sanitize ANSI/control-heavy history text, redact binary-like lines, and split pathological long unbroken tokens before rendering to prevent startup crashes on binary attachment history. (#13007) Thanks @wilkinspoe.
-
TUI: harden render-time sanitizer for narrow terminals by chunking moderately long unbroken tokens and adding fast-path sanitization guards to reduce overhead on normal text. (#5355) Thanks @tingxueren.
-
TUI: render assistant body text in terminal default foreground (instead of fixed light ANSI color) so contrast remains readable on light themes such as Solarized Light. (#16750) Thanks @paymog.
-
TUI/Hooks: pass explicit reset reason (new vs reset) through sessions.reset and emit internal command hooks for gateway-triggered resets so /new hook workflows fire in TUI/webchat.
-
Cron: prevent cron list/cron status from silently skipping past-due recurring jobs by using maintenance recompute semantics. (#16156) Thanks @zerone0x.
-
Cron: repair missing/corrupt nextRunAtMs for the updated job without globally recomputing unrelated due jobs during cron update. (#15750)
-
Cron: skip missed-job replay on startup for jobs interrupted mid-run (stale runningAtMs markers), preventing restart loops for self-restarting jobs such as update tasks. (#16694) Thanks @sbmilburn.
-
Discord: prefer gateway guild id when logging inbound messages so cached-miss guilds do not appear as guild=dm. Thanks @thewilloftheshadow.
-
Discord: treat empty per-guild channels: {} config maps as no channel allowlist (not deny-all), so groupPolicy: "open" guilds without explicit channel entries continue to receive messages. (#16714) Thanks @xqliu.
-
Models/CLI: guard models status string trimming paths to prevent crashes from malformed non-string config values. (#16395) Thanks @BinHPdev.
-
Gateway/Subagents: preserve queued announce items and summary state on delivery errors, retry failed announce drains, and avoid dropping unsent announcements on timeout/failure. (#16729) Thanks @Clawdette-Workspace.
-
Gateway/Sessions: abort active embedded runs and clear queued session work before sessions.reset, returning unavailable if the run does not stop in time. (#16576) Thanks @Grynn.
-
Sessions/Agents: harden transcript path resolution for mismatched agent context by preserving explicit store roots and adding safe absolute-path fallback to the correct agent sessions directory. (#16288) Thanks @robbyczgw-cla.
-
Agents: add a safety timeout around embedded session.compact() to ensure stalled compaction runs settle and release blocked session lanes. (#16331) Thanks @BinHPdev.
-
Agents: keep unresolved mutating tool failures visible until the same action retry succeeds, scope mutation-error surfacing to mutating calls (including session_status model changes), and dedupe duplicate failure warnings in outbound replies. (#16131) Thanks @Swader.
-
Agents/Process/Bootstrap: preserve unbounded process log offset-only pagination (default tail applies only when both offset and limit are omitted) and enforce strict bootstrapTotalMaxChars budgeting across injected bootstrap content (including markers), skipping additional injection when remaining budget is too small. (#16539) Thanks @CharlieGreenman.
-
Agents/Workspace: persist bootstrap onboarding state so partially initialized workspaces recover missing BOOTSTRAP.md once, while completed onboarding keeps BOOTSTRAP deleted even if runtime files are later recreated. Thanks @gumadeiras.
-
Agents/Workspace: create BOOTSTRAP.md when core workspace files are seeded in partially initialized workspaces, while keeping BOOTSTRAP one-shot after onboarding deletion. (#16457) Thanks @robbyczgw-cla.
-
Agents: classify external timeout aborts during compaction the same as internal timeouts, preventing unnecessary auth-profile rotation and preserving compaction-timeout snapshot fallback behavior. (#9855) Thanks @mverrilli.
-
Agents: treat empty-stream provider failures (request ended without sending any chunks) as timeout-class failover signals, enabling auth-profile rotation/fallback and showing a friendly timeout message instead of raw provider errors. (#10210) Thanks @zenchantlive.
-
Agents: treat read tool file_path arguments as valid in tool-start diagnostics to avoid false “read tool called without path” warnings when alias parameters are used. (#16717) Thanks @Stache73.
-
Ollama/Agents: avoid forcing tag enforcement for Ollama models, which could suppress all output as (no output). (#16191) Thanks @Glucksberg.
-
Plugins: suppress false duplicate plugin id warnings when the same extension is discovered via multiple paths (config/workspace/global vs bundled), while still warning on genuine duplicates. (#16222) Thanks @shadril238.
-
Skills: watch SKILL.md only when refreshing skills snapshot to avoid file-descriptor exhaustion in large data trees. (#11325) Thanks @household-bard.
-
Memory/QMD: make memory status read-only by skipping QMD boot update/embed side effects for status-only manager checks.
-
Memory/QMD: keep original QMD failures when builtin fallback initialization fails (for example missing embedding API keys), instead of replacing them with fallback init errors.
-
Memory/Builtin: keep memory status dirty reporting stable across invocations by deriving status-only manager dirty state from persisted index metadata instead of process-start defaults. (#10863) Thanks @BarryYangi.
-
Memory/QMD: cap QMD command output buffering to prevent memory exhaustion from pathological qmd command output.
-
Memory/QMD: parse qmd scope keys once per request to avoid repeated parsing in scope checks.
-
Memory/QMD: query QMD index using exact docid matches before falling back to prefix lookup for better recall correctness and index efficiency.
-
Memory/QMD: pass result limits to search/vsearch commands so QMD can cap results earlier.
-
Memory/QMD: avoid reading full markdown files when a from/lines window is requested in QMD reads.
-
Memory/QMD: skip rewriting unchanged session export markdown files during sync to reduce disk churn.
-
Memory/QMD: make QMD result JSON parsing resilient to noisy command output by extracting the first JSON array from noisy stdout.
-
Memory/QMD: treat prefixed no results found marker output as an empty result set in qmd JSON parsing. (#11302) Thanks @blazerui.
-
Memory/QMD: avoid multi-collection query ranking corruption by running one qmd query -c per managed collection and merging by best score (also used for search/vsearch fallback-to-query). (#16740) Thanks @volarian-vai.
Memory/QMD/Security: add rawKeyPrefix support for QMD scope rules and preserve legacy keyPrefix: "agent:..." matching, preventing scoped deny bypass when operators match agent-prefixed session keys.
-
Memory/Builtin: narrow memory watcher targets to markdown globs and ignore dependency/venv directories to reduce file-descriptor pressure during memory sync startup. (#11721) Thanks @rex05ai.
-
Security/Memory-LanceDB: treat recalled memories as untrusted context (escape injected memory text + explicit non-instruction framing), skip likely prompt-injection payloads during auto-capture, and restrict auto-capture to user messages to reduce memory-poisoning risk. (#12524) Thanks @davidschmid24.
-
Security/Memory-LanceDB: require explicit autoCapture: true opt-in (default is now disabled) to prevent automatic PII capture unless operators intentionally enable it. (#12552) Thanks @fr33d3m0n.
-
Diagnostics/Memory: prune stale diagnostic session state entries and cap tracked session states to prevent unbounded in-memory growth on long-running gateways. (#5136) Thanks @coygeek and @vignesh07.
-
Gateway/Memory: clean up agentRunSeq tracking on run completion/abort and enforce maintenance-time cap pruning to prevent unbounded sequence-map growth over long uptimes. (#6036) Thanks @coygeek and @vignesh07.
-
Auto-reply/Memory: bound ABORT_MEMORY growth by evicting oldest entries and deleting reset (false) flags so abort state tracking cannot grow unbounded over long uptimes. (#6629) Thanks @coygeek and @vignesh07.
-
Slack/Memory: bound thread-starter cache growth with TTL + max-size pruning to prevent long-running Slack gateways from accumulating unbounded thread cache state. (#5258) Thanks @coygeek and @vignesh07.
-
Outbound/Memory: bound directory cache growth with max-size eviction and proactive TTL pruning to prevent long-running gateways from accumulating unbounded directory entries. (#5140) Thanks @coygeek and @vignesh07.
-
Skills/Memory: remove disconnected nodes from remote-skills cache to prevent stale node metadata from accumulating over long uptimes. (#6760) Thanks @coygeek.
-
Sandbox/Tools: make sandbox file tools bind-mount aware (including absolute container paths) and enforce read-only bind semantics for writes. (#16379) Thanks @tasaankaeris.
-
Media/Security: allow local media reads from OpenClaw state workspace/ and sandboxes/ roots by default so generated workspace media can be delivered without unsafe global path bypasses. (#15541) Thanks @lanceji.
-
Media/Security: harden local media allowlist bypasses by requiring an explicit readFile override when callers mark paths as validated, and reject filesystem-root localRoots entries. (#16739)
-
Discord/Security: harden voice message media loading (SSRF + allowed-local-root checks) so tool-supplied paths/URLs cannot be used to probe internal URLs or read arbitrary local files.
-
Security/BlueBubbles: require explicit mediaLocalRoots allowlists for local outbound media path reads to prevent local file disclosure. (#16322) Thanks @mbelinky.
-
Security/BlueBubbles: reject ambiguous shared-path webhook routing when multiple webhook targets match the same guid/password.
-
Security/BlueBubbles: harden BlueBubbles webhook auth behind reverse proxies by only accepting passwordless webhooks for direct localhost loopback requests (forwarded/proxied requests now require a password). Thanks @simecek.
-
Feishu/Security: harden media URL fetching against SSRF and local file disclosure. (#16285) Thanks @mbelinky.
-
Security/Zalo: reject ambiguous shared-path webhook routing when multiple webhook targets match the same secret.
Security/Signal: harden signal-cli archive extraction during install to prevent path traversal outside the install root.
-
Security/Hooks: restrict hook transform modules to ~/.openclaw/hooks/transforms (prevents path traversal/escape module loads via config). Config note: hooks.transformsDir must now be within that directory. Thanks @akhmittra.
-
Security/Hooks: ignore hook package manifest entries that point outside the package directory (prevents out-of-tree handler loads during hook discovery).
-
Security/Archive: enforce archive extraction entry/size limits to prevent resource exhaustion from high-expansion ZIP/TAR archives. Thanks @vincentkoc.
-
Security/Media: reject oversized base64-backed input media before decoding to avoid large allocations. Thanks @vincentkoc.
-
Security/Media: stream and bound URL-backed input media fetches to prevent memory exhaustion from oversized responses. Thanks @vincentkoc.
-
Security/Skills: harden archive extraction for download-installed skills to prevent path traversal outside the target directory. Thanks @markmusson.
-
Security/Slack: compute command authorization for DM slash commands even when dmPolicy=open, preventing unauthorized users from running privileged commands via DM. Thanks @christos-eth.
-
Security/iMessage: keep DM pairing-store identities out of group allowlist authorization (prevents cross-context command authorization). Thanks @vincentkoc.
-
Security/Google Chat: deprecate users/ allowlists (treat users/... as immutable user id only); keep raw email allowlists for usability. Thanks @vincentkoc.
Telegram/Security: require numeric Telegram sender IDs for allowlist authorization (reject @username principals), auto-resolve @username to IDs in openclaw doctor --fix (when possible), and warn in openclaw security audit when legacy configs contain usernames. Thanks @vincentkoc.
-
Telegram/Security: reject Telegram webhook startup when webhookSecret is missing or empty (prevents unauthenticated webhook request forgery). Thanks @yueyueL.
-
Security/Windows: avoid shell invocation when spawning child processes to prevent cmd.exe metacharacter injection via untrusted CLI arguments (e.g. agent prompt text).
-
Telegram: set webhook callback timeout handling to onTimeout: "return" (10s) so long-running update processing no longer emits webhook 500s and retry storms. (#16763) Thanks @chansearrington.
-
Signal: preserve case-sensitive group: target IDs during normalization so mixed-case group IDs no longer fail with Group not found. (#16748) Thanks @repfigit.
-
Feishu/Security: harden media URL fetching against SSRF and local file disclosure. (#16285) Thanks @mbelinky.
-
Security/Agents: scope CLI process cleanup to owned child PIDs to avoid killing unrelated processes on shared hosts. Thanks @aether-ai-agent.
-
Security/Agents: enforce workspace-root path bounds for apply_patch in non-sandbox mode to block traversal and symlink escape writes. Thanks @p80n-sec.
-
Security/Agents: enforce symlink-escape checks for apply_patch delete hunks under workspaceOnly, while still allowing deleting the symlink itself. Thanks @p80n-sec.
-
Security/Agents (macOS): prevent shell injection when writing Claude CLI keychain credentials. (#15924) Thanks @aether-ai-agent.
-
macOS: hard-limit unkeyed openclaw://agent deep links and ignore deliver / to / channel unless a valid unattended key is provided. Thanks @Cillian-Collins.
-
Scripts/Security: validate GitHub logins and avoid shell invocation in scripts/update-clawtributors.ts to prevent command injection via malicious commit records. Thanks @scanleale.
-
Security: fix Chutes manual OAuth login state validation by requiring the full redirect URL (reject code-only pastes) (thanks @aether-ai-agent).
-
Security/Gateway: harden tool-supplied gatewayUrl overrides by restricting them to loopback or the configured gateway.remote.url. Thanks @p80n-sec.
-
Security/Gateway: block system.execApprovals.* via node.invoke (use exec.approvals.node.* instead). Thanks @christos-eth.
-
Security/Gateway: reject oversized base64 chat attachments before decoding to avoid large allocations. Thanks @vincentkoc.
-
Security/Gateway: stop returning raw resolved config values in skills.status requirement checks (prevents operator.read clients from reading secrets). Thanks @simecek.
Security/Exec approvals: prevent safeBins allowlist bypass via shell expansion (host exec allowlist mode only; not enabled by default). Thanks @christos-eth.
-
Security/Exec: harden PATH handling by disabling project-local node_modules/.bin bootstrapping by default, disallowing node-host PATH overrides, and spawning ACP servers via the current executable by default. Thanks @akhmittra.
-
Security/Tlon: harden Urbit URL fetching against SSRF by blocking private/internal hosts by default (opt-in: channels.tlon.allowPrivateNetwork). Thanks @p80n-sec.
-
Security/Voice Call (Telnyx): require webhook signature verification when receiving inbound events; configs without telnyx.publicKey are now rejected unless skipSignatureVerification is enabled. Thanks @p80n-sec.
-
Security/Voice Call: require valid Twilio webhook signatures even when ngrok free tier loopback compatibility mode is enabled. Thanks @p80n-sec.
-
Security/Discovery: stop treating Bonjour TXT records as authoritative routing (prefer resolved service endpoints) and prevent discovery from overriding stored TLS pins; autoconnect now requires a previously trusted gateway. Thanks @simecek.
+
macOS app/chat UI: route browser proxy through the local node browser service, preserve plain-text paste semantics, strip completed assistant trace/debug wrapper noise from transcripts, refresh permission state after returning from System Settings, and tolerate malformed cron rows in the macOS tab. (#39516) Thanks @Imhermes1.
+
Android/Play distribution: remove self-update, background location, screen.record, and background mic capture from the Android app, narrow the foreground service to dataSync only, and clean up the legacy location.enabledMode=always preference migration. (#39660) Thanks @obviyus.
+
Telegram/DM routing: dedupe inbound Telegram DMs per agent instead of per session key so the same DM cannot trigger duplicate replies when both agent:main:main and agent:main:telegram:direct: resolve for one agent. Fixes #40005. Supersedes #40116. (#40519) thanks @obviyus.
+
Cron/Telegram announce delivery: route text-only announce jobs through the real outbound adapters after finalizing descendant output so plain Telegram targets no longer report delivered: true when no message actually reached Telegram. (#40575) thanks @obviyus.
+
Matrix/DM routing: add safer fallback detection for broken m.direct homeservers, honor explicit room bindings over DM classification, and preserve room-bound agent selection for Matrix DM rooms. (#19736) Thanks @derbronko.
+
Feishu/plugin onboarding: clear the short-lived plugin discovery cache before reloading the registry after installing a channel plugin, so onboarding no longer re-prompts to download Feishu immediately after a successful install. Fixes #39642. (#39752) Thanks @GazeKingNuWu.
+
Plugins/channel onboarding: prefer bundled channel plugins over duplicate npm-installed copies during onboarding and release-channel sync, preventing bundled plugins from being shadowed by npm installs with the same plugin ID. (#40092)
+
Config/runtime snapshots: keep secrets-runtime-resolved config and auth-profile snapshots intact after config writes so follow-up reads still see file-backed secret values while picking up the persisted config update. (#37313) thanks @bbblending.
+
Gateway/Control UI: resolve bundled dashboard assets through symlinked global wrappers and auto-detected package roots, while keeping configured and custom roots on the strict hardlink boundary. (#40385) Thanks @LarytheLord.
+
Browser/extension relay: add browser.relayBindHost so the Chrome relay can bind to an explicit non-loopback address for WSL2 and other cross-namespace setups, while preserving loopback-only defaults. (#39364) Thanks @mvanhorn.
+
Browser/CDP: normalize loopback direct WebSocket CDP URLs back to HTTP(S) for /json/* tab operations so local ws:// / wss:// profiles can still list, focus, open, and close tabs after the new direct-WS support lands. (#31085) Thanks @shrey150.
+
Browser/CDP: rewrite wildcard ws://0.0.0.0 and ws://[::] debugger URLs from remote /json/version responses back to the external CDP host/port, fixing Browserless-style container endpoints. (#17760) Thanks @joeharouni.
+
Browser/extension relay: wait briefly for a previously attached Chrome tab to reappear after transient relay drops before failing with tab not found, reducing noisy reconnect flakes. (#32461) Thanks @AaronWander.
+
macOS/Tailscale gateway discovery: keep Tailscale Serve probing alive when other remote gateways are already discovered, prefer direct transport for resolved .ts.net and Tailscale Serve gateways, and set TERM=dumb for GUI-launched Tailscale CLI discovery. (#40167) thanks @ngutman.
+
TUI/theme: detect light terminal backgrounds via COLORFGBG and pick a WCAG AA-compliant light palette, with OPENCLAW_THEME=light|dark override for terminals without auto-detection. (#38636) Thanks @ademczuk and @vincentkoc.
+
Agents/openai-codex: normalize gpt-5.4 fallback transport back to openai-codex-responses on chatgpt.com/backend-api when config drifts to the generic OpenAI responses endpoint. (#38736) Thanks @0xsline.
+
Models/openai-codex GPT-5.4 forward-compat: use the GPT-5.4 1,050,000-token context window and 128,000 max tokens for openai-codex/gpt-5.4 instead of inheriting stale legacy Codex limits in resolver fallbacks and model listing. (#37876) thanks @yuweuii.
+
Tools/web search: restore Perplexity OpenRouter/Sonar compatibility for legacy OPENROUTER_API_KEY, sk-or-..., and explicit perplexity.baseUrl / model setups while keeping direct Perplexity keys on the native Search API path. (#39937) Thanks @obviyus.
+
Agents/failover: detect Amazon Bedrock Too many tokens per day quota errors as rate limits across fallback, cron retry, and memory embeddings while keeping context-window too many tokens per request errors out of the rate-limit lane. (#39377) Thanks @gambletan.
+
Mattermost replies: keep root_id pinned to the existing thread root when an agent replies inside a thread, while still using reply-target threading for top-level posts. (#27744) thanks @hnykda.
+
Telegram/DM partial streaming: keep DM preview lanes on real message edits instead of native draft materialization so final replies no longer flash a second duplicate copy before collapsing back to one.
+
macOS overlays: fix VoiceWake, Talk, and Notify overlay exclusivity crashes by removing shared inout visibility mutation from OverlayPanelFactory.present, and add a repeated Talk overlay smoke test. (#39275, #39321) Thanks @fellanH.
+
macOS Talk Mode: set the speech recognition request taskHint to .dictation for mic capture, and add regression coverage for the request defaults. (#38445) Thanks @dmiv.
+
macOS release packaging: default scripts/package-mac-app.sh to universal binaries for BUILD_CONFIG=release, and clarify that scripts/package-mac-dist.sh already produces the release zip + DMG. (#33891) Thanks @cgdusek.
+
Hooks/session-memory: keep /new and /reset memory artifacts in the bound agent workspace and align saved reset session keys with that workspace when stale main-agent keys leak into the hook path. (#39875) thanks @rbutera.
+
Sessions/model switch: clear stale cached contextTokens when a session changes models so status and runtime paths recompute against the active model window. (#38044) thanks @yuweuii.
+
ACP/session history: persist transcripts for successful ACP child runs, preserve exact transcript text, record ACP spawned-session lineage, and keep spawn-time transcript-path persistence best-effort so history storage failures do not block execution. (#40137) thanks @mbelinky.
+
Docs/browser: add a layered WSL2 + Windows remote Chrome CDP troubleshooting guide, including Control UI origin pitfalls and extension-relay bind-address guidance. (#39407) Thanks @Owlock.
+
Context engine registry/bundled builds: share the registry state through a globalThis singleton so duplicated bundled module copies can resolve engines registered by each other at runtime, with regression coverage for duplicate-module imports. (#40115) thanks @jalehman.
+
Podman/setup: fix cannot chdir: Permission denied in run_as_user when setup-podman.sh is invoked from a directory the target user cannot access, by wrapping user-switch calls in a subshell that cd's to /tmp with / fallback. (#39435) Thanks @langdon and @jlcbk.
+
Podman/SELinux: auto-detect SELinux enforcing/permissive mode and add :Z relabel to bind mounts in run-openclaw-podman.sh and the Quadlet template, fixing EACCES on Fedora/RHEL hosts. Supports OPENCLAW_BIND_MOUNT_OPTIONS override. (#39449) Thanks @langdon and @githubbzxs.
+
Agents/context-engine plugins: bootstrap runtime plugins once at embedded-run, compaction, and subagent boundaries so plugin-provided context engines and hooks load from the active workspace before runtime resolution. (#40232)
+
Docs/Changelog: correct the contributor credit for the bundled Control UI global-install fix to @LarytheLord. (#40420) Thanks @velvet-shark.
+
Telegram/media downloads: time out only stalled body reads so polling recovers from hung file downloads without aborting slow downloads that are still streaming data. (#40098) thanks @tysoncung.
+
Docker/runtime image: prune dev dependencies, strip build-only dist metadata for smaller Docker images. (#40307) Thanks @vincentkoc.
+
Gateway/restart timeout recovery: exit non-zero when restart-triggered shutdown drains time out so launchd/systemd restart the gateway instead of treating the failed restart as a clean stop. Landed from contributor PR #40380 by @dsantoreis. Thanks @dsantoreis.
+
Gateway/config restart guard: validate config before service start/restart and keep post-SIGUSR1 startup failures from crashing the gateway process, reducing invalid-config restart loops and macOS permission loss. Landed from contributor PR #38699 by @lml2468. Thanks @lml2468.
+
Gateway/launchd respawn detection: treat XPC_SERVICE_NAME as a launchd supervision hint so macOS restarts exit cleanly under launchd instead of attempting detached self-respawn. Landed from contributor PR #20555 by @dimat. Thanks @dimat.
+
Telegram/poll restart cleanup: abort the in-flight Telegram API fetch when shutdown or forced polling restarts stop a runner, preventing stale getUpdates long polls from colliding with the replacement runner. Landed from contributor PR #23950 by @Gkinthecodeland. Thanks @Gkinthecodeland.
+
Cron/restart catch-up staggering: limit immediate missed-job replay on startup and reschedule the deferred remainder from the post-catchup clock so restart bursts do not starve the gateway or silently skip overdue recurring jobs. Landed from contributor PR #18925 by @rexlunae. Thanks @rexlunae.
+
Cron/owner-only tools: pass trusted isolated cron runs into the embedded agent with owner context so cron/gateway tooling remains available after the owner-auth hardening narrowed direct-message ownership inference.
+
Browser/SSRF: block private-network intermediate redirect hops in strict browser navigation flows and fail closed when remote tab-open paths cannot inspect redirect chains. Thanks @zpbrent.
+
MS Teams/authz: keep groupPolicy: "allowlist" enforcing sender allowlists even when a team/channel route allowlist is configured, so route matches no longer widen group access to every sender in that route. Thanks @zpbrent.
+
Security/system.run: bind approved bun and deno run script operands to on-disk file snapshots so post-approval script rewrites are denied before execution.
+
Skills/download installs: pin the validated per-skill tools root before writing downloaded archives, so rebinding the lexical tools path cannot redirect download writes outside the intended tools directory. Thanks @tdjackey.
Discord: unlock rich interactive agent prompts with Components v2 (buttons, selects, modals, and attachment-backed file blocks) so for native interaction through Discord. Thanks @thewilloftheshadow.
Plugins: expose llm_input and llm_output hook payloads so extensions can observe prompt/input context and model output usage details. (#16724) Thanks @SecondThread.
-
Subagents: nested sub-agents (sub-sub-agents) with configurable depth. Set agents.defaults.subagents.maxSpawnDepth: 2 to allow sub-agents to spawn their own children. Includes maxChildrenPerAgent limit (default 5), depth-aware tool policy, and proper announce chain routing. (#14447) Thanks @tyler6204.
-
Slack/Discord/Telegram: add per-channel ack reaction overrides (account/channel-level) to support platform-specific emoji formats. (#17092) Thanks @zerone0x.
-
Cron/Gateway: add finished-run webhook delivery toggle (notify) and dedicated webhook auth token support (cron.webhookToken) for outbound cron webhook posts. (#14535) Thanks @advaitpaliwal.
-
Channels: deduplicate probe/token resolution base types across core + extensions while preserving per-channel error typing. (#16986) Thanks @iyoda and @thewilloftheshadow.
+
Agents/context engine plugin interface: add ContextEngine plugin slot with full lifecycle hooks (bootstrap, ingest, assemble, compact, afterTurn, prepareSubagentSpawn, onSubagentEnded), slot-based registry with config-driven resolution, LegacyContextEngine wrapper preserving existing compaction behavior, scoped subagent runtime for plugin runtimes via AsyncLocalStorage, and sessions.get gateway method. Enables plugins like lossless-claw to provide alternative context management strategies without modifying core compaction logic. Zero behavior change when no context engine plugin is configured. (#22201) thanks @jalehman.
+
ACP/persistent channel bindings: add durable Discord channel and Telegram topic binding storage, routing resolution, and CLI/docs support so ACP thread targets survive restarts and can be managed consistently. (#34873) Thanks @dutifulbob.
+
Telegram/ACP topic bindings: accept Telegram Mac Unicode dash option prefixes in /acp spawn, support Telegram topic thread binding (--thread here|auto), route bound-topic follow-ups to ACP sessions, add actionable Telegram approval buttons with prefixed approval-id resolution, and pin successful bind confirmations in-topic. (#36683) Thanks @huntharo.
+
Telegram/topic agent routing: support per-topic agentId overrides in forum groups and DM topics so topics can route to dedicated agents with isolated sessions. (#33647; based on #31513) Thanks @kesor and @Sid-Qin.
+
Web UI/i18n: add Spanish (es) locale support in the Control UI, including locale detection, lazy loading, and language picker labels across supported locales. (#35038) Thanks @DaoPromociones.
+
Onboarding/web search: add provider selection step and full provider list in configure wizard, with SecretRef ref-mode support during onboarding. (#34009) Thanks @kesku and @thewilloftheshadow.
+
Tools/Web search: switch Perplexity provider to Search API with structured results plus new language/region/time filters. (#33822) Thanks @kesku.
+
Gateway: add SecretRef support for gateway.auth.token with auth-mode guardrails. (#35094) Thanks @joshavant.
+
Docker/Podman extension dependency baking: add OPENCLAW_EXTENSIONS so container builds can preinstall selected bundled extension npm dependencies into the image for faster and more reproducible startup in container deployments. (#32223) Thanks @sallyom.
+
Plugins/before_prompt_build system-context fields: add prependSystemContext and appendSystemContext so static plugin guidance can be placed in system prompt space for provider caching and lower repeated prompt token cost. (#35177) thanks @maweibin.
+
Plugins/hook policy: add plugins.entries..hooks.allowPromptInjection, validate unknown typed hook names at runtime, and preserve legacy before_agent_start model/provider overrides while stripping prompt-mutating fields when prompt injection is disabled. (#36567) thanks @gumadeiras.
+
Hooks/Compaction lifecycle: emit session:compact:before and session:compact:after internal events plus plugin compaction callbacks with session/count metadata, so automations can react to compaction runs consistently. (#16788) thanks @vincentkoc.
+
Agents/compaction post-context configurability: add agents.defaults.compaction.postCompactionSections so deployments can choose which AGENTS.md sections are re-injected after compaction, while preserving legacy fallback behavior when the documented default pair is configured in any order. (#34556) thanks @efe-arv.
+
TTS/OpenAI-compatible endpoints: add messages.tts.openai.baseUrl config support with config-over-env precedence, endpoint-aware directive validation, and OpenAI TTS request routing to the resolved base URL. (#34321) thanks @RealKai42.
+
Slack/DM typing feedback: add channels.slack.typingReaction so Socket Mode DMs can show reaction-based processing status even when Slack native assistant typing is unavailable. (#19816) Thanks @dalefrieswthat.
+
Discord/allowBots mention gating: add allowBots: "mentions" to only accept bot-authored messages that mention the bot. Thanks @thewilloftheshadow.
+
Agents/tool-result truncation: preserve important tail diagnostics by using head+tail truncation for oversized tool results while keeping configurable truncation options. (#20076) thanks @jlwestsr.
+
Cron/job snapshot persistence: skip backup during normalization persistence in ensureLoaded so jobs.json.bak keeps the pre-edit snapshot for recovery, while preserving backup creation on explicit user-driven writes. (#35234) Thanks @0xsline.
+
CLI: make read-only SecretRef status flows degrade safely (#37023) thanks @joshavant.
+
Tools/Diffs guidance: restore a short system-prompt hint for enabled diffs while keeping the detailed instructions in the companion skill, so diffs usage guidance stays out of user-prompt space. (#36904) thanks @gumadeiras.
+
Tools/Diffs guidance loading: move diffs usage guidance from unconditional prompt-hook injection to the plugin companion skill path, reducing unrelated-turn prompt noise while keeping diffs tool behavior unchanged. (#32630) thanks @sircrumpet.
+
Docs/Web search: remove outdated Brave free-tier wording and replace prescriptive AI ToS guidance with neutral compliance language in Brave setup docs. (#26860) Thanks @HenryLoenwind.
+
Config/Compaction safeguard tuning: expose agents.defaults.compaction.recentTurnsPreserve and quality-guard retry knobs through the validated config surface and embedded-runner wiring, with regression coverage for real config loading and schema metadata. (#25557) thanks @rodrigouroz.
+
iOS/App Store Connect release prep: align iOS bundle identifiers under ai.openclaw.client, refresh Watch app icons, add Fastlane metadata/screenshot automation, and support Keychain-backed ASC auth for uploads. (#38936) Thanks @ngutman.
+
Mattermost/model picker: add Telegram-style interactive provider/model browsing for /oc_model and /oc_models, fix picker callback updates, and emit a normal confirmation reply when a model is selected. (#38767) thanks @mukhtharcm.
+
Docker/multi-stage build: restructure Dockerfile as a multi-stage build to produce a minimal runtime image without build tools, source code, or Bun; add OPENCLAW_VARIANT=slim build arg for a bookworm-slim variant. (#38479) Thanks @sallyom.
+
Google/Gemini 3.1 Flash-Lite: add first-class google/gemini-3.1-flash-lite-preview support across model-id normalization, default aliases, media-understanding image lookups, Google Gemini CLI forward-compat fallback, and docs.
+
+
Breaking
+
+
BREAKING: Gateway auth now requires explicit gateway.auth.mode when both gateway.auth.token and gateway.auth.password are configured (including SecretRefs). Set gateway.auth.mode to token or password before upgrade to avoid startup/pairing/TUI failures. (#35094) Thanks @joshavant.
Fixes
-
Security: replace deprecated SHA-1 sandbox configuration hashing with SHA-256 for deterministic sandbox cache identity and recreation checks. Thanks @kexinoh.
-
Security/Logging: redact Telegram bot tokens from error messages and uncaught stack traces to prevent accidental secret leakage into logs. Thanks @aether-ai-agent.
Sandbox: preserve array order in config hashing so order-sensitive Docker/browser settings trigger container recreation correctly. Thanks @kexinoh.
-
Gateway/Security: redact sensitive session/path details from status responses for non-admin clients; full details remain available to operator.admin. (#8590) Thanks @fr33d3m0n.
-
Gateway/Control UI: preserve requested operator scopes for Control UI bypass modes (allowInsecureAuth / dangerouslyDisableDeviceAuth) when device identity is unavailable, preventing false missing scope failures on authenticated LAN/HTTP operator sessions. (#17682) Thanks @leafbird.
-
LINE/Security: fail closed on webhook startup when channel token or channel secret is missing, and treat LINE accounts as configured only when both are present. (#17587) Thanks @davidahmann.
-
Skills/Security: restrict download installer targetDir to the per-skill tools directory to prevent arbitrary file writes. Thanks @Adam55A-code.
-
Skills/Linux: harden go installer fallback on apt-based systems by handling root/no-sudo environments safely, doing best-effort apt index refresh, and returning actionable errors instead of failing with spawn errors. (#17687) Thanks @mcrolly.
-
Web Fetch/Security: cap downloaded response body size before HTML parsing to prevent memory exhaustion from oversized or deeply nested pages. Thanks @xuemian168.
-
Config/Gateway: make sensitive-key whitelist suffix matching case-insensitive while preserving passwordFile path exemptions, preventing accidental redaction of non-secret config values like maxTokens and IRC password-file paths. (#16042) Thanks @akramcodez.
-
Dev tooling: harden git pre-commit hook against option injection from malicious filenames (for example --force), preventing accidental staging of ignored files. Thanks @mrthankyou.
-
Gateway/Agent: reject malformed agent:-prefixed session keys (for example, agent:main) in agent and agent.identity.get instead of silently resolving them to the default agent, preventing accidental cross-session routing. (#15707) Thanks @rodrigouroz.
-
Gateway/Chat: harden chat.send inbound message handling by rejecting null bytes, stripping unsafe control characters, and normalizing Unicode to NFC before dispatch. (#8593) Thanks @fr33d3m0n.
-
Gateway/Send: return an actionable error when send targets internal-only webchat, guiding callers to use chat.send or a deliverable channel. (#15703) Thanks @rodrigouroz.
-
Control UI: prevent stored XSS via assistant name/avatar by removing inline script injection, serving bootstrap config as JSON, and enforcing script-src 'self'. Thanks @Adam55A-code.
-
Agents/Security: sanitize workspace paths before embedding into LLM prompts (strip Unicode control/format chars) to prevent instruction injection via malicious directory names. Thanks @aether-ai-agent.
-
Agents/Sandbox: clarify system prompt path guidance so sandbox bash/exec uses container paths (for example /workspace) while file tools keep host-bridge mapping, avoiding first-attempt path misses from host-only absolute paths in sandbox command execution. (#17693) Thanks @app/juniordevbot.
-
Agents/Context: apply configured model contextWindow overrides after provider discovery so lookupContextTokens() honors operator config values (including discovery-failure paths). (#17404) Thanks @michaelbship and @vignesh07.
-
Agents/Context: derive lookupContextTokens() from auth-available model metadata and keep the smallest discovered context window for duplicate model ids, preventing cross-provider cache collisions from overestimating session context limits. (#17586) Thanks @githabideri and @vignesh07.
-
Agents/OpenAI: force store=true for direct OpenAI Responses/Codex runs to preserve multi-turn server-side conversation state, while leaving proxy/non-OpenAI endpoints unchanged. (#16803) Thanks @mark9232 and @vignesh07.
-
Memory/FTS: make buildFtsQuery Unicode-aware so non-ASCII queries (including CJK) produce keyword tokens instead of falling back to vector-only search. (#17672) Thanks @KinGP5471.
-
Auto-reply/Compaction: resolve memory/YYYY-MM-DD.md placeholders with timezone-aware runtime dates and append a Current time: line to memory-flush turns, preventing wrong-year memory filenames without making the system prompt time-variant. (#17603, #17633) Thanks @nicholaspapadam-wq and @vignesh07.
-
Agents: return an explicit timeout error reply when an embedded run times out before producing any payloads, preventing silent dropped turns during slow cache-refresh transitions. (#16659) Thanks @liaosvcaf and @vignesh07.
-
Group chats: always inject group chat context (name, participants, reply guidance) into the system prompt on every turn, not just the first. Prevents the model from losing awareness of which group it's in and incorrectly using the message tool to send to the same group. (#14447) Thanks @tyler6204.
-
Browser/Agents: when browser control service is unavailable, return explicit non-retry guidance (instead of "try again") so models do not loop on repeated browser tool calls until timeout. (#17673) Thanks @austenstone.
-
Subagents: use child-run-based deterministic announce idempotency keys across direct and queued delivery paths (with legacy queued-item fallback) to prevent duplicate announce retries without collapsing distinct same-millisecond announces. (#17150) Thanks @widingmarcus-cyber.
-
Subagents/Models: preserve agents.defaults.model.fallbacks when subagent sessions carry a model override, so subagent runs fail over to configured fallback models instead of retrying only the overridden primary model.
-
Telegram: omit message_thread_id for DM sends/draft previews and keep forum-topic handling (id=1 general omitted, non-general kept), preventing DM failures with 400 Bad Request: message thread not found. (#10942) Thanks @garnetlyx.
-
Telegram: replace inbound placeholder with successful preflight voice transcript in message body context, preventing placeholder-only prompt bodies for mention-gated voice messages. (#16789) Thanks @Limitless2023.
-
Telegram: retry inbound media getFile calls (3 attempts with backoff) and gracefully fall back to placeholder-only processing when retries fail, preventing dropped voice/media messages on transient Telegram network errors. (#16154) Thanks @yinghaosang.
-
Telegram: finalize streaming preview replies in place instead of sending a second final message, preventing duplicate Telegram assistant outputs at stream completion. (#17218) Thanks @obviyus.
-
Discord: preserve channel session continuity when runtime payloads omit message.channelId by falling back to event/raw channel_id values for routing/session keys, so same-channel messages keep history across turns/restarts. Also align diagnostics so active Discord runs no longer appear as sessionKey=unknown. (#17622) Thanks @shakkernerd.
-
Discord: dedupe native skill commands by skill name in multi-agent setups to prevent duplicated slash commands with _2 suffixes. (#17365) Thanks @seewhyme.
-
Discord: ensure role allowlist matching uses raw role IDs for message routing authorization. Thanks @xinhuagu.
-
Web UI/Agents: hide BOOTSTRAP.md in the Agents Files list after onboarding is completed, avoiding confusing missing-file warnings for completed workspaces. (#17491) Thanks @gumadeiras.
-
Auto-reply/WhatsApp/TUI/Web: when a final assistant message is NO_REPLY and a messaging tool send succeeded, mirror the delivered messaging-tool text into session-visible assistant output so TUI/Web no longer show NO_REPLY placeholders. (#7010) Thanks @Morrowind-Xie.
-
Cron: infer payload.kind="agentTurn" for model-only cron.update payload patches, so partial agent-turn updates do not fail validation when kind is omitted. (#15664) Thanks @rodrigouroz.
-
TUI: make searchable-select filtering and highlight rendering ANSI-aware so queries ignore hidden escape codes and no longer corrupt ANSI styling sequences during match highlighting. (#4519) Thanks @bee4come.
-
TUI/Windows: coalesce rapid single-line submit bursts in Git Bash into one multiline message as a fallback when bracketed paste is unavailable, preventing pasted multiline text from being split into multiple sends. (#4986) Thanks @adamkane.
-
TUI: suppress false (no output) placeholders for non-local empty final events during concurrent runs, preventing external-channel replies from showing empty assistant bubbles while a local run is still streaming. (#5782) Thanks @LagWizard and @vignesh07.
-
TUI: preserve copy-sensitive long tokens (URLs/paths/file-like identifiers) during wrapping and overflow sanitization so wrapped output no longer inserts spaces that corrupt copy/paste values. (#17515, #17466, #17505) Thanks @abe238, @trevorpan, and @JasonCry.
-
CLI/Build: make legacy daemon CLI compatibility shim generation tolerant of minimal tsdown daemon export sets, while preserving restart/register compatibility aliases and surfacing explicit errors for unavailable legacy daemon commands. Thanks @vignesh07.
+
Models/MiniMax: stop advertising removed MiniMax-M2.5-Lightning in built-in provider catalogs, onboarding metadata, and docs; keep the supported fast-tier model as MiniMax-M2.5-highspeed.
+
Security/Config: fail closed when loadConfig() hits validation or read errors so invalid configs cannot silently fall back to permissive runtime defaults. (#9040) Thanks @joetomasone.
+
Memory/Hybrid search: preserve negative FTS5 BM25 relevance ordering in bm25RankToScore() so stronger keyword matches rank above weaker ones instead of collapsing or reversing scores. (#33757) Thanks @lsdcc01.
+
LINE/requireMention group gating: align inbound and reply-stage LINE group policy resolution across raw, group:, and room: keys (including account-scoped group config), preserve plugin-backed reply-stage fallback behavior, and add regression coverage for prefixed-only group/room config plus reply-stage policy resolution. (#35847) Thanks @kirisame-wang.
+
Onboarding/local setup: default unset local tools.profile to coding instead of messaging, restoring file/runtime tools for fresh local installs while preserving explicit user-set profiles. (from #38241, overlap with #34958) Thanks @cgdusek.
+
Gateway/Telegram stale-socket restart guard: only apply stale-socket restarts to channels that publish event-liveness timestamps, preventing Telegram providers from being misclassified as stale solely due to long uptime and avoiding restart/pairing storms after upgrade. (openclaw#38464)
+
Onboarding/headless Linux daemon probe hardening: treat systemctl --user is-enabled probe failures as non-fatal during daemon install flow so onboarding no longer crashes on SSH/headless VPS environments before showing install guidance. (#37297) Thanks @acarbajal-web.
+
Memory/QMD mcporter Windows spawn hardening: when mcporter.cmd launch fails with spawn EINVAL, retry via bare mcporter shell resolution so QMD recall can continue instead of falling back to builtin memory search. (#27402) Thanks @i0ivi0i.
+
Tools/web_search Brave language-code validation: align search_lang handling with Brave-supported codes (including zh-hans, zh-hant, en-gb, and pt-br), map common alias inputs (zh, ja) to valid Brave values, and reject unsupported codes before upstream requests to prevent 422 failures. (#37260) Thanks @heyanming.
+
Models/openai-completions streaming compatibility: force compat.supportsUsageInStreaming=false for non-native OpenAI-compatible endpoints during model normalization, preventing usage-only stream chunks from triggering choices[0] parser crashes in provider streams. (#8714) Thanks @nonanon1.
+
Tools/xAI native web-search collision guard: drop OpenClaw web_search from tool registration when routing to xAI/Grok model providers (including OpenRouter x-ai/*) to avoid duplicate tool-name request failures against provider-native web_search. (#14749) Thanks @realsamrat.
+
TUI/token copy-safety rendering: treat long credential-like mixed alphanumeric tokens (including quoted forms) as copy-sensitive in render sanitization so formatter hard-wrap guards no longer inject visible spaces into auth-style values before display. (#26710) Thanks @jasonthane.
+
WhatsApp/self-chat response prefix fallback: stop forcing "[openclaw]" as the implicit outbound response prefix when no identity name or response prefix is configured, so blank/default prefix settings no longer inject branding text unexpectedly in self-chat flows. (#27962) Thanks @ecanmor.
+
Memory/QMD search result decoding: accept qmd search hits that only include file URIs (for example qmd://collection/path.md) without docid, resolve them through managed collection roots, and keep multi-collection results keyed by file fallback so valid QMD hits no longer collapse to empty memory_search output. (#28181) Thanks @0x76696265.
+
Memory/QMD collection-name conflict recovery: when qmd collection add fails because another collection already occupies the same path + pattern, detect the conflicting collection from collection list, remove it, and retry add so agent-scoped managed collections are created deterministically instead of being silently skipped; also add warning-only fallback when qmd metadata is unavailable to avoid destructive guesses. (#25496) Thanks @Ramsbaby.
+
Slack/app_mention race dedupe: when app_mention dispatch wins while same-tsmessage prepare is still in-flight, suppress the later message dispatch so near-simultaneous Slack deliveries do not produce duplicate replies; keep single-retry behavior and add regression coverage for both dropped and successful message-prepare outcomes. (#37033) Thanks @Takhoffman.
+
Gateway/chat streaming tool-boundary text retention: merge assistant delta segments into per-run chat buffers so pre-tool text is preserved in live chat deltas/finals when providers emit post-tool assistant segments as non-prefix snapshots. (#36957) Thanks @Datyedyeguy.
+
TUI/model indicator freshness: prevent stale session snapshots from overwriting freshly patched model selection (and reset per-session freshness when switching session keys) so /model updates reflect immediately instead of lagging by one or more commands. (#21255) Thanks @kowza.
+
TUI/final-error rendering fallback: when a chat final event has no renderable assistant content but includes envelope errorMessage, render the formatted error text instead of collapsing to "(no output)", preserving actionable failure context in-session. (#14687) Thanks @Mquarmoc.
+
TUI/session-key alias event matching: treat chat events whose session keys are canonical aliases (for example agent::main vs main) as the same session while preserving cross-agent isolation, so assistant replies no longer disappear or surface in another terminal window due to strict key-form mismatch. (#33937) Thanks @yjh1412.
+
OpenAI Codex OAuth/login parity: keep openclaw models auth login --provider openai-codex on the built-in path even without provider plugins, preserve Pi-generated authorize URLs without local scope rewriting, and stop validating successful Codex sign-ins against the public OpenAI Responses API after callback. (#37558; follow-up to #36660 and #24720) Thanks @driesvints, @Skippy-Gunboat, and @obviyus.
+
Agents/config schema lookup: add gateway tool action config.schema.lookup so agents can inspect one config path at a time before edits without loading the full schema into prompt context. (#37266) Thanks @gumadeiras.
+
Onboarding/API key input hardening: strip non-Latin1 Unicode artifacts from normalized secret input (while preserving Latin-1 content and internal spaces) so malformed copied API keys cannot trigger HTTP header ByteString construction crashes; adds regression coverage for shared normalization and MiniMax auth header usage. (#24496) Thanks @fa6maalassaf.
+
Kimi Coding/Anthropic tools compatibility: normalize anthropic-messages tool payloads to OpenAI-style tools[].function + compatible tool_choice when targeting Kimi Coding endpoints, restoring tool-call workflows that regressed after v2026.3.2. (#37038) Thanks @mochimochimochi-hub.
+
Heartbeat/workspace-path guardrails: append explicit workspace HEARTBEAT.md path guidance (and docs/heartbeat.md avoidance) to heartbeat prompts so heartbeat runs target workspace checklists reliably across packaged install layouts. (#37037) Thanks @stofancy.
+
Subagents/kill-complete announce race: when a late subagent-complete lifecycle event arrives after an earlier kill marker, clear stale kill suppression/cleanup flags and re-run announce cleanup so finished runs no longer get silently swallowed. (#37024) Thanks @cmfinlan.
+
Agents/tool-result cleanup timeout hardening: on embedded runner teardown idle timeouts, clear pending tool-call state without persisting synthetic missing tool result entries, preventing timeout cleanups from poisoning follow-up turns; adds regression coverage for timeout clear-vs-flush behavior. (#37081) Thanks @Coyote-Den.
+
Agents/openai-completions stream timeout hardening: ensure runtime undici global dispatchers use extended streaming body/header timeouts (including env-proxy dispatcher mode) before embedded runs, reducing forced mid-stream terminated failures on long generations; adds regression coverage for dispatcher selection and idempotent reconfiguration. (#9708) Thanks @scottchguard.
+
Agents/fallback cooldown probe execution: thread explicit rate-limit cooldown probe intent from model fallback into embedded runner auth-profile selection so same-provider fallback attempts can actually run when all profiles are cooldowned for rate_limit (instead of failing pre-run as No available auth profile), while preserving default cooldown skip behavior and adding regression tests at both fallback and runner layers. (#13623) Thanks @asfura.
+
Cron/OpenAI Codex OAuth refresh hardening: when openai-codex token refresh fails specifically on account-id extraction, reuse the cached access token instead of failing the run immediately, with regression coverage to keep non-Codex and unrelated refresh failures unchanged. (#36604) Thanks @laulopezreal.
+
TUI/session isolation for /new: make /new allocate a unique tui- session key instead of resetting the shared agent session, so multiple TUI clients on the same agent stop receiving each other’s replies; also sanitize /new and /reset failure text before rendering in-terminal. Landed from contributor PR #39238 by @widingmarcus-cyber. Thanks @widingmarcus-cyber.
+
Synology Chat/rate-limit env parsing: honor SYNOLOGY_RATE_LIMIT=0 as an explicit value while still falling back to the default limit for malformed env values instead of partially parsing them. Landed from contributor PR #39197 by @scoootscooob. Thanks @scoootscooob.
+
Voice-call/OpenAI Realtime STT config defaults: honor explicit vadThreshold: 0 and silenceDurationMs: 0 instead of silently replacing them with defaults. Landed from contributor PR #39196 by @scoootscooob. Thanks @scoootscooob.
+
Voice-call/OpenAI TTS speed config: honor explicit speed: 0 instead of silently replacing it with the default speed. Landed from contributor PR #39318 by @ql-wade. Thanks @ql-wade.
+
launchd/runtime PID parsing: reject pid <= 0 from launchctl print so the daemon state parser no longer treats kernel/non-running sentinel values as real process IDs. Landed from contributor PR #39281 by @mvanhorn. Thanks @mvanhorn.
+
Cron/file permission hardening: enforce owner-only (0600) cron store/backup/run-log files and harden cron store + run-log directories to 0700, including pre-existing directories from older installs. (#36078) Thanks @aerelune.
+
Gateway/remote WS break-glass hostname support: honor OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 for ws:// hostname URLs (not only private IP literals) across onboarding validation and runtime gateway connection checks, while still rejecting public IP literals and non-unicast IPv6 endpoints. (#36930) Thanks @manju-rn.
+
Routing/binding lookup scalability: pre-index route bindings by channel/account and avoid full binding-list rescans on channel-account cache rollover, preventing multi-second resolveAgentRoute stalls in large binding configurations. (#36915) Thanks @songchenghao.
+
Browser/session cleanup: track browser tabs opened by session-scoped browser tool runs and close tracked tabs during sessions.reset/sessions.delete runtime cleanup, preventing orphaned tabs and unbounded browser memory growth after session teardown. (#36666) Thanks @Harnoor6693.
+
Plugin/hook install rollback hardening: stage installs under the canonical install base, validate and run dependency installs before publish, and restore updates by rename instead of deleting the target path, reducing partial-replace and symlink-rebind risk during install failures.
+
Slack/local file upload allowlist parity: propagate mediaLocalRoots through the Slack send action pipeline so workspace-rooted attachments pass assertLocalMediaAllowed checks while non-allowlisted paths remain blocked. (synthesis: #36656; overlap considered from #36516, #36496, #36493, #36484, #32648, #30888) Thanks @2233admin.
+
Agents/compaction safeguard pre-check: skip embedded compaction before entering the Pi SDK when a session has no real conversation messages, avoiding unnecessary LLM API calls on idle sessions. (#36451) thanks @Sid-Qin.
+
Config/schema cache key stability: build merged schema cache keys with incremental hashing to avoid large single-string serialization and prevent RangeError: Invalid string length on high-cardinality plugin/channel metadata. (#36603) Thanks @powermaster888.
+
iMessage/cron completion announces: strip leaked inline reply tags (for example [[reply_to:6100]]) from user-visible completion text so announcement deliveries do not expose threading metadata. (#24600) Thanks @vincentkoc.
+
Control UI/iMessage duplicate reply routing: keep internal webchat turns on dispatcher delivery (instead of origin-channel reroute) so Control UI chats do not duplicate replies into iMessage, while preserving webchat-provider relayed routing for external surfaces. Fixes #33483. Thanks @alicexmolt.
+
Sessions/daily reset transcript archival: archive prior transcript files during stale-session scheduled/daily resets by capturing the previous session entry before rollover, preventing orphaned transcript files on disk. (#35493) Thanks @byungsker.
+
Feishu/group slash command detection: normalize group mention wrappers before command-authorization probing so mention-prefixed commands (for example @Bot/model and @Bot /reset) are recognized as gateway commands instead of being forwarded to the agent. (#35994) Thanks @liuxiaopai-ai.
+
Control UI/auth token separation: keep the shared gateway token in browser auth validation while reserving cached device tokens for signed device payloads, preventing false device token mismatch disconnects after restart/rotation. Landed from contributor PR #37382 by @FradSer. Thanks @FradSer.
+
Gateway/browser auth reconnect hardening: stop counting missing token/password submissions as auth rate-limit failures, and stop auto-reconnecting Control UI clients on non-recoverable auth errors so misconfigured browser tabs no longer lock out healthy sessions. Landed from contributor PR #38725 by @ademczuk. Thanks @ademczuk.
+
Gateway/service token drift repair: stop persisting shared auth tokens into installed gateway service units, flag stale embedded service tokens for reinstall, and treat tokenless service env as canonical so token rotation/reboot flows stay aligned with config/env resolution. Landed from contributor PR #28428 by @l0cka. Thanks @l0cka.
+
Control UI/agents-page selection: keep the edited agent selected after saving agent config changes and reloading the agents list, so /agents no longer snaps back to the default agent. Landed from contributor PR #39301 by @MumuTW. Thanks @MumuTW.
+
Gateway/auth follow-up hardening: preserve systemd EnvironmentFile= precedence/source provenance in daemon audits and doctor repairs, block shared-password override flows from piggybacking cached device tokens, and fail closed when config-first gateway SecretRefs cannot resolve. Follow-up to #39241.
+
Agents/context pruning: guard assistant thinking/text char estimation against malformed blocks (missing thinking/text strings or null entries) so pruning no longer crashes with malformed provider content. (openclaw#35146) thanks @Sid-Qin.
+
Agents/transcript policy: set preserveSignatures to Anthropic-only handling in resolveTranscriptPolicy so Anthropic thinking signatures are preserved while non-Anthropic providers remain unchanged. (#32813) thanks @Sid-Qin.
+
Agents/schema cleaning: detect Venice + Grok model IDs as xAI-proxied targets so unsupported JSON Schema keywords are stripped before requests, preventing Venice/Grok Invalid arguments failures. (openclaw#35355) thanks @Sid-Qin.
+
Skills/native command deduplication: centralize skill command dedupe by canonical skillName in listSkillCommandsForAgents so duplicate suffixed variants (for example _2) are no longer surfaced across interfaces outside Discord. (#27521) thanks @shivama205.
+
Agents/xAI tool-call argument decoding: decode HTML-entity encoded xAI/Grok tool-call argument values (&, ", <, >, numeric entities) before tool execution so commands with shell operators and quotes no longer fail with parse errors. (#35276) Thanks @Sid-Qin.
+
Linux/WSL2 daemon install hardening: add regression coverage for WSL environment detection, WSL-specific systemd guidance, and systemctl --user is-enabled failure paths so WSL2/headless onboarding keeps treating bus-unavailable probes as non-fatal while preserving real permission errors. Related: #36495. Thanks @vincentkoc.
+
Linux/systemd status and degraded-session handling: treat degraded-but-reachable systemctl --user status results as available, preserve early errors for truly unavailable user-bus cases, and report externally managed running services as running instead of not installed. Thanks @vincentkoc.
+
Agents/thinking-tag promotion hardening: guard promoteThinkingTagsToBlocks against malformed assistant content entries (null/undefined) before block.type reads so malformed provider payloads no longer crash session processing while preserving pass-through behavior. (#35143) thanks @Sid-Qin.
+
Gateway/Control UI version reporting: align runtime and browser client version metadata to avoid dev placeholders, wait for bootstrap version before first UI websocket connect, and only forward bootstrap serverVersion to same-origin gateway targets to prevent cross-target version leakage. (from #35230, #30928, #33928) Thanks @Sid-Qin, @joelnishanth, and @MoerAI.
+
Control UI/markdown parser crash fallback: catch marked.parse() failures and fall back to escaped plain-text rendering so malformed recursive markdown no longer crashes Control UI session rendering on load. (#36445) Thanks @BinHPdev.
+
Control UI/markdown fallback regression coverage: add explicit regression assertions for parser-error fallback behavior so malformed markdown no longer risks reintroducing hard-crash rendering paths in future markdown/parser upgrades. (#36445) Thanks @BinHPdev.
+
Web UI/config form: treat additionalProperties: true object schemas as editable map entries instead of unsupported fields so Accounts-style maps stay editable in form mode. (#35380, supersedes #32072) Thanks @stakeswky and @liuxiaopai-ai.
+
Feishu/streaming card delivery synthesis: unify snapshot and delta streaming merge semantics, apply overlap-aware final merge, suppress duplicate final text delivery (including text+media final packets), prefer topic-thread message.reply routing when a reply target exists, and tune card print cadence to avoid duplicate incremental rendering. (from #33245, #32896, #33840) Thanks @rexl2018, @kcinzgg, and @aerelune.
+
Feishu/group mention detection: carry startup-probed bot display names through monitor dispatch so requireMention checks compare against current bot identity instead of stale config names, fixing missed @bot handling in groups while preserving multi-bot false-positive guards. (#36317, #34271) Thanks @liuxiaopai-ai.
+
Security/dependency audit: patch transitive Hono vulnerabilities by pinning hono to 4.12.5 and @hono/node-server to 1.19.10 in production resolution paths. Thanks @shakkernerd.
+
Security/dependency audit: bump tar to 7.5.10 (from 7.5.9) to address the high-severity hardlink path traversal advisory (GHSA-qffp-2rhf-9h96). Thanks @shakkernerd.
+
Cron/announce delivery robustness: bypass pending-descendant announce guards for cron completion sends, ensure named-agent announce routes have outbound session entries, and fall back to direct delivery only when an announce send was actually attempted and failed. (from #35185, #32443, #34987) Thanks @Sid-Qin, @scoootscooob, and @bmendonca3.
+
Cron/announce best-effort fallback: run direct outbound fallback after attempted announce failures even when delivery is configured as best-effort, so Telegram cron sends are not left as attempted-but-undelivered after cron announce delivery failed warnings.
+
Auto-reply/system events: restore runtime system events to the message timeline (System: lines), preserve think-hint parsing with prepended events, and carry events into deferred followup/collect/steer-backlog prompts to keep cache behavior stable without dropping queued metadata. (#34794) Thanks @anisoptera.
+
Security/audit account handling: avoid prototype-chain account IDs in audit validation by using own-property checks for accounts. (#34982) Thanks @HOYALIM.
+
Cron/restart catch-up semantics: replay interrupted recurring jobs and missed immediate cron slots on startup without replaying interrupted one-shot jobs, with guarded missed-slot probing to avoid malformed-schedule startup aborts and duplicate-trigger drift after restart. (from #34466, #34896, #34625, #33206) Thanks @dunamismax, @dsantoreis, @Octane0411, and @Sid-Qin.
+
Venice/provider onboarding hardening: align per-model Venice completion-token limits with discovery metadata, clamp untrusted discovery values to safe bounds, sync the static Venice fallback catalog with current live model metadata, and disable tool wiring for Venice models that do not support function calling so default Venice setups no longer fail with max_completion_tokens or unsupported-tools 400s. Fixes #38168. Thanks @Sid-Qin, @powermaster888 and @vincentkoc.
+
Agents/session usage tracking: preserve accumulated usage metadata on embedded Pi runner error exits so failed turns still update session totalTokens from real usage instead of stale prior values. (#34275) thanks @RealKai42.
+
Slack/reaction thread context routing: carry Slack native DM channel IDs through inbound context and threading tool resolution so reaction targets resolve consistently for DM To=user:* sessions (including toolContext.currentChannelId fallback behavior). (from #34831; overlaps #34440, #34502, #34483, #32754) Thanks @dunamismax.
+
Subagents/announce completion scoping: scope nested direct-child completion aggregation to the current requester run window, harden frozen completion capture for deterministic descendant synthesis, and route completion announce delivery through parent-agent announce turns with provenance-aware internal events. (#35080) Thanks @tyler6204.
+
Nodes/system.run approval hardening: use explicit argv-mutation signaling when regenerating prepared rawCommand, and cover the system.run.prepare -> system.run handoff so direct PATH-based nodes.run commands no longer fail with rawCommand does not match command. (#33137) thanks @Sid-Qin.
+
Models/custom provider headers: propagate models.providers..headers across inline, fallback, and registry-found model resolution so header-authenticated proxies consistently receive configured request headers. (#27490) thanks @Sid-Qin.
+
Ollama/remote provider auth fallback: synthesize a local runtime auth key for explicitly configured models.providers.ollama entries that omit apiKey, so remote Ollama endpoints run without requiring manual dummy-key setup while preserving env/profile/config key precedence and missing-config failures. (#11283) Thanks @cpreecs.
+
Ollama/custom provider headers: forward resolved model headers into native Ollama stream requests so header-authenticated Ollama proxies receive configured request headers. (#24337) thanks @echoVic.
+
Ollama/compaction and summarization: register custom api: "ollama" handling for compaction, branch-style internal summarization, and TTS text summarization on current main, so native Ollama models no longer fail with No API provider registered for api: ollama outside the main run loop. Thanks @JaviLib.
+
Daemon/systemd install robustness: treat systemctl --user is-enabled exit-code-4 not-found responses as not-enabled by combining stderr/stdout detail parsing, so Ubuntu fresh installs no longer fail with systemctl is-enabled unavailable. (#33634) Thanks @Yuandiaodiaodiao.
+
Slack/system-event session routing: resolve reaction/member/pin/interaction system-event session keys through channel/account bindings (with sender-aware DM routing) so inbound Slack events target the correct agent session in multi-account setups instead of defaulting to agent:main. (#34045) Thanks @paulomcg, @daht-mad and @vincentkoc.
+
Slack/native streaming markdown conversion: stop pre-normalizing text passed to Slack native markdown_text in streaming start/append/stop paths to prevent Markdown style corruption from double conversion. (#34931)
+
Gateway/HTTP tools invoke media compatibility: preserve raw media payload access for direct /tools/invoke clients by allowing media nodes invoke commands only in HTTP tool context, while keeping agent-context media invoke blocking to prevent base64 prompt bloat. (#34365) Thanks @obviyus.
+
Security/archive ZIP hardening: extract ZIP entries via same-directory temp files plus atomic rename, then re-open and reject post-rename hardlink alias races outside the destination root.
+
Agents/Nodes media outputs: add dedicated photos_latest action handling, block media-returning nodes invoke commands, keep metadata-only camera.list invoke allowed, and normalize empty photos_latest results to a consistent response shape to prevent base64 context bloat. (#34332) Thanks @obviyus.
+
TUI/session-key canonicalization: normalize openclaw tui --session values to lowercase so uppercase session names no longer drop real-time streaming updates due to gateway/TUI key mismatches. (#33866, #34013) thanks @lynnzc.
+
iMessage/echo loop hardening: strip leaked assistant-internal scaffolding from outbound iMessage replies, drop reflected assistant-content messages before they re-enter inbound processing, extend echo-cache text retention for delayed reflections, and suppress repeated loop traffic before it amplifies into queue overflow. (#33295) Thanks @joelnishanth.
+
Skills/workspace boundary hardening: reject workspace and extra-dir skill roots or SKILL.md files whose realpath escapes the configured source root, and skip syncing those escaped skills into sandbox workspaces.
+
Outbound/send config threading: pass resolved SecretRef config through outbound adapters and helper send paths so send flows do not reload unresolved runtime config. (#33987) Thanks @joshavant.
+
gateway: harden shared auth resolution across systemd, discord, and node host (#39241) Thanks @joshavant.
+
Secrets/models.json persistence hardening: keep SecretRef-managed api keys + headers from persisting in generated models.json, expand audit/apply coverage, and harden marker handling/serialization. (#38955) Thanks @joshavant.
+
Sessions/subagent attachments: remove attachments[].content.maxLength from sessions_spawn schema to avoid llama.cpp GBNF repetition overflow, and preflight UTF-8 byte size before buffer allocation while keeping runtime file-size enforcement unchanged. (#33648) Thanks @anisoptera.
+
Runtime/tool-state stability: recover from dangling Anthropic tool_use after compaction, serialize long-running Discord handler runs without blocking new inbound events, and prevent stale busy snapshots from suppressing stuck-channel recovery. (from #33630, #33583) Thanks @kevinWangSheng and @theotarr.
+
ACP/Discord startup hardening: clean up stuck ACP worker children on gateway restart, unbind stale ACP thread bindings during Discord startup reconciliation, and add per-thread listener watchdog timeouts so wedged turns cannot block later messages. (#33699) Thanks @dutifulbob.
+
Extensions/media local-root propagation: consistently forward mediaLocalRoots through extension sendMedia adapters (Google Chat, Slack, iMessage, Signal, WhatsApp), preserving non-local media behavior while restoring local attachment resolution from configured roots. Synthesis of #33581, #33545, #33540, #33536, #33528. Thanks @bmendonca3.
+
Gateway/plugin HTTP auth hardening: require gateway auth when any overlapping matched route needs it, block mixed-auth fallthrough at dispatch, and reject mixed-auth exact/prefix route overlaps during plugin registration.
+
Feishu/video media send contract: keep mp4-like outbound payloads on msg_type: "media" (including reply and reply-in-thread paths) so videos render as media instead of degrading to file-link behavior, while preserving existing non-video file subtype handling. (from #33720, #33808, #33678) Thanks @polooooo, @dingjianrui, and @kevinWangSheng.
+
Gateway/security default response headers: add Permissions-Policy: camera=(), microphone=(), geolocation=() to baseline gateway HTTP security headers for all responses. (#30186) thanks @habakan.
+
Plugins/startup loading: lazily initialize plugin runtime, split startup-critical plugin SDK imports into openclaw/plugin-sdk/core and openclaw/plugin-sdk/telegram, and preserve api.runtime reflection semantics for plugin compatibility. (#28620) thanks @hmemcpy.
+
Plugins/startup performance: reduce bursty plugin discovery/manifest overhead with short in-process caches, skip importing bundled memory plugins that are disabled by slot selection, and speed legacy root openclaw/plugin-sdk compatibility via runtime root-alias routing while preserving backward compatibility. Thanks @gumadeiras.
+
Build/lazy runtime boundaries: replace ineffective dynamic import sites with dedicated lazy runtime boundaries across Slack slash handling, Telegram audit, CLI send deps, memory fallback, and outbound delivery paths while preserving behavior. (#33690) thanks @gumadeiras.
+
Gateway/password CLI hardening: add openclaw gateway run --password-file, warn when inline --password is used because it can leak via process listings, and document env/file-backed password input as the preferred startup path. Fixes #27948. Thanks @vibewrk and @vincentkoc.
+
Config/heartbeat legacy-path handling: auto-migrate top-level heartbeat into agents.defaults.heartbeat (with merge semantics that preserve explicit defaults), and keep startup failures on non-migratable legacy entries in the detailed invalid-config path instead of generic migration-failed errors. (#32706) thanks @xiwan.
+
Plugins/SDK subpath parity: expand plugin SDK subpaths across bundled channels/extensions (Discord, Slack, Signal, iMessage, WhatsApp, LINE, and bundled companion plugins), with build/export/type/runtime wiring so scoped imports resolve consistently in source and dist while preserving compatibility. (#33737) thanks @gumadeiras.
+
Google/Gemini Flash model selection: switch built-in gemini-flash defaults and docs/examples from the nonexistent google/gemini-3.1-flash-preview ID to the working google/gemini-3-flash-preview, while normalizing legacy OpenClaw config that still uses the old Flash 3.1 alias.
+
Plugins/bundled scoped-import migration: migrate bundled plugins from monolithic openclaw/plugin-sdk imports to scoped subpaths (or openclaw/plugin-sdk/core) across registration and startup-sensitive runtime files, add CI/release guardrails to prevent regressions, and keep root openclaw/plugin-sdk support for external/community plugins. Thanks @gumadeiras.
Routing/legacy session route inheritance: preserve external route metadata inheritance for legacy channel session keys (agent::: and ...:thread:) so chat.send does not incorrectly fall back to webchat when valid delivery context exists. Follow-up to #33786.
+
Routing/legacy route guard tightening: require legacy session-key channel hints to match the saved delivery channel before inheriting external routing metadata, preventing custom namespaced keys like agent::work: from inheriting stale non-webchat routes.
+
Gateway/internal client routing continuity: prevent webchat/TUI/UI turns from inheriting stale external reply routes by requiring explicit deliver: true for external delivery, keeping main-session external inheritance scoped to non-Webchat/UI clients, and honoring configured session.mainKey when identifying main-session continuity. (from #35321, #34635, #35356) Thanks @alexyyyander and @Octane0411.
+
Security/auth labels: remove token and API-key snippets from user-facing auth status labels so /status and /models do not expose credential fragments. (#33262) thanks @cu1ch3n.
+
Models/MiniMax portal vision routing: add MiniMax-VL-01 to the minimax-portal provider, route portal image understanding through the MiniMax VLM endpoint, and align media auto-selection plus Telegram sticker description with the shared portal image provider path. (#33953) Thanks @tars90percent.
+
Auth/credential semantics: align profile eligibility + probe diagnostics with SecretRef/expiry rules and harden browser download atomic writes. (#33733) thanks @joshavant.
+
Security/audit denyCommands guidance: suggest likely exact node command IDs for unknown gateway.nodes.denyCommands entries so ineffective denylist entries are easier to correct. (#29713) thanks @liquidhorizon88-bot.
+
Agents/overload failover handling: classify overloaded provider failures separately from rate limits/status timeouts, add short overload backoff before retry/failover, record overloaded prompt/assistant failures as transient auth-profile cooldowns (with probeable same-provider fallback) instead of treating them like persistent auth/billing failures, and keep one-shot cron retry classification aligned so overloaded fallback summaries still count as transient retries.
+
Docs/security hardening guidance: document Docker DOCKER-USER + UFW policy and add cross-linking from Docker install docs for VPS/public-host setups. (#27613) thanks @dorukardahan.
+
Docs/security threat-model links: replace relative .md links with Mintlify-compatible root-relative routes in security docs to prevent broken internal navigation. (#27698) thanks @clawdoo.
+
Plugins/Update integrity drift: avoid false integrity drift prompts when updating npm-installed plugins from unpinned specs, while keeping drift checks for exact pinned versions. (#37179) Thanks @vincentkoc.
+
iOS/Voice timing safety: guard system speech start/finish callbacks to the active utterance to avoid misattributed start events during rapid stop/restart cycles. (#33304) thanks @mbelinky; original implementation direction by @ngutman.
+
Gateway/chat.send command scopes: require operator.admin for persistent /config set|unset writes routed through gateway chat clients while keeping /config show available to normal write-scoped operator clients, preserving messaging-channel config command behavior without widening RPC write scope into admin config mutation. Thanks @tdjackey for reporting.
+
iOS/Talk incremental speech pacing: allow long punctuation-free assistant chunks to start speaking at safe whitespace boundaries so voice responses begin sooner instead of waiting for terminal punctuation. (#33305) thanks @mbelinky; original implementation by @ngutman.
+
iOS/Watch reply reliability: make watch session activation waiters robust under concurrent requests so status/send calls no longer hang intermittently, and align delegate callbacks with Swift 6 actor safety. (#33306) thanks @mbelinky; original implementation by @Rocuts.
+
Docs/tool-loop detection config keys: align docs/tools/loop-detection.md examples and field names with the current tools.loopDetection schema to prevent copy-paste validation failures from outdated keys. (#33182) Thanks @Mylszd.
+
Gateway/session agent discovery: include disk-scanned agent IDs in listConfiguredAgentIds even when agents.list is configured, so disk-only/ACP agent sessions remain visible in gateway session aggregation and listings. (#32831) thanks @Sid-Qin.
+
Discord/inbound debouncer: skip bot-own MESSAGE_CREATE events before they reach the debounce queue to avoid self-triggered slowdowns in busy servers. Thanks @thewilloftheshadow.
+
Discord/Agent-scoped media roots: pass mediaLocalRoots through Discord monitor reply delivery (message + component interaction paths) so local media attachments honor per-agent workspace roots instead of falling back to default global roots. Thanks @thewilloftheshadow.
+
Discord/slash command handling: intercept text-based slash commands in channels, register plugin commands as native, and send fallback acknowledgments for empty slash runs so interactions do not hang. Thanks @thewilloftheshadow.
+
Discord/thread session lifecycle: reset thread-scoped sessions when a thread is archived so reopening a thread starts fresh without deleting transcript history. Thanks @thewilloftheshadow.
+
Discord/presence defaults: send an online presence update on ready when no custom presence is configured so bots no longer appear offline by default. Thanks @thewilloftheshadow.
+
Discord/typing cleanup: stop typing indicators after silent/NO_REPLY runs by marking the run complete before dispatch idle cleanup. Thanks @thewilloftheshadow.
+
ACP/sandbox spawn parity: block /acp spawn from sandboxed requester sessions with the same host-runtime guard already enforced for sessions_spawn({ runtime: "acp" }), preserving non-sandbox ACP flows while closing the command-path policy gap. Thanks @patte.
+
Discord/config SecretRef typing: align Discord account token config typing with SecretInput so SecretRef tokens typecheck. (#32490) Thanks @scoootscooob.
+
Discord/voice messages: request upload slots with JSON fetch calls so voice message uploads no longer fail with content-type errors. Thanks @thewilloftheshadow.
+
Discord/voice decoder fallback: drop the native Opus dependency and use opusscript for voice decoding to avoid native-opus installs. Thanks @thewilloftheshadow.
+
Discord/auto presence health signal: add runtime availability-driven presence updates plus connected-state reporting to improve health monitoring and operator visibility. (#33277) Thanks @thewilloftheshadow.
+
HEIC image inputs: accept HEIC/HEIF input_image sources in Gateway HTTP APIs, normalize them to JPEG before provider delivery, and document the expanded default MIME allowlist. Thanks @vincentkoc.
+
Gateway/HEIC input follow-up: keep non-HEIC input_image MIME handling unchanged, make HEIC tests hermetic, and enforce chat-completions maxTotalImageBytes against post-normalization image payload size. Thanks @vincentkoc.
+
Telegram/draft-stream boundary stability: materialize DM draft previews at assistant-message/tool boundaries, serialize lane-boundary callbacks before final delivery, and scope preview cleanup to the active preview so multi-step Telegram streams no longer lose, overwrite, or leave stale preview bubbles. (#33842) Thanks @ngutman.
+
Telegram/DM draft finalization reliability: require verified final-text draft emission before treating preview finalization as delivered, and fall back to normal payload send when final draft delivery is not confirmed (preventing missing final responses and preserving media/button delivery). (#32118) Thanks @OpenCils.
+
Telegram/DM draft final delivery: materialize text-only sendMessageDraft previews into one permanent final message and skip duplicate final payload sends, while preserving fallback behavior when materialization fails. (#34318) Thanks @Brotherinlaw-13.
+
Telegram/DM draft duplicate display: clear stale DM draft previews after materializing the real final message, including threadless fallback when DM topic lookup fails, so partial streaming no longer briefly shows duplicate replies. (#36746) Thanks @joelnishanth.
+
Telegram/draft preview boundary + silent-token reliability: stabilize answer-lane message boundaries across late-partial/message-start races, preserve/reset finalized preview state at the correct boundaries, and suppress NO_REPLY lead-fragment leaks without broad heartbeat-prefix false positives. (#33169) Thanks @obviyus.
+
Telegram/native commands commands.allowFrom precedence: make native Telegram commands honor commands.allowFrom as the command-specific authorization source, including group chats, instead of falling back to channel sender allowlists. (#28216) Thanks @toolsbybuddy and @vincentkoc.
+
Telegram/groupAllowFrom sender-ID validation: restore sender-only runtime validation so negative chat/group IDs remain invalid entries instead of appearing accepted while still being unable to authorize group access. (#37134) Thanks @qiuyuemartin-max and @vincentkoc.
+
Telegram/native group command auth: authorize native commands in groups and forum topics against groupAllowFrom and per-group/topic sender overrides, while keeping auth rejection replies in the originating topic thread. (#39267) Thanks @edwluo.
+
Telegram/named-account DMs: restore non-default-account DM routing when a named Telegram account falls back to the default agent by keeping groups fail-closed but deriving a per-account session key for DMs, including identity-link canonicalization and regression coverage for account isolation. (from #32426; fixes #32351) Thanks @chengzhichao-xydt.
+
Discord/audit wildcard warnings: ignore "\*" wildcard keys when counting unresolved guild channels so doctor/status no longer warns on allow-all configs. (#33125) Thanks @thewilloftheshadow.
+
Discord/channel resolution: default bare numeric recipients to channels, harden allowlist numeric ID handling with safe fallbacks, and avoid inbound WS heartbeat stalls. (#33142) Thanks @thewilloftheshadow.
+
Discord/chunk delivery reliability: preserve chunk ordering when using a REST client and retry chunk sends on 429/5xx using account retry settings. (#33226) Thanks @thewilloftheshadow.
Discord/media SSRF allowlist: allow Discord CDN hostnames (including wildcard domains) in inbound media SSRF policy to prevent proxy/VPN fake-ip blocks. (#33275) Thanks @thewilloftheshadow.
+
Telegram/device pairing notifications: auto-arm one-shot notify on /pair qr, auto-ping on new pairing requests, and add manual fallback via /pair approve latest if the ping does not arrive. (#33299) thanks @mbelinky.
+
Exec heartbeat routing: scope exec-triggered heartbeat wakes to agent session keys so unrelated agents are no longer awakened by exec events, while preserving legacy unscoped behavior for non-canonical session keys. (#32724) thanks @altaywtf
+
macOS/Tailscale remote gateway discovery: add a Tailscale Serve fallback peer probe path (wss://.ts.net) when Bonjour and wide-area DNS-SD discovery return no gateways, and refresh both discovery paths from macOS onboarding. (#32860) Thanks @ngutman.
+
iOS/Gateway keychain hardening: move gateway metadata and TLS fingerprints to device keychain storage with safer migration behavior and rollback-safe writes to reduce credential loss risk during upgrades. (#33029) thanks @mbelinky.
+
iOS/Concurrency stability: replace risky shared-state access in camera and gateway connection paths with lock-protected access patterns to reduce crash risk under load. (#33241) thanks @mbelinky.
+
iOS/Security guardrails: limit production API-key sourcing to app config and make deep-link confirmation prompts safer by coalescing queued requests instead of silently dropping them. (#33031) thanks @mbelinky.
+
iOS/TTS playback fallback: keep voice playback resilient by switching from PCM to MP3 when provider format support is unavailable, while avoiding sticky fallback on generic local playback errors. (#33032) thanks @mbelinky.
+
Plugin outbound/text-only adapter compatibility: allow direct-delivery channel plugins that only implement sendText (without sendMedia) to remain outbound-capable, gracefully fall back to text delivery for media payloads when sendMedia is absent, and fail explicitly for media-only payloads with no text fallback. (#32788) thanks @liuxiaopai-ai.
+
Telegram/multi-account default routing clarity: warn only for ambiguous (2+) account setups without an explicit default, add openclaw doctor warnings for missing/invalid multi-account defaults across channels, and document explicit-default guidance for channel routing and Telegram config. (#32544) thanks @Sid-Qin.
+
Telegram/plugin outbound hook parity: run message_sending + message_sent in Telegram reply delivery, include reply-path hook metadata (mediaUrls, threadId), and report message_sent.success=false when hooks blank text and no outbound message is delivered. (#32649) Thanks @KimGLee.
+
CLI/Coding-agent reliability: switch default claude-cli non-interactive args to --permission-mode bypassPermissions, auto-normalize legacy --dangerously-skip-permissions backend overrides to the modern permission-mode form, align coding-agent + live-test docs with the non-PTY Claude path, and emit session system-event heartbeat notices when CLI watchdog no-output timeouts terminate runs. (#28610, #31149, #34055). Thanks @niceysam, @cryptomaltese and @vincentkoc.
+
Gateway/OpenAI chat completions: parse active-turn image_url content parts (including parameterized data URIs and guarded URL sources), forward them as multimodal images, accept image-only user turns, enforce per-request image-part/byte budgets, default URL-based image fetches to disabled unless explicitly enabled by config, and redact image base64 data in cache-trace/provider payload diagnostics. (#17685) Thanks @vincentkoc
+
ACP/ACPX session bootstrap: retry with sessions new when sessions ensure returns no session identifiers so ACP spawns avoid NO_SESSION/ACP_TURN_FAILED failures on affected agents. (#28786, #31338, #34055). Thanks @Sid-Qin and @vincentkoc.
+
ACP/sessions_spawn parent stream visibility: add streamTo: "parent" for runtime: "acp" to forward initial child-run progress/no-output/completion updates back into the requester session as system events (instead of direct child delivery), and emit a tail-able session-scoped relay log (.acp-stream.jsonl, returned as streamLogPath when available), improving orchestrator visibility for blocked or long-running harness turns. (#34310, #29909; reopened from #34055). Thanks @vincentkoc.
+
Agents/bootstrap truncation warning handling: unify bootstrap budget/truncation analysis across embedded + CLI runtime, /context, and openclaw doctor; add agents.defaults.bootstrapPromptTruncationWarning (off|once|always, default once) and persist warning-signature metadata so truncation warnings are consistent and deduped across turns. (#32769) Thanks @gumadeiras.
+
Agents/Skills runtime loading: propagate run config into embedded attempt and compaction skill-entry loading so explicitly enabled bundled companion skills are discovered consistently when skill snapshots do not already provide resolved entries. Thanks @gumadeiras.
+
Agents/Session startup date grounding: substitute YYYY-MM-DD placeholders in startup/post-compaction AGENTS context and append runtime current-time lines for /new and /reset prompts so daily-memory references resolve correctly. (#32381) Thanks @chengzhichao-xydt.
+
Agents/Compaction template heading alignment: update AGENTS template section names to Session Startup/Red Lines and keep legacy Every Session/Safety fallback extraction so post-compaction context remains intact across template versions. (#25098) thanks @echoVic.
+
Agents/Compaction continuity: expand staged-summary merge instructions to preserve active task status, batch progress, latest user request, and follow-up commitments so compaction handoffs retain in-flight work context. (#8903) thanks @joetomasone.
+
Agents/Compaction safeguard structure hardening: require exact fallback summary headings, sanitize untrusted compaction instruction text before prompt embedding, and keep structured sections when preserving all turns. (#25555) thanks @rodrigouroz.
+
Gateway/status self version reporting: make Gateway self version in openclaw status prefer runtime VERSION (while preserving explicit OPENCLAW_VERSION override), preventing stale post-upgrade app version output. (#32655) thanks @liuxiaopai-ai.
+
Memory/QMD index isolation: set QMD_CONFIG_DIR alongside XDG_CONFIG_HOME so QMD config state stays per-agent despite upstream XDG handling bugs, preventing cross-agent collection indexing and excess disk/CPU usage. (#27028) thanks @HenryLoenwind.
+
Memory/QMD collection safety: stop destructive collection rebinds when QMD collection list only reports names without path metadata, preventing memory search from dropping existing collections if re-add fails. (#36870) Thanks @Adnannnnnnna.
+
Memory/QMD duplicate-document recovery: detect UNIQUE constraint failed: documents.collection, documents.path update failures, rebuild managed collections once, and retry update so periodic QMD syncs recover instead of failing every run; includes regression coverage to avoid over-matching unrelated unique constraints. (#27649) Thanks @MiscMich.
+
Memory/local embedding initialization hardening: add regression coverage for transient initialization retry and mixed embedQuery + embedBatch concurrent startup to lock single-flight initialization behavior. (#15639) thanks @SubtleSpark.
+
CLI/Coding-agent reliability: switch default claude-cli non-interactive args to --permission-mode bypassPermissions, auto-normalize legacy --dangerously-skip-permissions backend overrides to the modern permission-mode form, align coding-agent + live-test docs with the non-PTY Claude path, and emit session system-event heartbeat notices when CLI watchdog no-output timeouts terminate runs. Related to #28261. Landed from contributor PRs #28610 and #31149. Thanks @niceysam, @cryptomaltese and @vincentkoc.
+
ACP/ACPX session bootstrap: retry with sessions new when sessions ensure returns no session identifiers so ACP spawns avoid NO_SESSION/ACP_TURN_FAILED failures on affected agents. Related to #28786. Landed from contributor PR #31338. Thanks @Sid-Qin and @vincentkoc.
LINE/media download synthesis: fix file-media download handling and M4A audio classification across overlapping LINE regressions. (from #26386, #27761, #27787, #29509, #29755, #29776, #29785, #32240) Thanks @kevinWangSheng, @loiie45e, @carrotRakko, @Sid-Qin, @codeafridi, and @bmendonca3.
+
LINE/context and routing synthesis: fix group/room peer routing and command-authorization context propagation, and keep processing later events in mixed-success webhook batches. (from #21955, #24475, #27035, #28286) Thanks @lailoo, @mcaxtr, @jervyclaw, @Glucksberg, and @Takhoffman.
+
LINE/status/config/webhook synthesis: fix status false positives from snapshot/config state and accept LINE webhook HEAD probes for compatibility. (from #10487, #25726, #27537, #27908, #31387) Thanks @BlueBirdBack, @stakeswky, @loiie45e, @puritysb, and @mcaxtr.
+
LINE cleanup/test follow-ups: fold cleanup/test learnings into the synthesis review path while keeping runtime changes focused on regression fixes. (from #17630, #17289) Thanks @Clawborn and @davidahmann.
+
Mattermost/interactive buttons: add interactive button send/callback support with directory-based channel/user target resolution, and harden callbacks via account-scoped HMAC verification plus sender-scoped DM routing. (#19957) thanks @tonydehnke.
+
Feishu/groupPolicy legacy alias compatibility: treat legacy groupPolicy: "allowall" as open in both schema parsing and runtime policy checks so intended open-group configs no longer silently drop group messages when groupAllowFrom is empty. (from #36358) Thanks @Sid-Qin.
+
Mattermost/plugin SDK import policy: replace remaining monolithic openclaw/plugin-sdk imports in Mattermost mention-gating paths/tests with scoped subpaths (openclaw/plugin-sdk/compat and openclaw/plugin-sdk/mattermost) so pnpm check passes lint:plugins:no-monolithic-plugin-sdk-entry-imports on baseline. (#36480) Thanks @Takhoffman.
+
Telegram/polls: add Telegram poll action support to channel action discovery and tool/CLI poll flows, with multi-account discoverability gated to accounts that can actually execute polls (sendMessage + poll). (#36547) thanks @gumadeiras.
+
Agents/failover cooldown classification: stop treating generic cooling down text as provider rate_limit so healthy models no longer show false global cooldown/rate-limit warnings while explicit model_cooldown markers still trigger failover. (#32972) thanks @stakeswky.
+
Agents/failover service-unavailable handling: stop treating bare proxy/CDN service unavailable errors as provider overload while keeping them retryable via the timeout/failover path, so transient outages no longer show false rate-limit warnings or block fallback. (#36646) thanks @jnMetaCode.
+
Plugins/HTTP route migration diagnostics: rewrite legacy api.registerHttpHandler(...) loader failures into actionable migration guidance so doctor/plugin diagnostics point operators to api.registerHttpRoute(...) or registerPluginHttpRoute(...). (#36794) Thanks @vincentkoc
+
Doctor/Heartbeat upgrade diagnostics: warn when heartbeat delivery is configured with an implicit directPolicy so upgrades pin direct/DM behavior explicitly instead of relying on the current default. (#36789) Thanks @vincentkoc.
+
Agents/current-time UTC anchor: append a machine-readable UTC suffix alongside local Current time: lines in shared cron-style prompt contexts so agents can compare UTC-stamped workspace timestamps without doing timezone math. (#32423) thanks @jriff.
+
Ollama/local model handling: preserve explicit lower contextWindow / maxTokens overrides during merge refresh, and keep native Ollama streamed replies from surfacing fallback thinking / reasoning text once real content starts streaming. (#39292) Thanks @vincentkoc.
+
TUI/webchat command-owner scope alignment: treat internal-channel gateway sessions with operator.admin as owner-authorized in command auth, restoring cron/gateway/connector tool access for affected TUI/webchat sessions while keeping external channels on identity-based owner checks. (from #35666, #35673, #35704) Thanks @Naylenv, @Octane0411, and @Sid-Qin.
+
Discord/inbound timeout isolation: separate inbound worker timeout tracking from listener timeout budgets so queued Discord replies are no longer dropped when listener watchdog windows expire mid-run. (#36602) Thanks @dutifulbob.
+
Memory/doctor SecretRef handling: treat SecretRef-backed memory-search API keys as configured, and fail embedding setup with explicit unresolved-secret errors instead of crashing. (#36835) Thanks @joshavant.
+
Memory/flush default prompt: ban timestamped variant filenames during default memory flush runs so durable notes stay in the canonical daily memory/YYYY-MM-DD.md file. (#34951) thanks @zerone0x.
+
Agents/reply delivery timing: flush embedded Pi block replies before waiting on compaction retries so already-generated assistant replies reach channels before compaction wait completes. (#35489) thanks @Sid-Qin.
+
Agents/gateway config guidance: stop exposing config.schema through the agent gateway tool, remove prompt/docs guidance that told agents to call it, and keep agents on config.get plus config.patch/config.apply for config changes. (#7382) thanks @kakuteki.
+
Provider/KiloCode: Keep duplicate models after malformed discovery rows, and strip legacy reasoning_effort when proxy reasoning injection is skipped. (#32352) Thanks @pandemicsyn and @vincentkoc.
+
Agents/failover: classify periodic provider limit exhaustion text (for example Weekly/Monthly Limit Exhausted) as rate_limit while keeping explicit 402 Payment Required variants in billing, so failover continues without misclassifying billing-wrapped quota errors. (#33813) thanks @zhouhe-xydt.
+
Mattermost/interactive button callbacks: allow external callback base URLs and stop requiring loopback-origin requests so button clicks work when Mattermost reaches the gateway over Tailscale, LAN, or a reverse proxy. (#37543) thanks @mukhtharcm.
+
Gateway/chat.send route inheritance: keep explicit external delivery for channel-scoped sessions while preventing shared-main and other channel-agnostic webchat sessions from inheriting stale external routes, so Control UI replies stay on webchat without breaking selected channel-target sessions. (#34669) Thanks @vincentkoc.
+
Telegram/Discord media upload caps: make outbound uploads honor channel mediaMaxMb config, raise Telegram's default media cap to 100MB, and remove MIME fallback limits that kept some Telegram uploads at 16MB. Thanks @vincentkoc.
+
Skills/nano-banana-pro resolution override: respect explicit --resolution values during image editing and only auto-detect output size from input images when the flag is omitted. (#36880) Thanks @shuofengzhang and @vincentkoc.
+
Skills/openai-image-gen CLI validation: validate --background and --style inputs early, normalize supported values, and warn when those flags are ignored for incompatible models. (#36762) Thanks @shuofengzhang and @vincentkoc.
+
Skills/openai-image-gen output formats: validate --output-format values early, normalize aliases like jpg -> jpeg, and warn when the flag is ignored for incompatible models. (#36648) Thanks @shuofengzhang and @vincentkoc.
+
ACP/skill env isolation: strip skill-injected API keys from ACP harness child-process environments so tools like Codex CLI keep their own auth flow instead of inheriting billed provider keys from active skills. (#36316) Thanks @taw0002 and @vincentkoc.
+
WhatsApp media upload caps: make outbound media sends and auto-replies honor channels.whatsapp.mediaMaxMb with per-account overrides so inbound and outbound limits use the same channel config. Thanks @vincentkoc.
+
Windows/Plugin install: when OpenClaw runs on Windows via Bun and npm-cli.js is not colocated with the runtime binary, fall back to npm.cmd/npx.cmd through the existing cmd.exe wrapper so openclaw plugins install no longer fails with spawn EINVAL. (#38056) Thanks @0xlin2023.
+
Telegram/send retry classification: retry grammY Network request ... failed after N attempts envelopes in send flows without reclassifying plain Network request ... failed! wrappers as transient, restoring the intended retry path while keeping broad send-context message matching tight. (#38056) Thanks @0xlin2023.
+
Gateway/probes: keep /health, /healthz, /ready, and /readyz reachable when the Control UI is mounted at /, preserve plugin-owned route precedence on those paths, and make /ready and /readyz report channel-backed readiness with startup grace plus 503 on disconnected managed channels, while /health and /healthz stay shallow liveness probes. (#18446) Thanks @vibecodooor, @mahsumaktas, and @vincentkoc.
+
Feishu/media downloads: drop invalid timeout fields from SDK method calls now that client-level httpTimeoutMs applies to requests. (#38267) Thanks @ant1eicher and @thewilloftheshadow.
+
PI embedded runner/Feishu docs: propagate sender identity into embedded attempts so Feishu doc auto-grant restores requester access for embedded-runner executions. (#32915) thanks @cszhouwei.
+
Agents/usage normalization: normalize missing or partial assistant usage snapshots before compaction accounting so openclaw agent --json no longer crashes when provider payloads omit totalTokens or related usage fields. (#34977) thanks @sp-hk2ldn.
+
Venice/default model refresh: switch the built-in Venice default to kimi-k2-5, update onboarding aliasing, and refresh Venice provider docs/recommendations to match the current private and anonymized catalog. (from #12964) Fixes #20156. Thanks @sabrinaaquino and @vincentkoc.
+
Agents/skill API write pacing: add a global prompt guardrail that treats skill-driven external API writes as rate-limited by default, so runners prefer batched writes, avoid tight request loops, and respect 429/Retry-After. Thanks @vincentkoc.
+
Google Chat/multi-account webhook auth fallback: when channels.googlechat.accounts.default carries shared webhook audience/path settings (for example after config normalization), inherit those defaults for named accounts while preserving top-level and per-account overrides, so inbound webhook verification no longer fails silently for named accounts missing duplicated audience fields. Fixes #38369.
+
Models/tool probing: raise the tool-capability probe budget from 32 to 256 tokens so reasoning models that spend tokens on thinking before returning a required tool call are less likely to be misclassified as not supporting tools. (#7521) Thanks @jakobdylanc.
+
Gateway/transient network classification: treat wrapped ...: fetch failed transport messages as transient while avoiding broad matches like Web fetch failed (404): ..., preventing Discord reconnect wrappers from crashing the gateway without suppressing non-network tool failures. (#38530) Thanks @xinhuagu.
+
ACP/console silent reply suppression: filter ACP NO_REPLY lead fragments and silent-only finals before openclaw agent logging/delivery so console-backed ACP sessions no longer leak NO/NO_REPLY placeholders. (#38436) Thanks @ql-wade.
+
Feishu/reply delivery reliability: disable block streaming in Feishu reply options so plain-text auto-render replies are no longer silently dropped before final delivery. (#38258) Thanks @xinhuagu.
+
Agents/reply MEDIA delivery: normalize local assistant MEDIA: paths before block/final delivery, keep media dedupe aligned with message-tool sends, and contain malformed media normalization failures so generated files send reliably instead of falling back to empty responses. (#38572) Thanks @obviyus.
+
Sessions/bootstrap cache rollover invalidation: clear cached workspace bootstrap snapshots whenever an existing sessionKey rolls to a new sessionId across auto-reply, command, and isolated cron session resolvers, so AGENTS.md/MEMORY.md/USER.md updates are reloaded after daily, idle, or forced session resets instead of staying stale until gateway restart. (#38494) Thanks @LivingInDrm.
+
Gateway/Telegram polling health monitor: skip stale-socket restarts for Telegram long-polling channels and thread channel identity through shared health evaluation so polling connections are not restarted on the WebSocket stale-socket heuristic. (#38395) Thanks @ql-wade and @Takhoffman.
+
Daemon/systemd fresh-install probe: check for OpenClaw's managed user unit before running systemctl --user is-enabled, so first-time Linux installs no longer fail on generic missing-unit probe errors. (#38819) Thanks @adaHubble.
+
Gateway/container lifecycle: allow openclaw gateway stop to SIGTERM unmanaged gateway listeners and openclaw gateway restart to SIGUSR1 a single unmanaged listener when no service manager is installed, so container and supervisor-based deployments are no longer blocked by service disabled no-op responses. Fixes #36137. Thanks @vincentkoc.
+
Gateway/Windows restart supervision: relaunch task-managed gateways through Scheduled Task with quoted helper-script command paths, distinguish restart-capable supervisors per platform, and stop orphaned Windows gateway children during self-restart. (#38825) Thanks @obviyus.
+
Telegram/native topic command routing: resolve forum-topic native commands through the same conversation route as inbound messages so topic agentId overrides and bound topic sessions target the active session instead of the default topic-parent session. (#38871) Thanks @obviyus.
+
Markdown/assistant image hardening: flatten remote markdown images to plain text across the Control UI, exported HTML, and shared Swift chat while keeping inline data:image/... markdown renderable, so model output no longer triggers automatic remote image fetches. (#38895) Thanks @obviyus.
+
Config/compaction safeguard settings: regression-test agents.defaults.compaction.recentTurnsPreserve through loadConfig() and cover the new help metadata entry so the exposed preserve knob stays wired through schema validation and config UX. (#25557) thanks @rodrigouroz.
+
iOS/Quick Setup presentation: skip automatic Quick Setup when a gateway is already configured (active connect config, last-known connection, preferred gateway, or manual host), so reconnecting installs no longer get prompted to connect again. (#38964) Thanks @ngutman.
+
CLI/Docs memory help accuracy: clarify openclaw memory status --deep behavior and align memory command examples/docs with the current search options. (#31803) Thanks @JasonOA888 and @Avi974.
+
Auto-reply/allowlist store account scoping: keep /allowlist ... --store writes scoped to the selected account and clear legacy unscoped entries when removing default-account store access, preventing cross-account default allowlist bleed-through from legacy pairing-store reads. Thanks @tdjackey for reporting and @vincentkoc for the fix.
+
Security/Nostr: harden profile mutation/import loopback guards by failing closed on non-loopback forwarded client headers (x-forwarded-for / x-real-ip) and rejecting sec-fetch-site: cross-site; adds regression coverage for proxy-forwarded and browser cross-site mutation attempts.
+
CLI/bootstrap Node version hint maintenance: replace hardcoded nvm 22 instructions in openclaw.mjs with MIN_NODE_MAJOR interpolation so future minimum-Node bumps keep startup guidance in sync automatically. (#39056) Thanks @onstash.
+
Discord/native slash command auth: honor commands.allowFrom.discord (and commands.allowFrom["*"]) in guild slash-command pre-dispatch authorization so allowlisted senders are no longer incorrectly rejected as unauthorized. (#38794) Thanks @jskoiz and @thewilloftheshadow.
+
Outbound/message target normalization: ignore empty legacy to/channelId fields when explicit target is provided so valid target-based sends no longer fail legacy-param validation; includes regression coverage. (#38944) Thanks @Narcooo.
+
Models/auth token prompts: guard cancelled manual token prompts so Symbol(clack:cancel) values cannot be persisted into auth profiles; adds regression coverage for cancelled models auth paste-token. (#38951) Thanks @MumuTW.
+
Gateway/loopback announce URLs: treat http:// and https:// aliases with the same loopback/private-network policy as websocket URLs so loopback cron announce delivery no longer fails secure URL validation. (#39064) Thanks @Narcooo.
+
Models/default provider fallback: when the hardcoded default provider is removed from models.providers, resolve defaults from configured providers instead of reporting stale removed-provider defaults in status output. (#38947) Thanks @davidemanuelDEV.
+
Agents/cache-trace stability: guard stable stringify against circular references in trace payloads so near-limit payloads no longer crash with Maximum call stack size exceeded; adds regression coverage. (#38935) Thanks @MumuTW.
+
Extensions/diffs CI stability: add headers to the localReq test helper in extensions/diffs/index.test.ts so forwarding-hint checks no longer crash with req.headers undefined. (supersedes #39063) Thanks @Shennng.
+
Agents/compaction thresholding: apply agents.defaults.contextTokens cap to the model passed into embedded run and /compact session creation so auto-compaction thresholds use the effective context window, not native model max context. (#39099) Thanks @MumuTW.
+
Models/merge mode provider precedence: when models.mode: "merge" is active and config explicitly sets a provider baseUrl, keep config as source of truth instead of preserving stale runtime models.jsonbaseUrl values; includes normalized provider-key coverage. (#39103) Thanks @BigUncle.
+
UI/Control chat tool streaming: render tool events live in webchat without requiring refresh by enabling tool-events capability, fixing stream/event correlation, and resetting/reloading stream state around tool results and terminal events. (#39104) Thanks @jakepresent.
+
Models/provider apiKey persistence hardening: when a provider apiKey value equals a known provider env var value, persist the canonical env var name into models.json instead of resolved plaintext secrets. (#38889) Thanks @gambletan.
+
Discord/model picker persistence check: add a short post-dispatch settle delay before reading back session model state so picker confirmations stop reporting false mismatch warnings after successful model switches. (#39105) Thanks @akropp.
+
Agents/OpenAI WS compat store flag: omit store from response.create payloads when model compat sets supportsStore: false, preventing strict OpenAI-compatible providers from rejecting websocket requests with unknown-field errors. (#39113) Thanks @scoootscooob.
+
Config/validation log sanitization: sanitize config-validation issue paths/messages before logging so control characters and ANSI escape sequences cannot inject misleading terminal output from crafted config content. (#39116) Thanks @powermaster888.
+
Agents/compaction counter accuracy: count successful overflow-triggered auto-compactions (willRetry=true) in the compaction counter while still excluding aborted/no-result events, so /status reflects actual safeguard compaction activity. (#39123) Thanks @MumuTW.
+
Gateway/chat delta ordering: flush buffered assistant deltas before emitting tool start events so pre-tool text is delivered to Control UI before tool cards, avoiding transient text/tool ordering artifacts in streaming. (#39128) Thanks @0xtangping.
+
Voice-call plugin schema parity: add missing manifest configSchema fields (webhookSecurity, streaming.preStartTimeoutMs|maxPendingConnections|maxPendingConnectionsPerIp|maxConnections, staleCallReaperSeconds) so gateway AJV validation accepts already-supported runtime config instead of failing with additionalProperties errors. (#38892) Thanks @giumex.
+
Agents/OpenAI WS reconnect retry accounting: avoid double retry scheduling when reconnect failures emit both error and close, so retry budgets track actual reconnect attempts instead of exhausting early. (#39133) Thanks @scoootscooob.
+
Daemon/Windows schtasks runtime detection: use locale-invariant Last Run Result running codes (0x41301/267009) as the primary running signal so openclaw node status no longer misreports active tasks as stopped on non-English Windows locales. (#39076) Thanks @ademczuk.
+
Usage/token count formatting: round near-million token counts to millions (1.0m) instead of 1000k, with explicit boundary coverage for 999_499 and 999_500. (#39129) Thanks @CurryMessi.
+
Gateway/session bootstrap cache invalidation ordering: clear bootstrap snapshots only after active embedded-run shutdown wait completes, preventing dying runs from repopulating stale cache between /new/sessions.reset turns. (#38873) Thanks @MumuTW.
+
Browser/dispatcher error clarity: preserve dispatcher-side failure context in browser fetch errors while still appending operator guidance and explicit no-retry model hints, preventing misleading "Can't reach service" wrapping and avoiding LLM retry loops. (#39090) Thanks @NewdlDewdl.
+
Telegram/polling offset safety: confirm persisted offsets before polling startup while validating stored lastUpdateId values as non-negative safe integers (with overflow guards) so malformed offset state cannot cause update skipping/dropping. (#39111) Thanks @MumuTW.
+
Telegram/status SecretRef read-only resolution: resolve env-backed bot-token SecretRefs in config-only/status inspection while respecting provider source/defaults and env allowlists, so status no longer crashes or reports false-ready tokens for disallowed providers. (#39130) Thanks @neocody.
+
Agents/OpenAI WS max-token zero forwarding: treat maxTokens: 0 as an explicit value in websocket response.create payloads (instead of dropping it as falsy), with regression coverage for zero-token forwarding. (#39148) Thanks @scoootscooob.
+
Podman/.env gateway bind precedence: evaluate OPENCLAW_GATEWAY_BIND after sourcing .env in run-openclaw-podman.sh so env-file overrides are honored. (#38785) Thanks @majinyu666.
+
Models/default alias refresh: bump gpt to openai/gpt-5.4 and Gemini defaults to gemini-3.1 preview aliases (including normalization/default wiring) to track current model IDs. (#38638) Thanks @ademczuk.
+
Config/env substitution degraded mode: convert missing ${VAR} resolution in config reads from hard-fail to warning-backed degraded behavior, while preventing unresolved placeholders from being accepted as gateway credentials. (#39050) Thanks @akz142857.
+
Discord inbound listener non-blocking dispatch: make MESSAGE_CREATE listener handoff asynchronous (no per-listener queue blocking), so long runs no longer stall unrelated incoming events. (#39154) Thanks @yaseenkadlemakki.
+
Daemon/Windows PATH freeze fix: stop persisting install-time PATH snapshots into Scheduled Task scripts so runtime tool lookup follows current host PATH updates; also refresh local TUI history on silent local finals. (#39139) Thanks @Narcooo.
+
Gateway/systemd service restart hardening: clear stale gateway listeners by explicit run-port before service bind, add restart stale-pid port-override support, tune systemd start/stop/exit handling, and disable detached child mode only in service-managed runtime so cgroup stop semantics clean up descendants reliably. (#38463) Thanks @spirittechie.
+
Discord/plugin native command aliases: let plugins declare provider-specific slash names so native Discord registration can avoid built-in command collisions; the bundled Talk voice plugin now uses /talkvoice natively on Discord while keeping text /voice.
+
Daemon/Windows schtasks status normalization: derive runtime state from locale-neutral numeric Last Run Result codes only (without language string matching) and surface unknown when numeric result data is unavailable, preventing locale-specific misclassification drift. (#39153) Thanks @scoootscooob.
+
Telegram/polling conflict recovery: reset the polling webhookCleared latch on getUpdates 409 conflicts so webhook cleanup re-runs on restart cycles and polling avoids infinite conflict loops. (#39205) Thanks @amittell.
+
Heartbeat/requests-in-flight scheduling: stop advancing nextDueMs and avoid immediate scheduleNext() timer overrides on requests-in-flight skips, so wake-layer retry cooldowns are honored and heartbeat cadence no longer drifts under sustained contention. (#39182) Thanks @MumuTW.
+
Memory/SQLite contention resilience: re-apply PRAGMA busy_timeout on every sync-store and QMD connection open so process restarts/reopens no longer revert to immediate SQLITE_BUSY failures under lock contention. (#39183) Thanks @MumuTW.
+
Gateway/webchat route safety: block webchat/control-ui clients from inheriting stored external delivery routes on channel-scoped sessions (while preserving route inheritance for UI/TUI clients), preventing cross-channel leakage from scoped chats. (#39175) Thanks @widingmarcus-cyber.
+
Telegram error-surface resilience: return a user-visible fallback reply when dispatch/debounce processing fails instead of going silent, while preserving draft-stream cleanup and best-effort thread-scoped fallback delivery. (#39209) Thanks @riftzen-bit.
+
Gateway/password auth startup diagnostics: detect unresolved provider-reference objects in gateway.auth.password and fail with a specific bootstrap-secrets error message instead of generic misconfiguration output. (#39230) Thanks @ademczuk.
+
Agents/OpenAI-responses compatibility: strip unsupported store payload fields when supportsStore=false (including OpenAI-compatible non-OpenAI providers) while preserving server-compaction payload behavior. (#39219) Thanks @ademczuk.
+
Agents/model fallback visibility: warn when configured model IDs cannot be resolved and fallback is applied, with log-safe sanitization of model text to prevent control-sequence injection in warning output. (#39215) Thanks @ademczuk.
+
Outbound delivery replay safety: use two-phase delivery ACK markers (.json -> .delivered -> unlink) and startup marker cleanup so crash windows between send and cleanup do not replay already-delivered messages. (#38668) Thanks @Gundam98.
+
Nodes/system.run approval binding: carry prepared approval plans through gateway forwarding and bind interpreter-style script operands across approval to execution, so post-approval script rewrites are denied while unchanged approved script runs keep working. Thanks @tdjackey for reporting.
+
Nodes/system.run PowerShell wrapper parsing: treat pwsh/powershell-EncodedCommand forms as shell-wrapper payloads so allowlist mode still requires approval instead of falling back to plain argv analysis. Thanks @tdjackey for reporting.
+
Control UI/auth error reporting: map generic browser Fetch failed websocket close errors back to actionable gateway auth messages (gateway token mismatch, authentication failed, retry later) so dashboard disconnects stop hiding credential problems. Landed from contributor PR #28608 by @KimGLee. Thanks @KimGLee.
+
Media/mime unknown-kind handling: return undefined (not "unknown") for missing/unrecognized MIME kinds and use document-size fallback caps for unknown remote media, preventing phantom Signal events from being treated as real messages. (#39199) Thanks @nicolasgrasset.
+
Nodes/system.run allow-always persistence: honor shell comment semantics during allowlist analysis so #-tailed payloads that never execute are not persisted as trusted follow-up commands. Thanks @tdjackey for reporting.
+
Signal/inbound attachment fan-in: forward all successfully fetched inbound attachments through MediaPaths/MediaUrls/MediaTypes (instead of only the first), and improve multi-attachment placeholder summaries in mention-gated pending history. (#39212) Thanks @joeykrug.
+
Nodes/system.run dispatch-wrapper boundary: keep shell-wrapper approval classification active at the depth boundary so env wrapper stacks cannot reach /bin/sh -c execution without the expected approval gate. Thanks @tdjackey for reporting.
+
Docker/token persistence on reconfigure: reuse the existing .env gateway token during docker-setup.sh reruns and align compose token env defaults, so Docker installs stop silently rotating tokens and breaking existing dashboard sessions. Landed from contributor PR #33097 by @chengzhichao-xydt. Thanks @chengzhichao-xydt.
+
Agents/strict OpenAI turn ordering: apply assistant-first transcript bootstrap sanitization to strict OpenAI-compatible providers (for example vLLM/Gemma via openai-completions) without adding Google-specific session markers, preventing assistant-first history rejections. (#39252) Thanks @scoootscooob.
+
Discord/exec approvals gateway auth: pass resolved shared gateway credentials into the Discord exec-approvals gateway client so token-auth installs stop failing approvals with gateway token mismatch. Related to #38179. Thanks @0riginal-claw for the adjacent PR #35147 investigation.
+
Subagents/workspace inheritance: propagate parent workspace directory to spawned subagent runs so child sessions reliably inherit workspace-scoped instructions (AGENTS.md, SOUL.md, etc.) without exposing workspace override through tool-call arguments. (#39247) Thanks @jasonQin6.
+
Exec approvals/gateway-node policy: honor explicit ask=off from exec-approvals.json even when runtime defaults are stricter, so trusted full/off setups stop re-prompting on gateway and node exec paths. Landed from contributor PR #26789 by @pandego. Thanks @pandego.
+
Exec approvals/config fallback: inherit ask from exec-approvals.json when tools.exec.ask is unset, so local full/off defaults no longer fall back to on-miss for exec tool and nodes run. Landed from contributor PR #29187 by @Bartok9. Thanks @Bartok9.
+
Exec approvals/allow-always shell scripts: persist and match script paths for wrapper invocations like bash scripts/foo.sh while still blocking -c/-s wrapper bypasses. Landed from contributor PR #35137 by @yuweuii. Thanks @yuweuii.
+
Queue/followup dedupe across drain restarts: dedupe queued redelivery message_id values after queue recreation so busy-session followups no longer duplicate on replayed inbound events. Landed from contributor PR #33168 by @rylena. Thanks @rylena.
+
Telegram/preview-final edit idempotence: treat message is not modified errors during preview finalization as delivered so partial-stream final replies do not fall back to duplicate sends. Landed from contributor PR #34983 by @HOYALIM. Thanks @HOYALIM.
+
Telegram/DM streaming transport parity: use message preview transport for all DM streaming lanes so final delivery can edit the active preview instead of sending duplicate finals. Landed from contributor PR #38906 by @gambletan. Thanks @gambletan.
+
Telegram/DM draft streaming restoration: restore native sendMessageDraft preview transport for DM answer streaming while keeping reasoning on message transport, with regression coverage to keep draft finalization from sending duplicate finals. (#39398) Thanks @obviyus.
+
Telegram/send retry safety: retry non-idempotent send paths only for pre-connect failures and make custom retry predicates strict, preventing ambiguous reconnect retries from sending duplicate messages. Landed from contributor PR #34238 by @hal-crackbot. Thanks @hal-crackbot.
+
ACP/run spawn delivery bootstrap: stop reusing requester inline delivery targets for one-shot mode: "run" ACP spawns, so fresh run-mode workers bootstrap in isolation instead of inheriting thread-bound session delivery behavior. (#39014) Thanks @lidamao633.
+
Discord/DM session-key normalization: rewrite legacy discord:dm:* and phantom direct-message discord:channel: session keys to discord:direct:* when the sender matches, so multi-agent Discord DMs stop falling into empty channel-shaped sessions and resume replying correctly.
+
Discord/native slash session fallback: treat empty configured bound-session keys as missing so /status and other native commands fall back to the routed slash session and routed channel session instead of blanking Discord session keys in normal channel bindings.
+
Agents/tool-call dispatch normalization: normalize provider-prefixed tool names before dispatch across toolCall, toolUse, and functionCall blocks, while preserving multi-segment tool suffixes when stripping provider wrappers so malformed-but-recoverable tool names no longer fail with Tool not found. (#39328) Thanks @vincentkoc.
+
Agents/parallel tool-call compatibility: honor parallel_tool_calls / parallelToolCalls extra params only for openai-completions and openai-responses payloads, preserve higher-precedence alias overrides across config and runtime layers, and ignore invalid non-boolean values so single-tool-call providers like NVIDIA-hosted Kimi stop failing on forced parallel tool-call payloads. (#37048) Thanks @vincentkoc.
+
Config/invalid-load fail-closed: stop converting INVALID_CONFIG into an empty runtime config, keep valid settings available only through explicit best-effort diagnostic reads, and route read-only CLI diagnostics through that path so unknown keys no longer silently drop security-sensitive config. (#28140) Thanks @bobsahur-robot and @vincentkoc.
+
Agents/codex-cli sandbox defaults: switch the built-in Codex backend from read-only to workspace-write so spawned coding runs can edit files out of the box. Landed from contributor PR #39336 by @0xtangping. Thanks @0xtangping.
+
Gateway/health-monitor restart reason labeling: report disconnected instead of stuck for clean channel disconnect restarts, so operator logs distinguish socket drops from genuinely stuck channels. (#36436) Thanks @Sid-Qin.
+
Control UI/agents-page overrides: auto-create minimal per-agent config entries when editing inherited agents, so model/tool/skill changes enable Save and inherited model fallbacks can be cleared by writing a primary-only override. Landed from contributor PR #39326 by @dunamismax. Thanks @dunamismax.
+
Gateway/Telegram webhook-mode recovery: add webhookCertPath to re-upload self-signed certificates during webhook registration and skip stale-socket detection for webhook-mode channels, so Telegram webhook setups survive health-monitor restarts. Landed from contributor PR #39313 by @fellanH. Thanks @fellanH.
+
Discord/config schema parity: add channels.discord.agentComponents to the strict Zod config schema so valid agentComponents.enabled settings (root and account-scoped) no longer fail with unrecognized-key validation errors. Landed from contributor PR #39378 by @gambletan. Thanks @gambletan and @thewilloftheshadow.
+
ACPX/MCP session bootstrap: inject configured MCP servers into ACP session/new and session/load for acpx-backed sessions, restoring Canva and other external MCP tools. Landed from contributor PR #39337. Thanks @goodspeed-apps.
+
Control UI/Telegram sender labels: preserve inbound sender labels in sanitized chat history so dashboard user-message groups split correctly and show real group-member names instead of You. (#39414) Thanks @obviyus.
Highlight: External Secrets Management introduces a full openclaw secrets workflow (audit, configure, apply, reload) with runtime snapshot activation, strict secrets apply target-path validation, safer migration scrubbing, ref-only auth-profile support, and dedicated docs. (#26155) Thanks @joshavant.
-
ACP/Thread-bound agents: make ACP agents first-class runtimes for thread sessions with acp spawn/send dispatch integration, acpx backend bridging, lifecycle controls, startup reconciliation, runtime cleanup, and coalesced thread replies. (#23580) thanks @osolmaz.
-
Agents/Routing CLI: add openclaw agents bindings, openclaw agents bind, and openclaw agents unbind for account-scoped route management, including channel-only to account-scoped binding upgrades, role-aware binding identity handling, plugin-resolved binding account IDs, and optional account-binding prompts in openclaw channels add. (#27195) thanks @gumadeiras.
-
Codex/WebSocket transport: make openai-codex WebSocket-first by default (transport: "auto" with SSE fallback), keep explicit per-model/runtime transport overrides, and add regression coverage + docs for transport selection.
-
Onboarding/Plugins: let channel plugins own interactive onboarding flows with optional configureInteractive and configureWhenConfigured hooks while preserving the generic fallback path. (#27191) thanks @gumadeiras.
-
Android/Nodes: add Android device capability plus device.status and device.info node commands, including runtime handler wiring and protocol/registry coverage for device status/info payloads. (#27664) Thanks @obviyus.
-
Android/Nodes: add notifications.list support on Android nodes and expose nodes notifications_list in agent tooling for listing active device notifications. (#27344) thanks @obviyus.
-
Docs/Contributing: add Nimrod Gutman to the maintainer roster in CONTRIBUTING.md. (#27840) Thanks @ngutman.
+
Secrets/SecretRef coverage: expand SecretRef support across the full supported user-supplied credential surface (64 targets total), including runtime collectors, openclaw secrets planning/apply/audit flows, onboarding SecretInput UX, and related docs; unresolved refs now fail fast on active surfaces while inactive surfaces report non-blocking diagnostics. (#29580) Thanks @joshavant.
+
Tools/PDF analysis: add a first-class pdf tool with native Anthropic and Google PDF provider support, extraction fallback for non-native models, configurable defaults (agents.defaults.pdfModel, pdfMaxBytesMb, pdfMaxPages), and docs/tests covering routing, validation, and registration. (#31319) Thanks @tyler6204.
+
Outbound adapters/plugins: add shared sendPayload support across direct-text-media, Discord, Slack, WhatsApp, Zalo, and Zalouser with multi-media iteration and chunk-aware text fallback. (#30144) Thanks @nohat.
+
Models/MiniMax: add first-class MiniMax-M2.5-highspeed support across built-in provider catalogs, onboarding flows, and MiniMax OAuth plugin defaults, while keeping legacy MiniMax-M2.5-Lightning compatibility for existing configs.
+
Sessions/Attachments: add inline file attachment support for sessions_spawn (subagent runtime only) with base64/utf8 encoding, transcript content redaction, lifecycle cleanup, and configurable limits via tools.sessions_spawn.attachments. (#16761) Thanks @napetrov.
+
Telegram/Streaming defaults: default channels.telegram.streaming to partial (from off) so new Telegram setups get live preview streaming out of the box, with runtime fallback to message-edit preview when native drafts are unavailable.
+
Telegram/DM streaming: use sendMessageDraft for private preview streaming, keep reasoning/answer preview lanes separated in DM reasoning-stream mode. (#31824) Thanks @obviyus.
+
Telegram/voice mention gating: add optional disableAudioPreflight on group/topic config to skip mention-detection preflight transcription for inbound voice notes where operators want text-only mention checks. (#23067) Thanks @yangnim21029.
+
CLI/Config validation: add openclaw config validate (with --json) to validate config files before gateway startup, and include detailed invalid-key paths in startup invalid-config errors. (#31220) thanks @Sid-Qin.
+
Tools/Diffs: add PDF file output support and rendering quality customization controls (fileQuality, fileScale, fileMaxWidth) for generated diff artifacts, and document PDF as the preferred option when messaging channels compress images. (#31342) Thanks @gumadeiras.
+
Memory/Ollama embeddings: add memorySearch.provider = "ollama" and memorySearch.fallback = "ollama" support, honor models.providers.ollama settings for memory embedding requests, and document Ollama embedding usage. (#26349) Thanks @nico-hoff.
+
Zalo Personal plugin (@openclaw/zalouser): rebuilt channel runtime to use native zca-js integration in-process, removing external CLI transport usage and keeping QR/login + send/listen flows fully inside OpenClaw.
+
Plugin SDK/channel extensibility: expose channelRuntime on ChannelGatewayContext so external channel plugins can access shared runtime helpers (reply/routing/session/text/media/commands) without internal imports. (#25462) Thanks @guxiaobo.
+
Plugin runtime/STT: add api.runtime.stt.transcribeAudioFile(...) so extensions can transcribe local audio files through OpenClaw's configured media-understanding audio providers. (#22402) Thanks @benthecarman.
+
Plugin hooks/session lifecycle: include sessionKey in session_start/session_end hook events and contexts so plugins can correlate lifecycle callbacks with routing identity. (#26394) Thanks @tempeste.
+
Hooks/message lifecycle: add internal hook events message:transcribed and message:preprocessed, plus richer outbound message:sent context (isGroup, groupId) for group-conversation correlation and post-transcription automations. (#9859) Thanks @Drickon.
+
Media understanding/audio echo: add optional tools.media.audio.echoTranscript + echoFormat to send a pre-agent transcript confirmation message to the originating chat, with echo disabled by default. (#32150) Thanks @AytuncYildizli.
+
Plugin runtime/system: expose runtime.system.requestHeartbeatNow(...) so extensions can wake targeted sessions immediately after enqueueing system events. (#19464) Thanks @AustinEral.
+
Plugin runtime/events: expose runtime.events.onAgentEvent and runtime.events.onSessionTranscriptUpdate for extension-side subscriptions, and isolate transcript-listener failures so one faulty listener cannot break the entire update fanout. (#16044) Thanks @scifantastic.
+
CLI/Banner taglines: add cli.banner.taglineMode (random | default | off) to control funny tagline behavior in startup output, with docs + FAQ guidance and regression tests for config override behavior.
+
+
Breaking
+
+
BREAKING: Onboarding now defaults tools.profile to messaging for new local installs (interactive + non-interactive). New setups no longer start with broad coding/system tools unless explicitly configured.
+
BREAKING: ACP dispatch now defaults to enabled unless explicitly disabled (acp.dispatch.enabled=false). If you need to pause ACP turn routing while keeping /acp controls, set acp.dispatch.enabled=false. Docs: https://docs.openclaw.ai/tools/acp-agents
+
BREAKING: Plugin SDK removed api.registerHttpHandler(...). Plugins must register explicit HTTP routes via api.registerHttpRoute({ path, auth, match, handler }), and dynamic webhook lifecycles should use registerPluginHttpRoute(...).
+
BREAKING: Zalo Personal plugin (@openclaw/zalouser) no longer depends on external zca-compatible CLI binaries (openzca, zca-cli) for runtime send/listen/login; operators should use openclaw channels login --channel zalouser after upgrade to refresh sessions in the new JS-native path.
Fixes
-
Telegram/DM allowlist runtime inheritance: enforce dmPolicy: "allowlist"allowFrom requirements using effective account-plus-parent config across account-capable channels (Telegram, Discord, Slack, Signal, iMessage, IRC, BlueBubbles, WhatsApp), and align openclaw doctor checks to the same inheritance logic so DM traffic is not silently dropped after upgrades. (#27936) Thanks @widingmarcus-cyber.
-
Delivery queue/recovery backoff: prevent retry starvation by persisting lastAttemptAt on failed sends and deferring recovery retries until each entry's lastAttemptAt + backoff window is eligible, while continuing to recover ready entries behind deferred ones. Landed from contributor PR #27710 by @Jimmy-xuzimo. Thanks @Jimmy-xuzimo.
-
Google Chat/Lifecycle: keep Google Chat startAccount pending until abort in webhook mode so startup is no longer interpreted as immediate exit, preventing auto-restart loops and webhook-target churn. (#27384) thanks @junsuwhy.
-
Temp dirs/Linux umask: force 0700 permissions after temp-dir creation and self-heal existing writable temp dirs before trust checks so umask 0002 installs no longer crash-loop on startup. Landed from contributor PR #27860 by @stakeswky. (#27853) Thanks @stakeswky.
-
Nextcloud Talk/Lifecycle: keep startAccount pending until abort and stop the webhook monitor on shutdown, preventing EADDRINUSE restart loops when the gateway manages account lifecycle. (#27897)
-
Microsoft Teams/File uploads: acknowledge fileConsent/invoke immediately (invokeResponse before upload + file card send) so Teams no longer shows false "Something went wrong" timeout banners while upload completion continues asynchronously; includes updated async regression coverage. Landed from contributor PR #27641 by @scz2011.
-
Queue/Drain/Cron reliability: harden lane draining with guaranteed draining flag reset on synchronous pump failures, reject new queue enqueues during gateway restart drain windows (instead of silently killing accepted tasks), add /stop queued-backlog cutoff metadata with stale-message skipping (while avoiding cross-session native-stop cutoff bleed), and raise isolated cron agentTurn outer safety timeout to avoid false 10-minute timeout races against longer agent session timeouts. (#27407, #27332, #27427)
-
Typing/Main reply pipeline: always mark dispatch idle in agent-runner finalization so typing cleanup runs even when dispatcher onIdle does not fire, preventing stuck typing indicators after run completion. (#27250) Thanks @Sid-Qin.
-
Typing/TTL safety net: add max-duration guardrails to shared typing callbacks so stuck lifecycle edges auto-stop typing indicators even when explicit idle/cleanup signals are missed. (#27428) Thanks @Crpdim.
-
Typing/Cross-channel leakage: unify run-scoped typing suppression for cross-channel/internal-webchat routes, preserve current inbound origin as embedded run message channel context, harden shared typing keepalive with consecutive-failure circuit breaker edge-case handling, and enforce dispatcher completion/idle waits in extension dispatcher callsites (Feishu, Matrix, Mattermost, MSTeams) so typing indicators always clean up on success/error paths. Related: #27647, #27493, #27598. Supersedes/replaces draft PRs: #27640, #27593, #27540.
-
Telegram/sendChatAction 401 handling: add bounded exponential backoff + temporary local typing suppression after repeated unauthorized failures to stop unbounded sendChatAction retry loops that can trigger Telegram abuse enforcement and bot deletion. (#27415) Thanks @widingmarcus-cyber.
-
Telegram/Webhook startup: clarify webhook config guidance, allow channels.telegram.webhookPort: 0 for ephemeral listener binding, and log both the local listener URL and Telegram-advertised webhook URL with the bound port. (#25732) thanks @huntharo.
-
Browser/Chrome extension handshake: bind relay WS message handling before onopen and add non-blocking connect.challenge response handling for gateway-style handshake frames, avoiding stuck … badge states when challenge frames arrive immediately on connect. Landed from contributor PR #22571 by @pandego. (#22553)
-
Browser/Extension relay init: dedupe concurrent same-port relay startup with shared in-flight initialization promises so callers await one startup lifecycle and receive consistent success/failure results. Landed from contributor PR #21277 by @HOYALIM. (Related #20688)
-
Browser/Fill relay + CLI parity: accept act.fill fields without explicit type by defaulting missing/empty type to text in both browser relay route parsing and openclaw browser fill CLI field parsing, so relay calls no longer fail when the model omits field type metadata. Landed from contributor PR #27662 by @Uface11. (#27296) Thanks @Uface11.
-
Feishu/Permission error dispatch: merge sender-name permission notices into the main inbound dispatch so one user message produces one agent turn/reply (instead of a duplicate permission-notice turn), with regression coverage. (#27381) thanks @byungsker.
-
Agents/Canvas default node resolution: when multiple connected canvas-capable nodes exist and no single mac-* candidate is selected, default to the first connected candidate instead of failing with node required for implicit-node canvas tool calls. Landed from contributor PR #27444 by @carbaj03. Thanks @carbaj03.
-
TUI/stream assembly: preserve streamed text across real tool-boundary drops without keeping stale streamed text when non-text blocks appear only in the final payload. Landed from contributor PR #27711 by @scz2011. (#27674)
-
Hooks/Internal message:sent: forward sessionKey on outbound sends from agent delivery, cron isolated delivery, gateway receipt acks, heartbeat sends, session-maintenance warnings, and restart-sentinel recovery so internal message:sent hooks consistently dispatch with session context, including openclaw agent --deliver runs resumed via --session-id (without explicit --session-key). Landed from contributor PR #27584 by @qualiobra. Thanks @qualiobra.
-
Pi image-token usage: stop re-injecting history image blocks each turn, process image references from the current prompt only, and prune already-answered user-image blocks in stored history to prevent runaway token growth. (#27602)
-
BlueBubbles/SSRF: auto-allowlist the configured serverUrl hostname for attachment fetches so localhost/private-IP BlueBubbles setups are no longer false-blocked by default SSRF checks. Landed from contributor PR #27648 by @lailoo. (#27599) Thanks @taylorhou for reporting.
-
Agents/Compaction + onboarding safety: prevent destructive double-compaction by stripping stale assistant usage around compaction boundaries, skipping post-compaction custom metadata writes in the same attempt, and cancelling safeguard compaction when there are no real conversation messages to summarize; harden workspace/bootstrap detection for memory-backed workspaces; and change openclaw onboard --reset default scope to config+creds+sessions (workspace deletion now requires --reset-scope full). (#26458, #27314) Thanks @jaden-clovervnd, @Sid-Qin, and @widingmarcus-cyber for fix direction in #26502, #26529, and #27492.
-
NO_REPLY suppression: suppress NO_REPLY before Slack API send and in sub-agent announce completion flow so sentinel text no longer leaks into user channels. Landed from contributor PRs #27529 (by @Sid-Qin) and #27535 (rewritten minimal landing by maintainers). (#27387, #27531)
-
Matrix/Group sender identity: preserve sender labels in Matrix group inbound prompt text (BodyForAgent) for both channel and threaded messages, and align group envelopes with shared inbound sender-prefix formatting so first-person requests resolve against the current sender. (#27401) thanks @koushikxd.
-
Auto-reply/Streaming: suppress only exact NO_REPLY final replies while still filtering streaming partial sentinel fragments (NO_, NO_RE, HEARTBEAT_...) so substantive replies ending with NO_REPLY are delivered and partial silent tokens do not leak during streaming. (#19576) Thanks @aldoeliacim.
-
Auto-reply/Inbound metadata: add a readable timestamp field to conversation info and ignore invalid/out-of-range timestamp values so prompt assembly never crashes on malformed timestamp inputs. (#17017) thanks @liuy.
-
Typing/Run completion race: prevent post-run keepalive ticks from re-triggering typing callbacks by guarding triggerTyping() with runComplete, with regression coverage for no-restart behavior during run-complete/dispatch-idle boundaries. (#27413) Thanks @widingmarcus-cyber.
-
Typing/Dispatch idle: force typing cleanup when markDispatchIdle never arrives after run completion, avoiding leaked typing keepalive loops in cron/announce edges. Landed from contributor PR #27541 by @Sid-Qin. (#27493)
-
Telegram/Inline buttons: allow callback-query button handling in groups (including /models follow-up buttons) when group policy authorizes the sender, by removing the redundant callback allowlist gate that blocked open-policy groups. (#27343) Thanks @GodsBoy.
-
Telegram/Streaming preview: when finalizing without an existing preview message, prime pending preview text with final answer before stop-flush so users do not briefly see stale 1-2 word fragments (for example no before no problem). (#27449) Thanks @emanuelst for the original fix direction in #19673.
-
Browser/Extension relay CORS: handle /json*OPTIONS preflight before auth checks, allow Chrome extension origins, and return extension-origin CORS headers on relay HTTP responses so extension token validation no longer fails cross-origin. Landed from contributor PR #23962 by @miloudbelarebia. (#23842)
-
Browser/Extension relay auth: allow ?token= query-param auth on relay /json* endpoints (consistent with relay WebSocket auth) so curl/devtools-style /json/version and /json/list probes work without requiring custom headers. Landed from contributor PR #26015 by @Sid-Qin. (#25928)
-
Browser/Extension relay shutdown: flush pending extension-request timers/rejections during relay stop() before socket/server teardown so in-flight extension waits do not survive shutdown windows. Landed from contributor PR #24142 by @kevinWangSheng.
-
Browser/Extension relay reconnect resilience: keep CDP clients alive across brief MV3 extension disconnect windows, wait briefly for extension reconnect before failing in-flight CDP commands, and only tear down relay target/client state after reconnect grace expires. Landed from contributor PR #27617 by @davidemanuelDEV.
-
Browser/Route decode hardening: guard malformed percent-encoding in relay target action routes and browser route-param decoding so crafted % paths return 400 instead of crashing/unhandled URI decode failures. Landed from contributor PR #11880 by @Yida-Dev.
-
Feishu/Inbound message metadata: include inbound message_id in BodyForAgent on a dedicated metadata line so agents can reliably correlate and act on media/message operations that require message IDs, with regression coverage. (#27253) thanks @xss925175263.
-
Feishu/Doc tools: route feishu_doc and feishu_app_scopes through the active agent account context (with explicit accountId override support) so multi-account agents no longer default to the first configured app, with regression coverage for context routing and explicit override behavior. (#27338) thanks @AaronL725.
-
LINE/Inline directives auth: gate directive parsing (/model, /think, /verbose, /reasoning, /queue) on resolved authorization (command.isAuthorizedSender) so commands.allowFrom-authorized LINE senders are not silently stripped when raw CommandAuthorized is unset. Landed from contributor PR #27248 by @kevinWangSheng. (#27240)
-
Onboarding/Gateway: seed default Control UI allowedOrigins for non-loopback binds during onboarding (localhost/127.0.0.1 plus custom bind host) so fresh non-loopback setups do not fail startup due to missing origin policy. (#26157) thanks @stakeswky.
-
Docker/GCP onboarding: reduce first-build OOM risk by capping Node heap during pnpm install, reuse existing gateway token during docker-setup.sh reruns so .env stays aligned with config, auto-bootstrap Control UI allowed origins for non-loopback Docker binds, and add GCP docs guidance for tokenized dashboard links + pairing recovery commands. (#26253) Thanks @pandego.
-
CLI/Gateway --force in non-root Docker: recover from lsof permission failures (EACCES/EPERM) by falling back to fuser kill + probe-based port checks, so openclaw gateway --force works for default container node user flows. (#27941)
-
Gateway/Bind visibility: emit a startup warning when binding to non-loopback addresses so operators get explicit exposure guidance in runtime logs. (#25397) thanks @let5sne.
-
Sessions cleanup/Doctor: add openclaw sessions cleanup --fix-missing to prune store entries whose transcript files are missing, including doctor guidance and CLI coverage. Landed from contributor PR #27508 by @Sid-Qin. (#27422)
-
Doctor/State integrity: ignore metadata-only slash routing sessions when checking recent missing transcripts so openclaw doctor no longer reports false-positive transcript-missing warnings for *:slash:* keys. (#27375) thanks @gumadeiras.
-
CLI/Gateway status: force local gateway status probe host to 127.0.0.1 for bind=lan so co-located probes do not trip non-loopback plaintext WebSocket checks. (#26997) thanks @chikko80.
-
CLI/Gateway auth: align gateway run --auth parsing/help text with supported gateway auth modes by accepting none and trusted-proxy (in addition to token/password) for CLI overrides. (#27469) thanks @s1korrrr.
-
CLI/Daemon status TLS probe: use wss:// and forward local TLS certificate fingerprint for TLS-enabled gateway daemon probes so openclaw daemon status works with gateway.bind=lan + gateway.tls.enabled=true. (#24234) thanks @liuy.
-
Podman/Default bind: change run-openclaw-podman.sh default gateway bind from lan to loopback and document explicit LAN opt-in with Control UI origin configuration. (#27491) thanks @robbyczgw-cla.
-
Daemon/macOS launchd: forward proxy env vars into supervised service environments, keep LaunchAgent KeepAlive=true semantics, and harden restart sequencing to print -> bootout -> wait old pid exit -> bootstrap -> kickstart. (#27276) thanks @frankekn.
-
Gateway/macOS restart-loop hardening: detect OpenClaw-managed supervisor markers during SIGUSR1 restart handoff, clean stale gateway PIDs before /restart launchctl/systemctl triggers, and set LaunchAgent ThrottleInterval=60 to bound launchd retry storms during lock-release races. Landed from contributor PRs #27655 (@taw0002), #27448 (@Sid-Qin), and #27650 (@kevinWangSheng). (#27605, #27590, #26904, #26736)
-
Models/MiniMax auth header defaults: set authHeader: true for both onboarding-generated MiniMax API providers and implicit built-in MiniMax (minimax, minimax-portal) provider templates so first requests no longer fail with MiniMax 401 authentication_error due to missing Authorization header. Landed from contributor PRs #27622 by @riccoyuanft and #27631 by @kevinWangSheng. (#27600, #15303)
-
Auth/Auth profiles: normalize auth-profiles.json alias fields (mode -> type, apiKey -> key) before credential validation so entries copied from openclaw.json auth examples are no longer silently dropped. (#26950) thanks @byungsker.
-
Models/Profile suffix parsing: centralize trailing @profile parsing and only treat @ as a profile separator when it appears after the final /, preserving model IDs like openai/@cf/... and openrouter/@preset/... across /model directive parsing and allowlist model resolution, with regression coverage.
-
Models/OpenAI Codex config schema parity: accept openai-codex-responses in the config model API schema and TypeScript ModelApi union, with regression coverage for config validation. Landed from contributor PR #27501 by @AytuncYildizli. Thanks @AytuncYildizli.
-
Agents/Models config: preserve agent-level provider apiKey and baseUrl during merge-mode models.json updates when agent values are present. (#27293) thanks @Sid-Qin.
-
Azure OpenAI Responses: force store=true for azure-openai-responses direct responses API calls to avoid multi-turn 400 failures. Landed from contributor PR #27499 by @polarbear-Yang. (#27497)
-
Security/Node exec approvals: require structured commandArgv approvals for host=node, enforce versioned systemRunBindingV1 matching for argv/cwd/session/agent/env context with fail-closed behavior on missing/mismatched bindings, and add GIT_EXTERNAL_DIFF to blocked host env keys. This ships in the next npm release (2026.2.26). Thanks @tdjackey for reporting.
-
Security/Plugin channel HTTP auth: normalize protected /api/channels path checks against canonicalized request paths (case + percent-decoding + slash normalization), resolve encoded dot-segment traversal variants, and fail closed on malformed %-encoded channel prefixes so alternate-path variants cannot bypass gateway auth. This ships in the next npm release (2026.2.26). Thanks @zpbrent for reporting.
-
Security/Gateway node pairing: pin paired-device platform/deviceFamily metadata across reconnects and bind those fields into device-auth signatures, so reconnect metadata spoofing cannot expand node command allowlists without explicit repair pairing. This ships in the next npm release (2026.2.26). Thanks @76embiid21 for reporting.
-
Security/Sandbox path alias guard: reject broken symlink targets by resolving through existing ancestors and failing closed on out-of-root targets, preventing workspace-only apply_patch writes from escaping sandbox/workspace boundaries via dangling symlinks. This ships in the next npm release (2026.2.26). Thanks @tdjackey for reporting.
-
Security/Workspace FS boundary aliases: harden canonical boundary resolution for non-existent-leaf symlink aliases while preserving valid in-root aliases, preventing first-write workspace escapes via out-of-root symlink targets. This ships in the next npm release (2026.2.26). Thanks @tdjackey for reporting.
-
Security/Config includes: harden $include file loading with verified-open reads, reject hardlinked include aliases, and enforce include file-size guardrails so config include resolution remains bounded to trusted in-root files. This ships in the next npm release (2026.2.26). Thanks @zpbrent for reporting.
-
Security/Node exec approvals hardening: freeze immutable approval-time execution plans (argv/cwd/agentId/sessionKey) via system.run.prepare, enforce those canonical plan values during approval forwarding/execution, and reject mutable parent-symlink cwd paths during approval-plan building to prevent approval bypass via symlink rebind. This ships in the next npm release (2026.2.26). Thanks @tdjackey for reporting.
-
Security/Microsoft Teams media fetch: route Graph message/hosted-content/attachment fetches and auth-scope fallback attachment downloads through shared SSRF-guarded fetch paths, and centralize hostname-suffix allowlist policy helpers in the plugin SDK to remove channel/plugin drift. This ships in the next npm release (2026.2.26). Thanks @tdjackey for reporting.
-
Security/Voice Call (Twilio): bind webhook replay + manager dedupe identity to authenticated request material, remove unsigned i-twilio-idempotency-token trust from replay/dedupe keys, and thread verified request identity through provider parse flow to harden cross-provider event dedupe. This ships in the next npm release (2026.2.26). Thanks @tdjackey for reporting.
-
Security/Exec approvals forwarding: prefer turn-source channel/account/thread metadata when resolving approval delivery targets so stale session routes do not misroute approval prompts.
-
Security/Pairing multi-account isolation: enforce account-scoped pairing allowlists and pending-request storage across core + extension message channels while preserving channel-scoped defaults for the default account. This ships in the next npm release (2026.2.26). Thanks @tdjackey for reporting and @gumadeiras for implementation.
-
Config/Plugins entries: treat unknown plugins.entries.* ids as startup warnings (ignored stale keys) instead of hard validation failures that can crash-loop gateway boot. Landed from contributor PR #27506 by @Sid-Qin. (#27455)
-
Telegram native commands: degrade command registration on BOT_COMMANDS_TOO_MUCH by retrying with fewer commands instead of crash-looping startup sync. Landed from contributor PR #27512 by @Sid-Qin. (#27456)
-
Web tools/Proxy: route web_search provider HTTP calls (Brave, Perplexity, xAI, Gemini, Kimi), redirect resolution, and web_fetch through a shared proxy-aware SSRF guard path so gateway installs behind HTTP_PROXY/HTTPS_PROXY/ALL_PROXY no longer fail with transport fetch failed errors. (#27430) thanks @kevinWangSheng.
Gateway shared-auth scopes: preserve requested operator scopes for shared-token clients when device identity is unavailable, instead of clearing scopes during auth handling. Landed from contributor PR #27498 by @kevinWangSheng. (#27494)
-
Cron/Hooks isolated routing: preserve canonical agent:* session keys in isolated runs so already-qualified keys are not double-prefixed (for example agent:main:main no longer becomes agent:main:agent:main:main). Landed from contributor PR #27333 by @MaheshBhushan. (#27289, #27282)
-
Channels/Multi-account config: when adding a non-default channel account to a single-account top-level channel setup, move existing account-scoped top-level single-account values into channels..accounts.default before writing the new account so the original account keeps working without duplicated account values at channel root; openclaw doctor --fix now repairs previously mixed channel account shapes the same way. (#27334) thanks @gumadeiras.
-
iOS/Talk mode: stop injecting the voice directive hint into iOS Talk prompts and remove the Voice Directive Hint setting, reducing model bias toward tool-style TTS directives and keeping relay responses text-first by default. (#27543) thanks @ngutman.
-
CI/Windows: shard the Windows checks-windows test lane into two matrix jobs and honor explicit shard index overrides in scripts/test-parallel.mjs to reduce CI critical-path wall time. (#27234) Thanks @joshavant.
+
Plugin command/runtime hardening: validate and normalize plugin command name/description at registration boundaries, and guard Telegram native menu normalization paths so malformed plugin command specs cannot crash startup (trim on undefined). (#31997) Fixes #31944. Thanks @liuxiaopai-ai.
+
Telegram: guard duplicate-token checks and gateway startup token normalization when account tokens are missing, preventing token.trim() crashes during status/start flows. (#31973) Thanks @ningding97.
+
Discord/lifecycle startup status: push an immediate connected status snapshot when the gateway is already connected before lifecycle debug listeners attach, with abort-guarding to avoid contradictory status flips during pre-aborted startup. (#32336) Thanks @mitchmcalister.
+
Feishu/LINE group system prompts: forward per-group systemPrompt config into inbound context GroupSystemPrompt for Feishu and LINE group/room events so configured group-specific behavior actually applies at dispatch time. (#31713) Thanks @whiskyboy.
+
Mentions/Slack formatting hardening: add null-safe guards for runtime text normalization paths so malformed/undefined text payloads do not crash mention stripping or mrkdwn conversion. (#31865) Thanks @stone-jin.
+
Feishu/Plugin sdk compatibility: add safe webhook default fallbacks when loading Feishu monitor state so mixed-version installs no longer crash if older openclaw/plugin-sdk builds omit webhook default constants. (#31606)
+
Feishu/group broadcast dispatch: add configurable multi-agent group broadcast dispatch with observer-session isolation, cross-account dedupe safeguards, and non-mention history buffering rules that avoid duplicate replay in broadcast/topic workflows. (#29575) Thanks @ohmyskyhigh.
+
Gateway/Subagent TLS pairing: allow authenticated local gateway-client backend self-connections to skip device pairing while still requiring pairing for non-local/direct-host paths, restoring sessions_spawn with gateway.tls.enabled=true in Docker/LAN setups. Fixes #30740. Thanks @Sid-Qin and @vincentkoc.
+
Browser/CDP startup diagnostics: include Chrome stderr output and a Linux no-sandbox hint in startup timeout errors so failed launches are easier to diagnose. (#29312) Thanks @veast.
+
Synology Chat/webhook ingress hardening: enforce bounded body reads (size + timeout) via shared request-body guards to prevent unauthenticated slow-body hangs before token validation. (#25831) Thanks @bmendonca3.
+
Feishu/Dedup restart resilience: warm persistent dedup state into memory on monitor startup so retry events after gateway restart stay suppressed without requiring initial on-disk probe misses. (#31605)
+
Voice-call/runtime lifecycle: prevent EADDRINUSE loops by resetting failed runtime promises, making webhook start() idempotent with the actual bound port, and fully cleaning up webhook/tunnel/tailscale resources after startup failures. (#32395) Thanks @scoootscooob.
+
Gateway/Security hardening: tie loopback-origin dev allowance to actual local socket clients (not Host header claims), add explicit warnings/metrics when gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback accepts websocket origins, harden safe-regex detection for quantified ambiguous alternation patterns (for example (a|aa)+), and bound large regex-evaluation inputs for session-filter and log-redaction paths.
+
Gateway/Plugin HTTP hardening: require explicit auth for plugin route registration, add route ownership guards for duplicate path+match registrations, centralize plugin path matching/auth logic into dedicated modules, and share webhook target-route lifecycle wiring across channel monitors to avoid stale or conflicting registrations. Thanks @tdjackey for reporting.
+
Browser/Profile defaults: prefer openclaw profile over chrome in headless/no-sandbox environments unless an explicit defaultProfile is configured. (#14944) Thanks @BenediktSchackenberg.
+
Gateway/WS security: keep plaintext ws:// loopback-only by default, with explicit break-glass private-network opt-in via OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1; align onboarding/client/call validation and tests to this strict-default policy. (#28670) Thanks @dashed, @vincentkoc.
+
OpenAI Codex OAuth/TLS prerequisites: add an OAuth TLS cert-chain preflight with actionable remediation for cert trust failures, and gate doctor TLS prerequisite probing to OpenAI Codex OAuth-configured installs (or explicit doctor --deep) to avoid unconditional outbound probe latency. (#32051) Thanks @alexfilatov.
+
Security/Webhook request hardening: enforce auth-before-body parsing for BlueBubbles and Google Chat webhook handlers, add strict pre-auth body/time budgets for webhook auth paths (including LINE signature verification), and add shared in-flight/request guardrails plus regression tests/lint checks to prevent reintroducing unauthenticated slow-body DoS patterns. Thanks @GCXWLP for reporting.
+
CLI/Config validation and routing hardening: dedupe openclaw config validate failures to a single authoritative report, expose allowed-values metadata/hints across core Zod and plugin AJV validation (including --json fields), sanitize terminal-rendered validation text, and make command-path parsing root-option-aware across preaction/route/lazy registration (including routed config get/unset with split root options). Thanks @gumadeiras.
+
Browser/Extension relay reconnect tolerance: keep /json/version and /cdp reachable during short MV3 worker disconnects when attached targets still exist, and retain clients across reconnect grace windows. (#30232) Thanks @Sid-Qin.
+
CLI/Browser start timeout: honor openclaw browser --timeout start and stop by removing the fixed 15000ms override so slower Chrome startups can use caller-provided timeouts. (#22412, #23427) Thanks @vincentkoc.
+
Synology Chat/gateway lifecycle: keep startAccount pending until abort for inactive and active account paths to prevent webhook route restart loops under gateway supervision. (#23074) Thanks @druide67.
+
Exec approvals/allowlist matching: escape regex metacharacters in path-pattern literals (while preserving glob wildcards), preventing crashes on allowlisted executables like /usr/bin/g++ and correctly matching mixed wildcard/literal token paths. (#32162) Thanks @stakeswky.
+
Synology Chat/webhook compatibility: accept JSON and alias payload fields, allow token resolution from body/query/header sources, and ACK webhook requests with 204 to avoid persistent Processing... states in Synology Chat clients. (#26635) Thanks @memphislee09-source.
+
Voice-call/Twilio signature verification: retry signature validation across deterministic URL port variants (with/without port) to handle mixed Twilio signing behavior behind reverse proxies and non-standard ports. (#25140) Thanks @drvoss.
+
Slack/Bolt startup compatibility: remove invalid message.channels and message.groups event registrations so Slack providers no longer crash on startup with Bolt 4.6+; channel/group traffic continues through the unified message handler (channel_type). (#32033) Thanks @mahopan.
+
Slack/socket auth failure handling: fail fast on non-recoverable auth errors (account_inactive, invalid_auth, etc.) during startup and reconnect instead of retry-looping indefinitely, including unable_to_socket_mode_start error payload propagation. (#32377) Thanks @scoootscooob.
+
Gateway/macOS LaunchAgent hardening: write Umask=077 in generated gateway LaunchAgent plists so npm upgrades preserve owner-only default file permissions for gateway-created state files. (#31919) Fixes #31905. Thanks @liuxiaopai-ai.
+
macOS/LaunchAgent security defaults: write Umask=63 (octal 077) into generated gateway launchd plists so post-update service reinstalls keep owner-only file permissions by default instead of falling back to system 022. (#32022) Fixes #31905. Thanks @liuxiaopai-ai.
+
Media understanding/provider HTTP proxy routing: pass a proxy-aware fetch function from HTTPS_PROXY/HTTP_PROXY env vars into audio/video provider calls (with graceful malformed-proxy fallback) so transcription/video requests honor configured outbound proxies. (#27093) Thanks @mcaxtr.
+
Sandbox/workspace mount permissions: make primary /workspace bind mounts read-only whenever workspaceAccess is not rw (including none) across both core sandbox container and sandbox browser create flows. (#32227) Thanks @guanyu-zhang.
+
Tools/fsPolicy propagation: honor tools.fs.workspaceOnly for image/pdf local-root allowlists so non-sandbox media paths outside workspace are rejected when workspace-only mode is enabled. (#31882) Thanks @justinhuangcode.
+
Daemon/Homebrew runtime pinning: resolve Homebrew Cellar Node paths to stable Homebrew-managed symlinks (including versioned formulas like node@22) so gateway installs keep the intended runtime across brew upgrades. (#32185) Thanks @scoootscooob.
+
Browser/Security output boundary hardening: replace check-then-rename output commits with root-bound fd-verified writes, unify install/skills canonical path-boundary checks, and add regression coverage for symlink-rebind race paths across browser output and shared fs-safe write flows. Thanks @tdjackey for reporting.
+
Gateway/Security canonicalization hardening: decode plugin route path variants to canonical fixpoint (with bounded depth), fail closed on canonicalization anomalies, and enforce gateway auth for deeply encoded /api/channels/* variants to prevent alternate-path auth bypass through plugin handlers. Thanks @tdjackey for reporting.
+
Browser/Gateway hardening: preserve env credentials for OPENCLAW_GATEWAY_URL / CLAWDBOT_GATEWAY_URL while treating explicit --url as override-only auth, and make container browser hardening flags optional with safer defaults for Docker/LXC stability. (#31504) Thanks @vincentkoc.
+
Gateway/Control UI basePath webhook passthrough: let non-read methods under configured controlUiBasePath fall through to plugin routes (instead of returning Control UI 405), restoring webhook handlers behind basePath mounts. (#32311) Thanks @ademczuk.
+
Control UI/Legacy browser compatibility: replace toSorted-dependent cron suggestion sorting in app-render with a compatibility helper so older browsers without Array.prototype.toSorted no longer white-screen. (#31775) Thanks @liuxiaopai-ai.
+
macOS/PeekabooBridge: add compatibility socket symlinks for legacy clawdbot, clawdis, and moltbot Application Support socket paths so pre-rename clients can still connect. (#6033) Thanks @lumpinif and @vincentkoc.
+
Gateway/message tool reliability: avoid false Unknown channel failures when message.* actions receive platform-specific channel ids by falling back to toolContext.currentChannelProvider, and prevent health-monitor restart thrash for channels that just (re)started by adding a per-channel startup-connect grace window. (from #32367) Thanks @MunemHashmi.
+
Windows/Spawn canonicalization: unify non-core Windows spawn handling across ACP client, QMD/mcporter memory paths, and sandbox Docker execution using the shared wrapper-resolution policy, with targeted regression coverage for .cmd shim unwrapping and shell fallback behavior. (#31750) Thanks @Takhoffman.
+
Security/ACP sandbox inheritance: enforce fail-closed runtime guardrails for sessions_spawn with runtime="acp" by rejecting ACP spawns from sandboxed requester sessions and rejecting sandbox="require" for ACP runtime, preventing sandbox-boundary bypass via host-side ACP initialization. (#32254) Thanks @tdjackey for reporting, and @dutifulbob for the fix.
+
Security/Web tools SSRF guard: keep DNS pinning for untrusted web_fetch and citation-redirect URL checks when proxy env vars are set, and require explicit dangerous opt-in before env-proxy routing can bypass pinned dispatch for trusted/operator-controlled endpoints. Thanks @tdjackey for reporting.
+
Gemini schema sanitization: coerce malformed JSON Schema properties values (null, arrays, primitives) to {} before provider validation, preventing downstream strict-validator crashes on invalid plugin/tool schemas. (#32332) Thanks @webdevtodayjason.
+
Media understanding/malformed attachment guards: harden attachment selection and decision summary formatting against non-array or malformed attachment payloads to prevent runtime crashes on invalid inbound metadata shapes. (#28024) Thanks @claw9267.
+
Browser/Extension navigation reattach: preserve debugger re-attachment when relay is temporarily disconnected by deferring relay attach events until reconnect/re-announce, reducing post-navigation tab loss. (#28725) Thanks @stone-jin.
+
Browser/Extension relay stale tabs: evict stale cached targets from /json/list when extension targets are destroyed/crashed or commands fail with missing target/session errors. (#6175) Thanks @vincentkoc.
+
Browser/CDP startup readiness: wait for CDP websocket readiness after launching Chrome and cleanly stop/reset when readiness never arrives, reducing follow-up PortInUseError races after browser start/open. (#29538) Thanks @AaronWander.
+
OpenAI/Responses WebSocket tool-call id hygiene: normalize blank/whitespace streamed tool-call ids before persistence, and block empty function_call_output.call_id payloads in the WS conversion path to avoid OpenAI 400 errors (Invalid 'input[n].call_id': empty string), with regression coverage for both inbound stream normalization and outbound payload guards.
+
Security/Nodes camera URL downloads: bind node camera.snap/camera.clip URL payload downloads to the resolved node host, enforce fail-closed behavior when node remoteIp is unavailable, and use SSRF-guarded fetch with redirect host/protocol checks to prevent off-node fetch pivots. Thanks @tdjackey for reporting.
+
Config/backups hardening: enforce owner-only (0600) permissions on rotated config backups and clean orphan .bak.* files outside the managed backup ring, reducing credential leakage risk from stale or permissive backup artifacts. (#31718) Thanks @YUJIE2002.
+
Telegram/inbound media filenames: preserve original file_name metadata for document/audio/video/animation downloads (with fetch/path fallbacks), so saved inbound attachments keep sender-provided names instead of opaque Telegram file paths. (#31837) Thanks @Kay-051.
+
Gateway/OpenAI chat completions: honor x-openclaw-message-channel when building agentCommand input for /v1/chat/completions, preserving caller channel identity instead of forcing webchat. (#30462) Thanks @bmendonca3.
+
Plugin SDK/runtime hardening: add package export verification in CI/release checks to catch missing runtime exports before publish-time regressions. (#28575) Thanks @Glucksberg.
+
Media/MIME normalization: normalize parameterized/case-variant MIME strings in kindFromMime (for example Audio/Ogg; codecs=opus) so WhatsApp voice notes are classified as audio and routed through transcription correctly. (#32280) Thanks @Lucenx9.
+
Discord/audio preflight mentions: detect audio attachments via Discord content_type and gate preflight transcription on typed text (not media placeholders), so guild voice-note mentions are transcribed and matched correctly. (#32136) Thanks @jnMetaCode.
+
Feishu/topic session routing: use thread_id as topic session scope fallback when root_id is absent, keep first-turn topic keys stable across thread creation, and force thread replies when inbound events already carry topic/thread context. (#29788) Thanks @songyaolun.
+
Gateway/Webchat NO_REPLY streaming: suppress assistant lead-fragment deltas that are prefixes of NO_REPLY and keep final-message buffering in sync, preventing partial NO leaks on silent-response runs while preserving legitimate short replies. (#32073) Thanks @liuxiaopai-ai.
+
Telegram/models picker callbacks: keep long model buttons selectable by falling back to compact callback payloads and resolving provider ids on selection (with provider re-prompt on ambiguity), avoiding Telegram 64-byte callback truncation failures. (#31857) Thanks @bmendonca3.
+
Context-window metadata warmup: add exponential config-load retry backoff (1s -> 2s -> 4s, capped at 60s) so transient startup failures recover automatically without hot-loop retries.
+
Voice-call/Twilio external outbound: auto-register webhook-first outbound-api calls (initiated outside OpenClaw) so media streams are accepted and call direction metadata stays accurate. (#31181) Thanks @scoootscooob.
+
Feishu/topic root replies: prefer root_id as outbound replyTargetMessageId when present, and parse millisecond message_create_time values correctly so topic replies anchor to the root message in grouped thread flows. (#29968) Thanks @bmendonca3.
+
Feishu/DM pairing reply target: send pairing challenge replies to chat: instead of user: so Lark/Feishu private chats with user-id-only sender payloads receive pairing messages reliably. (#31403) Thanks @stakeswky.
+
Feishu/Lark private DM routing: treat inbound chat_type: "private" as direct-message context for pairing/mention-forward/reaction synthetic handling so Lark private chats behave like Feishu p2p DMs. (#31400) Thanks @stakeswky.
+
Signal/message actions: allow react to fall back to toolContext.currentMessageId when messageId is omitted, matching Telegram behavior and unblocking agent-initiated reactions on inbound turns. (#32217) Thanks @dunamismax.
+
Discord/message actions: allow react to fall back to toolContext.currentMessageId when messageId is omitted, matching Telegram/Signal reaction ergonomics in inbound turns.
+
Synology Chat/reply delivery: resolve webhook usernames to Chat API user_id values for outbound chatbot replies, avoiding mismatches between webhook user IDs and method=chatbot recipient IDs in multi-account setups. (#23709) Thanks @druide67.
+
Slack/thread context payloads: only inject thread starter/history text on first thread turn for new sessions while preserving thread metadata, reducing repeated context-token bloat on long-lived thread sessions. (#32133) Thanks @sourman.
+
Slack/session routing: keep top-level channel messages in one shared session when replyToMode=off, while preserving thread-scoped keys for true thread replies and non-off modes. (#32193) Thanks @bmendonca3.
Webchat/Feishu session continuation: preserve routable OriginatingChannel/OriginatingTo metadata from session delivery context in chat.send, and prefer provider-normalized channel when deciding cross-channel route dispatch so Webchat replies continue on the selected Feishu session instead of falling back to main/internal session routing. (#31573)
+
Telegram/implicit mention forum handling: exclude Telegram forum system service messages (forum_topic_*, general_forum_topic_*) from reply-chain implicit mention detection so requireMention does not get bypassed inside bot-created topic lifecycle events. (#32262) Thanks @scoootscooob.
+
Slack/inbound debounce routing: isolate top-level non-DM message debounce keys by message timestamp to avoid cross-thread collisions, preserve DM batching, and flush pending top-level buffers before immediate non-debounce follow-ups to keep ordering stable. (#31951) Thanks @scoootscooob.
+
Feishu/Duplicate replies: suppress same-target reply dispatch when message-tool sends use generic provider metadata (provider: "message") and normalize lark/feishu provider aliases during duplicate-target checks, preventing double-delivery in Feishu sessions. (#31526)
+
Webchat/silent token leak: filter assistant NO_REPLY-only transcript entries from chat.history responses and add client-side defense-in-depth guards in the chat controller so internal silent tokens never render as visible chat bubbles. (#32015) Consolidates overlap from #32183, #32082, #32045, #32052, #32172, and #32112. Thanks @ademczuk, @liuxiaopai-ai, @ningding97, @bmendonca3, and @x4v13r1120.
+
Doctor/local memory provider checks: stop false-positive local-provider warnings when provider=local and no explicit modelPath is set by honoring default local model fallback while still warning when gateway probe reports local embeddings not ready. (#32014) Fixes #31998. Thanks @adhishthite.
+
Media understanding/parakeet CLI output parsing: read parakeet-mlx transcripts from --output-dir/.txt when txt output is requested (or default), with stdout fallback for non-txt formats. (#9177) Thanks @mac-110.
+
Media understanding/audio transcription guard: skip tiny/empty audio files (<1024 bytes) before provider/CLI transcription to avoid noisy invalid-audio failures and preserve clean fallback behavior. (#8388) Thanks @Glucksberg.
+
Gateway/Plugin HTTP route precedence: run explicit plugin HTTP routes before the Control UI SPA catch-all so registered plugin webhook/custom paths remain reachable, while unmatched paths still fall through to Control UI handling. (#31885) Thanks @Sid-Qin.
+
Gateway/Node browser proxy routing: honor profile from browser.request JSON body when query params omit it, while preserving query-profile precedence when both are present. (#28852) Thanks @Sid-Qin.
+
Gateway/Control UI basePath POST handling: return 405 for POST on exact basePath routes (for example /openclaw) instead of redirecting, and add end-to-end regression coverage that root-mounted webhook POST paths still pass through to plugin handlers. (#31349) Thanks @Sid-Qin.
+
Browser/default profile selection: default browser.defaultProfile behavior now prefers openclaw (managed standalone CDP) when no explicit default is configured, while still auto-provisioning the chrome relay profile for explicit opt-in use. (#32031) Fixes #31907. Thanks @liuxiaopai-ai.
+
Sandbox/mkdirp boundary checks: allow existing in-boundary directories to pass mkdirp boundary validation when directory open probes return platform-specific I/O errors, with regression coverage for directory-safe fallback behavior. (#31547) Thanks @stakeswky.
+
Models/config env propagation: apply config.env.vars before implicit provider discovery in models bootstrap so config-scoped credentials are visible to implicit provider resolution paths. (#32295) Thanks @hsiaoa.
+
Models/Codex usage labels: infer weekly secondary usage windows from reset cadence when API window seconds are ambiguously reported as 24h, so openclaw models status no longer mislabels weekly limits as daily. (#31938) Thanks @bmendonca3.
+
Gateway/Heartbeat model reload: treat models.* and agents.defaults.model config updates as heartbeat hot-reload triggers so heartbeat picks up model changes without a full gateway restart. (#32046) Thanks @stakeswky.
+
Memory/LanceDB embeddings: forward configured embedding.dimensions into OpenAI embeddings requests so vector size and API output dimensions stay aligned when dimensions are explicitly configured. (#32036) Thanks @scotthuang.
+
Gateway/Control UI method guard: allow POST requests to non-UI routes to fall through when no base path is configured, and add POST regression coverage for fallthrough and base-path 405 behavior. (#23970) Thanks @tyler6204.
+
Browser/CDP status accuracy: require a successful Browser.getVersion response over the CDP websocket (not just socket-open) before reporting cdpReady, so stale idle command channels are surfaced as unhealthy. (#23427) Thanks @vincentkoc.
+
Daemon/systemd checks in containers: treat missing systemctl invocations (including spawn systemctl ENOENT/EACCES) as unavailable service state during is-enabled checks, preventing container flows from failing with Gateway service check failed before install/status handling can continue. (#26089) Thanks @sahilsatralkar and @vincentkoc.
+
Security/Node exec approvals: revalidate approval-bound cwd identity immediately before execution/forwarding and fail closed with an explicit denial when cwd drifts after approval hardening.
+
Security audit/skills workspace hardening: add skills.workspace.symlink_escape warning in openclaw security audit when workspace skills/**/SKILL.md resolves outside the workspace root (for example symlink-chain drift), plus docs coverage in the security glossary.
+
Security/Node exec approvals: preserve shell/dispatch-wrapper argv semantics during approval hardening so approved wrapper commands (for example env sh -c ...) cannot drift into a different runtime command shape, and add regression coverage for both approval-plan generation and approved runtime execution paths. Thanks @tdjackey for reporting.
+
Security/fs-safe write hardening: make writeFileWithinRoot use same-directory temp writes plus atomic rename, add post-write inode/hardlink revalidation with security warnings on boundary drift, and avoid truncating existing targets when final rename fails.
+
Security/Skills archive extraction: unify tar extraction safety checks across tar.gz and tar.bz2 install flows, enforce tar compressed-size limits, and fail closed if tar.bz2 archives change between preflight and extraction to prevent bypasses of entry-type/size guardrails. Thanks @GCXWLP for reporting.
+
Security/Prompt spoofing hardening: stop injecting queued runtime events into user-role prompt text, route them through trusted system-prompt context, and neutralize inbound spoof markers like [System Message] and line-leading System: in untrusted message content. (#30448)
+
Sandbox/Docker setup command parsing: accept agents.*.sandbox.docker.setupCommand as either a string or a string array, and normalize arrays to newline-delimited shell scripts so multi-step setup commands no longer concatenate without separators. (#31953) Thanks @liuxiaopai-ai.
+
Sandbox/Bootstrap context boundary hardening: reject symlink/hardlink alias bootstrap seed files that resolve outside the source workspace and switch post-compaction AGENTS.md context reads to boundary-verified file opens, preventing host file content from being injected via workspace aliasing. Thanks @tdjackey for reporting.
+
Agents/Sandbox workdir mapping: map container workdir paths (for example /workspace) back to the host workspace before sandbox path validation so exec requests keep the intended directory in containerized runs instead of falling back to an unavailable host path. (#31841) Thanks @liuxiaopai-ai.
+
Docker/Sandbox bootstrap hardening: make OPENCLAW_SANDBOX opt-in parsing explicit (1|true|yes|on), support custom Docker socket paths via OPENCLAW_DOCKER_SOCKET, defer docker.sock exposure until sandbox prerequisites pass, and reset/roll back persisted sandbox mode to off when setup is skipped or partially fails to avoid stale broken sandbox state. (#29974) Thanks @jamtujest and @vincentkoc.
+
Hooks/webhook ACK compatibility: return 200 (instead of 202) for successful /hooks/agent requests so providers that require 200 (for example Forward Email) accept dispatched agent hook deliveries. (#28204) Thanks @Glucksberg.
+
Feishu/Run channel fallback: prefer Provider over Surface when inferring queued run messageProvider fallback (when OriginatingChannel is missing), preventing Feishu turns from being mislabeled as webchat in mixed relay metadata contexts. (#31880) Fixes #31859. Thanks @liuxiaopai-ai.
+
Skills/sherpa-onnx-tts: run the sherpa-onnx-tts bin under ESM (replace CommonJS require imports) and add regression coverage to prevent require is not defined in ES module scope startup crashes. (#31965) Thanks @bmendonca3.
+
Inbound metadata/direct relay context: restore direct-channel conversation metadata blocks for external channels (for example WhatsApp) while preserving webchat-direct suppression, so relay agents recover sender/message identifiers without reintroducing internal webchat metadata noise. (#31969) Fixes #29972. Thanks @Lucenx9.
+
Slack/Channel message subscriptions: register explicit message.channels and message.groups monitor handlers (alongside generic message) so channel/group event subscriptions are consumed even when Slack dispatches typed message event names. Fixes #31674.
+
Hooks/session-scoped memory context: expose ephemeral sessionId in embedded plugin tool contexts and before_tool_call/after_tool_call hook contexts (including compaction and client-tool wiring) so plugins can isolate per-conversation state across /new and /reset. Related #31253 and #31304. Thanks @Sid-Qin and @Servo-AIpex.
+
Voice-call/Twilio inbound greeting: run answered-call initial notify greeting for Twilio instead of skipping the manager speak path, with regression coverage for both Twilio and Plivo notify flows. (#29121) Thanks @xinhuagu.
+
Voice-call/stale call hydration: verify active calls with the provider before loading persisted in-progress calls so stale locally persisted records do not block or misroute new call handling after restarts. (#4325) Thanks @garnetlyx.
+
Feishu/File upload filenames: percent-encode non-ASCII/special-character file_name values in Feishu multipart uploads so Chinese/symbol-heavy filenames are sent as proper attachments instead of plain text links. (#31179) Thanks @Kay-051.
+
Media/MIME channel parity: route Telegram/Signal/iMessage media-kind checks through normalized kindFromMime so mixed-case/parameterized MIME values classify consistently across message channels.
+
WhatsApp/inbound self-message context: propagate inbound fromMe through the web inbox pipeline and annotate direct self messages as (self) in envelopes so agents can distinguish owner-authored turns from contact turns. (#32167) Thanks @scoootscooob.
+
Webchat/stream finalization: persist streamed assistant text when final events omit message, while keeping final payload precedence and skipping empty stream buffers to prevent disappearing replies after tool turns. (#31920) Thanks @Sid-Qin.
+
Feishu/Inbound ordering: serialize message handling per chat while preserving cross-chat concurrency to avoid same-chat race drops under bursty inbound traffic. (#31807)
+
Feishu/Typing notification suppression: skip typing keepalive reaction re-adds when the indicator is already active, preventing duplicate notification pings from repeated identical emoji adds. (#31580)
+
Feishu/Probe failure backoff: cache API and timeout probe failures for one minute per account key while preserving abort-aware probe timeouts, reducing repeated health-check retries during transient credential/network outages. (#29970)
+
Feishu/Streaming block fallback: preserve markdown block stream text as final streaming-card content when final payload text is missing, while still suppressing non-card internal block chunk delivery. (#30663)
+
Feishu/Bitable API errors: unify Feishu Bitable tool error handling with structured LarkApiError responses and consistent API/context attribution across wiki/base metadata, field, and record operations. (#31450)
+
Feishu/Missing-scope grant URL fix: rewrite known invalid scope aliases (contact:contact.base:readonly) to valid scope names in permission grant links, so remediation URLs open with correct Feishu consent scopes. (#31943)
+
BlueBubbles/Message metadata: harden send response ID extraction, include sender identity in DM context, and normalize inbound message_id selection to avoid duplicate ID metadata. (#23970) Thanks @tyler6204.
+
WebChat/markdown tables: ensure GitHub-flavored markdown table parsing is explicitly enabled at render time and add horizontal overflow handling for wide tables, with regression coverage for table-only and mixed text+table content. (#32365) Thanks @BlueBirdBack.
+
Feishu/default account resolution: always honor explicit channels.feishu.defaultAccount during outbound account selection (including top-level-credential setups where the preferred id is not present in accounts), instead of silently falling back to another account id. (#32253) Thanks @bmendonca3.
+
Feishu/Sender lookup permissions: suppress user-facing grant prompts for stale non-existent scope errors (contact:contact.base:readonly) during best-effort sender-name resolution so inbound messages continue without repeated false permission notices. (#31761)
+
Discord/dispatch + Slack formatting: restore parallel outbound dispatch across Discord channels with per-channel queues while preserving in-channel ordering, and run Slack preview/stream update text through mrkdwn normalization for consistent formatting. (#31927) Thanks @Sid-Qin.
+
Feishu/Inbound debounce: debounce rapid same-chat sender bursts into one ordered dispatch turn, skip already-processed retries when composing merged text, and preserve bot-mention intent across merged entries to reduce duplicate or late inbound handling. (#31548)
+
Tests/Sandbox + archive portability: use junction-compatible directory-link setup on Windows and explicit file-symlink platform guards in symlink escape tests where unprivileged file symlinks are unavailable, reducing false Windows CI failures while preserving traversal checks on supported paths. (#28747) Thanks @arosstale.
+
Browser/Extension re-announce reliability: keep relay state in connecting when re-announce forwarding fails and extend debugger re-attach retries after navigation to reduce false attached states and post-nav disconnect loops. (#27630) Thanks @markmusson.
+
Browser/Act request compatibility: accept legacy flattened action="act" params (kind/ref/text/...) in addition to request={...} so browser act calls no longer fail with request required. (#15120) Thanks @vincentkoc.
+
OpenRouter/x-ai compatibility: skip reasoning.effort injection for x-ai/* models (for example Grok) so OpenRouter requests no longer fail with invalid-arguments errors on unsupported reasoning params. (#32054) Thanks @scoootscooob.
+
Models/openai-completions developer-role compatibility: force supportsDeveloperRole=false for non-native endpoints, treat unparseable baseUrl values as non-native, and add regression coverage for empty/malformed baseUrl plus explicit-true override behavior. (#29479) thanks @akramcodez.
+
Browser/Profile attach-only override: support browser.profiles..attachOnly (fallback to global browser.attachOnly) so loopback proxy profiles can skip local launch/port-ownership checks without forcing attach-only mode for every profile. (#20595) Thanks @unblockedgamesstudio and @vincentkoc.
+
Sessions/Lock recovery: detect recycled Linux PIDs by comparing lock-file starttime with /proc//stat starttime, so stale .jsonl.lock files are reclaimed immediately in containerized PID-reuse scenarios while preserving compatibility for older lock files. (#26443) Fixes #27252. Thanks @HirokiKobayashi-R and @vincentkoc.
+
Cron/isolated delivery target fallback: remove early unresolved-target return so cron delivery can flow through shared outbound target resolution (including per-channel resolveDefaultTo fallback) when delivery.to is omitted. (#32364) Thanks @hclsys.
+
OpenAI media capabilities: include audio in the OpenAI provider capability list so audio transcription models are eligible in media-understanding provider selection. (#12717) Thanks @openjay.
+
Browser/Managed tab cap: limit loopback managed openclaw page tabs to 8 via best-effort cleanup after tab opens to reduce long-running renderer buildup while preserving attach-only and remote profile behavior. (#29724) Thanks @pandego.
+
Docker/Image health checks: add Dockerfile HEALTHCHECK that probes gateway GET /healthz so container runtimes can mark unhealthy instances without requiring auth credentials in the probe command. (#11478) Thanks @U-C4N and @vincentkoc.
+
Gateway/Node dangerous-command parity: include sms.send in default onboarding node denyCommands, share onboarding deny defaults with the gateway dangerous-command source of truth, and include sms.send in phone-control /phone arm writes handling so SMS follows the same break-glass flow as other dangerous node commands. Thanks @zpbrent.
+
Pairing/AllowFrom account fallback: handle omitted accountId values in readChannelAllowFromStore and readChannelAllowFromStoreSync as default, while preserving legacy unscoped allowFrom merges for default-account flows. Thanks @Sid-Qin and @vincentkoc.
+
Browser/Remote CDP ownership checks: skip local-process ownership errors for non-loopback remote CDP profiles when HTTP is reachable but the websocket handshake fails, and surface the remote websocket attach/retry path instead. (#15582) Landed from contributor (#28780) Thanks @stubbi, @bsormagec, @unblockedgamesstudio and @vincentkoc.
+
Browser/CDP proxy bypass: force direct loopback agent paths and scoped NO_PROXY expansion for localhost CDP HTTP/WS connections when proxy env vars are set, so browser relay/control still works behind global proxy settings. (#31469) Thanks @widingmarcus-cyber.
+
Sessions/idle reset correctness: preserve existing updatedAt during inbound metadata-only writes so idle-reset boundaries are not unintentionally refreshed before actual user turns. (#32379) Thanks @romeodiaz.
+
Sessions/lock recovery: reclaim orphan legacy same-PID lock files missing starttime when no in-process lock ownership exists, avoiding false lock timeouts after PID reuse while preserving active lock safety checks. (#32081) Thanks @bmendonca3.
+
Sessions/store cache invalidation: reload cached session stores when file size changes within the same mtime tick by keying cache validation on a single file-stat snapshot (mtimeMs + sizeBytes), with regression coverage for same-tick rewrites. (#32191) Thanks @jalehman.
+
Agents/Subagents sessions_spawn: reject malformed agentId inputs before normalization (for example error-message/path-like strings) to prevent unintended synthetic agent IDs and ghost workspace/session paths; includes strict validation regression coverage. (#31381) Thanks @openperf.
+
CLI/installer Node preflight: enforce Node.js v22.12+ consistently in both openclaw.mjs runtime bootstrap and installer active-shell checks, with actionable nvm recovery guidance for mismatched shell PATH/defaults. (#32356) Thanks @jasonhargrove.
+
Web UI/config form: support SecretInput string-or-secret-ref unions in map additionalProperties, so provider API key fields stay editable instead of being marked unsupported. (#31866) Thanks @ningding97.
+
Auto-reply/inline command cleanup: preserve newline structure when stripping inline /status and extracting inline slash commands by collapsing only horizontal whitespace, preventing paragraph flattening in multi-line replies. (#32224) Thanks @scoootscooob.
+
Config/raw redaction safety: preserve non-sensitive literals during raw redaction round-trips, scope SecretRef redaction to secret IDs (not structural fields like source/provider), and fall back to structured raw redaction when text replacement cannot restore the original config shape. (#32174) Thanks @bmendonca3.
+
Hooks/runtime stability: keep the internal hook handler registry on a globalThis singleton so hook registration/dispatch remains consistent when bundling emits duplicate module copies. (#32292) Thanks @Drickon.
+
Hooks/after_tool_call: include embedded session context (sessionKey, agentId) and fire the hook exactly once per tool execution by removing duplicate adapter-path dispatch in embedded runs. (#32201) Thanks @jbeno, @scoootscooob, @vincentkoc.
+
Hooks/tool-call correlation: include runId and toolCallId in plugin tool hook payloads/context and scope tool start/adjusted-param tracking by run to prevent cross-run collisions in before_tool_call and after_tool_call. (#32360) Thanks @vincentkoc.
+
Plugins/install diagnostics: reject legacy plugin package shapes without openclaw.extensions and return an explicit upgrade hint with troubleshooting docs for repackaging. (#32055) Thanks @liuxiaopai-ai.
+
Hooks/plugin context parity: ensure llm_input hooks in embedded attempts receive the same trigger and channelId-aware hookCtx used by the other hook phases, preserving channel/trigger-scoped plugin behavior. (#28623) Thanks @davidrudduck and @vincentkoc.
+
Plugins/hardlink install compatibility: allow bundled plugin manifests and entry files to load when installed via hardlink-based package managers (pnpm, bun) while keeping hardlink rejection enabled for non-bundled plugin sources. (#32119) Fixes #28175, #28404, #29455. Thanks @markfietje.
+
Cron/session reaper reliability: move cron session reaper sweeps into onTimerfinally and keep pruning active even when timer ticks fail early (for example cron store parse failures), preventing stale isolated run sessions from accumulating indefinitely. (#31996) Fixes #31946. Thanks @scoootscooob.
+
Cron/HEARTBEAT_OK summary leak: suppress fallback main-session enqueue for heartbeat/internal ack summaries in isolated announce mode so HEARTBEAT_OK noise never appears in user chat while real summaries still forward. (#32093) Thanks @scoootscooob.
+
Authentication: classify permission_error as auth_permanent for profile fallback. (#31324) Thanks @Sid-Qin.
+
Agents/host edit reliability: treat host edit-tool throws as success only when on-disk post-check confirms replacement likely happened (newText present and oldText absent), preventing false failure reports while avoiding pre-write false positives. (#32383) Thanks @polooooo.
+
Plugins/install fallback safety: resolve bare install specs to bundled plugin ids before npm lookup (for example diffs -> bundled @openclaw/diffs), keep npm fallback limited to true package-not-found errors, and continue rejecting non-plugin npm packages that fail manifest validation. (#32096) Thanks @scoootscooob.
+
Web UI/inline code copy fidelity: disable forced mid-token wraps on inline spans so copied UUID/hash/token strings preserve exact content instead of inserting line-break spaces. (#32346) Thanks @hclsys.
+
Restart sentinel formatting: avoid duplicate Reason: lines when restart message text already matches stats.reason, keeping restart notifications concise for users and downstream parsers. (#32083) Thanks @velamints2.
+
Auto-reply/followup queue: avoid stale callback reuse across idle-window restarts by caching the followup runner only when a drain actually starts, preserving enqueue ordering after empty-finalize paths. (#31902) Thanks @Lanfei.
+
Agents/tool-result guard: always clear pending tool-call state on interruptions even when synthetic tool results are disabled, preventing orphaned tool-use transcripts that cause follow-up provider request failures. (#32120) Thanks @jnMetaCode.
+
Failover/error classification: treat HTTP 529 (provider overloaded, common with Anthropic-compatible APIs) as rate_limit so model failover can engage instead of misclassifying the error path. (#31854) Thanks @bugkill3r.
+
Logging: use local time for logged timestamps instead of UTC, aligning log output with documented local timezone behavior and avoiding confusion during local diagnostics. (#28434) Thanks @liuy.
+
Agents/Subagent announce cleanup: keep completion-message runs pending while descendants settle, add a 30 minute hard-expiry backstop to avoid indefinite pending state, and keep retry bookkeeping resumable across deferred wakes. (#23970) Thanks @tyler6204.
+
Secrets/exec resolver timeout defaults: use provider timeoutMs as the default inactivity (noOutputTimeoutMs) watchdog for exec secret providers, preventing premature no-output kills for resolvers that start producing output after 2s. (#32235) Thanks @bmendonca3.
+
Auto-reply/reminder guard note suppression: when a turn makes reminder-like commitments but schedules no new cron jobs, suppress the unscheduled-reminder warning note only if an enabled cron already exists for the same session; keep warnings for unrelated sessions, disabled jobs, or unreadable cron store paths. (#32255) Thanks @scoootscooob.
+
Cron/isolated announce heartbeat suppression: treat multi-payload runs as skippable when any payload is a heartbeat ack token and no payload has media, preventing internal narration + trailing HEARTBEAT_OK from being delivered to users. (#32131) Thanks @adhishthite.
+
Cron/store migration: normalize legacy cron jobs with string schedule and top-level command/timeout fields into canonical schedule/payload/session-target shape on load, preventing schedule-error loops on old persisted stores. (#31926) Thanks @bmendonca3.
+
Tests/Windows backup rotation: skip chmod-only backup permission assertions on Windows while retaining compose/rotation/prune coverage across platforms to avoid false CI failures from Windows non-POSIX mode semantics. (#32286) Thanks @jalehman.
+
Tests/Subagent announce: set OPENCLAW_TEST_FAST=1 before importing subagent-announce format suites so module-level fast-mode constants are captured deterministically on Windows CI, preventing timeout flakes in nested completion announce coverage. (#31370) Thanks @zwffff.