SaylorTwift HF Staff commited on
Commit
bbcff29
·
verified ·
1 Parent(s): 205131a

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .env.example +95 -0
  2. .gitattributes +13 -35
  3. .gitignore +239 -0
  4. AGENTS.md +123 -0
  5. CHANGELOG/2026.1.24-2.md +6 -0
  6. CHANGELOG/2026.1.24-3.md +9 -0
  7. CHANGELOG/2026.1.29.md +122 -0
  8. CHANGELOG/2026.2.13.md +120 -0
  9. CHANGELOG/2026.2.17.md +179 -0
  10. CHANGELOG/2026.2.2-2.md +10 -0
  11. CHANGELOG/2026.2.2-3.md +6 -0
  12. CHANGELOG/2026.2.22.md +259 -0
  13. CHANGELOG/2026.2.23.md +63 -0
  14. CHANGELOG/2026.2.27.md +162 -0
  15. CHANGELOG/2026.4.1-beta.1.md +66 -0
  16. CHANGELOG/2026.4.1.md +37 -0
  17. CHANGELOG/2026.4.12.md +83 -0
  18. CHANGELOG/2026.4.26.md +310 -0
  19. CHANGELOG/2026.4.27.md +298 -0
  20. CHANGELOG/2026.4.5.md +348 -0
  21. CHANGELOG/2026.5.19.md +292 -0
  22. CHANGELOG/2026.5.28.md +54 -0
  23. CHANGELOG/2026.5.4.md +298 -0
  24. CHANGELOG/2026.5.5.md +95 -0
  25. CHANGELOG/2026.6.2.md +93 -0
  26. CHANGELOG/2026.6.6.md +238 -0
  27. CHANGELOG/2026.8.1.md +0 -0
  28. CHANGELOG/2026.9.1.md +1291 -0
  29. CLAUDE.md +123 -0
  30. CONTRIBUTING.md +233 -0
  31. SECURITY.md +385 -0
  32. VISION.md +144 -0
  33. appcast.xml +0 -0
  34. docker-compose.yml +135 -0
  35. fly.toml +41 -0
  36. git-hooks/pre-commit +9 -0
  37. node-runtime-recovery.d.mts +18 -0
  38. node-runtime-update.d.mts +4 -0
  39. node-version.d.mts +21 -0
  40. pnpm-workspace.yaml +121 -0
  41. test/cli-json-stdout.skills.e2e.test.ts +189 -0
  42. test/copilot-tool-policy-handoff.live.test.ts +338 -0
  43. test/extension-import-boundaries.test.ts +291 -0
  44. test/external-script-modules.d.ts +144 -0
  45. test/feishu-message-read-authority.integration.test.ts +755 -0
  46. test/gateway-cold-agent-abort.e2e.test.ts +162 -0
  47. test/gateway-external-state-ownership.e2e.test.ts +516 -0
  48. test/gateway-restored-requester-settle.e2e.test.ts +429 -0
  49. test/git-hooks-pre-commit-boundaries.test.ts +452 -0
  50. test/github-cli-preflight.e2e.test.ts +49 -0
.env.example ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenClaw .env example
2
+ #
3
+ # Quick start:
4
+ # 1) Copy this file to `.env` (for local runs from this repo), OR to `~/.openclaw/.env` (for launchd/systemd daemons).
5
+ # 2) Fill only the values you use.
6
+ # 3) Keep real secrets out of git.
7
+ #
8
+ # Env-source precedence for environment variables (highest -> lowest):
9
+ # process env, ./.env, ~/.openclaw/.env, then openclaw.json `env` block.
10
+ # Existing non-empty process env vars are not overridden by dotenv/config env loading.
11
+ # Note: direct config keys (for example `gateway.auth.token` or channel tokens in openclaw.json)
12
+ # are resolved separately from env loading and often take precedence over env fallbacks.
13
+
14
+ # -----------------------------------------------------------------------------
15
+ # Gateway auth + paths
16
+ # -----------------------------------------------------------------------------
17
+ # Required if the gateway binds beyond loopback. Leave blank to have OpenClaw
18
+ # auto-generate a token on first start, or provide your own using
19
+ # `openssl rand -hex 32`. The gateway will refuse to start if this is set to
20
+ # the documented example placeholder, so never copy-paste an example value
21
+ # from docs or tutorials into this file verbatim.
22
+ OPENCLAW_GATEWAY_TOKEN=
23
+
24
+ # Optional alternative auth mode (use token OR password).
25
+ # OPENCLAW_GATEWAY_PASSWORD=
26
+
27
+ # Optional path overrides (defaults shown for reference).
28
+ # OPENCLAW_STATE_DIR=~/.openclaw
29
+ # OPENCLAW_CONFIG_PATH=~/.openclaw/openclaw.json
30
+ # OPENCLAW_HOME=~
31
+ # Docker setup stores auth profile encryption key material outside the mounted
32
+ # OpenClaw state dir and mounts this host directory into the container.
33
+ # OPENCLAW_AUTH_PROFILE_SECRET_DIR=/absolute/path/to/.openclaw-auth-profile-secrets
34
+
35
+ # Allowlist of extra directories that `$include` directives in openclaw.json may
36
+ # resolve files from. Path-list separated (':' on POSIX, ';' on Windows). Each
37
+ # entry is tilde-expanded. Without this, `$include` is confined to the directory
38
+ # containing openclaw.json.
39
+ # OPENCLAW_INCLUDE_ROOTS=/etc/openclaw/shared:~/.openclaw/shared
40
+
41
+ # Optional: import missing keys from your login shell profile.
42
+ # OPENCLAW_LOAD_SHELL_ENV=1
43
+ # OPENCLAW_SHELL_ENV_TIMEOUT_MS=15000
44
+
45
+ # -----------------------------------------------------------------------------
46
+ # Model provider API keys (set at least one)
47
+ # -----------------------------------------------------------------------------
48
+ # OPENAI_API_KEY=sk-...
49
+ # ANTHROPIC_API_KEY=sk-ant-...
50
+ # GEMINI_API_KEY=...
51
+ # OPENROUTER_API_KEY=sk-or-...
52
+ # OPENCLAW_LIVE_OPENAI_KEY=sk-...
53
+ # OPENCLAW_LIVE_ANTHROPIC_KEY=sk-ant-...
54
+ # OPENCLAW_LIVE_GEMINI_KEY=...
55
+ # OPENAI_API_KEY_1=...
56
+ # ANTHROPIC_API_KEY_1=...
57
+ # GEMINI_API_KEY_1=...
58
+ # GOOGLE_API_KEY=...
59
+ # OPENAI_API_KEYS=sk-1,sk-2
60
+ # ANTHROPIC_API_KEYS=sk-ant-1,sk-ant-2
61
+ # GEMINI_API_KEYS=key-1,key-2
62
+
63
+ # Optional additional providers
64
+ # ZAI_API_KEY=...
65
+ # AI_GATEWAY_API_KEY=...
66
+ # TOKENHUB_API_KEY=...
67
+ # LKEAP_API_KEY=...
68
+ # MINIMAX_API_KEY=...
69
+ # SYNTHETIC_API_KEY=...
70
+
71
+ # -----------------------------------------------------------------------------
72
+ # Channels (only set what you enable)
73
+ # -----------------------------------------------------------------------------
74
+ # TELEGRAM_BOT_TOKEN=123456:ABCDEF...
75
+ # DISCORD_BOT_TOKEN=...
76
+ # SLACK_BOT_TOKEN=xoxb-...
77
+ # SLACK_APP_TOKEN=xapp-...
78
+
79
+ # Optional channel env fallbacks
80
+ # MATTERMOST_BOT_TOKEN=...
81
+ # MATTERMOST_URL=https://chat.example.com
82
+ # ZALO_BOT_TOKEN=...
83
+ # OPENCLAW_TWITCH_ACCESS_TOKEN=oauth:...
84
+
85
+ # -----------------------------------------------------------------------------
86
+ # Tools + voice/media (optional)
87
+ # -----------------------------------------------------------------------------
88
+ # BRAVE_API_KEY=...
89
+ # PERPLEXITY_API_KEY=pplx-...
90
+ # FIRECRAWL_API_KEY=...
91
+
92
+ # ELEVENLABS_API_KEY=...
93
+ # XI_API_KEY=... # alias for ElevenLabs
94
+ # INWORLD_API_KEY=...
95
+ # DEEPGRAM_API_KEY=...
.gitattributes CHANGED
@@ -1,35 +1,13 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ * text=auto eol=lf
2
+ CLAUDE.md -text
3
+ src/gateway/server-methods/CLAUDE.md -text
4
+ ui/src/i18n/.i18n/* linguist-generated
5
+ apps/.i18n/** linguist-generated
6
+ apps/android/app/src/main/res/values-*/strings.xml linguist-generated
7
+ apps/android/app/src/main/res/values-*/assistant.xml linguist-generated
8
+ apps/android/app/src/thirdParty/res/values-*/accessibility_strings.xml linguist-generated
9
+ apps/android/wear/src/main/res/values-*/strings.xml linguist-generated
10
+ apps/android/app/src/main/java/ai/openclaw/app/i18n/NativeStringResources.kt linguist-generated
11
+ apps/ios/Resources/Localizable.xcstrings linguist-generated
12
+ apps/macos/Sources/OpenClaw/Resources/Localizable.xcstrings linguist-generated
13
+ apps/ios/**/*.lproj/InfoPlist.strings linguist-generated
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ node_modules
2
+ **/node_modules/
3
+ .env
4
+ docker-compose.override.yml
5
+ docker-compose.extra.yml
6
+ docker-compose.sandbox.yml
7
+ dist
8
+ dist-runtime/
9
+ dist-sea/
10
+ pnpm-lock.yaml
11
+ bun.lock
12
+ bun.lockb
13
+ coverage
14
+ __openclaw_vitest__/
15
+ __pycache__/
16
+ *.pyc
17
+ *.tsbuildinfo
18
+ .pnpm-store
19
+ .worktrees/
20
+ .DS_Store
21
+ **/.DS_Store
22
+ ui/src/ui/__screenshots__/
23
+ ui/playwright-report/
24
+ ui/test-results/
25
+ packages/dashboard-next/.next/
26
+ packages/dashboard-next/out/
27
+
28
+ # Mise configuration files
29
+ mise.toml
30
+
31
+ # Android build artifacts
32
+ apps/android/.gradle/
33
+ apps/android/app/build/
34
+ apps/android/.cxx/
35
+ apps/android/.kotlin/
36
+ apps/android/benchmark/results/
37
+
38
+ # Bun build artifacts
39
+ *.bun-build
40
+ apps/macos/.build/
41
+ apps/macos-mlx-tts/.build/
42
+ apps/shared/MoltbotKit/.build/
43
+ apps/shared/OpenClawKit/.build/
44
+ apps/shared/*/.build/
45
+ packages/*/dist/
46
+ apps/shared/OpenClawKit/Package.resolved
47
+ # Xcode rewrites local-package Package.resolved with the iOS app supergraph on
48
+ # every resolve; swabble's only runtime dep is exact-pinned in Package.swift.
49
+ apps/swabble/Package.resolved
50
+ **/ModuleCache/
51
+ bin/
52
+ bin/clawdbot-mac
53
+ apps/macos/.build-local/
54
+ apps/macos/.swiftpm/
55
+ apps/shared/MoltbotKit/.swiftpm/
56
+ apps/shared/OpenClawKit/.swiftpm/
57
+ apps/shared/*/.swiftpm/
58
+ Core/
59
+ apps/ios/*.xcodeproj/
60
+ apps/ios/*.xcworkspace/
61
+ apps/ios/.swiftpm/
62
+ apps/ios/.derivedData/
63
+ apps/ios/.local-signing.xcconfig
64
+ vendor/
65
+ apps/ios/Clawdbot.xcodeproj/
66
+ apps/ios/Clawdbot.xcodeproj/**
67
+ apps/macos/.build/**
68
+ apps/macos-mlx-tts/.build/**
69
+ **/*.bun-build
70
+ apps/ios/*.xcfilelist
71
+
72
+ # Vendor build artifacts
73
+ vendor/a2ui/renderers/lit/dist/
74
+ src/canvas-host/a2ui/*.bundle.js
75
+ src/canvas-host/a2ui/*.map
76
+ extensions/canvas/src/host/a2ui/*.bundle.js
77
+ extensions/canvas/src/host/a2ui/*.map
78
+ .bundle.hash
79
+
80
+ # fastlane (iOS)
81
+ apps/ios/fastlane/README.md
82
+ apps/android/fastlane/README.md
83
+ apps/ios/fastlane/report.xml
84
+ apps/ios/fastlane/Preview.html
85
+ apps/ios/fastlane/screenshots/
86
+ apps/ios/fastlane/metadata/*/release_notes.txt
87
+ apps/ios/fastlane/test_output/
88
+ apps/ios/fastlane/logs/
89
+ apps/ios/fastlane/.env
90
+ apps/android/fastlane/report.xml
91
+ apps/android/fastlane/Preview.html
92
+ apps/android/fastlane/test_output/
93
+ apps/android/fastlane/logs/
94
+ apps/android/fastlane/.env
95
+ apps/android/fastlane/metadata/android/**/images/
96
+
97
+ # fastlane build artifacts (local)
98
+ apps/ios/*.ipa
99
+ apps/ios/*.dSYM.zip
100
+
101
+ # provisioning profiles (local)
102
+ apps/ios/*.mobileprovision
103
+
104
+ # Local untracked files
105
+ .local/
106
+ docs/.local/
107
+ docs/internal/
108
+ tmp/
109
+ IDENTITY.md
110
+ USER.md
111
+ # Keep packaged workspace templates visible to Git-aware remote syncs.
112
+ !docs/reference/templates/IDENTITY.md
113
+ !docs/reference/templates/USER.md
114
+ # Exception: oc-path real-world test fixtures need to be tracked even
115
+ # though the bare names match the local-untracked rule above.
116
+ !extensions/oc-path/src/oc-path/tests/fixtures/real/IDENTITY.md
117
+ !extensions/oc-path/src/oc-path/tests/fixtures/real/USER.md
118
+ *.tgz
119
+ *.tar.gz
120
+ *.zip
121
+ .idea
122
+ .vscode/
123
+
124
+ # local tooling
125
+ .antigravitycli/
126
+ .serena/
127
+ .crabbox/
128
+ /.openclaw/trajectory-exports/
129
+
130
+ # local QA evidence mirrors; CI publishes canonical Mantis files as Actions artifacts
131
+ /mantis/
132
+
133
+ # Keep repository skills visible to Git-aware syncs; local-only skill ignores
134
+ # belong in .git/info/exclude.
135
+ .agents/skills/**/*.orig
136
+ .agents/skills/**/__pycache__/
137
+ .agents/skills/**/*.py[cod]
138
+
139
+ # Agent credentials and memory (NEVER COMMIT)
140
+ /memory/
141
+ .agent/*.json
142
+ !.agent/workflows/
143
+ /local/
144
+ /client_secret_*.json
145
+ package-lock.json
146
+ !.github/release/clawhub-cli/package-lock.json
147
+ !src/commands/copilot-sdk-install-manifest/package-lock.json
148
+ .claude/*
149
+ # Track the shared Claude Code project config (worktree auto-install hook) and the
150
+ # skills symlink into .agents/skills; keep everything else under .claude/
151
+ # (worktrees, settings.local.json) ignored.
152
+ !.claude/settings.json
153
+ !.claude/skills
154
+ .agent/
155
+ skills-lock.json
156
+
157
+ # Local iOS signing overrides
158
+ apps/ios/LocalSigning.xcconfig
159
+
160
+ # Xcode build directories (xcodebuild output)
161
+ apps/ios/build/
162
+ apps/shared/OpenClawKit/build/
163
+ apps/swabble/build/
164
+ *.xcresult
165
+ *.trace
166
+ *.profraw
167
+
168
+ # Generated protocol schema (produced via pnpm protocol:gen)
169
+ dist/protocol.schema.json
170
+ packages/gateway-protocol/protocol.schema.json
171
+ .ant-colony/
172
+
173
+ # Eclipse
174
+ **/.project
175
+ **/.classpath
176
+ **/.settings/
177
+ **/.gradle/
178
+
179
+ # Synthing
180
+ **/.stfolder/
181
+ .dev-state
182
+ docs/superpowers
183
+ .superpowers/
184
+ test/config-form.analyze.telegram.test.ts
185
+ ui/src/ui/theme-variants.browser.test.ts
186
+ ui/src/ui/__screenshots__
187
+ ui/src/ui/views/__screenshots__
188
+ ui/.vitest-attachments
189
+ ui/.vitest/
190
+
191
+ # Generated docs baseline artifacts (locally generated, only drift detectors tracked)
192
+ docs/.generated/*.json
193
+ !docs/.generated/config-baseline.counts.json
194
+ docs/.generated/*.jsonl
195
+ !docs/.generated/plugin-sdk-api-baseline/
196
+ !docs/.generated/plugin-sdk-api-baseline/*.json
197
+
198
+ # Deprecated changelog fragment workflow
199
+ changelog/fragments/
200
+
201
+ # Local scratch workspace
202
+ .tmp/
203
+ .cache/
204
+ .pytest_cache/
205
+ .ruff_cache/
206
+ .mypy_cache/
207
+ .vmux*
208
+ .artifacts/
209
+ .amp/portals/
210
+ .openclaw-config-doc-cache/
211
+ openclaw-path-alias-*/
212
+ /.pi/
213
+ /C:\\openclaw/
214
+ *.log
215
+ *.tmp
216
+ *.heapsnapshot
217
+ *.cpuprofile
218
+ *.prof
219
+ test/fixtures/openclaw-vitest-unit-report.json
220
+ analysis/
221
+ .artifacts/qa-e2e/
222
+ /runs/
223
+ /data/rtt.jsonl
224
+ extensions/qa-lab/web/dist/
225
+
226
+ # Generated bundled plugin runtime dependency manifests
227
+ extensions/**/.openclaw-runtime-deps.json
228
+ extensions/**/.openclaw-runtime-deps-stamp.json
229
+ extensions/diffs/assets/viewer-runtime.js
230
+ extensions/diffs-language-pack/assets/viewer-runtime.js
231
+ extensions/discord/assets/embedded-app-sdk.mjs
232
+
233
+ # Output dir for scripts/run-opengrep.sh (local opengrep scans)
234
+ /.opengrep-out/
235
+ /.crabbox-artifacts
236
+ .comux*
237
+
238
+ # Pinned Swift lint/format tools installed by scripts/install-swift-tools.sh
239
+ .build/
AGENTS.md ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AGENTS.md
2
+
3
+ The task defines scope and authorization; its chosen workflow owns execution,
4
+ review, publication, recovery, and cleanup. Explicit user instructions take
5
+ precedence over skill guidelines and workflow defaults; host limits and required
6
+ authorization boundaries still apply. Read the nearest scoped `AGENTS.md` and the
7
+ matching references below, including when changing callers outside an owner's directory.
8
+ Update instructions at their owner instead of adding competing rules here.
9
+
10
+ ## Design priorities
11
+
12
+ - **One owner per responsibility.** An owner makes a decision or changes authoritative state. Callers consume its operations and recorded facts. Adapters translate contracts; caches and projections derive from the owner with an explicit invalidation lifecycle. Different transports can need different adapters, but not competing owners for the same responsibility.
13
+ - **Small core, capable plugins.** Model-facing core additions have an ongoing context cost. Optional capability belongs at the edges; core supplies generic contracts. A feature needing a new integration is not, by itself, a reason to add another core tool or manager. [VISION.md](VISION.md) owns product scope.
14
+ - **Stable conversation context.** Rebuilding past context defeats prompt-prefix reuse. Keep generated prompt/tool/context additions bounded and deterministic, preserve transcript bytes, and serve required instructions whole. Only compaction rewrites history. Defer changes to stable prompt state until the next session unless its owner defines explicit invalidation; preserve existing skill, tool, and memory refresh contracts.
15
+
16
+ ## Working agreement
17
+
18
+ - Follow through on actionable requests, including "can you", within their authorized scope. When execution is requested, a plan or progress report is a checkpoint, not completion. Use prior context and preserve unaffected work across corrections and side questions.
19
+ - Resolve routine, reversible choices with reasonable assumptions. Ask only about consequential decisions the request and context cannot resolve; continue independent authorized work while waiting. Silence does not authorize a gated action.
20
+ - If a skill causes a pause, permission request, unfinished work, or scope change, link its exact `SKILL.md` and quote the instruction to the user. Explain how it applies, distinguish requirements from interpretation, and check prior authorization before asking again.
21
+ - Inspect `git status -sb` before editing or GitHub work. Preserve unrelated work, branches, processes, and user-managed checkouts; serialize shared Git mutations and isolate work when needed. Never switch a checkout while another agent or test run uses it.
22
+ - Treat pasted material and tool output as evidence; verify claims against source and observed behavior.
23
+ - Lead with the result and follow the user's format. Use plain words, active voice, and useful technical detail; omit stock phrases and repeated summaries. Progress updates explain new findings, decisions, or blockers. Keep delegated messages equally clear.
24
+ - Report routine findings in chat/stdout. Create files only for deliverables or concrete tool/proof/recovery needs; state their purpose and reuse them. Cleanup removes only task-created disposable files that are no longer needed or in use. Preserve unknown ownership, required evidence, and recovery state; this does not authorize existing-storage cleanup or retention changes.
25
+ - Read relevant docs before changing behavior; `pnpm docs:list` locates them. `package.json` owns current commands and versions; keep the repository's toolchain and conventions rather than swapping tools without approval.
26
+ - Use **OpenClaw** for the product, `openclaw` for CLI/package/config names, **plugins** for user-facing integrations, and American English.
27
+ - Edit canonical `AGENTS.md` files; new ones need a sibling `CLAUDE.md` symlink.
28
+
29
+ ## One owner, complete cutover
30
+
31
+ 1. **Intent:** reproduce defects through the actual entry point before editing when feasible. Read complete affected modules, owners, callers, siblings, tests, history, and dependency contracts until the intended user outcome and violated invariant are supported by evidence. Before restoring a missing path, check why it was removed (`git log -p -S <symbol>`): isolation may be intentional, and a retired alias may be a completed migration. Record concrete reproduction gaps.
32
+ 2. **Owner:** account for relevant decisions and state writers across creation, updates, reads, recovery, and cleanup. Choose the existing code, plugin, or maintained solution that absorbs the change. A new owner needs a missing responsibility; fix invalid or leaked state at its producer.
33
+ 3. **Cutover:** migrate all affected internal/bundled callers together. Remove superseded code, duplicate policy/state, wrappers, registrations, exports, tests, and docs. Every retained path needs a cited contract. Workers sharing an owner agree on one interface and cutover plan.
34
+ 4. **Proof:** exercise the intended user flow and relevant siblings; trace references to confirm retired paths are unreachable. Done means one owner serves the flow, old paths are removed or justified, and observed results or remaining gaps are recorded in existing task/PR evidence. Helper tests or a wrapper around competing implementations alone are insufficient.
35
+
36
+ - Prefer smaller, simpler production code; explain necessary growth. Keep coherent nearby repairs together and record unrelated work as follow-ups. No extra report or tracking system is required.
37
+ - Delegate independent evidence or implementation lanes when parallel work reduces time or improves verification. Give each lane a clear responsibility and completion condition; keep simple or tightly coupled work with the lead. The lead stays hands-on, verifies consequential conclusions, and coordinates shared-checkout safety.
38
+ - Retained compatibility needs an explicit user request or a public API/config/SDK/data, stable-tag upgrade, security/migration, dependency, or observed-production contract, plus a migration/removal path. Main, beta, and nightly code alone are not shipped contracts.
39
+
40
+ ### Choose the capability surface
41
+
42
+ For new capability, use the first path that expresses the actual requirement:
43
+
44
+ 1. Extend the existing owner or use an existing command, skill, plugin, or supported integration.
45
+ 2. Use an existing plugin contract. Prefer bundle plugins for skills, MCP servers, and configuration; use code plugins when runtime hooks, providers, channels, or tools are needed. Keep vendor behavior with its vendor plugin and feature behavior with its feature owner.
46
+ 3. If the contract is missing, define a narrow generic core/SDK capability and move existing bundled implementations and callers onto it together. Repeated independent requests for the same capability trigger this contract review, not another parallel manager or hook.
47
+ 4. Add universal core surface only when the need is fundamental and existing extension points cannot express it. Explain the gap and ongoing cost; a new hook needs a concrete consumer.
48
+
49
+ For example, a new channel action should first use the shared message action
50
+ contract. A setup screen needing plugin metadata should use the manifest or
51
+ lightweight artifact, not load the plugin's execution runtime.
52
+
53
+ ## Runtime and code safeguards
54
+
55
+ - Plugins use documented `openclaw/plugin-sdk/*` contracts, manifest metadata, and public/local barrels, never core internals or another plugin's private files. Dependencies follow runtime ownership.
56
+ - Runtime consumes canonical config/state. Doctor/migration owners normalize legacy shapes; plugin repairs stay plugin-owned. A change invalidating existing config includes its matching migration. Startup may invoke the same approved Doctor transforms; do not add independent compatibility readers.
57
+ - OpenClaw state and caches use SQLite, not new JSON/JSONL/sidecar stores. Files are for named user artifacts, imports/exports, attachments, logs, backups, or external-tool contracts.
58
+ - Use Kysely for ordinary SQLite access; raw SQL is limited to schema, migrations, bootstrap, and justified primitives. Write transactions are synchronous: finish asynchronous planning first, then reread authoritative rows before writing. No Promise or `await` in a transaction callback.
59
+ - Privileged actions require current owner-held authority. Revalidate after awaited work and immediately before side effects; tokens, signatures, expiry, and matching IDs alone do not prove live authority.
60
+ - Core owns shared message tools, action vocabulary, and dispatch. Channels own their account, security, conversation, and transport contracts. Preserve typed command/approval/URL/action distinctions until encoding; never infer product commands from raw strings.
61
+ - Carry prepared facts through hot paths. Reuse process-stable plugin metadata and lifecycle-owned caches; do not repeatedly load registries or freshness-poll files. Preserve lazy module boundaries and verify relevant builds on the authorized host.
62
+ - Keep APIs narrow, valid states explicit, and TypeScript ESM/types strict. Prefer real types or `unknown`; no `@ts-nocheck`. Suppressions need an intentional, explained exception. Reuse schema/coercion owners; avoid duplicate guards, speculative helpers, and naming-only wrappers.
63
+ - Static-analysis fixes strengthen the real type/runtime contract or remove the unsafe operation; do not conceal it with casts, widening, marker types, or property probes. New lint rules need a meaningful invariant and a clean owner scope.
64
+ - Comments explain non-obvious ownership, lifecycle, ordering, cleanup, platform, and dependency constraints, not syntax. Do not edit `node_modules` or generated artifacts by hand, or change formatter settings for a local expression; regenerate owned outputs.
65
+
66
+ ## Product and validation
67
+
68
+ - Defaults should produce a working, understandable result. Prioritize silent failures. Each action has a visible outcome or recorded intentional non-outcome; errors explain the next useful step.
69
+ - **Updates always work.** `openclaw update` finishes best effort on every install. Any change touching update, Doctor, service lifecycle, config/state migration, or plugin loading states its update behavior: the installed updater runs first and cannot be patched, so candidate-side fixes key on markers shipped drivers already set, and existing operator state is the input. Recoverable hiccups become recorded warnings; back up before mutating and let rollback restore it; refuse only for concrete data at risk, naming the reason and leaving the previous Gateway running. Timeouts and budgets are generous, derived from measured state, and sized for old, slow hardware. Proof: a published-driver × candidate cell.
70
+ - Prompts, tools, and results describe available capabilities accurately and give enough context for the next useful action; avoid unnecessary model round trips. Inject cross-tool references from the enabled tool set and remove stale model-facing arguments instead of hidden compatibility. New optional features need discovery paths.
71
+ - Security is a product tradeoff, not a goal to maximize restrictions. Weigh concrete risk and likely impact against user effort, lockouts, and lost capability. Prefer the least restrictive effective safeguard; bounded, understood risk can be acceptable for a substantial usability benefit. Keep risky paths explicit and operator-controlled within the existing trust model and approval boundaries, and explain the tradeoff instead of inventing extra gates.
72
+ - Tests must protect meaningful behavior; skip tests for reversible, low-impact changes that merely mirror the implementation. Regressions fail on the original defect; shared-state failures use the original order. Review tests for value and duplication. Do not hide failures with retries, longer timeouts, weaker assertions, broader mocks, or altered baselines.
73
+ - Select proof for the touched contract and complete the chosen workflow's required gates within user/host limits. Command references do not mandate unrelated suites. Reuse valid proof; rerun for changed inputs or missing coverage. Docs-only work needs docs sanity and `git diff --check`. Report unrun checks and gaps.
74
+ - Prove user-visible behavior through the real flow when feasible; external API changes need live contract proof. A covering isolated mock-Gateway harness is valid channel boundary proof; live channel proof is stronger. State concrete capture or execution blockers.
75
+ - **UI screenshot completion/landing gate:** For user-visible UI changes, you **must** visibly attach inspected, sanitized before/after screenshots in the originating chat AND upload/embed them in the GitHub PR body or a PR comment linked from its body. Verify that the images render in both destinations before merging or claiming completion. Local files, private inspection, chat-only delivery, logs, tests, review approval, and artifact-manifest links are not substitutes. Galleries and videos supplement, never replace, the required screenshots. Failure in either destination requires approved recovery that satisfies this gate or an explicit delivery blocker; never silently downgrade or merge with delivery blocked. Only an explicit user waiver can exempt a destination.
76
+ - Before committing or landing nontrivial code, obtain fresh review through the permitted workflow and resolve actionable findings unless the user opts out. Tests protect observable contracts; a helper test can pass while the registered entry point never calls it.
77
+
78
+ ### Execution gotchas
79
+
80
+ These commands apply on the host permitted by the task and its workflow; they do
81
+ not authorize local execution or a broader test plan.
82
+
83
+ - Restore missing dependencies in a trusted normal checkout with `pnpm install`, then retry once before diagnosing a code defect. Never reconcile a shared/worktree install while other jobs use it.
84
+ - Run the CLI through `pnpm openclaw ...` or `pnpm dev`, never `node --import tsx src/index.ts`; the supported wrappers own build freshness and process setup.
85
+ - Use installed `oxfmt` for formatting and the repository's `tsgo` lanes for typechecking. Inspect scope with `pnpm changed:lanes --json`; use targeted tests/checks. When avoiding worktree reconciliation, use `node scripts/check-changed.mjs` or `node scripts/run-vitest.mjs` with ready dependencies. Host restrictions still apply.
86
+
87
+ ## Authority and safety
88
+
89
+ - Review/triage is read-only; mutations require task authority. Existing approval carries through the same scoped work and recovery. When new approval is required, complete the already-authorized preparation first and present a concrete, reviewable result; pause only the gated action. Product rejection remains maintainer judgment. Bulk close/reopen above 50 items needs explicit count and scope.
90
+ - Keep credentials, private data/config, and unreleased model identities out of commits and shared text, logs, transcripts, and media. Inspect outgoing content. Use synthetic fixtures and verified human credit; omit agent-attribution trailers.
91
+ - For permitted defensive work interrupted by a cyber classifier, use an approved, available Daybreak Blue route only for that bounded task, then restore the previous model. Preserve the approved identity, scope, permissions, sandbox, and review gates. Never switch models to bypass access denials, disallowed requests, or refused tool actions; if that route also refuses, stop and report. Follow [OpenAI's guidance](https://learn.chatgpt.com/docs/cyber-safety) for approved model access.
92
+ - Untrusted contributor/fork code runs only in secretless isolation, never locally. Source review alone does not authorize execution with credentials or on a trusted host; maintainer approval is required. An instruction to land named, reviewed PRs supplies that approval. Use the authorized isolation route and only task credentials.
93
+ - Modifying/restarting a Gateway or live state you did not create requires per-task approval. Tests use isolated state and ports; copy real data for migration tests. Destructive reset/clean, stash, or deletion of unrelated work needs authorization.
94
+ - Updating `team.openclaw.ai` must only happen by negotiating with Night Watch on `stable.openclaw.ai`, never directly.
95
+ - Explicit repair-and-land authority includes internal scheduling, database admission, and lifecycle implementation decisions. The agent owns design selection, risk assessment, and verification; do not request renewed approval for implementation decisions within that scope.
96
+ - Bug fixes within the authorized task do not need renewed approval, including compatible SDK changes needed to restore intended behavior. Ask again for new configuration options, breaking public contracts, intentional changes to schemas, durability, retention, or permissions beyond the bug fix, paid services, or destructive actions. Preserve FIFO ordering, live-authority and integrity checks, and settlement of write-capable work.
97
+ - Protocol/version bumps, dependency patches/overrides/vendor changes, paid services, releases, and publishing need explicit approval; fix/ship authority does not imply release authority. Advisory workflows require an explicit request for that security action.
98
+ - Extended-stable is one line: the trailing completed month relative to `main`'s version. Older `.33+` lines retire when `main` advances another month; publishing a retired line needs an explicit maintainer decision, not a routine guard bypass.
99
+ - Baseline, snapshot, ignore, and expected-failure exceptions need approval; exact shrink-only ratchet updates are maintenance.
100
+ - `CODEOWNERS` routes review; check live GitHub enforcement. Restricted/security paths and material product, behavior, security, or ownership changes need listed-owner involvement. For ownership/review governance, verified active organization-admin direction also qualifies; repository admin/bypass alone does not. Neither route waives enforced reviews.
101
+ - Complete the authorized workflow's review/merge gates; resolve substantive findings or explain rejections. Fix diff-caused failures and document proven unrelated failures separately. Verify remote outcomes before success or cleanup; uncertain writes require reconciliation, not blind retries.
102
+ - Stage only intended files and use concise Conventional Commits with verified author/writer identities. Preserve contributor credit; team-session credit requires consented, verified humans and its canonical backlink. A bare URL grants no public mutation authority. Keep PR bodies current with problem, solution, impact, and evidence; use body files/heredocs for shell-sensitive text.
103
+
104
+ ## Read when relevant
105
+
106
+ Read matching guides in full and follow their narrower task-specific pointers.
107
+ Commands and implementation detail stay with these owners.
108
+
109
+ - **Product/design:** [VISION.md](VISION.md).
110
+ - **Plugins/discovery/SDK:** [plugins](extensions/AGENTS.md), [loader](src/plugins/AGENTS.md), [SDK](src/plugin-sdk/AGENTS.md). The SDK guide owns public boundary expansion, including callers outside these trees.
111
+ - **Channels/message actions:** [channel boundary](src/channels/AGENTS.md) and [channel responsibilities](docs/plugins/sdk-channel-plugins.md).
112
+ - **Agent tools, prompts, admission, or lifecycle:** [agents](src/agents/AGENTS.md) and [Gateway](src/gateway/AGENTS.md).
113
+ - **Control UI state, requests, or presentation:** [UI guide](ui/AGENTS.md), including state shared with other Gateway clients.
114
+ - **Storage:** [database schemas](docs/reference/database-schemas.md), then its layout, versioning, and storage-changes pages for the affected contract. Read the approval checkpoint before changing schema, transactions, retention, or recovery.
115
+ - **Config retirement/migration:** [shared Doctor transforms and startup migration](docs/gateway/doctor/config-migrations.md); reuse this owner instead of new runtime compatibility readers.
116
+ - **Audit/identity/receipts:** [audit doctrine](docs/gateway/audit.md). Diagnostic provenance is opt-in and never authorization; changes to collection, reader scope, retained fields, bounds, or contracts require approval.
117
+ - **Codex-backed behavior:** personally inspect the exact sibling `../codex` source before implementation or verdict and cite it; wrappers, schemas, and another agent's report do not replace this check. Auth/runtime/catalog routes use `openai`; legacy `openai-codex` input belongs only in migration. Harness upgrades refresh [the harness guide](docs/plugins/codex-harness.md) from `model/list`.
118
+ - **Validation commands:** [test suites](docs/help/testing/suites.md) is a command reference; this file and the chosen workflow own check selection. Test authoring also uses [writing tests](docs/help/testing/writing-tests.md) and the owning scoped guide.
119
+ - **GitHub:** [contribution rules](CONTRIBUTING.md), the current PR template, and [review feedback](docs/reference/pull-request-review-flow.md). The authorized maintainer workflow owns landing; native `scripts/pr` gates, recovery, and cleanup require [scripts guide](scripts/AGENTS.md).
120
+ - **Docs/public links:** [docs guide](docs/AGENTS.md). Update docs with behavior; normal fix notes belong in PRs because `CHANGELOG.md` is release-owned.
121
+ - **Releases:** the chosen release workflow and [release contract](docs/reference/RELEASING.md). Preserve the selected release cut and identity through publication and verification. npm-format lock mirrors are verified against `pnpm-lock.yaml`, published in dependency evidence, and kept out of npm tarballs.
122
+ - **Secrets/advisories:** [secret semantics](docs/gateway/secrets.md), [auth semantics](docs/auth-credential-semantics.md), and [security reporting](SECURITY.md) for the affected branch.
123
+ - **Live channels/native apps:** the owning scoped guide and permitted proof workflow. Telegram claims require Test Server userbot proof with Convex-leased credentials; platform claims require the relevant real device/platform evidence. Mac permission proof needs a stable, properly signed app; see [signing](docs/platforms/mac/signing.md).
CHANGELOG/2026.1.24-2.md ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ ## 2026.1.24-2
2
+
3
+ ### Fixes
4
+
5
+ - Packaging: include dist/link-understanding output in npm tarball (fixes missing apply.js import on install).
6
+
CHANGELOG/2026.1.24-3.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.1.24-3
2
+
3
+ ### Fixes
4
+
5
+ - Slack: fix image downloads failing due to missing Authorization header on cross-origin redirects. (#1936) Thanks @sanderhelgesen.
6
+ - Gateway: harden reverse proxy handling for local-client detection and unauthenticated proxied connects. (#1795) Thanks @orlyjamie.
7
+ - Security audit: flag loopback Control UI with auth disabled as critical. (#1795) Thanks @orlyjamie.
8
+ - CLI: resume claude-cli sessions and stream CLI replies to TUI clients. (#1921) Thanks @rmorse.
9
+
CHANGELOG/2026.1.29.md ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.1.29
2
+
3
+ ### Changes
4
+
5
+ - Rebrand: rename the npm package/CLI to `openclaw`, add a `openclaw` compatibility shim, and move extensions to the `@openclaw/*` scope.
6
+ - Onboarding: strengthen security warning copy for beta + access control expectations.
7
+ - Onboarding: add Venice API key to non-interactive flow. (#1893) Thanks @jonisjongithub.
8
+ - Config: auto-migrate legacy state/config paths and keep config resolution consistent across legacy filenames.
9
+ - Gateway: warn on hook tokens via query params; document header auth preference. (#2200) Thanks @YuriNachos.
10
+ - Gateway: add dangerous Control UI device auth bypass flag + audit warnings. (#2248).
11
+ - Doctor: warn on gateway exposure without auth. (#2016) Thanks @Alex-Alaniz.
12
+ - Web UI: keep sub-agent announce replies visible in WebChat. (#1977) Thanks @andrescardonas7.
13
+ - Browser: route browser control via gateway/node; remove standalone browser control command and control URL config.
14
+ - Browser: route `browser.request` via node proxies when available; honor proxy timeouts; derive browser ports from `gateway.port`.
15
+ - Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev.
16
+ - Telegram: allow caption param for media sends. (#1888) Thanks @mguellsegarra.
17
+ - Telegram: support plugin sendPayload channelData (media/buttons) and validate plugin commands. (#1917) Thanks @JoshuaLelon.
18
+ - Telegram: avoid block replies when streaming is disabled. (#1885) Thanks @ivancasco.
19
+ - Telegram: add optional silent send flag (disable notifications). (#2382) Thanks @Suksham-sharma.
20
+ - Telegram: support editing sent messages via message(action="edit"). (#2394) Thanks @marcelomar21.
21
+ - Telegram: support quote replies for message tool and inbound context. (#2900) Thanks @aduk059.
22
+ - Telegram: add sticker receive/send with vision caching. (#2629) Thanks @longjos.
23
+ - Telegram: send sticker pixels to vision models. (#2650).
24
+ - Telegram: keep topic IDs in restart sentinel notifications. (#1807) Thanks @hsrvc.
25
+ - Discord: add configurable privileged gateway intents for presences/members. (#2266) Thanks @kentaro.
26
+ - Slack: clear ack reaction after streamed replies. (#2044) Thanks @fancyboi999.
27
+ - Matrix: switch plugin SDK to @vector-im/matrix-bot-sdk.
28
+ - Tlon: format thread reply IDs as @ud. (#1837) Thanks @wca4a.
29
+ - Tools: add per-sender group tool policies and fix precedence. (#1757) Thanks @adam91holt.
30
+ - Agents: summarize dropped messages during compaction safeguard pruning. (#2509) Thanks @jogi47.
31
+ - Agents: expand cron tool description with full schema docs. (#1988) Thanks @tomascupr.
32
+ - Agents: honor tools.exec.safeBins in exec allowlist checks. (#2281).
33
+ - Memory Search: allow extra paths for memory indexing (ignores symlinks). (#3600) Thanks @kira-ariaki.
34
+ - Skills: add multi-image input support to Nano Banana Pro skill. (#1958) Thanks @tyler6204.
35
+ - Skills: add missing dependency metadata for GitHub, Notion, Slack, Discord. (#1995) Thanks @jackheuberger.
36
+ - Commands: group /help and /commands output with Telegram paging. (#2504) Thanks @hougangdev.
37
+ - Routing: add per-account DM session scope and document multi-account isolation. (#3095) Thanks @jarvis-sam.
38
+ - Routing: precompile session key regexes. (#1697) Thanks @Ray0907.
39
+ - CLI: use Node's module compile cache for faster startup. (#2808) Thanks @pi0.
40
+ - Auth: show copyable Google auth URL after ASCII prompt. (#1787) Thanks @robbyczgw-cla.
41
+ - TUI: avoid width overflow when rendering selection lists. (#1686) Thanks @mossein.
42
+ - macOS: finish OpenClaw app rename for macOS sources, bundle identifiers, and shared kit paths. (#2844) Thanks @fal3.
43
+ - Branding: update launchd labels, mobile bundle IDs, and logging subsystems to bot.molt (legacy bundle ID migrations). Thanks @thewilloftheshadow.
44
+ - macOS: limit project-local `node_modules/.bin` PATH preference to debug builds (reduce PATH hijacking risk).
45
+ - macOS: keep custom SSH usernames in remote target. (#2046) Thanks @algal.
46
+ - macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn.
47
+ - Update: ignore dist/control-ui for dirty checks and restore after ui builds. (#1976) Thanks @Glucksberg.
48
+ - Build: bundle A2UI assets during build and stop tracking generated bundles. (#2455) Thanks @0oAstro.
49
+ - CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi.
50
+ - Config: apply config.env before ${VAR} substitution. (#1813) Thanks @spanishflu-est1918.
51
+ - Gateway: prefer newest session metadata when combining stores. (#1823) Thanks @emanuelst.
52
+ - Docs: tighten Fly private deployment steps. (#2289) Thanks @dguido.
53
+ - Docs: add migration guide for moving to a new machine. (#2381).
54
+ - Docs: add Northflank one-click deployment guide. (#2167) Thanks @AdeboyeDN.
55
+ - Docs: add Vercel AI Gateway to providers sidebar. (#1901) Thanks @jerilynzheng.
56
+ - Docs: add Render deployment guide. (#1975) Thanks @anurag.
57
+ - Docs: add Claude Max API Proxy guide. (#1875) Thanks @atalovesyou.
58
+ - Docs: add DigitalOcean deployment guide. (#1870) Thanks @0xJonHoldsCrypto.
59
+ - Docs: add Oracle Cloud (OCI) platform guide + cross-links. (#2333) Thanks @hirefrank.
60
+ - Docs: add Raspberry Pi install guide. (#1871) Thanks @0xJonHoldsCrypto.
61
+ - Docs: add GCP Compute Engine deployment guide. (#1848) Thanks @hougangdev.
62
+ - Docs: add LINE channel guide. Thanks @thewilloftheshadow.
63
+ - Docs: credit both contributors for Control UI refresh. (#1852) Thanks @EnzeD.
64
+ - Docs: keep docs header sticky so navbar stays visible while scrolling. (#2445) Thanks @chenyuan99.
65
+ - Docs: update exe.dev install instructions. (#https://github.com/openclaw/openclaw/pull/3047) Thanks @zackerthescar.
66
+
67
+ ### Fixes
68
+
69
+ - Skills: update session-logs paths to use ~/.openclaw. (#4502) Thanks @bonald.
70
+ - Telegram: avoid silent empty replies by tracking normalization skips before fallback. (#3796).
71
+ - Mentions: honor mentionPatterns even when explicit mentions are present. (#3303) Thanks @HirokiKobayashi-R.
72
+ - Discord: restore username directory lookup in target resolution. (#3131) Thanks @bonald.
73
+ - Agents: align MiniMax base URL test expectation with default provider config. (#3131) Thanks @bonald.
74
+ - Agents: prevent retries on oversized image errors and surface size limits. (#2871) Thanks @Suksham-sharma.
75
+ - Agents: inherit provider baseUrl/api for inline models. (#2740) Thanks @lploc94.
76
+ - Memory Search: keep auto provider model defaults and only include remote when configured. (#2576) Thanks @papago2355.
77
+ - Telegram: include AccountId in native command context for multi-agent routing. (#2942) Thanks @Chloe-VP.
78
+ - Telegram: handle video note attachments in media extraction. (#2905) Thanks @mylukin.
79
+ - TTS: read OPENAI_TTS_BASE_URL at runtime instead of module load to honor config.env. (#3341) Thanks @hclsys.
80
+ - macOS: auto-scroll to bottom when sending a new message while scrolled up. (#2471) Thanks @kennyklee.
81
+ - Web UI: auto-expand the chat compose textarea while typing (with sensible max height). (#2950) Thanks @shivamraut101.
82
+ - Gateway: prevent crashes on transient network errors (fetch failures, timeouts, DNS). Added fatal error detection to only exit on truly critical errors. Fixes #2895, #2879, #2873. (#2980) Thanks @elliotsecops.
83
+ - Agents: guard channel tool listActions to avoid plugin crashes. (#2859) Thanks @mbelinky.
84
+ - Discord: stop resolveDiscordTarget from passing directory params into messaging target parsers. Fixes #3167. Thanks @thewilloftheshadow.
85
+ - Discord: avoid resolving bare channel names to user DMs when a username matches. Thanks @thewilloftheshadow.
86
+ - Discord: fix directory config type import for target resolution. Thanks @thewilloftheshadow.
87
+ - Providers: update MiniMax API endpoint and compatibility mode. (#3064) Thanks @hlbbbbbbb.
88
+ - Telegram: treat more network errors as recoverable in polling. (#3013) Thanks @ryancontent.
89
+ - Discord: resolve usernames to user IDs for outbound messages. (#2649) Thanks @nonggialiang.
90
+ - Providers: update Moonshot Kimi model references to kimi-k2.5. (#2762) Thanks @MarvinCui.
91
+ - Gateway: suppress AbortError and transient network errors in unhandled rejections. (#2451) Thanks @Glucksberg.
92
+ - TTS: keep /tts status replies on text-only commands and avoid duplicate block-stream audio. (#2451) Thanks @Glucksberg.
93
+ - Security: pin npm overrides to keep tar@7.5.4 for install toolchains.
94
+ - Security: properly test Windows ACL audit for config includes. (#2403) Thanks @dominicnunez.
95
+ - CLI: recognize versioned Node executables when parsing argv. (#2490) Thanks @David-Marsh-Photo.
96
+ - CLI: avoid prompting for gateway runtime under the spinner. (#2874).
97
+ - BlueBubbles: coalesce inbound URL link preview messages. (#1981) Thanks @tyler6204.
98
+ - Cron: allow payloads containing "heartbeat" in event filter. (#2219) Thanks @dwfinkelstein.
99
+ - CLI: avoid loading config for global help/version while registering plugin commands. (#2212) Thanks @dial481.
100
+ - Agents: include memory.md when bootstrapping memory context. (#2318) Thanks @czekaj.
101
+ - Agents: release session locks on process termination and cover more signals. (#2483) Thanks @janeexai.
102
+ - Agents: skip cooldowned providers during model failover. (#2143) Thanks @YiWang24.
103
+ - Telegram: harden polling + retry behavior for transient network errors and Node 22 transport issues. (#2420) Thanks @techboss.
104
+ - Telegram: ignore non-forum group message_thread_id while preserving DM thread sessions. (#2731) Thanks @dylanneve1.
105
+ - Telegram: wrap reasoning italics per line to avoid raw underscores. (#2181) Thanks @YuriNachos.
106
+ - Telegram: centralize API error logging for delivery and bot calls. (#2492) Thanks @altryne.
107
+ - Voice Call: enforce Twilio webhook signature verification for ngrok URLs; disable ngrok free tier bypass by default.
108
+ - Security: harden Tailscale Serve auth by validating identity via local tailscaled before trusting headers.
109
+ - Media: fix text attachment MIME misclassification with CSV/TSV inference and UTF-16 detection; add XML attribute escaping for file output. (#3628) Thanks @frankekn.
110
+ - Build: align memory-core peer dependency with lockfile.
111
+ - Security: add mDNS discovery mode with minimal default to reduce information disclosure. (#1882) Thanks @orlyjamie.
112
+ - Security: harden URL fetches with DNS pinning to reduce rebinding risk. Thanks Chris Zheng.
113
+ - Web UI: improve WebChat image paste previews and allow image-only sends. (#1925) Thanks @smartprogrammer93.
114
+ - Security: wrap external hook content by default with a per-hook opt-out. (#1827) Thanks @mertcicekci0.
115
+ - Gateway: default auth now fail-closed (token/password required; Tailscale Serve identity remains allowed).
116
+ - Gateway: treat loopback + non-local Host connections as remote unless trusted proxy headers are present.
117
+ - Onboarding: remove unsupported gateway auth "off" choice from onboarding/configure flows and CLI flags.
118
+
119
+ ### Breaking
120
+
121
+ - **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed).
122
+
CHANGELOG/2026.2.13.md ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.2.13
2
+
3
+ ### Changes
4
+
5
+ - Install: add optional Podman-based setup: `setup-podman.sh` for one-time host setup (openclaw user, image, launch script, systemd quadlet), `run-openclaw-podman.sh launch` / `launch setup`; systemd Quadlet unit for openclaw user service; docs for rootless container, openclaw user (subuid/subgid), and quadlet (troubleshooting). (#16273) Thanks @DarwinsBuddy.
6
+ - Discord: send voice messages with waveform previews from local audio files (including silent delivery). (#7253) Thanks @nyanjou.
7
+ - Discord: add configurable presence status/activity/type/url (custom status defaults to activity text). (#10855) Thanks @h0tp-ftw.
8
+ - Slack/Plugins: add thread-ownership outbound gating via `message_sending` hooks, including @-mention bypass tracking and Slack outbound hook wiring for cancel/modify behavior. (#15775) Thanks @DarlingtonDeveloper.
9
+ - Agents: add synthetic catalog support for `hf:zai-org/GLM-5`. (#15867) Thanks @battman21.
10
+ - Skills: remove duplicate `local-places` Google Places skill/proxy and keep `goplaces` as the single supported Google Places path.
11
+ - Agents: add pre-prompt context diagnostics (`messages`, `systemPromptChars`, `promptChars`, provider/model, session file) before embedded runner prompt calls to improve overflow debugging. (#8930) Thanks @Glucksberg.
12
+ - Onboarding/Providers: add first-class Hugging Face Inference provider support (provider wiring, onboarding auth choice/API key flow, and default-model selection), and preserve Hugging Face auth intent in auth-choice remapping (`tokenProvider=huggingface` with `authChoice=apiKey`) while skipping env-override prompts when an explicit token is provided. (#13472) Thanks @Josephrp.
13
+ - Onboarding/Providers: add `minimax-api-key-cn` auth choice for the MiniMax China API endpoint. (#15191) Thanks @liuy.
14
+
15
+ ### Fixes
16
+
17
+ - Gateway/Auth: add trusted-proxy mode hardening follow-ups by keeping `OPENCLAW_GATEWAY_*` env compatibility, auto-normalizing invalid setup combinations in interactive `gateway configure` (trusted-proxy forces `bind=lan` and disables Tailscale serve/funnel), and suppressing shared-secret/rate-limit audit findings that do not apply to trusted-proxy deployments. (#15940) Thanks @nickytonline.
18
+ - Docs/Hooks: update hooks documentation URLs to the new `/automation/hooks` location. (#16165) Thanks @nicholascyh.
19
+ - Security/Audit: warn when `gateway.tools.allow` re-enables default-denied tools over HTTP `POST /tools/invoke`, since this can increase RCE blast radius if the gateway is reachable.
20
+ - Security/Plugins/Hooks: harden npm-based installs by restricting specs to registry packages only, passing `--ignore-scripts` to `npm pack`, and cleaning up temp install directories.
21
+ - Security/Sessions: preserve inter-session input provenance for routed prompts so delegated/internal sessions are not treated as direct external user instructions. Thanks @anbecker.
22
+ - Feishu: stop persistent Typing reaction on NO_REPLY/suppressed runs by wiring reply-dispatcher cleanup to remove typing indicators. (#15464) Thanks @arosstale.
23
+ - Agents: strip leading empty lines from `sanitizeUserFacingText` output and normalize whitespace-only outputs to empty text. (#16158) Thanks @mcinteerj.
24
+ - BlueBubbles: gracefully degrade when Private API is disabled by filtering private-only actions, skipping private-only reactions/reply effects, and avoiding private reply markers so non-private flows remain usable. (#16002) Thanks @L-U-C-K-Y.
25
+ - Outbound: add a write-ahead delivery queue with crash-recovery retries to prevent lost outbound messages after gateway restarts. (#15636) Thanks @nabbilkhan, @thewilloftheshadow.
26
+ - Auto-reply/Threading: auto-inject implicit reply threading so `replyToMode` works without requiring model-emitted `[[reply_to_current]]`, while preserving `replyToMode: "off"` behavior for implicit Slack replies and keeping block-streaming chunk coalescing stable under `replyToMode: "first"`. (#14976) Thanks @Diaspar4u.
27
+ - Auto-reply/Threading: honor explicit `[[reply_to_*]]` tags even when `replyToMode` is `off`. (#16174) Thanks @aldoeliacim.
28
+ - Plugins/Threading: rename `allowTagsWhenOff` to `allowExplicitReplyTagsWhenOff` and keep the old key as a deprecated alias for compatibility. (#16189).
29
+ - Outbound/Threading: pass `replyTo` and `threadId` from `message send` tool actions through the core outbound send path to channel adapters, preserving thread/reply routing. (#14948) Thanks @mcaxtr.
30
+ - Auto-reply/Media: allow image-only inbound messages (no caption) to reach the agent instead of short-circuiting as empty text, and preserve thread context in queued/followup prompt bodies for media-only runs. (#11916) Thanks @arosstale.
31
+ - Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.
32
+ - Web UI: add `img` to DOMPurify allowed tags and `src`/`alt` to allowed attributes so markdown images render in webchat instead of being stripped. (#15437) Thanks @lailoo.
33
+ - Telegram/Matrix: treat MP3 and M4A (including `audio/mp4`) as voice-compatible for `asVoice` routing, and keep WAV/AAC falling back to regular audio sends. (#15438) Thanks @azade-c.
34
+ - WhatsApp: preserve outbound document filenames for web-session document sends instead of always sending `"file"`. (#15594) Thanks @TsekaLuk.
35
+ - Telegram: cap bot menu registration to Telegram's 100-command limit with an overflow warning while keeping typed hidden commands available. (#15844) Thanks @battman21.
36
+ - Telegram: scope skill commands to the resolved agent for default accounts so `setMyCommands` no longer triggers `BOT_COMMANDS_TOO_MUCH` when multiple agents are configured. (#15599).
37
+ - Discord: avoid misrouting numeric guild allowlist entries to `/channels/<guildId>` by prefixing guild-only inputs with `guild:` during resolution. (#12326) Thanks @headswim.
38
+ - Memory/QMD: default `memory.qmd.searchMode` to `search` for faster CPU-only recall and always scope `search`/`vsearch` requests to managed collections (auto-falling back to `query` when required). (#16047) Thanks @togotago.
39
+ - Memory/LanceDB: add configurable `captureMaxChars` for auto-capture while keeping the legacy 500-char default. (#16641) Thanks @ciberponk.
40
+ - MS Teams: preserve parsed mention entities/text when appending OneDrive fallback file links, and accept broader real-world Teams mention ID formats (`29:...`, `8:orgid:...`) while still rejecting placeholder patterns. (#15436) Thanks @hyojin.
41
+ - Media: classify `text/*` MIME types as documents in media-kind routing so text attachments are no longer treated as unknown. (#12237) Thanks @arosstale.
42
+ - Inbound/Web UI: preserve literal `\n` sequences when normalizing inbound text so Windows paths like `C:\\Work\\nxxx\\README.md` are not corrupted. (#11547) Thanks @mcaxtr.
43
+ - TUI/Streaming: preserve richer streamed assistant text when final payload drops pre-tool-call text blocks, while keeping non-empty final payload authoritative for plain-text updates. (#15452) Thanks @TsekaLuk.
44
+ - Providers/MiniMax: switch implicit MiniMax API-key provider from `openai-completions` to `anthropic-messages` with the correct Anthropic-compatible base URL, fixing `invalid role: developer (2013)` errors on MiniMax M2.5. (#15275) Thanks @lailoo.
45
+ - Ollama/Agents: use resolved model/provider base URLs for native `/api/chat` streaming (including aliased providers), normalize `/v1` endpoints, and forward abort + `maxTokens` stream options for reliable cancellation and token caps. (#11853) Thanks @BrokenFinger98.
46
+ - OpenAI Codex/Spark: implement end-to-end `gpt-5.3-codex-spark` support across fallback/thinking/model resolution and `models list` forward-compat visibility. (#14990, #15174) Thanks @L-U-C-K-Y, @loiie45e.
47
+ - Agents/Codex: allow `gpt-5.3-codex-spark` in forward-compat fallback, live model filtering, and thinking presets, and fix model-picker recognition for spark. (#14990) Thanks @L-U-C-K-Y.
48
+ - Models/Codex: resolve configured `openai-codex/gpt-5.3-codex-spark` through forward-compat fallback during `models list`, so it is not incorrectly tagged as missing when runtime resolution succeeds. (#15174) Thanks @loiie45e.
49
+ - OpenAI Codex/Auth: bridge OpenClaw OAuth profiles into `pi` `auth.json` so model discovery and models-list registry resolution can use Codex OAuth credentials. (#15184) Thanks @loiie45e.
50
+ - Auth/OpenAI Codex: share OAuth login handling across onboarding and `models auth login --provider openai-codex`, keep onboarding alive when OAuth fails, and surface a direct OAuth help note instead of terminating the wizard. (#15406, follow-up to #14552) Thanks @zhiluo20.
51
+ - Onboarding/Providers: add vLLM as an onboarding provider with model discovery, auth profile wiring, and non-interactive auth-choice validation. (#12577) Thanks @gejifeng.
52
+ - Onboarding/CLI: restore terminal state without resuming paused `stdin`, so onboarding exits cleanly (including Docker TTY installs that would otherwise hang). (#12972) Thanks @vincentkoc.
53
+ - Signal/Install: auto-install `signal-cli` via Homebrew on non-x64 Linux architectures, avoiding x86_64 native binary `Exec format error` failures on arm64/arm hosts. (#15443) Thanks @jogvan-k.
54
+ - macOS Voice Wake: fix a crash in trigger trimming for CJK/Unicode transcripts by matching and slicing on original-string ranges instead of transformed-string indices. (#11052) Thanks @Flash-LHR.
55
+ - Mattermost (plugin): retry websocket monitor connections with exponential backoff and abort-aware teardown so transient connect failures no longer permanently stop monitoring. (#14962) Thanks @mcaxtr.
56
+ - Discord/Agents: apply channel/group `historyLimit` during embedded-runner history compaction to prevent long-running channel sessions from bypassing truncation and overflowing context windows. (#11224) Thanks @shadril238.
57
+ - Outbound targets: fail closed for WhatsApp/Twitch/Google Chat fallback paths so invalid or missing targets are dropped instead of rerouted, and align resolver hints with strict target requirements. (#13578) Thanks @mcaxtr.
58
+ - Gateway/Restart: clear stale command-queue and heartbeat wake runtime state after SIGUSR1 in-process restarts to prevent zombie gateway behavior where queued work stops draining. (#15195) Thanks @joeykrug.
59
+ - Heartbeat: prevent scheduler silent-death races during runner reloads, preserve retry cooldown backoff under wake bursts, and prioritize user/action wake causes over interval/retry reasons when coalescing. (#15108) Thanks @joeykrug.
60
+ - Heartbeat: allow explicit wake (`wake`) and hook wake (`hook:*`) reasons to run even when `HEARTBEAT.md` is effectively empty so queued system events are processed. (#14527) Thanks @arosstale.
61
+ - Auto-reply/Heartbeat: strip sentence-ending `HEARTBEAT_OK` tokens even when followed by up to 4 punctuation characters, while preserving surrounding sentence punctuation. (#15847) Thanks @Spacefish.
62
+ - Sessions/Agents: pass `agentId` when resolving existing transcript paths in reply runs so non-default agents and heartbeat/chat handlers no longer fail with `Session file path must be within sessions directory`. (#15141) Thanks @Goldenmonstew.
63
+ - Sessions/Agents: pass `agentId` through status and usage transcript-resolution paths (auto-reply, gateway usage APIs, and session cost/log loaders) so non-default agents can resolve absolute session files without path-validation failures. (#15103) Thanks @jalehman.
64
+ - Sessions: archive previous transcript files on `/new` and `/reset` session resets (including gateway `sessions.reset`) so stale transcripts do not accumulate on disk. (#14869) Thanks @mcaxtr.
65
+ - Status/Sessions: stop clamping derived `totalTokens` to context-window size, keep prompt-token snapshots wired through session accounting, and surface context usage as unknown when fresh snapshot data is missing to avoid false 100% reports. (#15114) Thanks @echoVic.
66
+ - Gateway/Routing: speed up hot paths for session listing (derived titles + previews), WS broadcast, and binding resolution.
67
+ - Gateway/Sessions: cache derived title + last-message transcript reads to speed up repeated sessions list refreshes.
68
+ - CLI/Completion: route plugin-load logs to stderr and write generated completion scripts directly to stdout to avoid `source <(openclaw completion ...)` corruption. (#15481) Thanks @arosstale.
69
+ - CLI: lazily load outbound provider dependencies and remove forced success-path exits so commands terminate naturally without killing intentional long-running foreground actions. (#12906) Thanks @DrCrinkle.
70
+ - CLI: speed up startup by lazily registering core commands (keeps rich `--help` while reducing cold-start overhead).
71
+ - Security/Gateway + ACP: block high-risk tools (`sessions_spawn`, `sessions_send`, `gateway`, `whatsapp_login`) from HTTP `/tools/invoke` by default with `gateway.tools.{allow,deny}` overrides, and harden ACP permission selection to fail closed when tool identity/options are ambiguous while supporting `allow_always`/`reject_always`. (#15390) Thanks @aether-ai-agent.
72
+ - Security/ACP: prompt for non-read/search permission requests in ACP clients (reduces silent tool approval risk). Thanks @aether-ai-agent.
73
+ - Security/Gateway: breaking default-behavior change - canvas IP-based auth fallback now only accepts machine-scoped addresses (RFC1918, link-local, ULA IPv6, CGNAT); public-source IP matches now require bearer token auth. (#14661) Thanks @sumleo.
74
+ - Security/Link understanding: block loopback/internal host patterns and private/mapped IPv6 addresses in extracted URL handling to close SSRF bypasses in link CLI flows. (#15604) Thanks @AI-Reviewer-QS.
75
+ - Security/Browser: constrain `POST /trace/stop`, `POST /wait/download`, and `POST /download` output paths to OpenClaw temp roots and reject traversal/escape paths.
76
+ - Security/Browser: sanitize download `suggestedFilename` to keep implicit `wait/download` paths within the downloads root. Thanks @1seal.
77
+ - Security/Browser: confine `POST /hooks/file-chooser` upload paths to an OpenClaw temp uploads root and reject traversal/escape paths. Thanks @1seal.
78
+ - Security/Browser: require auth for the sandbox browser bridge server (protects `/profiles`, `/tabs`, CDP URLs, and other control endpoints). Thanks @jackhax.
79
+ - Security: bind local helper servers to loopback and fail closed on non-loopback OAuth callback hosts (reduces localhost/LAN attack surface).
80
+ - Security/Canvas: serve A2UI assets via the shared safe-open path (`openFileWithinRoot`) to close traversal/TOCTOU gaps, with traversal and symlink regression coverage. (#10525) Thanks @abdelsfane.
81
+ - Security/WhatsApp: enforce `0o600` on `creds.json` and `creds.json.bak` on save/backup/restore paths to reduce credential file exposure. (#10529) Thanks @abdelsfane.
82
+ - Security/Gateway: sanitize and truncate untrusted WebSocket header values in pre-handshake close logs to reduce log-poisoning risk. Thanks @thewilloftheshadow.
83
+ - Security/Audit: add misconfiguration checks for sandbox Docker config with sandbox mode off, ineffective `gateway.nodes.denyCommands` entries, global minimal tool-profile overrides by agent profiles, and permissive extension-plugin tool reachability.
84
+ - Security/Audit: distinguish external webhooks (`hooks.enabled`) from internal hooks (`hooks.internal.enabled`) in attack-surface summaries to avoid false exposure signals when only internal hooks are enabled. (#13474) Thanks @mcaxtr.
85
+ - Security/Onboarding: clarify multi-user DM isolation remediation with explicit `openclaw config set session.dmScope ...` commands in security audit, doctor security, and channel onboarding guidance. (#13129) Thanks @VintLin.
86
+ - Security/Gateway: bind node `system.run` approval overrides to gateway exec-approval records (runId-bound), preventing approval-bypass via `node.invoke` param injection. Thanks @222n5.
87
+ - Agents/Nodes: harden node exec approval decision handling in the `nodes` tool run path by failing closed on unexpected approval decisions, and add regression coverage for approval-required retry/deny/timeout flows. (#4726) Thanks @rmorse.
88
+ - Android/Nodes: harden `app.update` by requiring HTTPS and gateway-host URL matching plus SHA-256 verification, stream URL camera downloads to disk with size guards to avoid memory spikes, and stop signing release builds with debug keys. (#13541) Thanks @smartprogrammer93.
89
+ - Routing: enforce strict binding-scope matching across peer/guild/team/roles so peer-scoped Discord/Slack bindings no longer match unrelated guild/team contexts or fallback tiers. (#15274) Thanks @lailoo.
90
+ - Exec/Allowlist: allow multiline heredoc bodies (`<<`, `<<-`) while keeping multiline non-heredoc shell commands blocked, so exec approval parsing permits heredoc input safely without allowing general newline command chaining. (#13811) Thanks @mcaxtr.
91
+ - Config: preserve `${VAR}` env references when writing config files so `openclaw config set/apply/patch` does not persist secrets to disk. Thanks @thewilloftheshadow.
92
+ - Config: remove a cross-request env-snapshot race in config writes by carrying read-time env context into write calls per request, preserving `${VAR}` refs safely under concurrent gateway config mutations. (#11560) Thanks @akoscz.
93
+ - Config: log overwrite audit entries (path, backup target, and hash transition) whenever an existing config file is replaced, improving traceability for unexpected config clobbers.
94
+ - Config: keep legacy audio transcription migration strict by rejecting non-string/unsafe command tokens while still migrating valid custom script executables. (#5042) Thanks @shayan919293.
95
+ - Config: accept `$schema` key in config file so JSON Schema editor tooling works without validation errors. (#14998) Thanks @lailoo.
96
+ - Gateway/Tools Invoke: sanitize `/tools/invoke` execution failures while preserving `400` for tool input errors and returning `500` for unexpected runtime failures, with regression coverage and docs updates. (#13185) Thanks @davidrudduck.
97
+ - Gateway/Hooks: preserve `408` for hook request-body timeout responses while keeping bounded auth-failure cache eviction behavior, with timeout-status regression coverage. (#15848) Thanks @AI-Reviewer-QS.
98
+ - Plugins/Hooks: fire `before_tool_call` hook exactly once per tool invocation in embedded runs by removing duplicate dispatch paths while preserving parameter mutation semantics. (#15635) Thanks @lailoo.
99
+ - Agents/Transcript policy: sanitize OpenAI/Codex tool-call ids during transcript policy normalization to prevent invalid tool-call identifiers from propagating into session history. (#15279) Thanks @divisonofficer.
100
+ - Agents/Image tool: cap image-analysis completion `maxTokens` by model capability (`min(4096, model.maxTokens)`) to avoid over-limit provider failures while still preventing truncation. (#11770) Thanks @detecti1.
101
+ - Agents/Compaction: centralize exec default resolution in the shared tool factory so per-agent `tools.exec` overrides (host/security/ask/node and related defaults) persist across compaction retries. (#15833) Thanks @napetrov.
102
+ - Gateway/Agents: stop injecting a phantom `main` agent into gateway agent listings when `agents.list` explicitly excludes it. (#11450) Thanks @arosstale.
103
+ - Process/Exec: avoid shell execution for `.exe` commands on Windows so env overrides work reliably in `runCommandWithTimeout`. Thanks @thewilloftheshadow.
104
+ - Daemon/Windows: preserve literal backslashes in `gateway.cmd` command parsing so drive and UNC paths are not corrupted in runtime checks and doctor entrypoint comparisons. (#15642) Thanks @arosstale.
105
+ - Sandbox: pass configured `sandbox.docker.env` variables to sandbox containers at `docker create` time. (#15138) Thanks @stevebot-alive.
106
+ - Voice Call: route webhook runtime event handling through shared manager event logic so rejected inbound hangups are idempotent in production, with regression tests for duplicate reject events and provider-call-ID remapping parity. (#15892) Thanks @dcantu96.
107
+ - Cron: add regression coverage for announce-mode isolated jobs so runs that already report `delivered: true` do not enqueue duplicate main-session relays, including delivery configs where `mode` is omitted and defaults to announce. (#15737) Thanks @brandonwise.
108
+ - Cron: honor `deleteAfterRun` in isolated announce delivery by mapping it to subagent announce cleanup mode, so cron run sessions configured for deletion are removed after completion. (#15368) Thanks @arosstale.
109
+ - Web tools/web_fetch: prefer `text/markdown` responses for Cloudflare Markdown for Agents, add `cf-markdown` extraction for markdown bodies, and redact fetched URLs in `x-markdown-tokens` debug logs to avoid leaking raw paths/query params. (#15376) Thanks @Yaxuan42.
110
+ - Tools/web_search: support `freshness` for the Perplexity provider by mapping `pd`/`pw`/`pm`/`py` to Perplexity `search_recency_filter` values and including freshness in the Perplexity cache key. (#15343) Thanks @echoVic.
111
+ - Clawdock: avoid Zsh readonly variable collisions in helper scripts. (#15501) Thanks @nkelner.
112
+ - Memory: switch default local embedding model to the QAT `embeddinggemma-300m-qat-Q8_0` variant for better quality at the same footprint. (#15429) Thanks @azade-c.
113
+ - Docs/Discord: expand quick setup and clarify guild workspace guidance. (#20088) Thanks @pejmanjohn, @thewilloftheshadow.
114
+ - Docs/Mermaid: remove hardcoded Mermaid init theme blocks from four docs diagrams so dark mode inherits readable theme defaults. (#15157) Thanks @heytulsiprasad.
115
+ - Security/Pairing: generate 256-bit base64url device and node pairing tokens and use byte-safe constant-time verification to avoid token-compare edge-case failures. (#16535) Thanks @FaizanKolega, @gumadeiras.
116
+
117
+ ### Breaking
118
+
119
+ - Config/State: removed legacy `.moltbot` auto-detection/migration and `moltbot.json` config candidates. If you still have state/config under `~/.moltbot`, move it to `~/.openclaw` (recommended) or set `OPENCLAW_STATE_DIR` / `OPENCLAW_CONFIG_PATH` explicitly.
120
+
CHANGELOG/2026.2.17.md ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.2.17
2
+
3
+ ### Changes
4
+
5
+ - Agents/Anthropic: add opt-in 1M context beta header support for Opus/Sonnet via model `params.context1m: true` (maps to `anthropic-beta: context-1m-2025-08-07`).
6
+ - Agents/Models: support Anthropic Sonnet 4.6 (`anthropic/claude-sonnet-4-6`) across aliases/defaults with forward-compat fallback when upstream catalogs still only expose Sonnet 4.5.
7
+ - Commands/Subagents: add `/subagents spawn` for deterministic subagent activation from chat commands. (#18218) Thanks @JoshuaLelon.
8
+ - Agents/Subagents: add an accepted response note for `sessions_spawn` explaining polling subagents are disabled for one-off calls. Thanks @tyler6204.
9
+ - Agents/Subagents: prefix spawned subagent task messages with context to preserve source information in downstream handling. Thanks @tyler6204.
10
+ - iOS/Share: add an iOS share extension that forwards shared URL/text/image content directly to gateway `agent.request`, with delivery-route fallback and optional receipt acknowledgements. (#19424) Thanks @mbelinky.
11
+ - iOS/Talk: add a `Background Listening` toggle that keeps Talk Mode active while the app is backgrounded (off by default for battery safety). Thanks @zeulewan.
12
+ - iOS/Talk: add a `Voice Directive Hint` toggle for Talk Mode prompts so users can disable ElevenLabs voice-switching instructions to save tokens when not needed. (#18250) Thanks @zeulewan.
13
+ - iOS/Talk: harden barge-in behavior by disabling interrupt-on-speech when output route is built-in speaker/receiver, reducing false interruptions from local TTS bleed-through. Thanks @zeulewan.
14
+ - Slack: add native single-message text streaming with Slack `chat.startStream`/`appendStream`/`stopStream`; keep reply threading aligned with `replyToMode`, default streaming to enabled, and fall back to normal delivery when streaming fails. (#9972) Thanks @natedenh.
15
+ - Slack: add configurable streaming modes for draft previews. (#18555) Thanks @Solvely-Colin.
16
+ - Telegram/Agents: add inline button `style` support (`primary|success|danger`) across message tool schema, Telegram action parsing, send pipeline, and runtime prompt guidance. (#18241) Thanks @obviyus.
17
+ - Telegram: surface user message reactions as system events, with configurable `channels.telegram.reactionNotifications` scope. (#10075) Thanks @Glucksberg.
18
+ - iMessage: support `replyToId` on outbound text/media sends and normalize leading `[[reply_to:<id>]]` tags so replies target the intended iMessage. Thanks @tyler6204.
19
+ - Tool Display/Web UI: add intent-first tool detail views and exec summaries. (#18592) Thanks @xdLawless2.
20
+ - Discord: expose native `/exec` command options (host/security/ask/node) so Discord slash commands get autocomplete and structured inputs. Thanks @thewilloftheshadow.
21
+ - Discord: allow reusable interactive components with `components.reusable=true` so buttons, selects, and forms can be used multiple times before expiring. Thanks @thewilloftheshadow.
22
+ - Discord: add per-button `allowedUsers` allowlist for interactive components to restrict who can click buttons. Thanks @thewilloftheshadow.
23
+ - Cron/Gateway: separate per-job webhook delivery (`delivery.mode = "webhook"`) from announce delivery, enforce valid HTTP(S) webhook URLs, and keep a temporary legacy `notify + cron.webhook` fallback for stored jobs. (#17901) Thanks @advaitpaliwal.
24
+ - Cron/CLI: add deterministic default stagger for recurring top-of-hour cron schedules (including 6-field seconds cron), auto-migrate existing jobs to persisted `schedule.staggerMs`, and add `openclaw cron add/edit --stagger <duration>` plus `--exact` overrides for per-job timing control.
25
+ - Cron: log per-run model/provider usage telemetry in cron run logs/webhooks and add a local usage report script for aggregating token usage by job. (#18172) Thanks @HankAndTheCrew.
26
+ - Tools/Web: add URL allowlists for `web_search` and `web_fetch`. (#18584) Thanks @smartprogrammer93.
27
+ - Browser: add `extraArgs` config for custom Chrome launch arguments. (#18443) Thanks @JayMishra-source.
28
+ - Voice Call: pre-cache inbound greeting TTS for faster first playback. (#18447) Thanks @JayMishra-source.
29
+ - Skills: compact skill file `<location>` paths in the system prompt by replacing home-directory prefixes with `~`, and add targeted compaction tests for prompt serialization behavior. (#14776) Thanks @bitfish3.
30
+ - Skills: refine skill-description routing boundaries with explicit "Use when"/"NOT for" guidance for coding-agent/github/weather, and clarify PTY/browser fallback wording. (#14577) Thanks @DylanWoodAkers.
31
+ - Auto-reply/Prompts: include trusted inbound `message_id` in conversation metadata payloads for downstream targeting workflows. Thanks @tyler6204.
32
+ - Auto-reply: include `sender_id` in trusted inbound metadata so moderation workflows can target the sender without relying on untrusted text. (#18303) Thanks @crimeacs.
33
+ - UI/Sessions: avoid duplicating typed session prefixes in display names (for example `Subagent Subagent ...`). Thanks @tyler6204.
34
+ - Agents/Z.AI: enable `tool_stream` by default for real-time tool call streaming, with opt-out via `params.tool_stream: false`. (#18173) Thanks @tianxiao1430-jpg.
35
+ - Plugins: add `before_agent_start` model/provider overrides before resolution. (#18568) Thanks @natefikru.
36
+ - Mattermost: add emoji reaction actions plus reaction event notifications, including an explicit boolean `remove` flag to avoid accidental removals. (#18608) Thanks @echo931.
37
+ - Memory/Search: add FTS fallback plus query expansion for memory search. (#18304) Thanks @irchelper.
38
+ - Agents/Models: support per-model `thinkingDefault` overrides in model config. (#18152) Thanks @wu-tian807.
39
+ - Agents: enable `llms.txt` discovery in default behavior. (#18158) Thanks @yolo-maxi.
40
+ - Extensions/Auth: add OpenAI Codex CLI auth provider integration. (#18009) Thanks @jiteshdhamaniya.
41
+ - Feishu: add Bitable create-app/create-field tools for automation workflows. (#17963) Thanks @gaowanqi08141999.
42
+ - Docker: add optional `OPENCLAW_INSTALL_BROWSER` build arg to preinstall Chromium + Xvfb in the Docker image, avoiding runtime Playwright installs. (#18449) Thanks @sebslight.
43
+
44
+ ### Fixes
45
+
46
+ - Agents/Antigravity: preserve unsigned Claude thinking blocks as plain text instead of dropping them during transcript sanitization, preventing reasoning context loss while avoiding `thinking.signature` request rejections.
47
+ - Agents/Google: clean tool JSON Schemas for `google-antigravity` the same as `google-gemini-cli` before Cloud Code Assist requests, preventing Claude tool calls from failing with `patternProperties` 400 errors. (#19860)
48
+ - Tests/Telegram: add regression coverage for command-menu sync that asserts all `setMyCommands` entries are Telegram-safe and hyphen-normalized across native/custom/plugin command sources. (#19703) Thanks @obviyus.
49
+ - Agents/Image: collapse resize diagnostics to one line per image and include visible pixel/byte size details in the log message for faster triage.
50
+ - Auth/Cooldowns: clear all usage stats fields (`disabledUntil`, `disabledReason`, `failureCounts`) in `clearAuthProfileCooldown` so manual cooldown resets fully recover billing-disabled profiles without requiring direct file edits. (#19211) Thanks @nabbilkhan.
51
+ - Agents/Subagents: preemptively guard accumulated tool-result context before model calls by truncating oversized outputs and compacting oldest tool-result messages to avoid context-window overflow crashes. Thanks @tyler6204.
52
+ - Agents/Subagents/CLI: fail `sessions_spawn` when subagent model patching is rejected, allow subagent model patch defaults from `subagents.model`, and keep `sessions list`/`status` model reporting aligned to runtime model resolution. (#18660) Thanks @robbyczgw-cla.
53
+ - Agents/Subagents: add explicit subagent guidance to recover from `[compacted: tool output removed to free context]` / `[truncated: output exceeded context limit]` markers by re-reading with smaller chunks instead of full-file `cat`. Thanks @tyler6204.
54
+ - Agents/Tools: make `read` auto-page across chunks (when no explicit `limit` is provided) and scale its per-call output budget from model `contextWindow`, so larger contexts can read more before context guards kick in. Thanks @tyler6204.
55
+ - Agents/Tools: strip duplicated `read` truncation payloads from tool-result `details` and make pre-call context guarding account for heavy tool-result metadata, so repeated `read` calls no longer bypass compaction and overflow model context windows. Thanks @tyler6204.
56
+ - Reply threading: keep reply context sticky across streamed/split chunks and preserve `replyToId` on all chunk sends across shared and channel-specific delivery paths (including iMessage, BlueBubbles, Telegram, Discord, and Matrix), so follow-up bubbles stay attached to the same referenced message. Thanks @tyler6204.
57
+ - Gateway/Agent: defer transient lifecycle `error` snapshots with a short grace window so `agent.wait` does not resolve early during retry/failover. Thanks @tyler6204.
58
+ - Gateway/Presence: centralize presence snapshot broadcasts and unify runtime version precedence (`OPENCLAW_VERSION` > `OPENCLAW_SERVICE_VERSION` > `npm_package_version`) so self-presence and websocket `hello-ok` report consistent versions (#19609). Thanks @gumadeiras.
59
+ - Hooks/Automation: bridge outbound/inbound message lifecycle into internal hook events (`message:received`, `message:sent`) with session-key correlation guards, while keeping per-payload success/error reporting accurate for chunked and best-effort deliveries. (PR #9387).
60
+ - Media understanding: honor `agents.defaults.imageModel` during auto-discovery so implicit image analysis uses configured primary/fallback image models. (PR #7607).
61
+ - iOS/Onboarding: stop auth Step 3 retry-loop churn by pausing reconnect attempts on unauthorized/missing-token gateway errors and keeping auth/pairing issue state sticky during manual retry. (#19153) Thanks @mbelinky.
62
+ - Voice-call: auto-end calls when media streams disconnect to prevent stuck active calls. (#18435) Thanks @JayMishra-source.
63
+ - Voice call/Gateway: prevent overlapping closed-loop turn races with per-call turn locking, route transcript dedupe via source-aware fingerprints with strict cache eviction bounds, and harden `voicecall latency` stats for large logs without spread-operator stack overflow. (#19140) Thanks @mbelinky.
64
+ - iOS/Chat: route ChatSheet RPCs through the operator session instead of the node session to avoid node-role authorization failures for `chat.history`, `chat.send`, and `sessions.list`. (#19320) Thanks @mbelinky.
65
+ - macOS/Update: correct the Sparkle appcast version for 2026.2.15 so updates are offered again. (#18201).
66
+ - Gateway/Auth: clear stale device-auth tokens after device token mismatch errors so re-paired clients can re-auth. (#18201).
67
+ - Telegram: enable DM voice-note transcription with CLI fallback handling. (#18564) Thanks @thhuang.
68
+ - Telegram/Polls: restore Telegram poll action wiring in channel handlers. (#18122) Thanks @akyourowngames.
69
+ - WebChat: strip reply/audio directive tags from rendered chat output. (#18093) Thanks @aldoeliacim.
70
+ - Discord: honor configured HTTP proxy for app-id and allowlist REST resolution. (#17958) Thanks @k2009.
71
+ - BlueBubbles: add fallback path to recover outbound `message_id` from `fromMe` webhooks when platform message IDs are missing. Thanks @tyler6204.
72
+ - BlueBubbles: match outbound message-id fallback recovery by chat identifier as well as account context. Thanks @tyler6204.
73
+ - BlueBubbles: include sender identifier in untrusted conversation metadata for conversation info payloads. Thanks @tyler6204.
74
+ - Security/Exec: fix the OC-09 credential-theft path via environment-variable injection. (#18048) Thanks @aether-ai-agent.
75
+ - Security/Config: confine `$include` resolution to the top-level config directory, harden traversal/symlink checks with cross-platform-safe path containment, and add doctor hints for invalid escaped include paths. (#18652) Thanks @aether-ai-agent.
76
+ - Security/Net: block SSRF bypass via ISATAP embedded IPv4 transition addresses and centralize hostname/IP blocking checks across URL safety validators. Thanks @zpbrent for reporting.
77
+ - Providers: improve error messaging for unconfigured local `ollama`/`vllm` providers. (#18183) Thanks @arosstale.
78
+ - TTS: surface all provider errors instead of only the last error in aggregated failures. (#17964) Thanks @ikari-pl.
79
+ - CLI/Doctor/Configure: skip gateway auth checks for loopback-only setups. (#18407) Thanks @sggolakiya.
80
+ - CLI/Doctor: reconcile gateway service-token drift after re-pair flows. (#18525) Thanks @norunners.
81
+ - Process/Windows: disable detached spawn in exec runs to prevent empty command output. (#18067) Thanks @arosstale.
82
+ - Process: gracefully terminate process trees with SIGTERM before SIGKILL. (#18626) Thanks @sauerdaniel.
83
+ - Sessions/Windows: use atomic session-store writes to prevent context loss on Windows. (#18347) Thanks @twcwinston.
84
+ - Agents/Image: validate base64 image payloads before provider submission. (#18263) Thanks @sriram369.
85
+ - Models CLI: validate catalog entries in `openclaw models set`. (#18129) Thanks @carrotRakko.
86
+ - Usage: isolate last-turn totals in token usage reporting to avoid mixed-turn totals. (#18052) Thanks @arosstale.
87
+ - Cron: resolve `accountId` from agent bindings in isolated sessions. (#17996) Thanks @simonemacario.
88
+ - Gateway/HTTP: preserve unbracketed IPv6 `Host` headers when normalizing requests. (#18061) Thanks @Clawborn.
89
+ - Sandbox: fix workspace-directory orphaning during SHA-1 -> SHA-256 slug migration. (#18523) Thanks @yinghaosang.
90
+ - Ollama/Qwen: handle Qwen 3 reasoning field format in Ollama responses. (#18631) Thanks @mr-sk.
91
+ - OpenAI/Transcripts: always drop orphaned reasoning blocks from transcript repair. (#18632) Thanks @TySabs.
92
+ - Fix types in all tests. Typecheck the whole repository. Thanks @cpojer.
93
+ - Gateway/Channels: wire `gateway.channelHealthCheckMinutes` into strict config validation, treat implicit account status as managed for health checks, and harden channel auto-restart flow (preserve restart-attempt caps across crash loops, propagate enabled/configured runtime flags, and stop pending restart backoff after manual stop).
94
+ - Gateway/WebChat: hard-cap `chat.history` oversized payloads by truncating high-cost fields and replacing over-budget entries with placeholders, so history fetches stay within configured byte limits and avoid chat UI freezes. (#18505) Thanks @sebslight.
95
+ - UI/Usage: replace lingering undefined `var(--text-muted)` usage with `var(--muted)` in usage date-range and chart styles to keep muted text visible across themes. (#17975) Thanks @jogelin.
96
+ - UI/Usage: preserve selected-range totals when timeline data is downsampled by bucket-aggregating timeseries points (instead of dropping intermediate points), so filtered tokens/cost stay accurate. (#17959) Thanks @jogelin.
97
+ - UI/Sessions: refresh the sessions table only after successful deletes and preserve delete errors on cancel/failure paths, so deleted sessions disappear automatically without masking delete failures. (#18507) Thanks @sebslight.
98
+ - Scripts/UI/Windows: fix `pnpm ui:*` spawn `EINVAL` failures by restoring shell-backed launch for `.cmd`/`.bat` runners, narrowing shell usage to launcher types that require it, and rejecting unsafe forwarded shell metacharacters in UI script args. (#18594) Thanks @sebslight.
99
+ - Hooks/Session-memory: recover `/new` conversation summaries when session pointers are reset-path or missing `sessionFile`, and consistently prefer the newest `.jsonl.reset.*` transcript candidate for fallback extraction. (#18088) Thanks @sebslight.
100
+ - Auto-reply/Sessions: prevent stale thread ID leakage into non-thread sessions so replies stay in the main DM after topic interactions. (#18528) Thanks @j2h4u.
101
+ - Slack: restrict forwarded-attachment ingestion to explicit shared-message attachments and skip non-Slack forwarded `image_url` fetches, preventing non-forward attachment unfurls from polluting inbound agent context while preserving forwarded message handling. Thanks @sebslight.
102
+ - Feishu: detect bot mentions in post messages with embedded docs when `message.mentions` is empty. (#18074) Thanks @popomore.
103
+ - Agents/Sessions: align session lock watchdog hold windows with run and compaction timeout budgets (plus grace), preventing valid long-running turns from being force-unlocked mid-run while still recovering hung lock owners. (#18060).
104
+ - Cron: preserve default model fallbacks for cron agent runs when only `model.primary` is overridden, so failover still follows configured fallbacks unless explicitly cleared with `fallbacks: []`. (#18210) Thanks @mahsumaktas.
105
+ - Cron/Isolation: treat non-finite `nextRunAtMs` as missing and repair isolated `every` anchor fallback so legacy jobs without valid timestamps self-heal and scheduler wake timing remains valid. (#19469) Thanks @guirguispierre.
106
+ - Cron: route text-only announce output through the main session announce flow via runSubagentAnnounceFlow so cron text-only output remains visible to the initiating session. Thanks @tyler6204.
107
+ - Cron: treat `timeoutSeconds: 0` as no-timeout (not clamped to 1), ensuring long-running cron runs are not prematurely terminated. Thanks @tyler6204.
108
+ - Cron announce injection now targets the session determined by delivery config (`to` + channel) instead of defaulting to the current session. Thanks @tyler6204.
109
+ - Cron/Heartbeat: canonicalize session-scoped reminder `sessionKey` routing and preserve explicit flat `sessionKey` cron tool inputs, preventing enqueue/wake namespace drift for session-targeted reminders. (#18637) Thanks @vignesh07.
110
+ - Cron/Webhooks: reuse existing session IDs for webhook/cron runs when the session key is stable and still fresh, preserving conversation history. (#18031) Thanks @Operative-001.
111
+ - Cron: prevent spin loops when cron jobs complete within the scheduled second by advancing the next run and enforcing a minimum refire gap. (#18073) Thanks @widingmarcus-cyber.
112
+ - OpenClawKit/iOS ChatUI: accept canonical session-key completion events for local pending runs and preserve message IDs across history refreshes, preventing stuck "thinking" state and message flicker after gateway replies. (#18165) Thanks @mbelinky.
113
+ - iOS/Onboarding: add QR-first onboarding wizard with setup-code deep link support, pairing/auth issue guidance, and device-pair QR generation improvements for Telegram/Web/TUI fallback flows. (#18162) Thanks @mbelinky and @Marvae.
114
+ - iOS/Gateway: stabilize connect/discovery state handling, add onboarding reset recovery in Settings, and fix iOS gateway-controller coverage for command-surface and last-connection persistence behavior. (#18164) Thanks @mbelinky.
115
+ - iOS/Talk: harden mobile talk config handling by ignoring redacted/env-placeholder API keys, support secure local keychain override, improve accessibility motion/contrast behavior in status UI, and tighten ATS to local-network allowance. (#18163) Thanks @mbelinky.
116
+ - iOS/Location: restore the significant location monitor implementation (service hooks + protocol surface + ATS key alignment) after merge drift so iOS builds compile again. (#18260) Thanks @ngutman.
117
+ - iOS/Signing: auto-select local Apple Development team during iOS project generation/build, prefer the canonical OpenClaw team when available, and support local per-machine signing overrides without committing team IDs. (#18421) Thanks @ngutman.
118
+ - Discord/Telegram: make per-account message action gates effective for both action listing and execution, and preserve top-level gate restrictions when account overrides only specify a subset of `actions` keys (account key -> base key -> default fallback). (#18494) Thanks @sebslight.
119
+ - Telegram: keep DM-topic replies and draft previews in the originating private-chat topic by preserving positive `message_thread_id` values for DM threads. (#18586) Thanks @sebslight.
120
+ - Telegram: preserve private-chat topic `message_thread_id` on outbound sends (message/sticker/poll), keep thread-not-found retry fallback, and avoid masking `chat not found` routing errors. (#18993) Thanks @obviyus.
121
+ - Discord: prevent duplicate media delivery when the model uses the `message send` tool with media, by skipping media extraction from messaging tool results since the tool already sent the message directly. (#18270)
122
+ - Discord: route `audioAsVoice` auto-replies through the voice message API so opt-in audio renders as voice messages. (#18041) Thanks @zerone0x.
123
+ - Discord: skip auto-thread creation in forum/media/voice/stage channels and keep group session last-route metadata fresh to avoid invalid thread API errors and lost follow-up sends. (#18098) Thanks @Clawborn.
124
+ - Discord/Commands: normalize `commands.allowFrom` entries with `user:`/`discord:`/`pk:` prefixes and `<@id>` mentions so command authorization matches Discord allowlist behavior. (#18042) Thanks @sebslight.
125
+ - Telegram: keep draft-stream preview replies attached to the user message for `replyToMode: "all"` in groups and DMs, preserving threaded reply context from preview through finalization. (#17880) Thanks @yinghaosang.
126
+ - Telegram: prevent streaming final replies from being overwritten by later final/error payloads, and suppress fallback tool-error warnings when a recovered assistant answer already exists after tool calls. (#17883) Thanks @Marvae and @obviyus.
127
+ - Telegram: debounce the first draft-stream preview update (30-char threshold) and finalize short responses by editing the stop-time preview message, improving first push notifications and avoiding duplicate final sends. (#18148) Thanks @Marvae.
128
+ - Telegram: disable block streaming when `channels.telegram.streamMode` is `off`, preventing newline/content-block replies from splitting into multiple messages. (#17679) Thanks @saivarunk.
129
+ - Telegram: keep `streamMode: "partial"` draft previews in a single message across assistant-message/reasoning boundaries, preventing duplicate preview bubbles during partial-mode tool-call turns. (#18956) Thanks @obviyus.
130
+ - Telegram: normalize native command names for Telegram menu registration (`-` -> `_`) to avoid `BOT_COMMAND_INVALID` command-menu wipeouts, and log failed command syncs instead of silently swallowing them. (#19257) Thanks @akramcodez.
131
+ - Telegram: route non-abort slash commands on the normal chat/topic sequential lane while keeping true abort requests (`/stop`, `stop`) on the control lane, preventing command/reply race conditions from control-lane bypass. (#17899) Thanks @obviyus.
132
+ - Telegram: ignore `<media:...>` placeholder lines when extracting `MEDIA:` tool-result paths, preventing false local-file reads and dropped replies. (#18510) Thanks @yinghaosang.
133
+ - Telegram: skip retries when inbound media `getFile` fails with Telegram's 20MB limit and continue processing message text, avoiding dropped messages for oversized attachments. (#18531) Thanks @brandonwise.
134
+ - Telegram: clear stored polling offsets when bot tokens change or accounts are deleted, preventing stale offsets after token rotations. (#18233) Thanks @sebslight.
135
+ - Telegram: enable `autoSelectFamily` by default on Node.js 22+ so IPv4 fallback works on broken IPv6 networks. (#18272) Thanks @nacho9900.
136
+ - Auto-reply/TTS: keep tool-result media delivery enabled in group chats and native command sessions (while still suppressing tool summary text) so `NO_REPLY` follow-ups do not drop successful TTS audio. (#17991) Thanks @zerone0x.
137
+ - Agents/Tools: deliver tool-result media even when verbose tool output is off so media attachments are not dropped. (#16679) Thanks @sebslight.
138
+ - Discord: optimize reaction notification handling to skip unnecessary message fetches in `off`/`all`/`allowlist` modes, streamline reaction routing, and improve reaction emoji formatting. (#18248) Thanks @thewilloftheshadow and @victorGPT.
139
+ - CLI/Pairing: make `openclaw qr --remote` prefer `gateway.remote.url` over tailscale/public URL resolution and register the `openclaw clawbot qr` legacy alias path. (#18091).
140
+ - CLI/QR: restore fail-fast validation for `openclaw qr --remote` when neither `gateway.remote.url` nor tailscale `serve`/`funnel` is configured, preventing unusable remote pairing QR flows. (#18166) Thanks @mbelinky.
141
+ - CLI: fix parent/subcommand option collisions across gateway, daemon, update, ACP, and browser command flows, while preserving legacy `browser set headers --json <payload>` compatibility (#18725). Thanks @gumadeiras.
142
+ - CLI/Doctor: ensure `openclaw doctor --fix --non-interactive --yes` exits promptly after completion so one-shot automation no longer hangs. (#18502) Thanks @sebslight.
143
+ - CLI/Doctor: auto-repair `dmPolicy="open"` configs missing wildcard allowlists and write channel-correct repair paths (including `channels.googlechat.dm.allowFrom`) so `openclaw doctor --fix` no longer leaves Google Chat configs invalid after attempted repair. (#18544) Thanks @sebslight.
144
+ - CLI/Doctor: detect gateway service token drift when the gateway token is only provided via environment variables, keeping service repairs aligned after token rotation. Thanks @sebslight.
145
+ - Gateway/Update: prevent restart crash loops after failed self-updates by restarting only on successful updates, stopping early on failed install/build steps, and running `openclaw doctor --fix` during updates to sanitize config. (#18131) Thanks @RamiNoodle733.
146
+ - Gateway/Update: preserve update.run restart delivery context so post-update status replies route back to the initiating channel/thread. (#18267) Thanks @yinghaosang.
147
+ - CLI/Update: run a standalone restart helper after updates, honoring service-name overrides and reporting restart initiation separately from confirmed restarts. (#18050) Thanks @sebslight.
148
+ - CLI/Daemon: warn when a gateway restart sees a stale service token so users can reinstall with `openclaw gateway install --force`, and skip drift warnings for non-gateway service restarts. (#18018) Thanks @sebslight.
149
+ - CLI/Daemon: prefer the active version-manager Node when installing daemons and include macOS version-manager bin directories in the service PATH so launchd services resolve user-managed runtimes. Thanks @sebslight.
150
+ - CLI/Status: fix `openclaw status --all` token summaries for bot-token-only channels so Mattermost/Zalo no longer show a bot+app warning. (#18527) Thanks @echo931.
151
+ - CLI/Configure: make the `/model picker` allowlist prompt searchable with tokenized matching in `openclaw configure` so users can filter huge model lists by typing terms like `gpt-5.2 openai/`. (#19010) Thanks @bjesuiter.
152
+ - CLI/Message: preserve `--components` JSON payloads in `openclaw message send` so Discord component payloads are no longer dropped. (#18222) Thanks @saurabhchopade.
153
+ - Voice Call: add an optional stale call reaper (`staleCallReaperSeconds`) to end stuck calls when enabled. (#18437) Thanks @sebslight.
154
+ - Auto-reply/Subagents: propagate group context (`groupId`, `groupChannel`, `space`) when spawning via `/subagents spawn`, matching tool-triggered subagent spawn behavior.
155
+ - Subagents: route nested announce results back to the parent session after the parent run ends, falling back only when the parent session is deleted. (#18043) Thanks @tyler6204.
156
+ - Subagents: cap announce retry loops with max attempts and expiry to prevent infinite retry spam after deferred announces. (#18444) Thanks @sebslight.
157
+ - Agents/Tools/exec: add a preflight guard that detects likely shell env var injection (e.g. `$DM_JSON`, `$TMPDIR`) in Python/Node scripts before execution, preventing recurring cron failures and wasted tokens when models emit mixed shell+language source. (#12836).
158
+ - Agents/Tools/exec: treat normal non-zero exit codes as completed and append the exit code to tool output to avoid false tool-failure warnings. (#18425) Thanks @sebslight.
159
+ - Agents/Tools: make loop detection progress-aware and phased by hard-blocking known `process(action=poll|log)` no-progress loops, warning on generic identical-call repeats, warning + no-progress-blocking ping-pong alternation loops (10/20), coalescing repeated warning spam into threshold buckets (including canonical ping-pong pairs), adding a global circuit breaker at 30 no-progress repeats, and emitting structured diagnostic `tool.loop` warning/error events for loop actions. (#16808) Thanks @akramcodez and @beca-oc.
160
+ - Agents/Hooks: preserve the `before_tool_call` wrapped-marker across abort-signal tool wrapping so the hook runs once per tool call in normal agent sessions. (#16852) Thanks @sreuter.
161
+ - Agents/Tests: add `before_message_write` persistence regression coverage for block/mutate behavior (including synthetic tool-result flushes) and thrown-hook fallback persistence. (#18197) Thanks @shakkernerd
162
+ - Agents/Tools: scope the `message` tool schema to the active channel so Telegram uses `buttons` and Discord uses `components`. (#18215) Thanks @obviyus.
163
+ - Agents/Image tool: replace Anthropic-incompatible union schema with explicit `image` (single) and `images` (multi) parameters, keeping tool schemas `anyOf`/`oneOf`/`allOf`-free while preserving multi-image analysis support. (#18551, #18566) Thanks @aldoeliacim.
164
+ - Agents/Models: probe the primary model when its auth-profile cooldown is near expiry (with per-provider throttling), so runs recover from temporary rate limits without staying on fallback models until restart. (#17478) Thanks @PlayerGhost.
165
+ - Agents/Failover: classify provider abort stop-reason errors (`Unhandled stop reason: abort`, `stop reason: abort`, `reason: abort`) as timeout-class failures so configured model fallback chains trigger instead of surfacing raw abort failures. (#18618) Thanks @sauerdaniel.
166
+ - Models/CLI: sync auth-profiles credentials into agent `auth.json` before registry availability checks so `openclaw models list --all` reports auth correctly for API-key/token providers, normalize provider-id aliases when bridging credentials, and skip expired token mirrors. (#18610, #18615) Thanks @sebslight.
167
+ - Agents/Context: raise default total bootstrap prompt cap from `24000` to `150000` chars (keeping `bootstrapMaxChars` at `20000`), include total-cap visibility in `/context`, and mark truncation from injected-vs-raw sizes so total-cap clipping is reflected accurately (#18229). Thanks @gumadeiras.
168
+ - 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.
169
+ - Cron: preserve per-job schedule-error isolation in post-run maintenance recompute so malformed sibling jobs no longer abort persistence of successful runs. (#17852) Thanks @pierreeurope.
170
+ - Gateway/Config: prevent `config.patch` object-array merges from falling back to full-array replacement when some patch entries lack `id`, so partial `agents.list` updates no longer drop unrelated agents. (#17989) Thanks @stakeswky.
171
+ - Gateway/Auth: trim whitespace around trusted proxy entries before matching so configured proxies with stray spaces still authorize. (#18084) Thanks @Clawborn.
172
+ - Config/Discord: require string IDs in Discord allowlists, keep onboarding inputs string-only, and add doctor repair for numeric entries. (#18220) Thanks @thewilloftheshadow.
173
+ - Security/Sessions: create new session transcript JSONL files with user-only (`0o600`) permissions and extend `openclaw security audit --fix` to remediate existing transcript file permissions.
174
+ - Sessions/Maintenance: archive transcripts when pruning stale sessions, clean expired media in subdirectories, and purge `.deleted` transcript archives after the prune window to prevent disk leaks. (#18538) Thanks @sebslight.
175
+ - Infra/Fetch: ensure foreign abort-signal listener cleanup never masks original fetch successes/failures, while still preventing detached-finally unhandled rejection noise in `wrapFetchWithAbortSignal`. Thanks @Jackten.
176
+ - Heartbeat: allow suppressing tool error warning payloads during heartbeat runs via a new heartbeat config flag. (#18497) Thanks @thewilloftheshadow.
177
+ - Heartbeat: include sender metadata (From/To/Provider) in heartbeat prompts so model context matches the delivery target. (#18532) Thanks @dinakars777.
178
+ - Heartbeat/Telegram: strip configured `responsePrefix` before heartbeat ack detection (with boundary-safe matching) so prefixed `HEARTBEAT_OK` replies are correctly suppressed instead of leaking into DMs. (#18602) Thanks @sebslight.
179
+
CHANGELOG/2026.2.2-2.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.2.2-2
2
+
3
+ ### Changes
4
+
5
+ - Docs: promote BlueBubbles as the recommended iMessage integration; mark imsg channel as legacy. (#8415) Thanks @tyler6204.
6
+
7
+ ### Fixes
8
+
9
+ - CLI status: resolve build-info from bundled dist output (fixes "unknown" commit in npm builds).
10
+
CHANGELOG/2026.2.2-3.md ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ ## 2026.2.2-3
2
+
3
+ ### Fixes
4
+
5
+ - Update: ship legacy daemon-cli shim for pre-tsdown update imports (fixes daemon restart after npm update).
6
+
CHANGELOG/2026.2.22.md ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.2.22
2
+
3
+ ### Changes
4
+
5
+ - Control UI/Agents: make the Tools panel data-driven from runtime `tools.catalog`, add per-tool provenance labels (`core` / `plugin:<id>` + optional marker), and keep a static fallback list when the runtime catalog is unavailable. Thanks @Takhoffman.
6
+ - Web Search/Gemini: add grounded Gemini provider support with provider auto-detection and config/docs updates. (#13075, #13074) Thanks @akoscz.
7
+ - Control UI/Cron: add full web cron edit parity (including clone and richer validation/help text), plus all-jobs run history with pagination/search/sort/multi-filter controls and improved cron page layout for cleaner scheduling and failure triage workflows. Thanks @Takhoffman.
8
+ - Provider/Mistral: add support for the Mistral provider, including memory embeddings and voice support. (#23845) Thanks @vincentkoc.
9
+ - Update/Core: add an optional built-in auto-updater for package installs (`update.auto.*`), default-off, with stable rollout delay+jitter and beta hourly cadence.
10
+ - CLI/Update: add `openclaw update --dry-run` to preview channel/tag/target/restart actions without mutating config, installing, syncing plugins, or restarting.
11
+ - Config/UI: add tag-aware settings filtering and broaden config labels/help copy so fields are easier to discover and understand in the dashboard config screen.
12
+ - Channels/Synology Chat: add a native Synology Chat channel plugin with webhook ingress, direct-message routing, outbound send/media support, per-account config, and DM policy controls. (#23012).
13
+ - iOS/Talk: prefetch TTS segments and suppress expected speech-cancellation errors for smoother talk playback. (#22833) Thanks @ngutman.
14
+ - Memory/FTS: add Spanish and Portuguese stop-word filtering for query expansion in FTS-only search mode, improving conversational recall for both languages. Thanks @vincentkoc.
15
+ - Memory/FTS: add Japanese-aware query expansion tokenization and stop-word filtering (including mixed-script terms like ASCII + katakana) for FTS-only search mode. Thanks @vincentkoc.
16
+ - Memory/FTS: add Korean stop-word filtering and particle-aware keyword extraction (including mixed Korean/English stems) for query expansion in FTS-only search mode. (#18899) Thanks @ruypang.
17
+ - Memory/FTS: add Arabic stop-word filtering for query expansion in FTS-only search mode to reduce conversational filler in Arabic memory searches. Thanks @vincentkoc.
18
+ - Discord/Allowlist: canonicalize resolved Discord allowlist names to IDs and split resolution flow for clearer fail-closed behavior.
19
+ - Channels/Config: unify channel preview streaming config handling with a shared resolver and canonical migration path.
20
+ - Gateway/Auth: unify call/probe/status/auth credential-source precedence on shared resolver helpers, with table-driven parity coverage across gateway entrypoints.
21
+ - Gateway/Auth: refactor gateway credential resolution and websocket auth handshake paths to use shared typed auth contexts, including explicit `auth.deviceToken` support in connect frames and tests.
22
+ - Skills: remove bundled `food-order` skill from this repo; manage/install it from ClawHub instead.
23
+ - Docs/Subagents: make thread-bound session guidance channel-first instead of Discord-specific, and list thread-supporting channels explicitly. (#23589) Thanks @osolmaz.
24
+
25
+ ### Fixes
26
+
27
+ - Sessions/Resilience: ignore invalid persisted `sessionFile` metadata and fall back to the derived safe transcript path instead of aborting session resolution for handlers and tooling. (#16061) Thanks @haoyifan and @vincentkoc.
28
+ - Sessions/Paths: resolve symlinked state-dir aliases during transcript-path validation while preserving safe cross-agent/state-root compatibility for valid `agents/<id>/sessions/**` paths. (#18593) Thanks @EpaL and @vincentkoc.
29
+ - Agents/Compaction: count auto-compactions only after a non-retry `auto_compaction_end`, keeping session `compactionCount` aligned to completed compactions (#24056). Thanks @Takhoffman.
30
+ - Security/CLI: redact sensitive values in `openclaw config get` output before printing config paths, preventing credential leakage to terminal output/history. (#13683) Thanks @SleuthCo.
31
+ - Agents/Moonshot: force `supportsDeveloperRole=false` for Moonshot-compatible `openai-completions` models (provider `moonshot` and Moonshot base URLs), so initial runs no longer send unsupported `developer` roles that trigger `ROLE_UNSPECIFIED` errors. (#21060, #22194) Thanks @ShengFuC.
32
+ - 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.
33
+ - 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.
34
+ - 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).
35
+ - 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.
36
+ - 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.
37
+ - 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).
38
+ - 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.
39
+ - Slack/Threading: respect `replyToMode` when Slack auto-populates top-level `thread_ts`, and ignore inline `replyToId` directive tags when `replyToMode` is `off` so thread forcing stays disabled unless explicitly configured. (#23839, #23320, #23513) Thanks @vincentkoc and @dorukardahan.
40
+ - Slack/Extension: forward `message read` `threadId` to `readMessages` and use delivery-context `threadId` as outbound `thread_ts` fallback so extension replies/reads stay in the correct Slack thread. (#22216, #22485, #23836) Thanks @vincentkoc, @lan17 and @dorukardahan.
41
+ - Slack/Upload: resolve bare user IDs (U-prefix) to DM channel IDs via `conversations.open`, and replace `files.uploadV2` with Slack's external 3-step upload flow (`files.getUploadURLExternal` → presigned upload POST → `files.completeUploadExternal`) to avoid `missing_scope`/`invalid_arguments` upload failures in DM and threaded media replies (#57018). Thanks @hydro13.
42
+ - Webchat/Chat: apply assistant `final` payload messages directly to chat state so sent turns render without waiting for a full history refresh cycle. (#14928) Thanks @BradGroux.
43
+ - Webchat/Chat: for out-of-band final events (for example tool-call side runs), append provided final assistant payloads directly instead of forcing a transient history reset. (#11139) Thanks @AkshayNavle.
44
+ - Webchat/Performance: reload `chat.history` after final events only when the final payload lacks a renderable assistant message, avoiding expensive full-history refreshes on normal turns. (#20588) Thanks @amzzzzzzz.
45
+ - Webchat/Sessions: preserve external session routing metadata when internal `chat.send` turns run under `webchat`, so explicit channel-keyed sessions (for example Telegram) no longer get rewritten to `webchat` and misroute follow-up delivery. (#23258) Thanks @binary64.
46
+ - Webchat/Sessions: preserve existing session `label` across `/new` and `/reset` rollovers so reset sessions remain discoverable in session history lists. (#23755) Thanks @ThunderStormer.
47
+ - Gateway/Chat UI: strip inline reply/audio directive tags from non-streaming final webchat broadcasts (including `chat.inject`) while preserving empty-string message content when tags are the entire reply. (#23298) Thanks @SidQin-cyber.
48
+ - Chat/UI: strip inline reply/audio directive tags (`[[reply_to_current]]`, `[[reply_to:<id>]]`, `[[audio_as_voice]]`) from displayed chat history, live chat event output, and session preview snippets so control tags no longer leak into user-visible surfaces.
49
+ - Gateway/Chat UI: sanitize non-streaming final `chat.send`/`chat.inject` payload text with the same envelope/untrusted-context stripping used by `chat.history`, preventing `<<<EXTERNAL_UNTRUSTED_CONTENT...>>>` wrapper markup from rendering in Control UI chat. (#24012) Thanks @mittelaltergouda.
50
+ - Telegram/Media: send a user-facing Telegram reply when media download fails (non-size errors) instead of silently dropping the message.
51
+ - Telegram/Webhook: keep webhook monitors alive until gateway abort signals fire, preventing false channel exits and immediate webhook auto-restart loops.
52
+ - Telegram/Polling: retry recoverable setup-time network failures in monitor startup and await runner teardown before retry to avoid overlapping polling sessions.
53
+ - Telegram/Polling: clear Telegram webhooks (`deleteWebhook`) before starting long-poll `getUpdates`, including retry handling for transient cleanup failures.
54
+ - Telegram/Webhook: add `channels.telegram.webhookPort` config support and pass it through plugin startup wiring to the monitor listener.
55
+ - Browser/Extension Relay: refactor the MV3 worker to preserve debugger attachments across relay drops, auto-reconnect with bounded backoff+jitter, persist and rehydrate attached tab state via `chrome.storage.session`, recover from `target_closed` navigation detaches, guard stale socket handlers, enforce per-tab operation locks and per-request timeouts, and add lifecycle keepalive/badge refresh hooks (`alarms`, `webNavigation`). (#15099, #6175, #8468, #9807).
56
+ - Browser/Relay: treat extension websocket as connected only when `OPEN`, allow reconnect when a stale `CLOSING/CLOSED` extension socket lingers, and guard stale socket message/close handlers so late events cannot clear active relay state; includes regression coverage for live-duplicate `409` rejection and immediate reconnect-after-close races. (#15099, #18698, #20688).
57
+ - Browser/Remote CDP: extend stale-target recovery so `ensureTabAvailable()` now reuses the sole available tab for remote CDP profiles (same behavior as extension profiles) while preserving strict `tab not found` errors when multiple tabs exist; includes remote-profile regression tests. (#15989).
58
+ - Gateway/Pairing: treat `operator.admin` as satisfying other `operator.*` scope checks during device-auth verification so local CLI/TUI sessions stop entering pairing-required loops for pairing/approval-scoped commands. (#22062, #22193, #21191) Thanks @Botaccess, @jhartshorn, and @ctbritt.
59
+ - Gateway/Pairing: auto-approve loopback `scope-upgrade` pairing requests (including device-token reconnects) so local clients do not disconnect on pairing-required scope elevation. (#23708) Thanks @widingmarcus-cyber.
60
+ - Gateway/Scopes: include `operator.read` and `operator.write` in default operator connect scope bundles across CLI, Control UI, and macOS clients so write-scoped announce/sub-agent follow-up calls no longer hit `pairing required` disconnects on loopback gateways. (#22582) thanks @YuzuruS.
61
+ - Gateway/Pairing: treat operator.admin pairing tokens as satisfying operator.write requests so legacy devices stop looping through scope-upgrade prompts introduced in 2026.2.19. (#23125, #23006) Thanks @vignesh07.
62
+ - Gateway/Restart: fix restart-loop edge cases by keeping `openclaw.mjs -> dist/entry.js` bootstrap detection explicit, reacquiring the gateway lock for in-process restart fallback paths, and tightening restart-loop regression coverage. (#23416) Thanks @jeffwnli.
63
+ - Gateway/Lock: use optional gateway-port reachability as a primary stale-lock liveness signal (and wire gateway run-loop lock acquisition to the resolved port), reducing false "already running" lockouts after unclean exits. (#23760) Thanks @Operative-001.
64
+ - Delivery/Queue: quarantine queue entries immediately on known permanent delivery errors (for example invalid recipients or missing conversation references) by moving them to `failed/` instead of retrying on every restart. (#23794) Thanks @aldoeliacim.
65
+ - Cron/Status: split execution outcome (`lastRunStatus`) from delivery outcome (`lastDeliveryStatus`) in persisted cron state, finished events, and run history so failed/unknown announcement delivery is visible without conflating it with run errors.
66
+ - Cron/Delivery: route text-only announce jobs with explicit thread/topic targets through direct outbound delivery so forum/thread destinations do not get dropped by intermediary announce turns. (#23841) Thanks @AndrewArto.
67
+ - 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.
68
+ - Cron/Run: enforce the same per-job timeout guard for manual `cron.run` executions as timer-driven runs, including abort propagation for isolated agent jobs, so forced runs cannot wedge indefinitely. (#23704) Thanks @tkuehnl.
69
+ - Cron/Run: persist the manual-run `runningAtMs` marker before releasing the cron lock so overlapping timer ticks cannot start the same job concurrently (#23993). Thanks @Takhoffman.
70
+ - Cron/Startup: enforce per-job timeout guards for startup catch-up replay runs so missed isolated jobs cannot hang indefinitely during gateway boot recovery (#23966). Thanks @Takhoffman.
71
+ - Cron/Main session: honor abort/timeout signals while retrying `wakeMode=now` heartbeat contention loops so main-target cron runs stop promptly instead of waiting through the full busy-retry window (#23967). Thanks @Takhoffman.
72
+ - Cron/Schedule: for `every` jobs, prefer `lastRunAtMs + everyMs` when still in the future after restarts, then fall back to anchor scheduling for catch-up windows, so NEXT timing matches the last successful cadence. (#22895) Thanks @SidQin-cyber.
73
+ - Cron/Service: execute manual `cron.run` jobs outside the cron lock (while still persisting started/finished state atomically) so `cron.list` and `cron.status` remain responsive during long forced runs. (#23628) Thanks @dsgraves.
74
+ - Cron/Timer: keep a watchdog recheck timer armed while `onTimer` is actively executing so the scheduler continues polling even if a due-run tick stalls for an extended period. (#23628) Thanks @dsgraves.
75
+ - Cron/Run log: clean up settled per-path run-log write queue entries so long-running cron uptime does not retain stale promise bookkeeping in memory (#23968). Thanks @Takhoffman.
76
+ - Cron/Run log: harden `cron.runs` run-log path resolution by rejecting path-separator `id`/`jobId` inputs and enforcing reads within the per-cron `runs/` directory. Thanks @Takhoffman.
77
+ - Cron/Announce: when announce delivery target resolution fails (for example multiple configured channels with no explicit target), skip injecting fallback `Cron (error): ...` into the main session so runs fail cleanly without accidental last-route sends. (#24074) Thanks @Takhoffman.
78
+ - Cron/Telegram: validate cron `delivery.to` with shared Telegram target parsing and resolve legacy `@username`/`t.me` targets to numeric IDs at send-time for deterministic delivery target writeback. (#21930) Thanks @kesor.
79
+ - Telegram/Targets: normalize unprefixed topic-qualified targets through the shared parse/normalize path so valid `@channel:topic:<id>` and `<chatId>:topic:<id>` routes are recognized again. (#24166) Thanks @obviyus.
80
+ - Cron/Isolation: force fresh session IDs for isolated cron runs so `sessionTarget="isolated"` executions never reuse prior run context. (#23470) Thanks @echoVic.
81
+ - Plugins/Install: strip `workspace:*` devDependency entries from copied plugin manifests before `npm install --omit=dev`, preventing `EUNSUPPORTEDPROTOCOL` install failures for npm-published channel plugins (including Feishu and MS Teams).
82
+ - Feishu/Plugins: restore bundled Feishu SDK availability for global installs and strip `openclaw: workspace:*` from plugin `devDependencies` during plugin-version sync so npm-installed Feishu plugins do not fail dependency install. (#23611, #23645, #23603).
83
+ - Config/Channels: auto-enable built-in channels by writing `channels.<id>.enabled=true` (not `plugins.entries.<id>`), and stop adding built-ins to `plugins.allow`, preventing `plugins.entries.telegram: plugin not found` validation failures.
84
+ - Config/Channels: when `plugins.allow` is active, auto-enable/enable flows now also allowlist configured built-in channels so `channels.<id>.enabled=true` cannot remain blocked by restrictive plugin allowlists.
85
+ - Plugins/Discovery: ignore scanned extension backup/disabled directory patterns (for example `.backup-*`, `.bak`, `.disabled*`) and move updater backup directories under `.openclaw-install-backups`, preventing duplicate plugin-id collisions from archived copies.
86
+ - Plugins/CLI: make `openclaw plugins enable` and plugin install/link flows update allowlists via shared plugin-enable policy so enabled plugins are not left disabled by allowlist mismatch. (#23190) Thanks @downwind7clawd-ctrl.
87
+ - Security/Voice Call: harden media stream WebSocket handling against pre-auth idle-connection DoS by adding strict pre-start timeouts, pending/per-IP connection limits, and total connection caps for streaming endpoints. Thanks @jiseoung for reporting.
88
+ - Security/Sessions: redact sensitive token patterns from `sessions_history` tool output and surface `contentRedacted` metadata when masking occurs. (#16928) Thanks @aether-ai-agent.
89
+ - Security/Exec: stop trusting `PATH`-derived directories for safe-bin allowlist checks, add explicit `tools.exec.safeBinTrustedDirs`, and pin safe-bin shell execution to resolved absolute executable paths to prevent binary-shadowing approval bypasses. Thanks @tdjackey for reporting.
90
+ - Security/Elevated: match `tools.elevated.allowFrom` against sender identities only (not recipient `ctx.To`), closing a recipient-token bypass for `/elevated` authorization. Thanks @jiseoung for reporting.
91
+ - Security/Feishu: enforce ID-only allowlist matching for DM/group sender authorization, normalize Feishu ID prefixes during checks, and ignore mutable display names so display-name collisions cannot satisfy allowlist entries. Thanks @jiseoung for reporting.
92
+ - Security/Group policy: harden `channels.*.groups.*.toolsBySender` matching by requiring explicit sender-key types (`id:`, `e164:`, `username:`, `name:`), preventing cross-identifier collisions across mutable/display-name fields while keeping legacy untyped keys on a deprecated ID-only path. Thanks @jiseoung for reporting.
93
+ - Channels/Group policy: fail closed when `groupPolicy: "allowlist"` is set without explicit `groups`, honor account-level `groupPolicy` overrides, and enforce `groupPolicy: "disabled"` as a hard group block. (#22215) Thanks @etereo.
94
+ - Telegram/Discord extensions: propagate trusted `mediaLocalRoots` through extension outbound `sendMedia` options so extension direct-send media paths honor agent-scoped local-media allowlists. (#20029, #21903, #23227).
95
+ - Agents/Exec: honor explicit agent context when resolving `tools.exec` defaults for runs with opaque/non-agent session keys, so per-agent `host/security/ask` policies are applied consistently. (#11832).
96
+ - CLI/Sessions: resolve implicit session-store path templates with the configured default agent ID so named-agent setups do not silently read/write stale `agent:main` session/auth stores. (#22685) Thanks @sene1337.
97
+ - Doctor/Security: add an explicit warning that `approvals.exec.enabled=false` disables forwarding only, while enforcement remains driven by host-local `exec-approvals.json` policy. (#15047).
98
+ - Sandbox/Docker: default sandbox container user to the workspace owner `uid:gid` when `agents.*.sandbox.docker.user` is unset, fixing non-root gateway file-tool permissions under capability-dropped containers. (#20979).
99
+ - Plugins/Media sandbox: propagate trusted `mediaLocalRoots` through plugin action dispatch (including Discord/Telegram action adapters) so plugin send paths enforce the same agent-scoped local-media sandbox roots as core outbound sends. (#20258, #22718).
100
+ - Agents/Workspace guard: map sandbox container-workdir file-tool paths (for example `/workspace/...` and `file:///workspace/...`) to host workspace roots before workspace-only validation, preventing false `Path escapes sandbox root` rejections for sandbox file tools. (#9560).
101
+ - Gateway/Exec approvals: expire approval requests immediately when no approval-capable gateway clients are connected and no forwarding targets are available, avoiding delayed approvals after restarts/offline approver windows. (#22144).
102
+ - Security/Exec approvals: when approving wrapper commands with allow-always in allowlist mode, persist inner executable paths for known dispatch wrappers (`env`, `nice`, `nohup`, `stdbuf`, `timeout`) and fail closed (no persisted entry) when wrapper unwrapping is not safe, preventing wrapper-path approval bypasses. Thanks @tdjackey for reporting.
103
+ - Node/macOS exec host: default headless macOS node `system.run` to local execution and only route through the companion app when `OPENCLAW_NODE_EXEC_HOST=app` is explicitly set, avoiding companion-app filesystem namespace mismatches during exec. (#23547).
104
+ - Sandbox/Media: map container workspace paths (`/workspace/...` and `file:///workspace/...`) back to the host sandbox root for outbound media validation, preventing false deny errors for sandbox-generated local media. (#23083) Thanks @echo931.
105
+ - Sandbox/Docker: apply custom bind mounts after workspace mounts and prioritize bind-source resolution on overlapping paths, so explicit workspace binds are no longer ignored. (#22669) Thanks @tasaankaeris.
106
+ - Exec approvals/Forwarding: restore Discord text forwarding when component approvals are not configured, and carry request snapshots through resolve events so resolved notices still forward after cache misses/restarts. (#22988) Thanks @bubmiller.
107
+ - Control UI/WebSocket: stop and clear the browser gateway client on UI teardown so remounts cannot leave orphan websocket clients that create duplicate active connections. (#23422) Thanks @floatinggball-design.
108
+ - Control UI/WebSocket: send a stable per-tab `instanceId` in websocket connect frames so reconnect cycles keep a consistent client identity for diagnostics and presence tracking. (#23616) Thanks @zq58855371-ui.
109
+ - Config/Memory: allow `"mistral"` in `agents.defaults.memorySearch.provider` and `agents.defaults.memorySearch.fallback` schema validation. (#14934) Thanks @ThomsenDrake.
110
+ - 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).
111
+ - 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.
112
+ - 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.
113
+ - 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).
114
+ - 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.
115
+ - 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.
116
+ - Providers/OpenRouter: inject `cache_control` on system prompts for OpenRouter Anthropic models to improve prompt-cache reuse. (#17473) Thanks @rrenamed.
117
+ - Installer/Smoke tests: remove legacy `OPENCLAW_USE_GUM` overrides from docker install-smoke runs so tests exercise installer auto TTY detection behavior directly.
118
+ - Providers/OpenRouter: allow pass-through OpenRouter and Opencode model IDs in live model filtering so custom routed model IDs are treated as modern refs. (#14312) Thanks @Joly0.
119
+ - Providers/OpenRouter: default reasoning to enabled when the selected model advertises `reasoning: true` and no session/directive override is set. (#22513) Thanks @zwffff.
120
+ - Providers/OpenRouter: map `/think` levels to `reasoning.effort` in embedded runs while preserving explicit `reasoning.max_tokens` payloads. (#17236) Thanks @robbyczgw-cla.
121
+ - Providers/OpenRouter: preserve stored session provider when model IDs are vendor-prefixed (for example, `anthropic/...`) so follow-up turns do not incorrectly route to direct provider APIs. (#22753) Thanks @dndodson.
122
+ - Providers/OpenRouter: preserve the required `openrouter/` prefix for OpenRouter-native model IDs during model-ref normalization. (#12942) Thanks @omair445.
123
+ - Providers/OpenRouter: pass through provider routing parameters from model params.provider to OpenRouter request payloads for provider selection controls. (#17148) Thanks @carrotRakko.
124
+ - Providers/OpenRouter: preserve model allowlist entries containing OpenRouter preset paths (for example `openrouter/@preset/...`) by treating `/model ...@profile` auth-profile parsing as a suffix-only override. (#14120) Thanks @NotMainstream.
125
+ - Cron/Auth: propagate auth-profile resolution to isolated cron sessions so provider API keys are resolved the same way as main sessions, fixing 401 errors when using providers configured via auth-profiles. (#20689) Thanks @lailoo.
126
+ - Cron/Follow-up: pass resolved `agentDir` through isolated cron and queued follow-up embedded runs so auth/profile lookups stay scoped to the correct agent directory. (#22845) Thanks @seilk.
127
+ - Agents/Media: route tool-result `MEDIA:` extraction through shared parser validation so malformed prose like `MEDIA:-prefixed ...` is no longer treated as a local file path (prevents Telegram ENOENT tool-error overrides). (#18780) Thanks @HOYALIM.
128
+ - Logging: cap single log-file size with `logging.maxFileBytes` (default 500 MB) and suppress additional writes after cap hit to prevent disk exhaustion from repeated error storms.
129
+ - Memory/Remote HTTP: centralize remote memory HTTP calls behind a shared guarded helper (`withRemoteHttpResponse`) so embeddings and batch flows use one request/release path.
130
+ - Memory/Embeddings: apply configured remote-base host pinning (`allowedHostnames`) across OpenAI/Voyage/Gemini embedding requests to keep private/self-hosted endpoints working without cross-host drift. (#18198) Thanks @ianpcook.
131
+ - Memory/Batch: route OpenAI/Voyage/Gemini batch upload/create/status/download requests through the same guarded HTTP path for consistent SSRF policy enforcement.
132
+ - Memory/Index: detect memory source-set changes (for example enabling `sessions` after an existing memory-only index) and trigger a full reindex so existing session transcripts are indexed without requiring `--force`. (#17576) Thanks @TarsAI-Agent.
133
+ - Memory/Embeddings: enforce a per-input 8k safety cap before embedding batching and apply a conservative 2k fallback limit for local providers without declared input limits, preventing oversized session/memory chunks from triggering provider context-size failures during sync/indexing. (#6016) Thanks @batumilove.
134
+ - Memory/QMD: on Windows, resolve bare `qmd`/`mcporter` command names to npm shim executables (`.cmd`) before spawning, so qmd boot updates and mcporter-backed searches no longer fail with `spawn .. ENOENT` on default npm installs. (#23899) Thanks @arcbuilder-ai.
135
+ - Memory/QMD: parse plain-text `qmd collection list --json` output when older qmd builds ignore JSON mode, and retry memory searches once after re-ensuring managed collections when qmd returns `Collection not found ...`. (#23613) Thanks @leozhucn.
136
+ - iOS/Watch: normalize watch quick-action notification payloads, support mirrored indexed actions beyond primary/secondary, and fix iOS test-target signing/compile blockers for watch notify coverage. (#23636) Thanks @mbelinky.
137
+ - Signal/RPC: guard malformed Signal RPC JSON responses with a clear status-scoped error and add regression coverage for invalid JSON responses. (#22995) Thanks @adhitShet.
138
+ - Gateway/Subagents: guard gateway and subagent session-key/message trim paths against undefined inputs to prevent early `Cannot read properties of undefined (reading 'trim')` crashes during subagent spawn and wait flows.
139
+ - Agents/Workspace: guard `resolveUserPath` against undefined/null input to prevent `Cannot read properties of undefined (reading 'trim')` crashes when workspace paths are missing in embedded runner flows.
140
+ - Auth/Profiles: keep active `cooldownUntil`/`disabledUntil` windows immutable across retries so mid-window failures cannot extend recovery indefinitely; only recompute a backoff window after the previous deadline has expired. This resolves cron/inbound retry loops that could trap gateways until manual `usageStats` cleanup. (#23516, #23536) Thanks @arosstale.
141
+ - Channels/Security: fail closed on missing provider group policy config by defaulting runtime group policy to `allowlist` (instead of inheriting `channels.defaults.groupPolicy`) when `channels.<provider>` is absent across message channels, and align runtime + security warnings/docs to the same fallback behavior (Slack, Discord, iMessage, Telegram, WhatsApp, Signal, LINE, Matrix, Mattermost, Google Chat, IRC, Nextcloud Talk, Feishu, and Zalo user flows; plus Discord message/native-command paths). (#23367) Thanks @bmendonca3.
142
+ - Gateway/Onboarding: harden remote gateway onboarding defaults and guidance by defaulting discovered direct URLs to `wss://`, rejecting insecure non-loopback `ws://` targets in onboarding validation, and expanding remote-security remediation messaging across gateway client/call/doctor flows. (#23476) Thanks @bmendonca3.
143
+ - CLI/Sessions: pass the configured sessions directory when resolving transcript paths in `agentCommand`, so custom `session.store` locations resume sessions reliably. Thanks @davidrudduck.
144
+ - Signal/Monitor: treat user-initiated abort shutdowns as clean exits when auto-started `signal-cli` is terminated, while still surfacing unexpected daemon exits as startup/runtime failures. (#23379) Thanks @frankekn.
145
+ - Channels/Dedupe: centralize plugin dedupe primitives in plugin SDK (memory + persistent), move Feishu inbound dedupe to a namespace-scoped persistent store, and reuse shared dedupe cache logic for Zalo webhook replay + Tlon processed-message tracking to reduce duplicate handling during reconnect/replay paths. (#23377) Thanks @SidQin-cyber.
146
+ - Channels/Delivery: remove hardcoded WhatsApp delivery fallbacks; require explicit/session channel context or auto-pick the sole configured channel when unambiguous. (#23357) Thanks @lbo728.
147
+ - ACP/Gateway: wait for gateway hello before opening ACP requests, and fail fast on pre-hello connect failures to avoid startup hangs and early `gateway not connected` request races. (#23390) Thanks @janckerchen.
148
+ - Gateway/Auth: preserve `OPENCLAW_GATEWAY_PASSWORD` env override precedence for remote gateway call credentials after shared resolver refactors, preventing stale configured remote passwords from overriding runtime secret rotation.
149
+ - Gateway/Auth: preserve shared-token `gateway token mismatch` auth errors when `auth.token` fallback device-token checks fail, and reserve `device token mismatch` guidance for explicit `auth.deviceToken` failures.
150
+ - Gateway/Tools: when agent tools pass an allowlisted `gatewayUrl` override, resolve local override tokens from env/config fallback but keep remote overrides strict to `gateway.remote.token`, preventing local token leakage to remote targets.
151
+ - Gateway/Client: keep cached device-auth tokens on `device token mismatch` closes when the client used explicit shared token/password credentials, avoiding accidental pairing-token churn during explicit-auth failures.
152
+ - Node host/Exec: keep strict Windows allowlist behavior for `cmd.exe /c` shell-wrapper runs, and return explicit approval guidance when blocked (`SYSTEM_RUN_DENIED: allowlist miss`).
153
+ - Control UI: show pairing-required guidance (commands + mobile tokenized URL reminder) when the dashboard disconnects with `1008 pairing required`.
154
+ - Security/Audit: add `openclaw security audit` detection for open group policies that expose runtime/filesystem tools without sandbox/workspace guards (`security.exposure.open_groups_with_runtime_or_fs`).
155
+ - Security/Audit: make `gateway.real_ip_fallback_enabled` severity conditional for loopback trusted-proxy setups (warn for loopback-only `trustedProxies`, critical when non-loopback proxies are trusted). (#23428) Thanks @bmendonca3.
156
+ - Security/Exec env: block request-scoped `HOME` and `ZDOTDIR` overrides in host exec env sanitizers (Node + macOS), preventing shell startup-file execution before allowlist-evaluated command bodies. Thanks @tdjackey for reporting.
157
+ - Security/Exec env: block `SHELLOPTS`/`PS4` in host exec env sanitizers and restrict shell-wrapper (`bash|sh|zsh .. -c/-lc`) request env overrides to a small explicit allowlist (`TERM`, `LANG`, `LC_*`, `COLORTERM`, `NO_COLOR`, `FORCE_COLOR`) on both node host and macOS companion paths, preventing xtrace prompt command-substitution allowlist bypasses. Thanks @tdjackey for reporting.
158
+ - WhatsApp/Security: enforce `allowFrom` for direct-message outbound targets in all send modes (including `mode: "explicit"`), preventing sends to non-allowlisted numbers. (#20108) Thanks @zahlmann.
159
+ - Security/Exec approvals: fail closed on shell line continuations (`\\\n`/`\\\r\n`) and treat shell-wrapper execution as approval-required in allowlist mode, preventing `$\\` newline command-substitution bypasses. Thanks @tdjackey for reporting.
160
+ - Security/Gateway: emit a startup security warning when insecure/dangerous config flags are enabled (including `gateway.controlUi.dangerouslyDisableDeviceAuth=true`) and point operators to `openclaw security audit`.
161
+ - Security/Hooks auth: normalize hook auth rate-limit client IP keys so IPv4 and IPv4-mapped IPv6 addresses share one throttle bucket, preventing dual-form auth-attempt budget bypasses. Thanks @aether-ai-agent for reporting.
162
+ - Security/Exec approvals: treat `env` and shell-dispatch wrappers as transparent during allowlist analysis on node-host and macOS companion paths so policy checks match the effective executable/inline shell payload instead of the wrapper binary, blocking wrapper-smuggled allowlist bypasses. Thanks @tdjackey for reporting.
163
+ - Security/Exec approvals: require explicit safe-bin profiles for `tools.exec.safeBins` entries in allowlist mode (remove generic safe-bin profile fallback), and add `tools.exec.safeBinProfiles` for safe custom binaries so unprofiled interpreter-style entries cannot be treated as stdin-safe. Thanks @tdjackey for reporting.
164
+ - Security/Channels: harden Slack external menu token handling by switching to CSPRNG tokens, validating token shape, requiring user identity for external option lookups, and avoiding fabricated timestamp `trigger_id` fallbacks; also switch Tlon Urbit channel IDs to CSPRNG UUIDs, centralize secure ID/token generation via shared infra helpers, and add a guardrail test to block new runtime `Date.now()+Math.random()` token/id patterns.
165
+ - Security/Hooks transforms: enforce symlink-safe containment for webhook transform module paths (including `hooks.transformsDir` and `hooks.mappings[].transform.module`) by resolving existing-path ancestors via realpath before import, while preserving in-root symlink support; add regression coverage for both escape and allow cases. Thanks @aether-ai-agent for reporting.
166
+ - Telegram/WSL2: disable `autoSelectFamily` by default on WSL2 and memoize WSL2 detection in Telegram network decision logic to avoid repeated sync `/proc/version` probes on fetch/send paths. (#21916) Thanks @MizukiMachine.
167
+ - Telegram/Network: default Node 22+ DNS result ordering to `ipv4first` for Telegram fetch paths and add `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER`/`channels.telegram.network.dnsResultOrder` overrides to reduce IPv6-path fetch failures. (#5405) Thanks @Glucksberg.
168
+ - Telegram/Forward bursts: coalesce forwarded text+media updates through a dedicated forward lane debounce window that works with default inbound debounce config, while keeping forwarded control commands immediate. (#19476) thanks @napetrov.
169
+ - Telegram/Streaming: preserve archived draft preview mapping after flush and clean superseded reasoning preview bubbles so multi-message preview finals no longer cross-edit or orphan stale messages under send/rotation races. (#23202) Thanks @obviyus.
170
+ - Telegram/Replies: scope messaging-tool text/media dedupe to same-target sends only, so cross-target tool sends can no longer silently suppress Telegram final replies.
171
+ - Telegram/Replies: normalize `file://` and local-path media variants during messaging dedupe so equivalent media paths do not produce duplicate Telegram replies.
172
+ - Telegram/Replies: extract forwarded-origin context from unified reply targets (`reply_to_message` and `external_reply`) so forward+comment metadata is preserved across partial reply shapes. (#9720) thanks @mcaxtr.
173
+ - Telegram/Polling: persist a safe update-offset watermark bounded by pending updates so crash/restart cannot skip queued lower `update_id` updates after out-of-order completion. (#23284) thanks @frankekn.
174
+ - Telegram/Polling: force-restart stuck runner instances when recoverable unhandled network rejections escape the polling task path, so polling resumes instead of silently stalling. (#19721) Thanks @jg-noncelogic.
175
+ - Slack/Slash commands: preserve the Bolt app receiver when registering external select options handlers so monitor startup does not crash on runtimes that require bound `app.options` calls. (#23209) Thanks @0xgaia.
176
+ - Slack/Telegram slash sessions: await session metadata persistence before dispatch so first-turn native slash runs do not race session-origin metadata updates. (#23065) thanks @hydro13.
177
+ - Slack/Queue routing: preserve string `thread_ts` values through collect-mode queue drain and DM `deliveryContext` updates so threaded follow-ups do not leak to the main channel when Slack thread IDs are strings. (#11934) Thanks @sandieman2 and @vincentkoc.
178
+ - Telegram/Native commands: set `ctx.Provider="telegram"` for native slash-command context so elevated gate checks resolve provider correctly (fixes `provider (ctx.Provider)` failures in `/elevated` flows). (#23748) Thanks @serhii12.
179
+ - Agents/Ollama: preserve unsafe integer tool-call arguments as exact strings during NDJSON parsing, preventing large numeric IDs from being rounded before tool execution. (#23170) Thanks @BestJoester.
180
+ - Cron/Gateway: keep `cron.list` and `cron.status` responsive during startup catch-up by avoiding a long-held cron lock while missed jobs execute. (#23106) Thanks @jayleekr.
181
+ - Gateway/Config reload: compare array-valued config paths structurally during diffing so unchanged `memory.qmd.paths` and `memory.qmd.scope.rules` no longer trigger false restart-required reloads. (#23185) Thanks @rex05ai.
182
+ - Gateway/Config reload: retry short-lived missing config snapshots during reload before skipping, preventing atomic-write unlink windows from triggering restart loops. (#23343) Thanks @lbo728.
183
+ - Cron/Scheduling: validate runtime cron expressions before schedule/stagger evaluation so malformed persisted jobs report a clear `invalid cron schedule: expr is required` error instead of crashing with `undefined.trim` failures and auto-disable churn. (#23223) Thanks @asimons81.
184
+ - Memory/QMD: migrate legacy unscoped collection bindings (for example `memory-root`) to per-agent scoped names (for example `memory-root-main`) during startup when safe, so QMD-backed `memory_search` no longer fails with `Collection not found` after upgrades. (#23228, #20727) Thanks @JLDynamics and @AaronFaby.
185
+ - Memory/QMD: normalize Han-script BM25 search queries before invoking `qmd search` so mixed CJK+Latin prompts no longer return empty results due to tokenizer mismatch. (#23426) Thanks @LunaLee0130.
186
+ - TUI/Input: enable multiline-paste burst coalescing on macOS Terminal.app and iTerm so pasted blocks no longer submit line-by-line as separate messages. (#18809) Thanks @fwends.
187
+ - TUI/RTL: isolate right-to-left script lines (Arabic/Hebrew ranges) with Unicode bidi isolation marks in TUI text sanitization so RTL assistant output no longer renders in reversed visual order in terminal chat panes. (#21936) Thanks @Asm3r96.
188
+ - TUI/Status: request immediate renders after setting `sending`/`waiting` activity states so in-flight runs always show visible progress indicators instead of appearing idle until completion. (#21549) Thanks @13Guinness.
189
+ - TUI/Input: arm Ctrl+C exit timing when clearing non-empty composer text and add a SIGINT fallback path so double Ctrl+C exits remain responsive during active runs instead of requiring an extra press or appearing stuck. (#23407) Thanks @tinybluedev.
190
+ - Agents/Fallbacks: treat JSON payloads with `type: "api_error"` + `"Internal server error"` as transient failover errors so Anthropic 500-style failures trigger model fallback. (#23193) Thanks @jarvis-lane.
191
+ - Agents/Google: sanitize non-base64 `thought_signature`/`thoughtSignature` values from assistant replay transcripts for native Google Gemini requests while preserving valid signatures and tool-call order. (#23457) Thanks @echoVic.
192
+ - Agents/Transcripts: validate assistant tool-call names (syntax/length + registered tool allowlist) before transcript persistence and during replay sanitization so malformed failover tool names no longer poison sessions with repeated provider HTTP 400 errors. (#23324) Thanks @johnsantry.
193
+ - Agents/Mistral: sanitize tool-call IDs in the embedded agent loop and generate strict provider-safe pending tool-call IDs, preventing Mistral strict9 `HTTP 400` failures on tool continuations. (#23698) Thanks @echoVic.
194
+ - Agents/Compaction: strip stale assistant usage snapshots from pre-compaction turns when replaying history after a compaction summary so context-token estimation no longer reuses pre-compaction totals and immediately re-triggers destructive follow-up compactions. (#19127) Thanks @tedwatson.
195
+ - Agents/Replies: emit a default completion acknowledgement (`✅ Done.`) only for direct/private tool-only completions with no final assistant text, while suppressing synthetic acknowledgements for channel/group sessions and runs that already delivered output via messaging tools. (#22834) Thanks @Oldshue.
196
+ - Agents/Subagents: honor `tools.subagents.tools.alsoAllow` and explicit subagent `allow` entries when resolving built-in subagent deny defaults, so explicitly granted tools (for example `sessions_send`) are no longer blocked unless re-denied in `tools.subagents.tools.deny`. (#23359) Thanks @goren-beehero.
197
+ - Agents/Subagents: make announce call timeouts configurable via `agents.defaults.subagents.announceTimeoutMs` and restore a 60s default to prevent false timeout failures on slower announce paths. (#22719) Thanks @Valadon.
198
+ - Agents/Diagnostics: include resolved lifecycle error text in `embedded run agent end` warnings so UI/TUI "Connection error" runs expose actionable provider failure reasons in gateway logs. (#23054) Thanks @Raize.
199
+ - Agents/Auth profiles: resolve `agentCommand` session scope before choosing `agentDir`/workspace so resumed runs no longer read auth from `agents/main/agent` when the resolved session belongs to a different/default agent (for example `agent:exec:*` sessions). (#24016) Thanks @abersonFAC.
200
+ - Agents/Auth profiles: skip auth-profile cooldown writes for timeout failures in embedded runner rotation so model/network timeouts do not poison same-provider fallback model selection while still allowing in-turn account rotation. (#22622) Thanks @vageeshkumar.
201
+ - Plugins/Hooks: run legacy `before_agent_start` once per agent turn and reuse that result across model-resolve and prompt-build compatibility paths, preventing duplicate hook side effects (for example duplicate external API calls). (#23289) Thanks @ksato8710.
202
+ - Models/Config: default missing Anthropic provider/model `api` fields to `anthropic-messages` during config validation so custom relay model entries are preserved instead of being dropped by runtime model registry validation. (#23332) Thanks @bigbigmonkey123.
203
+ - Gateway/Pairing: preserve existing approved token scopes when processing repair pairings that omit `scopes`, preventing empty-scope token regressions on reconnecting clients. (#21906) Thanks @paki81.
204
+ - Memory/QMD: add optional `memory.qmd.mcporter` search routing so QMD `query/search/vsearch` can run through mcporter keep-alive flows (including multi-collection paths) to reduce cold starts, while keeping searches on agent-scoped QMD state for consistent recall. (#19617) Thanks @nicole-luxe and @vignesh07.
205
+ - Infra/Network: classify undici `TypeError: fetch failed` as transient in unhandled-rejection detection even when nested causes are unclassified, preventing avoidable gateway crash loops on flaky networks. (#14345) Thanks @Unayung.
206
+ - Telegram/Retry: classify undici `TypeError: fetch failed` as recoverable in both polling and send retry paths so transient fetch failures no longer fail fast. (#16699) thanks @Glucksberg.
207
+ - Docs/Telegram: correct Node 22+ network defaults (`autoSelectFamily`, `dnsResultOrder`) and clarify Telegram setup does not use positional `openclaw channels login telegram`. (#23609) Thanks @ryanbastic.
208
+ - BlueBubbles/DM history: restore DM backfill context with account-scoped rolling history, bounded backfill retries, and safer history payload limits. (#20302) Thanks @Ryan-Haines.
209
+ - BlueBubbles/Private API cache: treat unknown (`null`) private-API cache status as disabled for send/attachment/reply flows to avoid stale-cache 500s, and log a warning when reply/effect features are requested while capability is unknown. (#23459) Thanks @echoVic.
210
+ - BlueBubbles/Webhooks: accept inbound/reaction webhook payloads when BlueBubbles omits `handle` but provides DM `chatGuid`, and harden payload extraction for array/string-wrapped message bodies so valid webhook events no longer get rejected as unparseable. (#23275) Thanks @toph31.
211
+ - Security/Audit: add `openclaw security audit` finding `gateway.nodes.allow_commands_dangerous` for risky `gateway.nodes.allowCommands` overrides, with severity upgraded to critical on remote gateway exposure.
212
+ - Gateway/Control plane: reduce cross-client write limiter contention by adding `connId` fallback keying when device ID and client IP are both unavailable.
213
+ - Security/Config: block prototype-key traversal during config merge patch and legacy migration merge helpers (`__proto__`, `constructor`, `prototype`) to prevent prototype pollution during config mutation flows. (#22968) Thanks @Clawborn.
214
+ - Security/Shell env: validate login-shell executable paths for shell-env fallback (`/etc/shells` + trusted prefixes), block `SHELL`/`HOME`/`ZDOTDIR` in config env ingestion before fallback execution, and sanitize fallback shell exec env to pin `HOME` to the real user home while dropping `ZDOTDIR` and other dangerous startup vars. Thanks @tdjackey for reporting.
215
+ - Network/SSRF: enable `autoSelectFamily` on pinned undici dispatchers (with attempt timeout) so IPv6-unreachable environments can quickly fall back to IPv4 for guarded fetch paths. (#19950) Thanks @ENAwareness.
216
+ - Security/Config: make parsed chat allowlist checks fail closed when `allowFrom` is empty, restoring expected DM/pairing gating.
217
+ - Security/Exec: in non-default setups that manually add `sort` to `tools.exec.safeBins`, block `sort --compress-program` so allowlist-mode safe-bin checks cannot bypass approval. Thanks @tdjackey for reporting.
218
+ - Security/Exec approvals: when users choose `allow-always` for shell-wrapper commands (for example `/bin/zsh -lc ...`), persist allowlist patterns for the inner executable(s) instead of the wrapper shell binary, preventing accidental broad shell allowlisting in moderate mode. (#23276) Thanks @xrom2863.
219
+ - Security/Exec: fail closed when `tools.exec.host=sandbox` is configured/requested but sandbox runtime is unavailable. (#23398) Thanks @bmendonca3.
220
+ - Security/Exec: restore runtime-aware implicit exec host selection so sandbox-off sessions keep defaulting to the gateway host, while explicit `host=sandbox` still fails closed without a sandbox runtime. (#56800).
221
+ - Security/macOS app beta: enforce path-only `system.run` allowlist matching (drop basename matches like `echo`), migrate legacy basename entries to last resolved paths when available, and harden shell-chain handling to fail closed on unsafe parse/control syntax (including quoted command substitution/backticks). This is an optional allowlist-mode feature; default installs remain deny-by-default. Thanks @tdjackey for reporting.
222
+ - Security/Agents: auto-generate and persist a dedicated `commands.ownerDisplaySecret` when `commands.ownerDisplay=hash`, remove gateway token fallback from owner-ID prompt hashing across CLI and embedded agent runners, and centralize owner-display secret resolution in one shared helper. Thanks @aether-ai-agent for reporting.
223
+ - Security/SSRF: expand IPv4 fetch guard blocking to include RFC special-use/non-global ranges (including benchmarking, TEST-NET, multicast, and reserved/broadcast blocks), centralize range checks into a single CIDR policy table, and reuse one shared host/IP classifier across literal + DNS checks to reduce classifier drift. Thanks @princeeismond-dot for reporting.
224
+ - Security/SSRF: block RFC2544 benchmarking range (`198.18.0.0/15`) across direct and embedded-IP paths, and normalize IPv6 dotted-quad transition literals (for example `::127.0.0.1`, `64:ff9b::8.8.8.8`) in shared IP parsing/classification.
225
+ - Security/Archive: block zip symlink escapes during archive extraction.
226
+ - Security/Media sandbox: keep tmp media allowance for absolute tmp paths only and enforce symlink-escape checks before sandbox-validated reads, preventing tmp symlink exfiltration and relative `../` sandbox escapes when sandboxes live under tmp. (#17892) Thanks @dashed.
227
+ - Browser/Upload: accept canonical in-root upload paths when the configured uploads directory is a symlink alias (for example `/tmp` -> `/private/tmp` on macOS), so browser upload validation no longer rejects valid files during client->server revalidation. (#23300, #23222, #22848) Thanks @bgaither4, @parkerati, and @Nabsku.
228
+ - Security/Discord: add `openclaw security audit` warnings for name/tag-based Discord allowlist entries (DM allowlists, guild/channel `users`, and pairing-store entries), highlighting slug-collision risk while keeping name-based matching supported, and canonicalize resolved Discord allowlist names to IDs at runtime without rewriting config files. Thanks @tdjackey for reporting.
229
+ - Security/Gateway: block node-role connections when device identity metadata is missing.
230
+ - Security/Media: enforce inbound media byte limits during download/read across Discord, Telegram, Zalo, Microsoft Teams, and BlueBubbles to prevent oversized payload memory spikes before rejection. Thanks @tdjackey for reporting.
231
+ - Media/Understanding: preserve `application/pdf` MIME classification during text-like file heuristics so PDF uploads use PDF extraction paths instead of being inlined as raw text. (#23191) Thanks @claudeplay2026-byte.
232
+ - Security/Control UI: block symlink-based out-of-root static file reads by enforcing realpath containment and file-identity checks when serving Control UI assets and SPA fallback `index.html`. Thanks @tdjackey for reporting.
233
+ - Security/Gateway avatars: block symlink traversal during local avatar `data:` URL resolution by enforcing realpath containment and file-identity checks before reads. Thanks @tdjackey for reporting.
234
+ - Security/Control UI: centralize avatar URL/path validation across gateway/config helpers and enforce a 2 MB max size for local agent avatar files before `/avatar` resolution, reducing oversized-avatar memory risk without changing supported avatar formats.
235
+ - 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.
236
+ - 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.
237
+ - 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.
238
+ - 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.
239
+ - 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.
240
+ - 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.
241
+ - 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.
242
+ - CI/Tests: fix TypeScript case-table typing and lint assertion regressions so `pnpm check` passes again after Synology Chat landing. (#23012) Thanks @druide67.
243
+ - Security/Browser relay: harden extension relay auth token handling for `/extension` and `/cdp` pathways.
244
+ - Cron: persist `delivered` state in cron job records so delivery failures remain visible in status and logs. (#19174) Thanks @simonemacario.
245
+ - Config/Doctor: only repair the OAuth credentials directory when affected channels are configured, avoiding fresh-install noise.
246
+ - Config/Channels: whitelist `channels.modelByChannel` in config validation and exclude it from plugin auto-enable channel detection so model overrides no longer trigger `unknown channel id` validation errors or bogus `modelByChannel` plugin enables. (#23412) Thanks @ProspectOre.
247
+ - Config/Bindings: allow optional `bindings[].comment` in strict config validation so annotated binding entries no longer fail load. (#23458) Thanks @echoVic.
248
+ - Usage/Pricing: correct MiniMax M2.5 pricing defaults to fix inflated cost reporting. (#22755) Thanks @miloudbelarebia.
249
+ - Gateway/Daemon: verify gateway health after daemon restart.
250
+ - Agents/UI text: stop rewriting normal assistant billing/payment language outside explicit error contexts. (#17834) Thanks @niceysam.
251
+
252
+ ### Breaking
253
+
254
+ - **BREAKING:** removed Google Antigravity provider support and the bundled `google-antigravity-auth` plugin. Existing `google-antigravity/*` model/profile configs no longer work; migrate to `google-gemini-cli` or other supported providers.
255
+ - **BREAKING:** tool-failure replies now hide raw error details by default. OpenClaw still sends a failure summary, but detailed error suffixes (for example provider/runtime messages and local path fragments) now require `/verbose on` or `/verbose full`.
256
+ - **BREAKING:** CLI local onboarding now sets `session.dmScope` to `per-channel-peer` by default for new/implicit DM scope configuration. If you depend on shared DM continuity across senders, explicitly set `session.dmScope` to `main`. (#23468) Thanks @bmendonca3.
257
+ - **BREAKING:** unify channel preview-streaming config to `channels.<channel>.streaming` with enum values `off | partial | block | progress`, and move Slack native stream toggle to `channels.slack.nativeStreaming`. Legacy keys (`streamMode`, Slack boolean `streaming`) are still read and migrated by `openclaw doctor --fix`, but canonical saved config/docs now use the unified names.
258
+ - **BREAKING:** remove legacy Gateway device-auth signature `v1`. Device-auth clients must now sign `v2` payloads with the per-connection `connect.challenge` nonce and send `device.nonce`; nonce-less connects are rejected.
259
+
CHANGELOG/2026.2.23.md ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.2.23
2
+
3
+ ### Changes
4
+
5
+ - Providers/Kilo Gateway: add first-class `kilocode` provider support (auth, onboarding, implicit provider detection, model defaults, transcript/cache-ttl handling, and docs), with default model `kilocode/anthropic/claude-opus-4.6`. (#20212) Thanks @jrf0110 and @markijbema.
6
+ - Providers/Vercel AI Gateway: accept Claude shorthand model refs (`vercel-ai-gateway/claude-*`) by normalizing to canonical Anthropic-routed model ids. (#23985) Thanks @sallyom, @markbooch, and @vincentkoc.
7
+ - Docs/Prompt caching: add a dedicated prompt-caching reference covering `cacheRetention`, per-agent `params` merge precedence, Bedrock/OpenRouter behavior, and cache-ttl + heartbeat tuning. Thanks @svenssonaxel.
8
+ - Gateway/HTTP security headers: add optional `gateway.http.securityHeaders.strictTransportSecurity` support to emit `Strict-Transport-Security` for direct HTTPS deployments, with runtime wiring, validation, tests, and hardening docs.
9
+ - Sessions/Cron: harden session maintenance with `openclaw sessions cleanup`, per-agent store targeting, disk-budget controls (`session.maintenance.maxDiskBytes` / `highWaterBytes`), and safer transcript/archive cleanup + run-log retention behavior. (#24753) thanks @gumadeiras.
10
+ - Tools/web_search: add `provider: "kimi"` (Moonshot) support with key/config schema wiring and a corrected two-step `$web_search` tool flow that echoes tool results before final synthesis, including citation extraction from search results. (#16616, #18822) Thanks @adshine.
11
+ - Media understanding/Video: add a native Moonshot video provider and include Moonshot in auto video key detection, plus refactor video execution to honor `entry/config/provider` baseUrl+header precedence (matching audio behavior). (#12063) Thanks @xiaoyaner0201.
12
+ - Agents/Config: support per-agent `params` overrides merged on top of model defaults (including `cacheRetention`) so mixed-traffic agents can tune cache behavior independently. (#17470, #17112) Thanks @rrenamed.
13
+ - Agents/Bootstrap: cache bootstrap file snapshots per session key and clear them on session reset/delete, reducing prompt-cache invalidations from in-session `AGENTS.md`/`MEMORY.md` writes. (#22220) Thanks @anisoptera.
14
+
15
+ ### Fixes
16
+
17
+ - Security/Config: redact sensitive-looking dynamic catchall keys in `config.get` snapshots (for example `env.*` and `skills.entries.*.env.*`) and preserve round-trip restore behavior for those redacted sentinels. Thanks @merc1305.
18
+ - Tests/Vitest: tier local parallel worker defaults by host memory, keep gateway serial by default on non-high-memory hosts, and document a low-profile fallback command for memory-constrained land/gate runs to prevent local OOMs. (#24719) Thanks @ngutman.
19
+ - WhatsApp/Group policy: fix `groupAllowFrom` sender filtering when `groupPolicy: "allowlist"` is set without explicit `groups` - previously all group messages were blocked even for allowlisted senders. (#24670) Thanks @lailoo.
20
+ - Agents/Context pruning: extend `cache-ttl` eligibility to Moonshot/Kimi and ZAI/GLM providers (including OpenRouter model refs), so `contextPruning.mode: "cache-ttl"` is no longer silently skipped for those sessions. (#24497) Thanks @lailoo.
21
+ - Doctor/Memory: query gateway-side default-agent memory embedding readiness during `openclaw doctor` (instead of inferring from generic gateway health), and warn when the gateway memory probe is unavailable or not ready while keeping `openclaw configure` remediation guidance. (#22327) thanks @therk.
22
+ - Sessions/Store: canonicalize inbound mixed-case session keys for metadata and route updates, and migrate legacy case-variant entries to a single lowercase key to prevent duplicate sessions and missing TUI/WebUI history. (#9561) Thanks @hillghost86.
23
+ - Telegram/Reactions: soft-fail reaction action errors (policy/token/emoji/API), accept snake_case `message_id`, and fallback to inbound message-id context when explicit `messageId` is omitted so DM reactions stay stable without regeneration loops. (#20236, #21001) Thanks @PeterShanxin and @vincentkoc.
24
+ - Telegram/Polling: scope persisted polling offsets to bot identity and reuse a single awaited runner-stop path on abort/retry, preventing cross-token offset bleed and overlapping pollers during restart/error recovery. (#10850, #11347) Thanks @talhaorak, @anooprdawar, and @vincentkoc.
25
+ - Telegram/Reasoning: when `/reasoning off` is active, suppress reasoning-only delivery segments and block raw fallback resend of suppressed `Reasoning:`/`<think>` text, preventing internal reasoning leakage in legacy sessions while preserving answer delivery. (#24626, #24518).
26
+ - 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.
27
+ - Agents/Reasoning: avoid classifying provider reasoning-required errors as context overflows so these failures no longer trigger compaction-style overflow recovery. (#24593) Thanks @vincentkoc.
28
+ - 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.
29
+ - Agents/Compaction: pass `agentDir` into manual `/compact` command runs so compaction auth/profile resolution stays scoped to the active agent. (#24133) thanks @miloudbelarebia.
30
+ - 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.
31
+ - 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.
32
+ - 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.
33
+ - 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.
34
+ - Agents/Overflow: add Chinese context-overflow pattern detection in `isContextOverflowError` so localized provider errors route through overflow recovery paths. (#22855) Thanks @Clawborn.
35
+ - 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.
36
+ - 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.
37
+ - Auto-reply/Inbound metadata: move dynamic inbound `flags` (reply/forward/thread/history) from system metadata to user-context conversation info, preventing turn-by-turn prompt-cache invalidation from flag toggles. (#21785) Thanks @aidiffuser.
38
+ - Auto-reply/Sessions: remove auth-key labels from `/new` and `/reset` confirmation messages so session reset notices never expose API key prefixes or env-key labels in chat output. (#24384, #24409) Thanks @Clawborn.
39
+ - Slack/Group policy: move Slack account `groupPolicy` defaulting to provider-level schema defaults so multi-account configs inherit top-level `channels.slack.groupPolicy` instead of silently overriding inheritance with per-account `allowlist`. (#17579) Thanks @ZetiMente.
40
+ - Providers/Anthropic: skip `context-1m-*` beta injection for OAuth/subscription tokens (`sk-ant-oat-*`) while preserving OAuth-required betas, avoiding Anthropic 401 auth failures when `params.context1m` is enabled. (#10647, #20354) Thanks @ClumsyWizardHands and @dcruver.
41
+ - Providers/DashScope: mark DashScope-compatible `openai-completions` endpoints as `supportsDeveloperRole=false` so OpenClaw sends `system` instead of unsupported `developer` role on Qwen/DashScope APIs. (#19130) Thanks @Putzhuawa and @vincentkoc.
42
+ - Providers/Bedrock: disable prompt-cache retention for non-Anthropic Bedrock models so Nova/Mistral requests do not send unsupported cache metadata. (#20866) Thanks @pierreeurope.
43
+ - Providers/Bedrock: apply Anthropic-Claude cacheRetention defaults and runtime pass-through for `amazon-bedrock/*anthropic.claude*` model refs, while keeping non-Anthropic Bedrock models excluded. (#22303) Thanks @snese.
44
+ - Providers/OpenRouter: remove conflicting top-level `reasoning_effort` when injecting nested `reasoning.effort`, preventing OpenRouter 400 payload-validation failures for reasoning models. (#24120) thanks @tenequm.
45
+ - 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.
46
+ - 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.
47
+ - Providers/Groq: avoid classifying Groq TPM limit errors as context overflow so throttling paths no longer trigger overflow recovery logic. (#16176) Thanks @dddabtc.
48
+ - 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.
49
+ - 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.
50
+ - 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).
51
+ - Security/Exec: detect obfuscated commands before exec allowlist decisions and require explicit approval for obfuscation patterns. (#8592) Thanks @CornBrother0x and @vincentkoc.
52
+ - Security/ACP: harden ACP client permission auto-approval to require trusted core tool IDs, ignore untrusted `toolCall.kind` hints, and scope `read` auto-approval to the active working directory so unknown tool names and out-of-scope file reads always prompt. Thanks @nedlir for reporting.
53
+ - Security/Skills: escape user-controlled prompt, filename, and output-path values in `openai-image-gen` HTML gallery generation to prevent stored XSS in generated `index.html` output. (#12538) Thanks @CornBrother0x.
54
+ - Security/Skills: harden `skill-creator` packaging by skipping symlink entries and rejecting files whose resolved paths escape the selected skill root. (#24260, #16959) Thanks @CornBrother0x and @vincentkoc.
55
+ - Security/OTEL: redact sensitive values (API keys, tokens, credential fields) from diagnostics-otel log bodies, log attributes, and error/reason span fields before OTLP export. (#12542) Thanks @brandonwise.
56
+ - Security/CI: add pre-commit security hook coverage for private-key detection and production dependency auditing, and enforce those checks in CI alongside baseline secret scanning. Thanks @vincentkoc.
57
+ - Skills/Python: harden skill script packaging and validation edge cases (self-including `.skill` outputs, CRLF frontmatter parsing, strict `--days` validation, and safer image file loading), with expanded Python regression coverage. Thanks @vincentkoc.
58
+ - Skills/Python: add CI + pre-commit linting (`ruff`) and pytest discovery coverage for Python scripts/tests under `skills/`, including package test execution from repo root. Thanks @vincentkoc.
59
+
60
+ ### Breaking
61
+
62
+ - **BREAKING:** browser SSRF policy now defaults to trusted-network mode (`browser.ssrfPolicy.dangerouslyAllowPrivateNetwork=true` when unset), and canonical config uses `browser.ssrfPolicy.dangerouslyAllowPrivateNetwork` instead of `browser.ssrfPolicy.allowPrivateNetwork`. `openclaw doctor --fix` migrates the legacy key automatically.
63
+
CHANGELOG/2026.2.27.md ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.2.27
2
+
3
+ ### Changes
4
+
5
+ - 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.
6
+ - 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.
7
+
8
+ ### Fixes
9
+
10
+ - 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.
11
+ - 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)
12
+ - 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.
13
+ - 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.
14
+ - 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.
15
+ - 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.
16
+ - 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.
17
+ - 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.
18
+ - 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.
19
+ - 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.
20
+ - 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.
21
+ - 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.
22
+ - 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.
23
+ - 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.
24
+ - 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.
25
+ - 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.
26
+ - 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.
27
+ - 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.
28
+ - 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.
29
+ - 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.
30
+ - 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.
31
+ - 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.
32
+ - 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.
33
+ - 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.
34
+ - 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.
35
+ - 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.
36
+ - 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.
37
+ - 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.
38
+ - Web UI/Assistant text: strip internal `<relevant-memories>...</relevant-memories>` 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.
39
+ - 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.
40
+ - 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.
41
+ - 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.
42
+ - 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.
43
+ - Gateway/Control UI origins: support wildcard `"*"` in `gateway.controlUi.allowedOrigins` for trusted remote access setups. Landed from contributor PR #31088. Thanks @frankekn.
44
+ - Gateway/Cron auditability: add gateway info logs for successful cron create, update, and remove operations. (#25090) Thanks @MoerAI.
45
+ - 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 (#57018). Thanks @hydro13.
46
+ - 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.
47
+ - 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.
48
+ - Channels/Multi-account default routing: add optional `channels.<channel>.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).
49
+ - 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).
50
+ - Telegram/DM topic session isolation: scope DM topic thread session keys by chat ID (`<chatId>:<threadId>`) and parse scoped thread IDs in outbound recovery so parallel DMs cannot collide on shared topic IDs. Landed from contributor PR #31064. Thanks @0xble.
51
+ - 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.
52
+ - 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.
53
+ - 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.
54
+ - 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.
55
+ - 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.
56
+ - 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.
57
+ - 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.
58
+ - 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.
59
+ - 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.
60
+ - 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.
61
+ - 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.
62
+ - 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.
63
+ - 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.
64
+ - 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.
65
+ - 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.
66
+ - 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.
67
+ - 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.
68
+ - 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.
69
+ - 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.
70
+ - 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.
71
+ - 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.
72
+ - 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.
73
+ - 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.
74
+ - Slack/Thread session isolation: route channel/group top-level messages into thread-scoped sessions (`:thread:<ts>`) and read inbound `previousTimestamp` from the resolved thread session key, preventing cross-thread context bleed and stale timestamp lookups. (#10686) Thanks @pablohrcarvalho.
75
+ - 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.
76
+ - 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.
77
+ - 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.
78
+ - 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.
79
+ - 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.
80
+ - 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.
81
+ - 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.
82
+ - 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.
83
+ - 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.
84
+ - 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.
85
+ - 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.
86
+ - 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.
87
+ - Discord/EventQueue timeout config: expose per-account `channels.discord.accounts.<id>.eventQueue.listenerTimeout` (and related queue options) so long-running handlers can avoid Carbon listener timeout drops. Landed from contributor PR #24270. Thanks @pdd-cli.
88
+ - 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.
89
+ - 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.
90
+ - 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.
91
+ - 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.
92
+ - 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.
93
+ - 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.
94
+ - 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.
95
+ - 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.
96
+ - Cron/One-shot reliability: retry transient one-shot failures with bounded backoff and configurable retry policy before disabling. (#24435) Thanks @hugenshen.
97
+ - Cron/Schedule errors: notify users when a job is auto-disabled after repeated schedule computation failures. (#29098) Thanks @ningding97.
98
+ - 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.
99
+ - 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.
100
+ - 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.
101
+ - 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.
102
+ - 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.
103
+ - 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. Thanks @vignesh07.
104
+ - 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) Thanks @liuxiaopai-ai.
105
+ - 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.
106
+ - 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).
107
+ - 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.
108
+ - 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.
109
+ - 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.
110
+ - Models/provider config precedence: prefer exact `models.providers.<name>` 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.
111
+ - 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.
112
+ - 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.
113
+ - 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.
114
+ - 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).
115
+ - 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.
116
+ - Sessions/Followup queue: always schedule followup drain even when unexpected runtime exceptions escape `runReplyAgent`, preventing silent stuck followup backlogs after failed turns. (#30627).
117
+ - 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).
118
+ - 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).
119
+ - 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).
120
+ - 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.
121
+ - 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.
122
+ - 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.
123
+ - Agents/Ollama discovery: skip Ollama discovery when explicit models are configured. (#28827) Thanks @Kansodata and @vincentkoc.
124
+ - 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.
125
+ - 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.
126
+ - 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.
127
+ - 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.
128
+ - 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.
129
+ - 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.
130
+ - 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.
131
+ - 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
132
+ - 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.
133
+ - 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.
134
+ - 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.
135
+ - 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.
136
+ - 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.
137
+ - 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.
138
+ - 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.
139
+ - 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.
140
+ - Podman/Quadlet setup: fix `sed` escaping and UID mismatch in Podman Quadlet setup. (#26414) Thanks @KnHack and @vincentkoc.
141
+ - 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.
142
+ - 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.
143
+ - 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.
144
+ - 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.
145
+ - 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.
146
+ - 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.
147
+ - 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.
148
+ - 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.
149
+ - 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.
150
+ - 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.
151
+ - 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.
152
+ - 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.
153
+ - 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.
154
+ - 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.
155
+ - 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.
156
+ - 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.
157
+ - Browser/Navigate: resolve the correct `targetId` in navigate responses after renderer swaps. (#25326) Thanks @stone-jin and @vincentkoc.
158
+ - 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.
159
+ - 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).
160
+ - Agents/error classification: check billing errors before context overflow heuristics in the agent runner catch block so spend-limit and quota errors show the billing-specific message instead of being misclassified as "Context overflow: prompt too large". (#40409) Thanks @ademczuk.
161
+ - Memory/MMR CJK tokenization: add Han, kana, and hangul tokens plus adjacent bigrams so memory-search reranking can detect overlap for CJK text instead of treating unrelated snippets as identical. (#29396) Thanks @buyitsydney.
162
+
CHANGELOG/2026.4.1-beta.1.md ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.4.1-beta.1
2
+
3
+ - Providers/transport policy: centralize request auth, proxy, TLS, and header shaping across shared HTTP, stream, and websocket paths, block insecure TLS/runtime transport overrides, and keep proxy-hop TLS separate from target mTLS settings. (#59682) Thanks @vincentkoc.
4
+ - Providers/OpenRouter: gate documented OpenRouter attribution to native OpenRouter endpoints or the default route so custom proxy base URLs do not inherit OpenRouter request headers (#60200). Thanks @vincentkoc.
5
+ - Providers/Copilot: classify native GitHub Copilot API hosts in the shared provider endpoint resolver and harden token-derived proxy endpoint parsing so Copilot base URL routing stays centralized and fails closed on malformed hints. (#59644) Thanks @vincentkoc.
6
+ - Providers/streaming headers: centralize default and attribution header merging across OpenAI websocket, embedded-runner, and proxy stream paths so provider-specific headers stay consistent and caller overrides only win where intended. (#59542) Thanks @vincentkoc.
7
+ - Providers/media HTTP: centralize base URL normalization, default auth/header injection, and explicit header override handling across shared OpenAI-compatible audio, Deepgram audio, Gemini media/image, and Moonshot video request paths. (#59469) Thanks @vincentkoc.
8
+ - Providers/OpenAI-compatible routing: centralize native-vs-proxy request policy so hidden attribution and related OpenAI-family defaults only apply on verified native endpoints across stream, websocket, and shared audio HTTP paths. (#59433) Thanks @vincentkoc.
9
+ - Providers/Anthropic routing: centralize native-vs-proxy endpoint classification for direct Anthropic `service_tier` handling so spoofed or proxied hosts do not inherit native Anthropic defaults. (#59608) Thanks @vincentkoc.
10
+ - Gateway/exec loopback: restore legacy-role fallback for empty paired-device token maps and allow silent local role upgrades so local exec and node clients stop failing with pairing-required errors after `2026.3.31`. (#59092) Thanks @openperf.
11
+ - Agents/subagents: pin admin-only subagent gateway calls to `operator.admin` while keeping `agent` at least privilege, so `sessions_spawn` no longer dies on loopback scope-upgrade pairing with `close(1008) "pairing required"`. (#59555) Thanks @openperf.
12
+ - Exec approvals/config: strip invalid `security`, `ask`, and `askFallback` values from `~/.openclaw/exec-approvals.json` during normalization so malformed policy enums fall back cleanly to the documented defaults instead of corrupting runtime policy resolution. (#59112) Thanks @openperf.
13
+ - Exec approvals/doctor: report host policy sources from the real approvals file path and ignore malformed host override values when attributing effective policy conflicts. (#59367) Thanks @gumadeiras.
14
+ - Exec/runtime: treat `tools.exec.host=auto` as routing-only, keep implicit no-config exec on sandbox when available or gateway otherwise, and reject per-call host overrides that would bypass the configured sandbox or host target. (#58897) Thanks @vincentkoc.
15
+ - Slack/mrkdwn formatting: add built-in Slack mrkdwn guidance in inbound context so Slack replies stop falling back to generic Markdown patterns that render poorly in Slack. (#59100) Thanks @jadewon.
16
+ - WhatsApp/presence: send `unavailable` presence on connect in self-chat mode so personal-phone users stop losing all push notifications while the gateway is running. (#59410) Thanks @mcaxtr.
17
+ - WhatsApp/media: add HTML, XML, and CSS to the MIME map and fall back gracefully for unknown media types instead of dropping the attachment. (#51562) Thanks @bobbyt74.
18
+ - Matrix/onboarding: restore guided setup in `openclaw channels add` and `openclaw configure --section channels`, while keeping custom plugin wizards on the shared `setupWizard` seam. (#59462) Thanks @gumadeiras.
19
+ - Matrix/streaming: keep live partial previews for the current assistant block while preserving completed block updates as separate messages when `channels.matrix.blockStreaming` is enabled. (#59384) Thanks @gumadeiras.
20
+ - Feishu/comment threads: harden document comment-thread delivery so whole-document comments fall back to `add_comment`, delayed reply lookups retry more reliably, and user-visible replies avoid reasoning/planning spillover. (#59129) Thanks @wittam-01.
21
+ - MS Teams/streaming: strip already-streamed text from fallback block delivery when replies exceed the 4000-character streaming limit so long responses stop duplicating content. (#59297) Thanks @bradgroux.
22
+ - Slack/thread context: filter thread starter and history by the effective conversation allowlist without dropping valid open-room, DM, or group DM context. (#58380) Thanks @jacobtomlinson.
23
+ - Mattermost/probes: route status probes through the SSRF guard and honor `allowPrivateNetwork` so connectivity checks stay safe for self-hosted Mattermost deployments. (#58529) Thanks @mappel-nv.
24
+ - Zalo/webhook replay: scope replay dedupe key by chat and sender so reused message IDs across different chats or senders no longer collide, and harden metadata reads for partially missing payloads. (#58444).
25
+ - QQBot/structured payloads: restrict local file paths to QQ Bot-owned media storage, block traversal outside that root, reduce path leakage in logs, and keep inline image data URLs working. (#58453) Thanks @jacobtomlinson.
26
+ - Image generation/providers: route OpenAI, MiniMax, and fal image requests through the shared provider HTTP transport path so custom base URLs, guarded private-network routing, and provider request defaults stay aligned with the rest of provider HTTP. Thanks @vincentkoc.
27
+ - Image generation/providers: stop inferring private-network access from configured OpenAI, MiniMax, and fal image base URLs, and cap shared HTTP error-body reads so hostile or misconfigured endpoints fail closed without relaxing SSRF policy or buffering unbounded error payloads. Thanks @vincentkoc.
28
+ - Browser/host inspection: keep static Chrome inspection helpers out of the activated browser runtime so `openclaw doctor browser` and related checks do not eagerly load the bundled browser plugin. (#59471) Thanks @vincentkoc.
29
+ - Browser/CDP: normalize trailing-dot localhost absolute-form hosts before loopback checks so remote CDP websocket URLs like `ws://localhost.:...` rewrite back to the configured remote host. (#59236) Thanks @mappel-nv.
30
+ - Browser/attach-only profiles: disconnect cached Playwright CDP sessions when stopping attach-only or remote CDP profiles, while still reporting never-started local managed profiles as not stopped. (#60097) Thanks @pedh.
31
+ - Agents/output sanitization: strip namespaced `antml:thinking` blocks from user-visible text so Anthropic-style internal monologue tags do not leak into replies. (#59550) Thanks @obviyus.
32
+ - Kimi Coding/tools: normalize Anthropic tool payloads into the OpenAI-compatible function shape Kimi Coding expects so tool calls stop losing required arguments. (#59440) Thanks @obviyus.
33
+ - Image tool/paths: resolve relative local media paths against the agent `workspaceDir` instead of `process.cwd()` so inputs like `inbox/receipt.png` pass the local-path allowlist reliably. (#57222) Thanks Priyansh Gupta. Thanks @jacobtomlinson.
34
+ - Podman/launch: remove noisy container output from `scripts/run-openclaw-podman.sh` and align the Podman install guidance with the quieter startup flow. (#59368) Thanks @sallyom.
35
+ - Plugins/runtime: keep LINE reply directives and browser-backed cleanup/reset flows working even when those plugins are disabled while tightening bundled plugin activation guards. (#59412) Thanks @vincentkoc.
36
+ - ACP/gateway reconnects: keep ACP prompts alive across transient websocket drops while still failing boundedly when reconnect recovery does not complete. (#59473) Thanks @obviyus.
37
+ - ACP/gateway reconnects: reject stale pre-ack ACP prompts after reconnect grace expiry so callers fail cleanly instead of hanging indefinitely when the gateway never confirms the run.
38
+ - Gateway/session kill: enforce HTTP operator scopes on session kill requests and gate authorization before session lookup so unauthenticated callers cannot probe session existence. (#59128) Thanks @jacobtomlinson.
39
+ - MS Teams/logging: format non-`Error` failures with the shared unknown-error helper so logs stop collapsing caught SDK or Axios objects into `[object Object]`. (#59321) Thanks @bradgroux.
40
+ - Channels/setup: ignore untrusted workspace channel plugins during setup resolution so a shadowing workspace plugin cannot override built-in channel setup/login flows unless explicitly trusted in config. (#59158) Thanks @mappel-nv.
41
+ - Exec/Windows: restore allowlist enforcement with quote-aware `argPattern` matching across gateway and node exec, and surface accurate dynamic pre-approved executable hints in the exec tool description. (#56285) Thanks @kpngr.
42
+ - Gateway: prune empty `node-pending-work` state entries after explicit acknowledgments and natural expiry so the per-node state map no longer grows indefinitely. (#58179) Thanks @gavyngong.
43
+ - Webhooks/secret comparison: replace ad-hoc timing-safe secret comparisons across BlueBubbles, Feishu, Mattermost, Telegram, Twilio, and Zalo webhook handlers with the shared `safeEqualSecret` helper and reject empty auth tokens in BlueBubbles. (#58432) Thanks @eleqtrizit.
44
+ - OpenShell/mirror: constrain `remoteWorkspaceDir` and `remoteAgentWorkspaceDir` to the managed `/sandbox` and `/agent` roots, and keep mirror sync from overwriting or removing user-added shell roots during config synchronization. (#58515) Thanks @eleqtrizit.
45
+ - Plugins/activation: preserve explicit, auto-enabled, and default activation provenance plus reason metadata across CLI, gateway bootstrap, and status surfaces so plugin enablement state stays accurate after auto-enable resolution. (#59641) Thanks @vincentkoc.
46
+ - Exec/env: block additional host environment override pivots for package roots, language runtimes, compiler include paths, and credential/config locations so request-scoped exec cannot redirect trusted toolchains or config lookups. (#59233) Thanks @drobison00.
47
+ - Dotenv/workspace overrides: block workspace `.env` files from overriding `OPENCLAW_PINNED_PYTHON` and `OPENCLAW_PINNED_WRITE_PYTHON` so trusted helper interpreters cannot be redirected by repo-local env injection. (#58473) Thanks @eleqtrizit.
48
+ - Plugins/install: accept JSON5 syntax in `openclaw.plugin.json` and bundle `plugin.json` manifests during install/validation, so third-party plugins with trailing commas, comments, or unquoted keys no longer fail to install. (#59084) Thanks @singleGanghood.
49
+ - Telegram/exec approvals: rewrite shared `/approve … allow-always` callback payloads to `/approve … always` before Telegram button rendering so plugin approval IDs still fit Telegram's `callback_data` limit and keep the Allow Always action visible. (#59217) Thanks @jameslcowan.
50
+ - Cron/exec timeouts: surface timed-out `exec` and `bash` failures in isolated cron runs even when `verbose: off`, including custom session-target cron jobs, so scheduled runs stop failing silently. (#58247) Thanks @skainguyen1412.
51
+ - Telegram/exec approvals: fall back to the origin session key for async approval followups and keep resume-failure status delivery sanitized so Telegram followups still land without leaking raw exec metadata. (#59351) Thanks @seonang.
52
+ - Node-host/exec approvals: bind `pnpm dlx` invocations through the approval planner's mutable-script path so the effective runtime command is resolved for approval instead of being left unbound. (#58374) Thanks @jacobtomlinson.
53
+ - Exec/node hosts: stop forwarding the gateway workspace cwd to remote node exec when no workdir was explicitly requested, so cross-platform node approvals fall back to the node default cwd instead of failing with `SYSTEM_RUN_DENIED`. (#58977) Thanks @Starhappysh.
54
+ - TUI/chat: keep pending local sends visible and reconciled across history reloads, make busy/error recovery clearer through fallback and terminal-error paths, and reclaim transcript width for long links and paths. (#59800) Thanks @vincentkoc.
55
+ - Exec approvals/channels: decouple initiating-surface approval availability from native delivery enablement so Telegram, Slack, and Discord still expose approvals when approvers exist and native target routing is configured separately. (#59776) Thanks @joelnishanth.
56
+ - Agents/logging: keep orphaned-user transcript repair warnings focused on interactive runs, and downgrade background-trigger repairs (`heartbeat`, `cron`, `memory`, `overflow`) to debug logs to reduce false-alarm gateway noise. Thanks @vignesh07.
57
+ - Gateway/node pairing: require `operator.pairing` for node approvals end-to-end, while still requiring `operator.write` or `operator.admin` when the pending node commands need those higher scopes. (#60461) Thanks @eleqtrizit.
58
+ - Providers/OpenRouter: gate Anthropic prompt-cache `cache_control` markers to native/default OpenRouter routes and preserve them for native OpenRouter hosts behind custom provider ids. Thanks @vincentkoc.
59
+ - Browser/CDP: validate both initial and discovered CDP websocket endpoints before connect so strict SSRF policy blocks cross-host pivots and direct websocket targets. (#60469) Thanks @eleqtrizit.
60
+ - Browser/profiles: reject remote browser profile `cdpUrl` values that violate strict SSRF policy before saving config, with clearer validation errors for blocked endpoints. (#60477) Thanks @eleqtrizit.
61
+ - Browser/screenshots: stop sending `fromSurface: false` on CDP screenshots so managed Chrome 146+ browsers can capture images again. (#60682) Thanks @mvanhorn.
62
+ - Mattermost/slash commands: harden native slash-command callback token validation to use constant-time secret comparison, matching the existing interaction-token path.
63
+ - Control UI/mobile chat: reduce narrow-screen overflow by shrinking the chat pane minimum width, removing extra mobile padding, widening message groups, and hiding avatars on very small screens. (#60220) Thanks @macdao.
64
+ - Android/Talk Mode: route spoken replies through `talk.speak`, keep compressed playback cleanup deterministic, and fall back to local TTS for legacy gateways that omit Talk error reasons. (#60954) Thanks @obviyus.
65
+ - Android/Talk Mode: keep reply-speaker routing and teardown behavior aligned with the new remote playback path. (#60954) Thanks @MKV21.
66
+
CHANGELOG/2026.4.1.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.4.1
2
+
3
+ ### Changes
4
+
5
+ - macOS/Voice Wake: add the Voice Wake option to trigger Talk Mode. (#58490) Thanks @SmoothExec.
6
+ - Tasks/chat: add `/tasks` as a chat-native background task board for the current session, with recent task details and agent-local fallback counts when no linked tasks are visible. Related #54226. Thanks @vincentkoc.
7
+ - Web search/SearXNG: add the bundled SearXNG provider plugin for `web_search` with configurable host support. (#57317) Thanks @cgdusek.
8
+ - Telegram/errors: add configurable `errorPolicy` and `errorCooldownMs` controls so Telegram can suppress repeated delivery errors per account, chat, and topic without muting distinct failures. (#51914) Thanks @chinar-amrutkar
9
+ - Gateway/webchat: make `chat.history` text truncation configurable with `gateway.webchat.chatHistoryMaxChars` and per-request `maxChars`, while preserving silent-reply filtering and existing default payload limits. (#58900)
10
+ - Amazon Bedrock/Guardrails: add Bedrock Guardrails support to the bundled provider. (#58588) Thanks @MikeORed.
11
+ - ZAI/models: add `glm-5.1` and `glm-5v-turbo` to the bundled Z.AI provider catalog. (#58793) Thanks @tomsun28
12
+ - Agents/default params: add `agents.defaults.params` for global default provider parameters. (#58548) Thanks @lpender.
13
+ - Agents/failover: cap prompt-side and assistant-side same-provider auth-profile retries for rate-limit failures before cross-provider model fallback, add the `auth.cooldowns.rateLimitedProfileRotations` knob, and document the new fallback behavior. (#58707) Thanks @Forgely3D
14
+ - Agents/compaction: resolve `agents.defaults.compaction.model` consistently for manual `/compact` and other context-engine compaction paths, so engine-owned compaction uses the configured override model across runtime entrypoints. (#56710) Thanks @oliviareid-svg
15
+ - Cron/tools allowlist: add `openclaw cron --tools` for per-job tool allowlists. (#58504) Thanks @andyk-ms.
16
+
17
+ ### Fixes
18
+
19
+ - Chat/error replies: stop leaking raw provider/runtime failures into external chat channels, return a friendly retry message instead, and add a specific `/new` hint for Bedrock toolResult/toolUse session mismatches. (#58831) Thanks @ImLukeF.
20
+ - Sessions/model switching: keep `/model` changes queued behind busy runs instead of interrupting the active turn, and retarget queued followups so later work picks up the new model as soon as the current turn finishes.
21
+ - Web UI/OpenResponses: preserve rewritten stream snapshots in webchat and keep OpenResponses final streamed text aligned when models rewind earlier output. (#58641) Thanks @neeravmakwana
22
+ - Discord/inbound media: pass Discord attachment and sticker downloads through the shared idle-timeout and worker-abort path so slow or stuck inbound media fetches stop hanging message processing. (#58593) Thanks @aquaright1
23
+ - Telegram/retries: keep non-idempotent sends on the strict safe-send path, retry wrapped pre-connect failures, and preserve `429` / `retry_after` backoff for safe delivery retries. (#51895) Thanks @chinar-amrutkar
24
+ - Telegram/exec approvals: route topic-aware exec approval followups through Telegram-owned threading and approval-target parsing, so forum-topic approvals stay in the originating topic instead of falling back to the root chat. (#58783).
25
+ - Telegram/local Bot API: preserve media MIME types for absolute-path downloads so local audio files still trigger transcription and other MIME-based handling. (#54603) Thanks @jzakirov
26
+ - Channels/WhatsApp: pass inbound message timestamp to model context so the AI can see when WhatsApp messages were sent. (#58590) Thanks @Maninae
27
+ - QQBot/voice: lazy-load `silk-wasm` in `audio-convert.ts` so qqbot still starts when the optional voice dependency is missing, while voice encode/decode degrades gracefully instead of crashing at module load time. (#58829) Thanks @WideLee.
28
+ - WhatsApp/groups: fix bot waking up on self-number quoted replies in groups with `selfChatMode` enabled. (#60148) Thanks @lurebat
29
+ - Device pairing: require `operator.pairing` or `operator.admin` for internal `/pair` setup-code, QR, and cleanup commands so lower-privilege gateway callers cannot mint or revoke pairing bootstrap material. (#60491) Thanks @eleqtrizit.
30
+ - Agents/failover: unify structured and raw provider error classification so provider-specific `400`/`422` payloads no longer get forced into generic format failures before retry, billing, or compaction logic can inspect them. (#58856) Thanks @aaron-he-zhu.
31
+ - Auth profiles/store: coerce misplaced SecretRef objects out of plaintext `key` and `token` fields during store load so agents without ACP runtime stop crashing on `.trim()` after upgrade. (#58923) Thanks @openperf.
32
+ - ACPX/runtime: repair `queue owner unavailable` session recovery by replacing dead named sessions and resuming the backend session when ACPX exposes a stable session id, so the first ACP prompt no longer inherits a dead handle. (#58669) Thanks @neeravmakwana
33
+ - ACPX/runtime: retry dead-session queue-owner repair without `--resume-session` when the reported ACPX session id is stale, so recovery still creates a fresh named session instead of failing session init. Thanks @obviyus.
34
+ - Tools/web_search (Kimi): replay native Moonshot `$web_search` arguments verbatim, disable thinking for `kimi-k2.5`, and add Moonshot region/model setup prompts so bundled Kimi web search works again. (#59356) Thanks @Innocent-children.
35
+ - Auth/OpenAI Codex: persist plugin-refreshed OAuth credentials to `auth-profiles.json` before returning them, so rotated Codex refresh tokens survive restart and stop falling into `refresh_token_reused` loops. (#53082) Thanks @hxy91819.
36
+ - Discord/gateway: hand reconnect ownership back to Carbon, keep runtime status aligned with close/reconnect state, and force-stop sockets that open without reaching READY so Discord monitors recover promptly instead of waiting on stale health timeouts. (#59019) Thanks @obviyus
37
+
CHANGELOG/2026.4.12.md ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.4.12
2
+
3
+ ### Changes
4
+
5
+ - QA/lab: add Convex-backed pooled Telegram credential leasing plus `openclaw qa credentials` admin commands and broker setup docs. (#65596) Thanks @joshavant.
6
+ - Memory/Active Memory: add a new optional Active Memory plugin that gives OpenClaw a dedicated memory sub-agent right before the main reply, so ongoing chats can automatically pull in relevant preferences, context, and past details without making users remember to manually say "remember this" or "search memory" first. Includes configurable message/recent/full context modes, live `/verbose` inspection, advanced prompt/thinking overrides for tuning, and opt-in transcript persistence for debugging. Docs: https://docs.openclaw.ai/concepts/active-memory. (#63286) Thanks @Takhoffman.
7
+ - macOS/Talk: add an experimental local MLX speech provider for Talk Mode, with explicit provider selection, local utterance playback, interruption handling, and system-voice fallback. (#63539) Thanks @ImLukeF.
8
+ - CLI/exec policy: add a local `openclaw exec-policy` command with `show`, `preset`, and `set` subcommands for synchronizing requested `tools.exec.*` config with the local exec approvals file, plus follow-up hardening for node-host rejection, rollback safety, and sync conflict detection. (#64050) Thanks @rugvedS07.
9
+ - Gateway: add a `commands.list` RPC so remote gateway clients can discover runtime-native, text, skill, and plugin commands with surface-aware naming and serialized argument metadata. (#62656) Thanks @samzong.
10
+ - Models/providers: add per-provider `models.providers.*.request.allowPrivateNetwork` for trusted self-hosted OpenAI-compatible endpoints, keep the opt-in scoped to model request surfaces, and refresh cached WebSocket managers when request transport overrides change. (#63671) Thanks @qas.
11
+ - QA/testing: add a `--runner multipass` lane for `openclaw qa suite` so repo-backed QA scenarios can run inside a disposable Linux VM and write back the usual report, summary, and VM logs. (#63426) Thanks @shakkernerd.
12
+ - Docs i18n: chunk raw doc translation, reject truncated tagged outputs, avoid ambiguous body-only wrapper unwrapping, and recover from terminated Pi translation sessions without changing the default `openai/gpt-5.4` path. (#62969, #63808) Thanks @hxy91819.
13
+ - Control UI/dreaming: simplify the Scene and Diary surfaces, preserve unknown phase state for partial status payloads, and stabilize waiting-entry recency ordering so Dreaming status and review lists stay clear and deterministic. (#64035) Thanks @davemorin.
14
+ - Gateway: split startup and runtime seams so gateway lifecycle sequencing, reload state, and shutdown behavior stay easier to maintain without changing observed behavior. (#63975) Thanks @gumadeiras.
15
+ - Matrix/partial streaming: add MSC4357 live markers to draft preview sends and edits so supporting Matrix clients can render a live/typewriter animation and stop it when the final edit lands. (#63513) Thanks @TigerInYourDream.
16
+ - QA/Telegram: add a live `openclaw qa telegram` lane for private-group bot-to-bot checks, harden its artifact handling, and preserve native Telegram command reply threading for QA verification. (#64303) Thanks @obviyus.
17
+ - Models/Codex: add the bundled Codex provider and plugin-owned app-server harness so `codex/gpt-*` models use Codex-managed auth, native threads, model discovery, and compaction while `openai/gpt-*` stays on the normal OpenAI provider path. (#64298).
18
+ - Models/providers: add a bundled LM Studio provider with onboarding, runtime model discovery, stream preload support, and memory-search embeddings for local/self-hosted OpenAI-compatible models. (#53248) Thanks @rugvedS07.
19
+ - Plugins/loading: narrow CLI, provider, and channel activation to manifest-declared needs, preserve explicit scope and trust boundaries, and centralize manifest-owner policy so startup, command discovery, and runtime activation avoid loading unrelated plugin runtime. (#65120, #65259, #65298, #65429, #65459) Thanks @vincentkoc.
20
+ - Memory/active-memory: default QMD recall to search and surface better search-path telemetry so memory-backed recall works more predictably out of the box. (#65068) Thanks @Takhoffman.
21
+ - Docs/providers: expand bundled provider docs with richer capability, env-var, and setup guidance across provider pages.
22
+ - Docs/memory-wiki: add the recommended QMD + bridge-mode hybrid recipe plus zero-artifact troubleshooting guidance for `memory-wiki` bridge setups. (#63165) Thanks @sercada and @vincentkoc.
23
+
24
+ ### Fixes
25
+
26
+ - fix(security): remove busybox/toybox from interpreter-like safe bins [AI-assisted]. (#65713) Thanks @pgondhi987.
27
+ - fix(approval-auth): prevent empty approver list from granting explicit approval authorization [AI]. (#65714) Thanks @pgondhi987.
28
+ - fix(security): broaden shell-wrapper detection and block env-argv assignment injection [AI-assisted]. (#65717) Thanks @pgondhi987.
29
+ - Gateway/startup: defer scheduled services until sidecars finish, gate chat history and model listing during sidecar resume, and let Control UI retry startup-gated history loads so Sandbox wake resumes channels first. (#65365) Thanks @lml2468.
30
+ - Control UI/chat: load the live gateway slash-command catalog into the composer and command palette so dock commands, plugin commands, and direct skill aliases appear in chat, while keeping trusted local commands authoritative and bounding remote command metadata. (#65620) Thanks @BunsDev.
31
+ - CLI/update: respawn tracked plugin refresh from the updated entrypoint after package self-updates so `openclaw update` stops failing on stale hashed `dist/install.runtime-*.js` chunk imports. (#65471).
32
+ - Memory/active-memory: keep recall runs on the resolved channel when wrappers like `mx-claw` are enabled, improve lexical fallback ranking, and keep lexical boosts out of hybrid search so recall finds the right memories more consistently. (#65049, #65395) Thanks @Takhoffman.
33
+ - Dreaming: consume managed heartbeat events exactly once, stage light-sleep confidence from all recorded short-term signals, wake scheduled jobs immediately, raise dreaming-only promotion enough to cross the durable-memory gate, and stop dreaming from re-ingesting its own narrative transcripts.
34
+ - Dreaming/narrative: harden transient narrative cleanup by retrying timed-out deletes, scrubbing stale dreaming session artifacts through the lock-aware session-store path, and isolating transient narrative session keys per workspace. (#65320, #61674).
35
+ - Memory/wiki: preserve Unicode letters, digits, and combining marks in wiki slugs and contradiction clustering, and cap Unicode filename segments to safe byte lengths so non-ASCII titles stop collapsing or overflowing path limits. (#64742) Thanks @zhouhe-xydt.
36
+ - Memory/short-term recall: allow nested daily notes under `memory/**/YYYY-MM-DD.md` to feed short-term recall, while still excluding generated dream reports under `memory/dreaming/**` so dreaming does not promote its own output. (#64682) Thanks @SARAMALI15792.
37
+ - UI/WebChat: hide synthetic transcript-repair tool results from chat history reloads so internal recovery markers do not leak into visible chat after reconnects. (#65247) Thanks @wangwllu.
38
+ - WhatsApp/outbound: fall back to the first `mediaUrls` entry when `mediaUrl` is empty so gateway media sends stop silently dropping attachments that already have a resolved media list. (#64394) Thanks @eric-fr4 and @vincentkoc.
39
+ - Doctor/Discord: stop `openclaw doctor --fix` from rewriting legacy Discord preview-streaming config into the nested modern shape, so downgrades can still recover without hand-editing `channels.discord.streaming`. (#65035) Thanks @vincentkoc.
40
+ - Gateway/auth: blank the shipped example gateway credential in `.env.example` and fail startup when a copied placeholder token or password is still configured, so operators cannot accidentally launch with a publicly known secret. (#64586) Thanks @navarrotech and @vincentkoc.
41
+
42
+ - Memory/active-memory+dreaming: keep active-memory recall runs on the strongest resolved channel, consume managed dreaming heartbeat events exactly once, stop dreaming from re-ingesting its own narrative transcripts, and add explicit repair/dedupe recovery flows in CLI, doctor, and the Dreams UI.
43
+ - Agents/queueing: carry orphaned active-turn user text into the next prompt before repairing transcript ordering, so follow-up messages that arrive mid-run are no longer silently dropped. (#65388) Thanks @adminfedres and @vincentkoc.
44
+ - Gateway/keepalive: stop marking WebSocket tick broadcasts as droppable so slow or backpressured clients do not self-disconnect with `tick timeout` while long-running work is still alive. (#65256) Thanks @100yenadmin and @vincentkoc.
45
+ - Matrix/mentions: keep room mention gating strict while accepting visible `@displayName` Matrix URI labels, so `requireMention` works for non-OpenClaw Matrix clients again. (#64796) Thanks @hclsys.
46
+ - Doctor: warn when on-disk agent directories still exist under `~/.openclaw/agents/<id>/agent` but the matching `agents.list[]` entries are missing from config. (#65113) Thanks @neeravmakwana.
47
+ - Telegram: route approval button callback queries onto a separate sequentializer lane so plugin approval clicks can resolve immediately instead of deadlocking behind the blocked agent turn. (#64979) Thanks @nk3750.
48
+ - Telegram/direct sessions: keep commentary-only assistant fallback payloads out of visible direct delivery, so Codex planning chatter cannot leak into Telegram DMs when a run has no `final_answer` text.
49
+ - Gateway/keepalive: stop marking WebSocket tick broadcasts as droppable so slow or backpressured clients do not self-disconnect with `tick timeout` while long-running work is still alive. (#65436).
50
+ - Gateway/plugins: always send a non-empty `idempotencyKey` for plugin subagent runs, so dreaming narrative jobs stop failing gateway schema validation. (#65354) Thanks @CodeForgeNet.
51
+ - Gateway/auth: blank the shipped example gateway credential in `.env.example` and fail startup when a copied placeholder token or password is still configured, so operators cannot accidentally launch with a publicly known secret. (#64586) Thanks @navarrotech.
52
+ - Plugins/memory-core dreaming: keep bundled `memory-core` loaded alongside an explicit external memory slot owner only when that owner enables dreaming, while preserving `plugins.slots.memory = "none"` disable semantics. (#65411) Thanks @pradeep7127.
53
+ - Doctor/Discord: stop `openclaw doctor --fix` from rewriting legacy Discord preview-streaming config into the nested modern shape, so downgrades can still recover without hand-editing `channels.discord.streaming`.
54
+ - Doctor: warn when on-disk agent directories still exist under `~/.openclaw/agents/<id>/agent` but the matching `agents.list[]` entries are missing from config. (#65113) Thanks @neeravmakwana.
55
+ - CLI/plugins: honor `memory-wiki` when `plugins.allow` is set for `openclaw wiki`, and pass the active app config into the metadata registrar so plugin-owned wiki commands resolve the live plugin config instead of falling back to defaults. (#64779, #65012).
56
+ - QA/packaging: stop packaged QA helpers from crashing when optional scenario execution config is unavailable, so npm distributions can skip the repo-only scenario pack without breaking completion-cache and startup paths. (#65118) Thanks @EdderTalmor.
57
+ - Media/audio transcription: surface the real provider failure when every audio transcription attempt fails, so status output and the CLI stop collapsing those errors into generic skips. (#65096) Thanks @l0cka.
58
+ - Infra/net: fix multipart FormData fields (including `model`) being silently dropped when a guarded runtime fetch body crosses a FormData implementation boundary, restoring OpenAI audio transcription requests that failed with HTTP 400. (#64349) Thanks @petr-sloup.
59
+ - Dreaming/diary: use the host local timezone for diary timestamps when `dreaming.timezone` is unset, and include the timezone abbreviation so `DREAMS.md` and the UI make local or UTC time explicit. (#65034, #65057).
60
+ - Dreaming/promotion: raise phase reinforcement enough for repeated dreaming-only revisits to clear the default durable-memory gate after multiple days, instead of stalling just below the score threshold. (#64068) Thanks @vincentkoc.
61
+ - Dreaming/light-sleep: compute staged candidate confidence from all recorded short-term signals instead of recall-only counts, so dreaming-only entries stop rendering as `confidence: 0.00`. (#64599) Thanks @vincentkoc.
62
+ - Plugins/memory: restore cached memory capability public artifacts on plugin-registry cache hits so memory-backed artifact surfaces stay visible after warm loads.
63
+ - Gateway/cron: preserve requested isolated-agent config across runtime reloads so subagent jobs and heartbeat overrides keep the right workspace and heartbeat settings when the hot-loaded snapshot is stale.
64
+ - Cron/isolated sessions: persist the right transcript path for each isolated run, including fresh session rollovers, so cron runs stop appending to stale session files.
65
+ - Discord/gateway: clear stale heartbeat timers before reconnecting so zombie gateway callbacks cannot crash the process and drop in-flight replies. (#65009) Thanks @SARAMALI15792.
66
+ - Matrix/mentions: keep room mention gating strict while accepting visible `@displayName` Matrix URI labels, so `requireMention` works for non-OpenClaw Matrix clients again. (#64796) Thanks @hclsys.
67
+ - Agents/Anthropic replay: preserve immutable signed-thinking replay safety across stored and live reruns, keep non-thinking embedded `tool_result` user blocks intact, and drop conflicting preserved tool IDs before validation so retries stop degrading into omitted tool calls. (#65126) Thanks @shakkernerd.
68
+ - Memory/QMD: allow channel sessions in the shipped default QMD scope, while still denying groups.
69
+ - Memory/QMD: stop registering the legacy lowercase root memory file as a separate default collection, so QMD now prefers `MEMORY.md` and the `memory/` tree without duplicate collection-add warnings.
70
+ - Memory/memory-core: watch the `memory` directory directly and ignore non-markdown churn so nested note changes still sync on macOS + Node 25 environments where recursive `memory/**/*.md` glob watching fails. (#64711) Thanks @jasonxargs-boop and @vincentkoc.
71
+ - WhatsApp: centralize per-account connection ownership so reconnects, login recovery, and outbound readiness stay attached to the live socket instead of drifting across monitor and login paths. (#65290) Thanks @mcaxtr and @vincentkoc.
72
+ - iMessage: retry transient `watch.subscribe` startup failures before tearing down the monitor, and sanitize startup error logging so brief local transport stalls do not immediately bounce the channel or leak raw imsg RPC payloads into logs. (#65393) Thanks @vincentkoc.
73
+ - CLI/audio providers: report env-authenticated providers as configured in `openclaw infer audio providers --json`, while keeping trusted workspace provider env lookup defaults stable during auth setup. (#65491) Thanks @scoootscooob.
74
+ - Plugins/install: reinstall bundled runtime packages when the matching platform native optional child is missing, so packaged Windows installs can recover dependencies that were packed on another host OS.
75
+ - Memory/QMD: preserve explicit `memory.qmd.command` paths, create missing agent workspaces before QMD probes, and keep the current Node binary on QMD subprocess PATH so service and gateway environments do not fall back to builtin search unnecessarily.
76
+ - Plugins/Lobster: load the published `@clawdbot/lobster/core` runtime in process so bundled Lobster runs stop depending on private package internals. (#64755) Thanks @mbelinky.
77
+ - Agents/CLI: keep unrelated config, session, transcript, and MCP bootstrap runtime off common `openclaw agent` cold paths so provider selection and agent startup stop stalling on heavyweight imports. Thanks @vincentkoc.
78
+ - Setup/config/install: stop setup, config dry-runs, and daemon install from eagerly booting auth-profile and plugin repair runtime when those paths are not needed, so onboarding and local service setup avoid long cold-start stalls. Thanks @vincentkoc.
79
+ - Cron/direct delivery: slim isolated-agent delivery cold paths so direct channel delivery and related cron execution spend less time loading unrelated auth, plugin, and channel runtime. Thanks @vincentkoc.
80
+ - Channels/replay dedupe: standardize replay claims, retryable-failure release, and post-success commit behavior across Telegram, Discord, Slack, Mattermost, WhatsApp, Matrix, LINE, Feishu, Zalo, Nextcloud Talk, TLON, Nostr, Voice Call, and shared plugin interactive callbacks so duplicate deliveries stay reply-once after success but retry cleanly after pre-delivery failures. Thanks @vincentkoc.
81
+ - Agents/OpenAI mini reasoning: remap unsupported `low` and `minimal` reasoning effort to `medium` for affected OpenAI mini models, and add a live regression lane to keep the compatibility fix covered. (#65478) Thanks @vincentkoc.
82
+ - Configure/wizard: replay wizard edits onto the latest config snapshot after a hash conflict so plugin-auth writes no longer get dropped during `openclaw configure`, including nested config under shared sections such as `plugins`. (#64188) Thanks @feiskyer and @vincentkoc.
83
+
CHANGELOG/2026.4.26.md ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.4.26
2
+
3
+ ### Changes
4
+
5
+ - Control UI/Talk: add a generic browser realtime transport contract, Google Live browser Talk sessions with constrained ephemeral tokens, and a Gateway relay for backend-only realtime voice plugins. Thanks @VACInc.
6
+ - CLI/models: route provider-filtered model listing through an explicit source plan so user config, installed manifest rows, Provider Index previews, and scoped runtime fallbacks keep a stable authority order without adding another catalog cache. Thanks @shakkernerd.
7
+ - Plugins/cron: add a typed `cron_changed` hook for observing gateway-owned cron lifecycle updates without depending on internal cron events. Thanks @amknight.
8
+ - Providers: add Cerebras as a bundled plugin with onboarding, static model catalog, docs, and manifest-owned endpoint metadata.
9
+ - Memory/OpenAI-compatible: add optional `memorySearch.inputType`, `queryInputType`, and `documentInputType` config for asymmetric embedding endpoints, including direct query embeddings and provider batch indexing. Carries forward #63313 and #60727. Thanks @HOYALIM and @prospect1314521.
10
+ - Ollama/memory: add model-specific retrieval query prefixes for `nomic-embed-text`, `qwen3-embedding`, and `mxbai-embed-large` memory-search queries while leaving document batches unchanged. Carries forward #45013. Thanks @laolin5564.
11
+ - Plugins/providers: move pre-runtime model-id normalization, provider endpoint host metadata, and OpenAI-compatible request-family hints into plugin manifests so core no longer carries bundled-provider routing tables.
12
+ - Plugins/config: deprecate direct plugin config load/write helpers in favor of passed runtime snapshots plus transactional mutation helpers with explicit restart follow-up policy, scanner guardrails, runtime warnings, and revision-based cache invalidation.
13
+ - Plugins/install: allow `OPENCLAW_PLUGIN_STAGE_DIR` to contain layered runtime-dependency roots, resolving read-only preinstalled deps before installing missing deps into the final writable root. Fixes #72396. Thanks @liorb-mountapps.
14
+ - Control UI: add a raw config pending-changes diff panel that parses JSON5, redacts sensitive values until reveal, and avoids fake raw-edit callbacks when opening the panel. Refs #39831; supersedes #48621 and #46654. Thanks @JiajunBernoulli and @BunsDev.
15
+ - Control UI: polish the quick settings dashboard grid so common cards align across desktop, tablet, and mobile layouts without wasting horizontal space. Thanks @BunsDev.
16
+ - Matrix/E2EE: add `openclaw matrix encryption setup` to enable Matrix encryption, bootstrap recovery, and print verification status from one setup flow. Thanks @gumadeiras.
17
+ - Agents/compaction: add an opt-in `agents.defaults.compaction.maxActiveTranscriptBytes` preflight trigger that runs normal local compaction when the active JSONL grows too large, requiring transcript rotation so successful compaction moves future turns onto a smaller successor file instead of raw byte-splitting history. Thanks @vincentkoc.
18
+ - CLI/migration: add a bundled Claude importer that previews and applies Claude Code and Claude Desktop instructions, MCP servers, skills, command prompts, and safe archive/manual-review state. Thanks @vincentkoc.
19
+ - CLI/migration: add `openclaw migrate` with plan, dry-run, JSON, pre-migration backup, onboarding detection, archive-only report copies, and a bundled Hermes importer for configuration, memory/plugin hints, model providers, MCP servers, skills, and supported credentials. Thanks @NousResearch.
20
+
21
+ ### Fixes
22
+
23
+ - Gateway/device tokens: stop echoing rotated bearer tokens from shared/admin `device.token.rotate` responses while preserving the same-device token handoff needed by token-only clients before reconnect. (#66773) Thanks @MoerAI.
24
+ - Agents/sessions_spawn: resolve configured bare model aliases for spawn model overrides using the target agent runtime default provider, carrying forward the alias-specific #69029 review fixes from #59681 without the unrelated active-session pruning path. Fixes #59681. Thanks @HowdyDooToYou.
25
+ - Control UI/Talk: keep Google Live browser sessions on the WebSocket transport instead of falling back to WebRTC, validate browser Google Live WebSocket endpoints, cap Gateway relay sessions per browser connection, and remove stale browser-native voice buttons that did not use the configured Talk/TTS provider. Thanks @BunsDev.
26
+ - Gateway/startup: reuse config snapshot plugin manifests for startup auto-enable before plugin bootstrap plans plugin loading. Thanks @shakkernerd.
27
+ - Agents/subagents: enforce `subagents.allowAgents` for explicit same-agent `sessions_spawn(agentId=...)` calls instead of auto-allowing requester self-targets. Fixes #72827. Thanks @oiGaDio.
28
+ - ACP/sessions_spawn: let explicit `sessions_spawn(runtime="acp")` bootstrap turns run while `acp.dispatch.enabled=false` still blocks automatic ACP thread dispatch. Fixes #63591. Thanks @moeedahmed.
29
+ - CLI/update: install npm global updates into a verified temporary prefix before swapping the package tree into place, preventing mixed old/new installs and stale packaged files from breaking `openclaw update` verification. Thanks @shakkernerd.
30
+ - Gateway: skip CLI startup self-respawn for foreground gateway runs so low-memory Linux/Node 24 hosts start through the same path as direct `dist/index.js` without hanging before logs. Fixes #72720. Thanks @sign-2025.
31
+ - Google Meet: grant Meet media permissions through browser control and pin local Chrome audio defaults to `BlackHole 2ch`, so joined agents no longer show `Permission needed` or use macOS default audio devices. Thanks @DougButdorf.
32
+ - Gateway: treat uncaught broken-pipe stream errors like `EPIPE` as non-fatal so Discord delivery or closed pipes no longer crash the Gateway after a reply is ready.
33
+ - Google Meet: route local Chrome joins through OpenClaw browser control instead of raw default Chrome, so agents use the configured OpenClaw browser profile when opening Meet. Thanks @oromeis.
34
+ - Plugins/discovery: follow symlinked plugin directories in global and workspace plugin roots while keeping broken links ignored and existing package safety checks in place. Fixes #36754; carries forward #72695 and #63206. Thanks @Quackstro, @ming1523, and @xsfX20.
35
+ - Plugins/install: skip test files and directories during install security scans while still force-scanning declared runtime entrypoints, so packaged test mocks no longer block plugin installs. Fixes #66840; carries forward #67050. Thanks @saurabhjain1592 and @Magicray1217.
36
+ - Plugins/install: allow exact package-manager peer links back to the trusted OpenClaw host package during install security scans while continuing to block spoofed or nested escaping `node_modules` symlinks. Carries forward #70819. Thanks @fgabelmannjr.
37
+ - Plugins/install: resolve plugin install destinations from the active profile state dir across CLI, ClawHub, marketplace, local path, and channel setup installs, so `openclaw --profile <name> plugins install ...` no longer writes into the default profile. Fixes #69960; carries forward #69971. Thanks @FrancisLyman and @Sanjays2402.
38
+ - Plugins/registry: suppress duplicate-plugin startup warnings when a tracked npm-installed plugin intentionally overrides the bundled plugin with the same id. Carries forward #48673. Thanks @abdushsk.
39
+ - Plugins/startup: reuse canonical realpath lookups throughout each plugin discovery pass, including package and manifest boundary checks, so Windows npm-global startups no longer repeat expensive path resolution for the same plugin roots. Fixes #65733. Thanks @welfo-beo.
40
+ - Gateway/proxy: pass `ALL_PROXY` / `all_proxy` into the global Undici env-proxy dispatcher and provider proxy-fetch helper while keeping SSRF trusted-proxy auto-upgrade on `HTTP_PROXY` / `HTTPS_PROXY` only, so gateway/provider calls honor all-proxy setups without weakening guarded fetches. Fixes #43821; carries forward #43919. Thanks @RickyTong1.
41
+ - Providers/LiteLLM: honor `--custom-base-url` during non-interactive API-key onboarding without adding proxy discovery side effects, so scripted remote LiteLLM setup keeps the requested endpoint instead of falling back to localhost. Carries forward #66160. Thanks @dongs0104.
42
+ - Reply/link understanding: keep media and link preprocessing on stable runtime entrypoints and continue with raw message content if optional enrichment fails, so URL-bearing messages are no longer dropped after stale runtime chunk upgrades. Fixes #68466. Thanks @songshikang0111.
43
+ - Discord: persist routed model-picker overrides when the hidden `/model` dispatch succeeds but the bound thread session store is still stale, including LM Studio suffixed model ids. Carries forward #61473. Thanks @Nanako0129.
44
+ - Nodes/CLI: add `openclaw nodes remove --node <id|name|ip>` and `node.pair.remove` so stale gateway-owned node pairing records can be cleaned without hand-editing state files.
45
+ - Gateway: include the connecting client and fresh presence version in the initial `hello-ok` snapshot, so clients no longer need a follow-up event before seeing themselves online.
46
+ - Docker: install the CA certificate bundle in the slim runtime image so HTTPS calls from containerized gateways no longer fail TLS setup after the `bookworm-slim` base switch. Fixes #72787. Thanks @ryuhaneul.
47
+ - Providers/OpenRouter: remove retired Hunter Alpha and Healer Alpha static catalog rows and disable proxy reasoning injection for stale Hunter Alpha configs, so replies are not hidden when OpenRouter returns answer text in reasoning fields. Fixes #43942. Thanks @EvanDataForge.
48
+ - Providers/reasoning: let Groq and LM Studio declare provider-native reasoning effort values, so Qwen thinking models receive `none`/`default` or `off`/`on` instead of OpenAI-only `low`/`medium` values. Fixes #32638. Thanks @Aqu1bp, @mgoulart, @Norpps, and @BSTail.
49
+ - Local models: default custom providers with only `baseUrl` to the Chat Completions adapter and trust loopback model requests automatically, so local OpenAI-compatible proxies receive `/v1/chat/completions` without timing out. Fixes #40024. Thanks @parachuteshe.
50
+ - Channels/message tool: surface Discord, Slack, and Mattermost `user:`/`channel:` target syntax in the shared message target schema and Discord ambiguity errors, so DM sends by numeric id stop burning retries before finding `user:<id>`. Fixes #72401. Thanks @garyd9, @hclsys, and @praveen9354.
51
+ - Agents/tools: scope tool-loop detection history to the active run when available, so scheduled heartbeat cycles no longer inherit stale repeated-call counts from previous runs. Fixes #40144. Thanks @mattbrown319.
52
+ - Agents/subagents: preserve requester delivery for completion announces when a child agent is bound to a different channel account while keeping same-channel thread completions routed to the child thread. Thanks @sfuminya.
53
+ - Agents/subagents: fail closed instead of selecting a single child thread binding when completion delivery lacks requester conversation signal. Thanks @suyua9.
54
+ - Agents/status: persist the post-compaction token estimate from auto-compaction when providers omit usage metadata, so `/status` and session lists keep showing fresh context usage after compaction. Fixes #67667; carries forward #72822. Thanks @Jimmy-xuzimo and @skylight-9.
55
+ - Control UI: show loading, reload, and retry states when a lazy dashboard panel cannot load after an upgrade, so the Logs tab no longer appears blank on stale browser bundles. Fixes #72450. Thanks @sobergou.
56
+ - Gateway/plugins: start the Gateway in degraded mode when a single plugin entry has invalid schema config, and let `openclaw doctor --fix` quarantine that plugin config instead of crash-looping every channel. Fixes #62976 and #70371. Thanks @Doraemon-Claw and @pksidekyk.
57
+ - Agents/plugins: skip malformed plugin tools with missing schema objects and report plugin diagnostics, so one broken tool no longer crashes Anthropic agent runs. Fixes #69423. Thanks @jmnickels.
58
+ - Dashboard: log a CVE-safe self-recovery hint pointing users to `OPENCLAW_GATEWAY_TOKEN`, `gateway.auth.token`, and fragment key `token` when neither clipboard nor browser delivery places the token-bearing URL within reach, so headless and WSL invocations are not stranded on the bare URL. Fixes #72081. Thanks @praveen9354 and @BunsDev.
59
+ - Agents/reasoning: recover fully wrapped unclosed `<think>` replies that would otherwise sanitize to empty text while keeping strict stripping for closed reasoning blocks and unclosed tails after visible text. Fixes #37696; supersedes #51915. Thanks @druide67 and @okuyam2y.
60
+ - Control UI/Gateway: bind WebChat handshakes to their active socket and reject post-close server registrations, so aborted connects no longer leave zombie clients or misleading duplicate WebSocket connection logs. Fixes #72753. Thanks @LumenFromTheFuture.
61
+ - Agents/fallback: split ambiguous provider failures into `empty_response`, `no_error_details`, and `unclassified`, and add flat fallback-step fields to structured fallback logs so primary-model failures stay visible when later fallbacks also fail. Fixes #71922; refs #71744. Thanks @andyk-ms and @nikolaykazakovvs-ux.
62
+ - Gateway/startup: reuse the plugin manifest registry inside config validation so restrictive plugin allowlists avoid a duplicate manifest pass during startup. Thanks @shakkernerd.
63
+ - Gateway/startup: run plugin auto-enable from authored source config and skip disabled setup probes, avoiding runtime-default plugin allowlist writes and a second config snapshot read during startup. Thanks @shakkernerd.
64
+ - Plugins/Windows: normalize Windows absolute paths before handing bundled plugin modules to Jiti, so Feishu/Lark message sending no longer fails with unsupported `c:` ESM loader URLs. Fixes #72783. Thanks @jackychen-png.
65
+ - CLI/doctor: run bundled plugin runtime-dependency repairs through the async npm installer with spinner/line progress and heartbeat updates, so long `openclaw doctor --fix` installs no longer look hung in TTY or piped output. Fixes #72775. Thanks @dfpalhano.
66
+ - Feishu/Windows: normalize bundled channel sidecar loads before Jiti evaluates them, so Feishu outbound sends no longer fail with raw `C:` ESM loader errors on Windows. Fixes #72783. Thanks @jackychen-png.
67
+ - Agents/tools: ignore volatile `exec` runtime metadata when comparing tool-loop outcomes, so enabled loop detection can stop repeated identical shell-command results instead of resetting on duration, PID, session, or cwd changes. Fixes #34574; supersedes #41502. Thanks @gucasbrg and @Zcg2021.
68
+ - Agents/fallback: classify internal live-session model switch conflicts as unknown fallback failures instead of provider overloads, preventing local vLLM endpoints from receiving misleading overloaded cooldowns. Refs #63229. Thanks @clawdia-lobster.
69
+ - Discord: let thread sessions inherit the parent channel's session-level `/model` override as a model-only fallback without enabling parent transcript inheritance. Fixes #72755. Thanks @solavrc.
70
+ - Gateway/plugins: skip stale configured channels whose matching plugin is no longer discoverable, point cleanup at `openclaw doctor --fix`, and keep unrelated channel typos fatal so one missing channel plugin no longer crash-loops the Gateway. Fixes #53311. Thanks @futhgar.
71
+ - Control UI: keep session-specific assistant identity loads authoritative after WebSocket connect, so non-main agent chat sessions do not show the main agent name in the header after bootstrap refreshes. Fixes #72776. Thanks @rockytian-top.
72
+ - Agents/Qwen: preserve exact custom `modelstudio` provider configs with foreign `api` owners so explicit OpenAI-compatible Model Studio endpoints no longer get normalized into the bundled Qwen plugin path. Fixes #64483. Thanks @FiredMosquito831.
73
+ - MCP/bundle-mcp: normalize CLI-native `type: "http"` MCP server entries to OpenClaw `transport: "streamable-http"` on save, repair existing configs with doctor, and keep embedded Pi from falling back to legacy SSE GET-first startup for those servers. Fixes #72757. Thanks @Studioscale.
74
+ - OpenCode: expose Anthropic Opus/Sonnet 4.x thinking levels for proxied Claude models, so `/think xhigh`, `/think adaptive`, and `/think max` validate consistently with the direct Anthropic provider. Fixes #72729. Thanks @haishmg and @aaajiao.
75
+ - Media-understanding/audio: migrate deprecated `{input}` placeholders in legacy `audio.transcription.command` configs to `{{MediaPath}}`, so custom audio transcribers no longer receive the literal placeholder after doctor repair. Fixes #72760. Thanks @krisfanue3-hash.
76
+ - Ollama/WSL2: warn when GPU-backed WSL2 installs combine CUDA visibility with an autostarting `ollama.service` using `Restart=always`, and document the systemd, `.wslconfig`, and keep-alive mitigation for crash loops. Carries forward #61022; fixes #61185. Thanks @yhyatt.
77
+ - Ollama/onboarding: de-dupe suggested bare local models against installed `:latest` tags and skip redundant pulls, so setup shows the installed model once and no longer says it is downloading an already available model. Fixes #68952. Thanks @tleyden.
78
+ - Memory-core/doctor: keep `doctor.memory.status` on the cached path by default and only run live embedding pings for explicit deep probes, preventing slow local embedding backends from blocking Gateway status checks. Fixes #71568. Thanks @apex-system.
79
+ - Memory/QMD: group same-source collections into one QMD search invocation when the installed QMD supports multiple `-c` filters, while keeping older QMD builds on the per-collection fallback. Fixes #72484; supersedes #72485 and #69583. Thanks @BsnizND and @zeroaltitude.
80
+ - Memory/QMD: accept QMD status vector-count variants such as `Vectors = 42`, `Vectors:42`, and `Vectors: 42 embedded`, so `memory status --deep` no longer reports embeddings unavailable for healthy QMD wrappers. Fixes #63652; carries forward #63678. Thanks @apoapostolov and @WarrenJones.
81
+ - Memory/QMD: skip QMD vector status probes and embedding maintenance in lexical `searchMode: "search"`, so BM25-only QMD setups on ARM do not trigger llama.cpp/Vulkan builds during status checks or embed cycles. Fixes #59234 and #67113. Thanks @PrinceOfEgypt, @Vksh07, @Snipe76, @NomLom, @t4r3e2q1-commits, and @dmak.
82
+ - Memory/QMD: report the live watcher dirty state in memory status, so changed QMD-backed memory files show as dirty until the queued sync finishes. Fixes #60244. Thanks @xinzf.
83
+ - Compaction: skip oversized pre-compaction checkpoint snapshots and prune duplicate long user turns from compaction input and rotated successor transcripts, preventing retry storms from being preserved across checkpoint cycles. Fixes #72780. Thanks @SweetSophia.
84
+ - Control UI/Cron: render cron job prompts and run summaries as sanitized markdown in the dashboard, with full-width block content, safer link clicks, and no duplicate error text when a failed run has no summary. Supersedes #48504. Thanks @garethdaine.
85
+ - Control UI/Gateway: preserve WebChat client version labels across localhost, 127.0.0.1, and IPv6 loopback aliases on the same port, avoiding misleading `vcontrol-ui` connection logs while investigating duplicate-message reports. Refs #72753 and #72742. Thanks @LumenFromTheFuture and @allesgutefy.
86
+ - Agents/reasoning: treat orphan closing reasoning tags with following answer text as a privacy boundary across delivery, history, streaming, and Control UI sanitizers so malformed local-model output cannot leak chain-of-thought text. Fixes #67092. Thanks @AnildoSilva.
87
+ - Memory-core: run one-shot memory CLI commands through transient builtin and QMD managers so `memory index`, `memory status --index`, and `memory search` no longer start long-lived file watchers that can hit macOS `EMFILE` limits. Fixes #59101; carries forward #49851. Thanks @mbear469210-coder and @maoyuanxue.
88
+ - Agents/ACP: ship the Claude ACP adapter with OpenClaw and require Claude result messages before idle can complete a prompt, preventing parent agents from waking early on long-running `sessions_spawn(runtime: "acp", agentId: "claude")` children. Fixes #72080. Thanks @siavash-saki and @iannwu.
89
+ - CLI/tasks: route `tasks --json`, `tasks list --json`, and `tasks audit --json` through a lean JSON path so read-only task inspection no longer loads unrelated plugin/runtime command graphs. Fixes #66238. Thanks @ChuckChambers.
90
+ - Memory-core: re-resolve the active runtime config whenever `memory_search` or `memory_get` executes, so provider changes made by `config.patch` stop leaving stale embedding backends behind in existing tool instances. Fixes #61098. Thanks @BradGroux and @Linux2010.
91
+ - WebChat: keep bare `/new` and `/reset` startup instructions out of visible chat history while preserving `/reset <note>` as user-visible transcript text. Fixes #72369. Thanks @collynes and @haishmg.
92
+ - Tasks/memory: checkpoint and truncate SQLite WAL sidecars on a timer and before close for task, Task Flow, proxy capture, and builtin memory databases, bounding long-running gateway `*.sqlite-wal` growth. Fixes #72774. Thanks @dfpalhano.
93
+ - CLI/doctor: remove dangling channel config, heartbeat targets, and channel model overrides when stale plugin repair removes a missing channel plugin, preventing Gateway boot loops after failed plugin reinstalls. Fixes #65293. Thanks @yidecode.
94
+ - Control UI/Gateway: cache, coalesce, stale-refresh, and invalidate effective tool inventory on channel registry changes while reusing the gateway-bound plugin registry and avoiding model/auth discovery, so chat runs no longer stall Control UI requests on repeated plugin/model setup. Fixes #72365; supersedes #72558. Thanks @Gabiii2398 and @1yihui.
95
+ - Channels/setup: treat bundled channel plugins as already bundled during `channels add` and onboarding, enabling them without writing redundant `plugins.load.paths` entries or path install records. Fixes #72740. Thanks @iCodePoet.
96
+ - WhatsApp: honor gateway `HTTPS_PROXY` / `HTTP_PROXY` env vars for QR-login WebSocket connections, while respecting `NO_PROXY`, so proxied networks no longer fall back to direct `mmg.whatsapp.net` connections that time out with 408. Fixes #72547; supersedes #72692. Thanks @mebusw and @SymbolStar.
97
+ - Bonjour: default mDNS advertisements to the system hostname when it is DNS-safe, avoiding `openclaw.local` probing conflicts and Gateway restart loops on hosts such as `Lobster` or `ubuntu`. Fixes #72355 and #72689; supersedes #72694. Thanks @mscheuerlein-bot, @gcusms, @moyuwuhen601, @pavan987, @zml-0912, @hhq365, and @SymbolStar.
98
+ - Agents/OpenAI-compatible: retry replay-safe empty `stop` turns once for `openai-completions` endpoints, so transient empty local backend responses no longer surface as “Agent couldn't generate a response” when a continuation succeeds, and restore `openclaw agent --model` for one-shot CLI runs. Fixes #72751. Thanks @moooV252.
99
+ - Git hooks: skip ignored staged paths when formatting and restaging pre-commit files, so merge commits no longer abort when `.gitignore` newly ignores staged merged content. Fixes #72744. Thanks @100yenadmin.
100
+ - Memory-core/dreaming: add a supported `dreaming.model` knob for Dream Diary narrative subagents, wired through phase config and the existing plugin subagent model-override trust gate. Refs #65963. Thanks @esqandil and @mjamiv.
101
+ - Agents/Anthropic: remove trailing assistant prefill payloads when extended thinking is enabled, so Opus 4.7/Sonnet 4.6 requests do not fail Anthropic's user-final-turn validation. Fixes #72739. Thanks @superandylin.
102
+ - Agents/vLLM/Qwen: add plugin-owned Qwen thinking controls for vLLM chat-template kwargs and DashScope-style top-level `enable_thinking` flags, including preserved thinking for agent loops. Fixes #72329. Thanks @stavrostzagadouris.
103
+ - Memory-core/dreaming: treat request-scoped narrative fallback as expected, skip session cleanup when no subagent run was created, and remove duplicate phase-level cleanup so fallback no longer emits warning noise. Fixes #67152. Thanks @jsompis.
104
+ - Agents/exec: apply configured `tools.exec.timeoutSec` to background, `yieldMs`, and node `system.run` commands when no per-call timeout is set, preventing auto-backgrounded and remote node commands from running indefinitely. Fixes #67600; supersedes #67603. Thanks @dlmpx and @kagura-agent.
105
+ - Config/doctor: stop masking unknown-key validation diagnostics such as `agents.defaults.llm`, and have `openclaw doctor --fix` remove the retired `agents.defaults.llm` timeout block. Thanks @aidiffuser.
106
+ - CLI/startup: keep the built pre-dispatch CLI graph free of package-level imports and extend packaged CLI smoke coverage to onboard and doctor help paths, preventing missing runtime dependencies such as tslog from killing onboarding before repair code can run. Fixes #63024. Thanks @hu19940121.
107
+ - CLI/plugins: preserve unversioned ClawHub install specs so `plugins update` can follow newer ClawHub releases instead of pinning to the initially resolved version. Fixes #63010; supersedes #58426. Thanks @kangsen1234 and @robinspt.
108
+ - Memory-core/subagents: tag plugin-created subagent sessions with their plugin owner so dreaming narrative cleanup can delete its own ephemeral sessions without granting broad admin session deletion. Fixes #72712. Thanks @BSG2000.
109
+ - Gateway/models: move local-provider pricing opt-outs, OpenRouter/LiteLLM aliases, and proxy passthrough pricing lookup into plugin manifest metadata so core no longer carries extension-specific pricing tables.
110
+ - CLI/update: honor `OPENCLAW_NO_AUTO_UPDATE=1` as a gateway startup kill-switch for configured background package auto-updates, so operators can hold a deliberate downgrade during incident recovery without editing config first. Fixes #72715. Thanks @Xivi08.
111
+ - Agents/Claude CLI: force live-session launches to include `--output-format stream-json` whenever OpenClaw adds `--input-format stream-json`, so new Claude CLI sessions no longer fail immediately while reusable sessions keep working. Fixes #72206. Thanks @kwangwonkoh and @Xivi08.
112
+ - CLI/plugins: accept ClawHub plugin API wildcard ranges such as `*` without rejecting compatible plugin installs, while still requiring a valid runtime API version. Fixes #56446; supersedes #56466. Thanks @darconada and @claygeo.
113
+ - CLI/plugins: add an explicit `npm:<package>` install prefix that skips ClawHub lookup for known npm packages while keeping bare package specs ClawHub-first. Fixes #55805; supersedes #54377. Thanks @Zeoy2020 and @vagusX.
114
+ - CLI/plugins: let config-gated bundled plugins install without persisting invalid placeholder config entries, so install/uninstall sweeps can cover plugins such as memory-lancedb before the user configures credentials. Thanks @vincentkoc.
115
+ - CLI/plugins: reject malformed ClawHub plugin specs with trailing `@` before registry lookup, so empty-version typos report as invalid specs instead of package-not-found errors. Fixes #56579; supersedes #56582. Thanks @Kansodata.
116
+ - Agents/sessions: acquire the session write lock only after cold bootstrap, plugin, and tool setup so fallback runs are not blocked by stalled pre-model startup work.
117
+ - Browser/plugins: auto-start the bundled browser plugin when root `browser` config is present, including restrictive plugin allowlists, and ignore stale persisted plugin registries whose package paths no longer exist.
118
+ - Browser: circuit-break repeated managed Chrome launch failures per profile so browser requests stop spawning Chromium indefinitely when CDP cannot start. Fixes #64271. Thanks @TheophilusChinomona.
119
+ - Gateway/models: skip external OpenRouter and LiteLLM pricing refreshes for local/self-hosted model endpoints so startup does not wait on remote pricing catalogs for local-only Ollama, vLLM, and compatible providers.
120
+ - CLI/plugins: stop security-blocked plugin installs from retrying as hook packs, so normal plugin packages report the scanner failure without a misleading "not a valid hook pack" follow-up. Fixes #61175; supersedes #64102. Thanks @KonsultDigital and @ziyincody.
121
+ - Agents/Anthropic: strip stale trailing assistant prefill turns from outbound replay so context-engine short circuits cannot send unsupported assistant-prefill payloads to provider APIs. Fixes #72556. Thanks @Veda-openclaw.
122
+ - Agents/Google: strip stale trailing assistant/model prefill turns from Gemini outbound replay so Google Generative AI requests end with a user turn or function response. Follow-up to #72556. Thanks @Veda-openclaw.
123
+ - Control UI/Dreaming: require explicit confirmation before applying restart-impacting Dreaming mode changes, with restart warning copy and loading feedback. Fixes #63804. (#63807) Thanks @bbddbb1.
124
+ - CLI/agent: mark Gateway-to-embedded fallback runs with `meta.transport: "embedded"` and `meta.fallbackFrom: "gateway"` in JSON output, and make the terminal diagnostic explicit so scripts and operators can distinguish fallback runs from Gateway runs. Fixes #71416. Thanks @amknight.
125
+ - Agents/tools: normalize `null` or missing tool-call arguments to `{}` for parameterless object schemas before Pi validation, so empty-argument tools run instead of failing argument validation. Fixes #72587. Thanks @amknight.
126
+ - Agents/subagents: clear active embedded-run state before terminal lifecycle events so post-completion cleanup no longer treats finished child runs as still active and skips archive or announcement bookkeeping. (#70187) Thanks @amknight.
127
+ - CLI/update: keep the automatic post-update completion refresh on the core-command tree so it no longer stages bundled plugin runtime deps before the Gateway restart path, avoiding `.24` update hangs and 1006 disconnect cascades. Fixes #72665. Thanks @sakalaboator and @He-Pin.
128
+ - Control UI: make explicit Reload Config actions discard stale local config edits while passive refreshes and failed-save recovery keep pending drafts intact. Fixes #40352; carries forward #40443. Thanks @realmikechong-dotcom.
129
+ - Agents/Bedrock: stop heartbeat runs from persisting blank user transcript turns and repair existing blank user text messages before replay, preventing AWS Bedrock `ContentBlock` blank-text validation failures. Fixes #72640 and #72622. Thanks @goldzulu.
130
+ - Agents/LM Studio: promote standalone bracketed local-model tool requests into registered tool calls and hide unsupported bracket blocks from visible replies, so MemPalace MCP lookups do not print raw `[tool]` JSON scaffolding in chat. Fixes #66178. Thanks @detroit357.
131
+ - Local models: warn when an assistant reply looks like a tool call but the provider emitted plain text instead of a structured tool invocation, making fake/non-executed tool calls visible in logs. Fixes #51332. Thanks @emilclaw.
132
+ - Local models: accept persisted non-secret local auth markers for private-LAN custom OpenAI-compatible providers, so LAN Ollama configs no longer fail with missing auth when `ollama-local` is saved as the key. Fixes #49736. Thanks @charles-zh.
133
+ - TUI/local models: treat visible gateway client labels such as `openclaw-tui` as the current requester session for session-aware tools, so Ollama tool calls no longer fail by resolving the UI label as a session id. Fixes #66391. Thanks @kickingzebra.
134
+ - Local models: route self-hosted OpenAI-compatible model discovery through the guarded fetch path pinned to the configured host, covering vLLM and SGLang setup without reopening local/LAN SSRF probes. Supersedes #46359. Thanks @cdxiaodong.
135
+ - Local models: classify terminated, reset, closed, timeout, and aborted model-call failures and attach a process memory snapshot to the diagnostic event, making LM Studio/Ollama RAM-pressure failures easier to prove from stability bundles. Refs #65551. Thanks @BigWiLLi111.
136
+ - Local models: pass configured provider request timeouts through OpenAI SDK transports and the model idle watchdog so long-running local or custom OpenAI-compatible streams use one timeout knob instead of hitting the SDK's 10-minute default or the 120s idle default. Fixes #63663. Thanks @aidiffuser.
137
+ - LM Studio: trust configured LM Studio loopback, LAN, and tailnet endpoints for guarded model requests by default, preserving explicit private-network opt-outs. Refs #60994. Thanks @tnowakow.
138
+ - Docker/setup: route Docker onboarding defaults for host-side LM Studio and Ollama through `host.docker.internal` and add the Linux host-gateway mapping to the bundled Compose file, so containerized gateways can reach local providers without using container loopback. Fixes #68684; supersedes #68702. Thanks @safrano9999 and @skolez.
139
+ - Agents/LM Studio: strip prior-turn Gemma 4 reasoning from OpenAI-compatible replay while preserving active tool-call continuation reasoning. Fixes #68704. Thanks @chip-snomo and @Kailigithub.
140
+ - LM Studio: allow interactive onboarding to leave the API key blank for unauthenticated local servers, using local synthetic auth while clearing stale LM Studio auth profiles. Fixes #66937. Thanks @olamedia.
141
+ - Plugins/startup: use a `PluginLookUpTable` during Gateway startup so channel ownership, deferred channel loading, and startup plugin IDs reuse the same installed manifest registry instead of rebuilding manifest metadata on the boot path. Thanks @shakkernerd.
142
+ - Plugins/startup: pass the Gateway `PluginLookUpTable` through plugin loading so auto-enable checks and startup-scope fallback reuse the same manifest registry instead of doing another manifest pass. Thanks @shakkernerd.
143
+ - Plugins/startup: carry the Gateway `PluginLookUpTable` into deferred channel full-runtime reloads so post-listen startup does not rebuild manifest metadata after the provisional setup-runtime load. Thanks @shakkernerd.
144
+ - Gateway/models: reuse Gateway plugin manifest metadata during the initial model-pricing refresh so pricing policies and configured plugin web-search models do not rebuild plugin lookups during startup. Thanks @shakkernerd.
145
+ - Gateway/startup: extend `OPENCLAW_GATEWAY_STARTUP_TRACE=1` with per-phase event-loop delay plus plugin lookup-table timing and count metrics for installed-index, manifest, startup-plan, and owner-map work, and include the new timing fields in startup benchmark summaries. Thanks @shakkernerd.
146
+ - Plugins/channels: resolve read-only channel command defaults from one plugin index plus manifest pass instead of reloading plugin metadata while checking candidate plugin enablement. Thanks @shakkernerd.
147
+ - Plugins/capabilities: cache manifest-derived capability provider plugin IDs per config snapshot so repeated TTS, media, realtime, memory, image, video, and music provider resolution avoids redundant manifest scans. Thanks @shakkernerd.
148
+ - Plugins/contracts: resolve runtime manifest-contract plugin owners from one plugin index plus manifest pass instead of rebuilding manifest metadata separately for all owners and enabled owners. Thanks @shakkernerd.
149
+ - Plugins/extractors: reuse one manifest registry pass while resolving bundled document and web-content extractor plugins instead of rereading manifests for compatibility and enablement filtering. Thanks @shakkernerd.
150
+ - Plugins/providers: reuse one plugin registry snapshot and manifest registry while resolving provider discovery entries instead of rebuilding manifest metadata after provider owner discovery. Thanks @shakkernerd.
151
+ - Plugins/registry: resolve lookup-table owner maps for providers, CLI backends, setup providers, command aliases, model catalogs, channel configs, and manifest contracts while preserving setup-only CLI backend ownership. Thanks @shakkernerd.
152
+ - Plugins/registry: cache repeated installed-index manifest registry fallback rebuilds behind a bounded invalidating cache so cold provider-discovery paths avoid rereading unchanged manifests. Thanks @mcaxtr.
153
+ - Plugins/web: reuse manifest records already loaded for bundled web provider candidate discovery when falling back to public artifact provider loading. Thanks @shakkernerd.
154
+ - Mattermost: keep direct-message replies top-level by suppressing reply roots for DM delivery while preserving channel and group thread roots, and derive inbound chat kind from the trusted channel lookup instead of the websocket event channel type. Carries forward #60115, #55186, #72305, and #72659; refs #59758, #59981, #59791, and #57565. Thanks @vincentkoc, @jwchmodx, and @hnykda.
155
+ - Docker: pre-create `/home/node/.openclaw` with node ownership and private permissions so first-run Docker Compose named volumes no longer fail startup with EACCES. (#48072, #63959; fixes #61279) Thanks @timoxue and @jeanibarz.
156
+ - CLI/Gateway: treat local restart probe policy closes for connect, exact `device required`, pairing, and auth failures as Gateway reachability proof without accepting empty, broad standalone token/password/scope/role, or pair-substring 1008 close reasons. Fixes #48771; carries forward #48801; related #63491. Thanks @MarsDoge and @genoooool.
157
+ - Feishu: send outgoing interactive reply payloads as native cards with clickable buttons while preserving text, media, and document-comment fallbacks. Fixes #13175 and #58298; carries forward #47891. Thanks @Horacehxw.
158
+ - Control UI/WebChat: skip redundant final-event history reloads when the assistant payload already rendered, and keep deferred `session.message` reloads attached to the active run so final reconciliation no longer splits, duplicates, or drops assistant bubbles. Fixes #66875 and #66274; follows #66997 and #67037. Thanks @BiznessFish, @scotthuang, and @hansolo949.
159
+ - CLI/Agents: route new `openclaw agent --to` sessions through the configured default agent while migrating legacy `agent:main:<mainKey>` rows into the default-agent store, preserving the default-agent fix from #64108. Fixes #63992; related #56370, #56453, and #42009. Thanks @mushuiyu886 and @voocel.
160
+ - Process/Windows: decode command stdout and stderr from raw bytes with console-codepage awareness, while preserving valid UTF-8 output and multibyte characters split across chunks. Fixes #50519. Thanks @iready, @kevinten10, @zhangyongjie1997, @knightplat-blip, @heiqishi666, and @slepybear.
161
+ - Bonjour/Windows: hide the bundled mDNS advertiser's Windows ARP shell probe so Gateway startup no longer flashes command-prompt windows. Fixes #70238. Thanks @alexandre-leng, @PratikRai0101, @infinitypacific, and @tomerpeled.
162
+ - Agents/bootstrap: dedupe hook-injected bootstrap context files by workspace-relative path and store normalized resolved paths so duplicate relative and absolute hook paths no longer depend on the process cwd. (#59344; fixes #59319; related #56721, #56725, and #57587) Thanks @koen666.
163
+ - Agents/bootstrap: refresh cached workspace bootstrap snapshots on long-lived main-session turns when `AGENTS.md`, `SOUL.md`, `MEMORY.md`, or `TOOLS.md` change on disk, while preserving unchanged snapshot identity through the workspace file cache. (#64871; related #43901, #26497, #28594, #30896) Thanks @aimqwest and @mikejuyoon.
164
+ - macOS Gateway: detect installed-but-unloaded LaunchAgent split-brain states during status, doctor, and restart, and re-bootstrap launchd supervision before falling back to unmanaged listener restarts. Fixes #67335, #53475, and #71060; refs #58890, #60885, and #70801. Thanks @ze1tgeist88, @dafacto, and @vishutdhar.
165
+ - WhatsApp: clear cached Web auth and active listener state after terminal 440/401 conflict/logout closes so linked/OK status no longer masks a dead inbound listener after relink or restart. Fixes #45474; refs #49305, #63855, #66920, and #70856. Thanks @juvenalmakoszay and @dsantoreis.
166
+ - Gateway/restart: keep local restart-health probes on configured local daemon auth without falling back to remote gateway credentials. (#57374, #59439) Thanks @zssggle-rgb and @roytong9.
167
+ - Plugins/install: treat mirrored core logger dependencies as staged bundled runtime deps so packaged Gateway starts do not crash when the external plugin-runtime-deps root is missing `tslog`. Fixes #72228; supersedes #72493. Thanks @deepujain.
168
+ - Build/plugins: preserve active bundled runtime-dependency staging temp directories owned by live build processes so overlapping postbuild runs no longer delete each other's staged deps mid-prune. Supersedes #72220. Thanks @VACInc.
169
+ - Plugins/install: hide bundled runtime-dependency npm child windows on Windows across Gateway startup, postinstall, and packaged staging paths so Telegram/Anthropic dependency repair no longer flashes shell windows. Fixes #72315. Thanks @athuljayaram and @joshfeng.
170
+ - Agents/Windows: normalize lazy agent runtime imports before Node ESM loading so Windows drive-letter `subagent-registry` runtime paths no longer fail every agent task with `ERR_UNSUPPORTED_ESM_URL_SCHEME`. Fixes #72636; carries forward #72716. Thanks @Andyz-CData and @xialonglee.
171
+ - Plugins/Windows: normalize lazy plugin service override imports before Node ESM loading so drive-letter browser-control module paths no longer fail with `ERR_UNSUPPORTED_ESM_URL_SCHEME`. Fixes #72573; supersedes #72599 and #72582. Thanks @llzzww316, @feineryonah-byte, and @WuKongAI-CMU.
172
+ - Browser/plugins: load `playwright-core` through the browser runtime shim so packaged installs can run Playwright actions from staged plugin runtime deps after doctor/startup repair. Fixes #72168; supersedes #72238. Thanks @zdg1110 and @yetval.
173
+ - Plugins/install: stage bundled plugin runtime dependencies before Gateway startup, drain update restarts, and materialize plugin-owned root chunks in external mirrors so staged deps resolve under native ESM. Fixes #72058; supersedes #72084. Thanks @amnesia106 and @drvoss.
174
+ - TTS/SecretRef: resolve `messages.tts.providers.*.apiKey` from the active runtime snapshot so SecretRef-backed MiniMax and other TTS provider keys work in runtime reply/audio paths. Fixes #68690. Thanks @joshavant.
175
+ - Gateway/install: surface systemd user-bus recovery hints during Linux service activation and retry via the target user scope when `systemctl --user` reports no-medium bus failures, without letting stale `SUDO_USER` override `sudo -u` installs. Fixes #39673; refs #44417 and #63561. Thanks @Arbor4, @myrsu, @mssteuer, and @boyuaner.
176
+ - CLI/nodes: make unfiltered `openclaw nodes list` prefer the effective paired-node view used by `nodes status` while preserving pending rows, pairing-scope fallback, terminal-safe table rendering, and paired JSON metadata. Fixes #46871; carries forward #65772 through the ProjectClownfish #72619 repair. Thanks @skainguyen1412.
177
+ - Memory Wiki/CLI: route active bridge-mode status, doctor, and bridge imports through Gateway RPC so CLI checks use the runtime memory plugin context while disabled bridge imports stay local/offline. Carries forward #67208 and #71479; related #70185. Thanks @moorsecopers99, @vincentkoc, and @prasad-yashdeep.
178
+ - CLI/startup: read generated startup metadata from the bundled `dist` layout before falling back to live help rendering, so root/browser help and channel-option bootstrap stay on the fast path. Thanks @vincentkoc.
179
+ - Feishu/Lark: stop treating broadcast-only `@all`/`@_all` messages as bot mentions while preserving direct bot mentions, including messages that also include `@all`. Fixes #37706. Thanks @JosepLee.
180
+ - CLI/help: treat positional `help` invocations like `openclaw channels help` as help paths for startup gating, avoiding model/auth warmup while preserving positional arguments such as `openclaw docs help`. Thanks @gumadeiras.
181
+ - Web search: route plugin-scoped web_search SecretRefs through the active runtime config snapshot so provider execution receives resolved credentials across app/runtime paths, including `plugins.entries.brave.config.webSearch.apiKey`. Fixes #68690. Thanks @VACInc.
182
+ - Voice Call: allow SecretRef-backed Twilio auth tokens and call-specific OpenAI/ElevenLabs TTS API keys through the plugin config surface. Fixes #68690. Thanks @joshavant.
183
+ - Google Meet: clean stale chrome-node realtime audio bridges by URL before rejoining, expose active node bridge inspection, and tolerate transient node input pull failures instead of dropping the Meet session. Fixes #72371. (#72372) Thanks @BsnizND.
184
+ - Google Meet: use 24 kHz PCM16 for Chrome command-pair realtime audio by default, preserve legacy 8 kHz G.711 mu-law custom command pairs, and let realtime providers negotiate the selected bridge audio format. Fixes #72525. Thanks @BsnizND.
185
+ - Google Meet: clear queued Gemini Live playback when realtime interruptions arrive, restart Chrome command-pair audio output after clears, and expose Google Live interruption/VAD config knobs for Meet and Voice Call realtime bridges. Fixes #72523. (#72524) Thanks @BsnizND.
186
+ - Google Meet: add `realtime.agentId` so live meeting consults can target a named OpenClaw agent instead of always using `main`. (#72381) Thanks @BsnizND.
187
+ - Google Meet: route stateful `google_meet` tool actions through the gateway-owned runtime so created or joined realtime sessions remain visible to status, speak, and leave after the agent turn ends. Fixes #72440. (#72441) Thanks @BsnizND.
188
+ - Google Meet/Voice Call: send Gemini Live a non-blocking consult continuation before long OpenClaw agent consults finish, then deliver the final result when idle so calls and meetings do not sit silent during tool-backed answers. (#72189) Thanks @VACInc.
189
+ - Google Meet: preserve Gemini Live function names when replying to realtime tool calls so Google SDK validation accepts the `FunctionResponse` payload. Fixes #72425. (#72426) Thanks @BsnizND.
190
+ - Discord/media: keep incidental Markdown image badges in final replies as text unless a channel opts into Markdown-image media extraction, while preserving Telegram Markdown-image media replies and explicit `MEDIA:` attachments. Fixes #72642. Thanks @solavrc and @Bartok9.
191
+ - Matrix/E2EE: stabilize recovery and broken-device QA flows while avoiding Matrix device-cleanup sync races that could leave shutdown-time crypto work running. Thanks @gumadeiras.
192
+ - Cron: apply `cron.maxConcurrentRuns` to a dedicated `cron-nested` isolated agent-turn lane as well as cron dispatch, so parallel cron jobs no longer serialize on inner LLM execution while non-cron nested flows keep their existing lane behavior. Fixes #72707. Thanks @kagura-agent.
193
+ - Cron: report isolated runs as successful when verified cron delivery already delivered the reply, while keeping unresolved Message/Canvas tool failures fatal. Fixes #72732 and #50170; follow-up to #54188. Thanks @zNatix, @pixeldyn, and @ChickenEggRoll.
194
+ - Cron: treat isolated run-level agent failures as job errors even when no reply payload is produced, synthesizing a safe error payload so model/provider failures increment error counters and trigger failure notifications instead of clearing as successful. Fixes #43604; carries forward #43631. Thanks @SPFAdvisors.
195
+ - Cron: preserve exact `NO_REPLY` tool results from isolated jobs with empty final assistant turns as quiet successes instead of surfacing incomplete-turn errors. Fixes #68452; carries forward #68453. Thanks @anyech.
196
+ - Cron: resolve failure alerts and failure-destination announcements against `session:<id>` targets before falling back to the creator session, so jobs created from group chats can notify the targeted direct session without cross-account routing errors. Refs #62777; carries forward #68535. Thanks @slideshow-dingo and @likewen-tech.
197
+ - Discord: preserve explicit `user:` and `channel:` delivery targets through plugin routing so cron announcements and failure alerts keep their intended recipient kind. Refs #62777; carries forward #62798. Thanks @neeravmakwana.
198
+ - Cron: add `failureAlert.includeSkipped` and `openclaw cron edit --failure-alert-include-skipped` so persistently skipped jobs can alert without counting skips as execution errors or affecting retry backoff. Fixes #60846. Thanks @slideshow-dingo.
199
+ - Cron: invalidate stale pending runtime slots after live or offline `jobs.json` schedule edits, while preserving due slots for formatting-only rewrites. Fixes #27996 and #71607; carries forward #71651. Thanks @xialonglee and @fagnersouza666.
200
+ - Cron: keep legacy flat `jobs.json` rows loadable while comparing split-state schedule identities, so old cron stores do not crash before in-memory hydration can normalize them.
201
+ - Cron: start isolated agent-turn execution timeouts after the runner enters its effective execution lane, so queued cron/manual runs no longer spend their whole timeout budget before useful work begins. Fixes #41783. Thanks @ayanesakura and @Hurray0.
202
+ - Cron/Telegram: preserve direct-chat thread IDs and optional account IDs when inferring reminder delivery from Telegram direct-thread session keys. Fixes #44270; carries forward #44325, #44351, #44412, and #72657. Thanks @RunMintOn, @arkyu2077, @0xsline, and @vincentkoc.
203
+ - Cron: omit synthetic `delivery.resolved` errors from `--no-deliver` run records while preserving explicit no-deliver target traces for agent-initiated messages. Fixes #72210; carries forward #72219. Thanks @hatemclawbot-collab and @xydigit-sj.
204
+ - Cron: classify isolated runs as errors from structured embedded-run execution-denial metadata, with final-output marker fallback for `SYSTEM_RUN_DENIED`, `INVALID_REQUEST`, and approval-binding refusals, so blocked commands no longer appear green in cron history. Fixes #67172; carries forward #67186. Thanks @oc-gh-dr, @hclsys, and @1yihui.
205
+ - Subagents: keep the delegated task only in the subagent system prompt and send a short initial kickoff message, avoiding duplicate task tokens while preserving multiline task formatting. Fixes #72019; carries forward #72053. Thanks @Wizongod and @ly85206559.
206
+ - Onboarding/GitHub Copilot: add manifest-owned `--github-copilot-token` support for non-interactive setup, including env fallback, tokenRef storage in ref mode, saved-profile reuse, and current Copilot default-model wiring. Refs #50002 and supersedes #50003. Thanks @scottgl9.
207
+ - Gateway/install: add a validated `--wrapper`/`OPENCLAW_WRAPPER` service install path that persists executable LaunchAgent/systemd wrappers across forced reinstalls, updates, and doctor repairs instead of falling back to raw node/bun `ProgramArguments`. Fixes #69400. (#72445) Thanks @willtmc.
208
+ - Plugins: fail plugin registration when loader-owned acceptance gates reject missing hook names or memory-only capability registration from non-memory plugins, surfacing the issue through plugin status and doctor instead of silently dropping the registration. Fixes #72459. Thanks @amknight.
209
+ - macOS Gateway: write launchd services with a state-dir `WorkingDirectory`, use a durable state-dir temp path instead of freezing macOS session `TMPDIR`, create that temp directory before bootstrap, and label abort-shaped launchd exits as `SIGABRT/abort` in status output. Fixes #53679 and #70223; refs #71848. Thanks @dlturock, @stammi922, and @palladius.
210
+ - Control UI/update: make `Update now` require a real gateway process replacement, report skipped/error update outcomes with stable reasons, and verify the running gateway version after restart so global installs cannot silently keep old code in memory. Fixes #62492; addresses #64892 and #63562. Thanks @IAMSamuelRodda.
211
+ - Exec approvals: accept runtime-owned `source: "allow-always"` and `commandText` allowlist metadata in gateway and node approval-set payloads so Control UI round-trips no longer fail with `unexpected property 'source'`. Fixes #60000; carries forward #60064. Thanks @sd1471123, @sharkqwy, and @luoyanglang.
212
+ - Exec/node: skip approval-plan preparation for full-trust `host=node` runs so interpreter and script commands no longer fail with `SYSTEM_RUN_DENIED: approval cannot safely bind` when effective policy is `security=full` and `ask=off`. Fixes #48457 and duplicate #69251. Thanks @ajtran303, @jaserNo1, @Blakeshannon, @lesliefag, and @AvIsBeastMC.
213
+ - Exec/node: synthesize a local approval plan when a paired node advertises `system.run` without `system.run.prepare`, unblocking approval-required `host=node` exec on current macOS companion nodes while preserving remote prepare for node hosts that support it. Fixes #37591 and duplicate #66839; carries forward #69725. Thanks @soloclz.
214
+ - Memory/QMD: prefer QMD's `--mask` collection pattern flag so root memory indexing stays scoped to `MEMORY.md` instead of widening to every markdown file in the workspace. Fixes #65480; supersedes #65481 and #66259. Thanks @ccage-simp, @Bortlesboat, @seank-com, and @crazyscience.
215
+ - Memory/doctor: treat the specific `gateway timeout after ...` gateway memory probe result as inconclusive instead of reporting embeddings not ready, while preserving warnings for explicit failures. Fixes #44426; carries forward #46576 with the Greptile review feedback applied. Thanks Cengiz (@ghost).
216
+ - Gateway/memory: defer QMD startup for implicit non-default agents and scope memory runtime loading to the selected memory slot so Gateway boot and first memory recall avoid broad plugin runtime fanout. Thanks @vincentkoc.
217
+ - Gateway/startup: keep core request handlers, setup wizard, and channel runtime helpers off the boot path until the first matching request, wizard run, or channel start, reducing no-plugin Gateway ready RSS and avoidable startup imports. Thanks @vincentkoc.
218
+ - Gateway/startup: keep CLI outbound channel send dependencies as lazy request-time senders so Gateway boot no longer imports channel plugin registration just to construct default deps. Thanks @vincentkoc.
219
+ - Gateway/startup: split lightweight HTTP auth helpers away from model-override helpers so Gateway bind no longer imports model catalog selection while wiring base HTTP routes. Thanks @vincentkoc.
220
+ - Gateway/startup: lazy-load plugin HTTP route dispatch when active plugin routes exist so no-plugin Gateway boot skips plugin route runtime scope setup. Thanks @vincentkoc.
221
+ - Gateway/startup: move chat run/subscriber registries onto a lightweight state module and defer chat/session event projection until the first event so Gateway boot skips session IO imports. Thanks @vincentkoc.
222
+ - Gateway/startup: keep node session runtime on a lightweight JSON parser instead of importing gateway method validation helpers during boot. Thanks @vincentkoc.
223
+ - Gateway/startup: read embedded-run activity from a lightweight shared state module so restart deferral no longer imports the embedded runner during Gateway boot. Thanks @vincentkoc.
224
+ - Gateway/startup: defer MCP loopback server imports until Gateway shutdown so normal boot no longer loads the loopback HTTP/tool schema stack just to register close handlers. Thanks @vincentkoc.
225
+ - Gateway/startup: resolve channel runtime helpers asynchronously only when an enabled/configured channel starts, so no-channel Gateway boot skips auto-reply, media, pairing, and outbound channel helper imports. Thanks @vincentkoc.
226
+ - Gateway/startup: lazy-load HTTP auth, canvas auth, and plugin route scope helpers from their request paths so Gateway bind no longer pays those utility graphs during boot. Thanks @vincentkoc.
227
+ - Gateway/startup: defer isolated cron runner imports until `/hooks/agent` dispatch so Gateway boot skips the agent-turn runtime on installs that only need normal HTTP bind. Thanks @vincentkoc.
228
+ - Gateway/startup: split hook request parsing into a request-path module and load the Gateway hook dispatcher only when a request matches the hooks base path, keeping hook mapping and throttle helpers off plain HTTP bind. Thanks @vincentkoc.
229
+ - CLI/Gateway: use a parse-only config snapshot for plain `gateway status` reads and reuse same-path service config context so status no longer spends tens of seconds in full config validation before printing. Thanks @vincentkoc.
230
+ - Lobster/Gateway: memoize repeated Ajv schema compilation before loading the embedded Lobster runtime so scheduled workflows and `llm.invoke` loops stop growing gateway heap on content-identical schemas. Fixes #71148. Thanks @cmi525, @vsolaz, and @vincentkoc.
231
+ - Codex harness: normalize cached input tokens before session/context accounting so prompt cache reads are not double-counted in `/status`, `session_status`, or persisted `sessionEntry.totalTokens`. Fixes #69298. Thanks @richardmqq.
232
+ - Hooks/session-memory: use the host local timezone for memory filenames, fallback timestamp slugs, and markdown headers instead of UTC dates. Fixes #46703. (#46721) Thanks @Astro-Han.
233
+ - Gateway health: preserve live runtime-backed channel/account state in `gateway.health` snapshots and cached refreshes while keeping raw probe payloads on sensitive/admin paths only. (#39921, #42586, #46527, #52770, #42543) Thanks @FAL1989, @rstar327, @0xble, and @ajayr.
234
+ - Feishu: extract quoted/replied interactive-card text across schema 1.0, schema 2.0, i18n, template-variable, and post-format fallback shapes without carrying broad generated/config churn from related parser experiments. (#38776, #60383, #42218, #45936) Thanks @lishuaigit, @lskun, @just2gooo, and @Br1an67.
235
+ - Telegram/agents: hide raw failed write/edit warning messages in Telegram when the assistant already explicitly acknowledges the failed action, while keeping warnings when the reply claims success or omits the failure; #39406 remains the broader configurable delivery-policy follow-up. Fixes #51065; covers #39631. Thanks @Bartok9 and @Bortlesboat.
236
+ - TUI: clear stale streaming status when an orphaned final event or watchdog reset leaves no tracked active run, flushing deferred local history refreshes without surfacing inactive-run failures. Fixes #64825; carries forward #52745. Thanks @lyksdu.
237
+ - Exec approvals: accept a symlinked `OPENCLAW_HOME` as the trusted approvals root while still rejecting symlinked `.openclaw` path components below it. (#64663) Thanks @FunJim.
238
+ - Logging: add top-level `hostname`, flattened `message`, and available `agent_id`, `session_id`, and `channel` fields to file-log JSONL records for multi-agent filtering without removing existing structured log arguments. Fixes #51075. Thanks @stevengonsalvez.
239
+ - ACP: route server logs to stderr before Gateway config/bootstrap work so ACP stdout remains JSON-RPC only for IDE integrations. Fixes #49060. Thanks @Hollychou924.
240
+ - Logging: propagate internal request trace scopes through Gateway HTTP requests and WebSocket frames so file logs, diagnostic events, agent run traces, model-call traces, OTEL spans, and trusted provider `traceparent` headers share a correlatable `traceId` without logging raw request or model content. Fixes #40353. Thanks @liangruochong44-ui.
241
+ - Diagnostics/OTEL: capture privacy-safe model-call request payload bytes, streamed response bytes, first-response latency, and total duration in diagnostic events, plugin hooks, stability snapshots, and OTEL model-call spans/metrics without logging raw model content. Fixes #33832. Thanks @wwh830.
242
+ - Logging: write validated diagnostic trace context as top-level `traceId`, `spanId`, `parentSpanId`, and `traceFlags` fields in file-log JSONL records so traced requests and model calls are easier to correlate in log processors. Refs #40353. Thanks @liangruochong44-ui.
243
+ - Logging/sessions: apply configured redaction patterns to persisted session transcript text and accept escaped character classes in safe custom redaction regexes, so transcript JSONL no longer keeps matching sensitive text in the clear. Fixes #42982. Thanks @panpan0000.
244
+ - Agents/sessions: let `sessions_spawn runtime="subagent"` ignore ACP-only `streamTo` and `resumeSessionId` fields while keeping ACP passthrough and documenting `streamTo` as ACP-only. Fixes #43556 and #63120; covers #56326, #61724, #64714, and #67248; carries forward #68397, #65282, #58686, #56342, and #40102. Thanks @skernelx, @damselem, @Br1an67, @Mintalix, @IsaacAPerez, @vvitovec, @Sanjays2402, @shenkq97, and @1034378361.
245
+ - Providers/Ollama: honor `/api/show` capabilities when registering local models so non-tool Ollama models no longer receive the agent tool surface, and keep native Ollama thinking opt-in instead of enabling it by default. Fixes #64710 and duplicate #65343. Thanks @yuan-b, @netherby, @xilopaint, and @Diyforfun2026.
246
+ - Image tool/media: honor `tools.media.image.timeoutSeconds` and matching per-model image timeouts in explicit image analysis, including the MiniMax VLM fallback path, so slow local vision models are not capped by hardcoded 30s/60s aborts. Fixes #67889; supersedes #67929. Thanks @AllenT22 and @alchip.
247
+ - Providers/Ollama: read larger custom Modelfile `PARAMETER num_ctx` values from `/api/show` so auto-discovered Ollama models with expanded context no longer stay pinned to the base model context. Fixes #68344. Thanks @neeravmakwana.
248
+ - Providers/Ollama: honor configured model `params.num_ctx` in native and OpenAI-compatible Ollama requests so local models can cap runtime context without rebuilding Modelfiles. Fixes #44550 and #52206; supersedes #69464. Thanks @taitruong, @armi0024, and @LokiCode404.
249
+ - Providers/Ollama: stop forcing native Ollama requests to use the full configured `contextWindow` as `options.num_ctx` unless `params.num_ctx` is explicit, so local models can keep Ollama's VRAM/env default instead of looking hung on first turns. Fixes #49684 and #68662. Thanks @zhouZcong and @dshenster-byte.
250
+ - Providers/Ollama: forward whitelisted native Ollama model params such as `temperature`, `top_p`, and top-level `think` so users can disable API-level thinking or tune local models from config without proxy shims. Fixes #48010. Thanks @tangzhi, @pandego, @maweibin, @Adam-Researchh, and @EmpireCreator.
251
+ - Providers/Ollama: expose native Ollama thinking effort levels so `/think max` is accepted for reasoning-capable Ollama models and maps to Ollama's highest supported `think` effort. Fixes #71584. Thanks @g0st1n.
252
+ - Providers/Ollama: strip the active custom Ollama provider prefix before native chat and embedding requests, so custom provider ids like `ollama-spark/qwen3:32b` reach Ollama as the real model name. Fixes #72353. Thanks @maximus-dss and @hclsys.
253
+ - Providers/Ollama: parse stringified native tool-call arguments before dispatch, preserving unsafe integer values so Ollama tool use receives structured parameters. Fixes #69735; supersedes #69910. Thanks @rongshuzhao and @yfge.
254
+ - Providers/Ollama: skip ambient localhost discovery unless Ollama auth or meaningful config opts in, preventing unexpected probes to `127.0.0.1:11434` for users who are not using Ollama. Fixes #56939; supersedes #57116. Thanks @IanxDev and @tsukhani.
255
+ - Providers/Ollama: skip implicit localhost discovery when a custom remote `api: "ollama"` provider is configured, while still treating `127/8` loopback hosts as local. Carries forward #43224. Thanks @issacthekaylon.
256
+ - Providers/models: honor provider-level `contextWindow`, `contextTokens`, and `maxTokens` as defaults when resolving discovered models, so local Ollama and other self-hosted providers can cap all models without repeating per-model entries. Fixes #44786; carries forward #44955. Thanks @voltwake and @maweibin.
257
+ - Providers/Ollama: move memory embeddings to Ollama's current `/api/embed` endpoint with batched `input` requests while preserving vector normalization and custom provider auth/header overrides. Fixes #39983. Thanks @sskkcc and @LiudengZhang.
258
+ - Providers/Ollama: route local web search through Ollama's signed `/api/experimental/web_search` daemon proxy, use hosted `/api/web_search` directly for `ollama.com`, and keep `OLLAMA_API_KEY` scoped to cloud fallback auth. Fixes #69132. Thanks @yoon1012 and @hyspacex.
259
+ - Providers/Ollama: accept OpenAI SDK-style `baseURL` as an alias for `baseUrl` across discovery, streaming, setup pulls, embeddings, and web search so remote Ollama hosts are not silently ignored. Fixes #62533; supersedes #62549. Thanks @Julien-BKK and @Linux2010.
260
+ - Providers/Ollama: scope synthetic local auth and embedding bearer headers to declared Ollama host boundaries so cloud keys are not sent to local/self-hosted embedding endpoints and remote/cloud Ollama endpoints no longer receive the `ollama-local` marker as if it were a real token. Supersedes #69261 and #69857; refs #43945. Thanks @hyspacex, @maxramsay, and @Meli73.
261
+ - Providers/Ollama: resolve custom-named local Ollama providers such as `ollama-remote` through the Ollama synthetic-auth hook so subagents no longer miss `ollama-local` auth and silently fall back to cloud models. Fixes #43945. Thanks @Meli73 and @maxramsay.
262
+ - Providers/Ollama: add provider-scoped model request timeouts, thread them through guarded fetch connect/header/body/abort handling, and document `params.keep_alive` for cold local models so first-turn Ollama loads no longer require global agent timeout changes. Fixes #64541 and #68796; supersedes #65143 and #66511. Thanks @LittleJakub, @Juankcba, @uninhibite-scholar, and @yfge.
263
+ - Providers/Ollama: preserve explicit configured model input modalities when merging discovered provider metadata so custom vision models keep image support instead of silently dropping attachments. Fixes #39690; carries forward #39785. Thanks @Skrblik and @Mriris.
264
+ - Providers/Ollama: estimate native Ollama transcript usage when `/api/chat` omits prompt/eval counters while preserving exact zero counters, keeping local model runs visible in usage surfaces. Carries forward #39112. Thanks @TylonHH.
265
+ - Agents/Ollama: retry native Ollama turns that finish without user-visible text, including unsigned thinking-only responses, so constrained reasoning turns can continue instead of surfacing an empty reply. Carries forward #66552 and #61223. Thanks @yfge and @L3G.
266
+ - Docs/Ollama: expand setup recipes for local, LAN, cloud, multi-host, web search, embeddings, thinking control, and large-context troubleshooting.
267
+ - Providers/PDF/Ollama: add bounded network timeouts for Ollama model pulls and native Anthropic/Gemini PDF analysis requests so unresponsive provider endpoints no longer hang sessions indefinitely. Fixes #54142; supersedes #54144 and #54145. Thanks @jinduwang1001-max and @arkyu2077.
268
+ - LLM Task/Ollama: accept model overrides that already include the selected provider prefix, avoiding doubled ids such as `ollama/ollama/llama3.2:latest`, and live-verify local Ollama JSON tasks return parsed output. Fixes #50052. Thanks @ralphy-maplebots and @Hollychou924.
269
+ - Memory/doctor: treat Ollama memory embeddings as key-optional so `openclaw doctor` no longer warns about a missing API key when the gateway reports embeddings are ready. Fixes #46584. Thanks @fengly78.
270
+ - Agents/Ollama: apply provider-owned replay turn normalization to native Ollama chat so Cloud models no longer reject non-alternating replay history in agent/Gateway runs. Fixes #71697. Thanks @ismael-81.
271
+ - Control UI/Ollama: show the resolved configured thinking default in chat and session thinking dropdowns so inherited `adaptive`/per-model thinking config no longer appears as `Default (off)` or a generic inherit value. Fixes #72407. Thanks @NotecAG.
272
+ - Agents/Ollama: validate explicit `--thinking max` against catalog-discovered Ollama reasoning metadata so local agent runs accept the same native thinking levels shown in the model catalog. Fixes #71584. Thanks @g0st1n.
273
+ - CLI/models: include explicitly configured provider models in `openclaw models list --provider <id>` without requiring the full catalog path, so configured Ollama models are visible. Fixes #65207. Thanks @drzeast-png.
274
+ - Docker/QA: add observability coverage to the normal Docker aggregate so QA-lab OTEL and Prometheus diagnostics run inside Docker. Thanks @vincentkoc.
275
+ - Auto-reply: poison inbound message dedupe after replay-unsafe provider/runtime failures so retries stay safe before visible progress but cannot duplicate messages after block output, tool side effects, or session progress. Fixes #69303; keeps #58549 and #64606 as duplicate validation. Thanks @martingarramon, @NikolaFC, and @zeroth-blip.
276
+ - Agents/model fallback: keep auto-persisted fallback model overrides selected across turns until `/new` or reset clears them, avoiding repeated probes of a known-bad primary while `/status` shows the selected and active models. Thanks @kibedu.
277
+ - Agents/model fallback: jump directly to a known later live-session model redirect instead of walking unrelated fallback candidates, while preserving the already-landed live-session/fallback loop guard. Fixes #57471; related loop family already closed via #58496. Thanks @yuxiaoyang2007-prog.
278
+ - Gateway/Bonjour: keep @homebridge/ciao cancellation handlers registered across advertiser restarts so late probing cancellations cannot crash Linux and other mDNS-churned gateways.
279
+ - Plugins/startup: load the default `memory-core` slot during Gateway startup when permitted so active-memory recall can call `memory_search` and `memory_get` without requiring an explicit `plugins.slots.memory` entry, while preserving `plugins.slots.memory: "none"`.
280
+ - Plugins/CLI: prefer native require for compiled bundled plugin JavaScript before jiti so read-only config, status, device, and node commands avoid unnecessary transform overhead on slow hosts. Fixes #62842. Thanks @Effet.
281
+ - Plugins/compat: inventory doctor-side deprecation migrations separately from runtime plugin compatibility so release sweeps preserve needed repairs while enforcing dated removal windows. Thanks @vincentkoc.
282
+ - Plugins/compat: add missing dated compatibility records for legacy extension-api, memory registration, provider hook/type aliases, runtime aliases, channel SDK helpers, and approval/test utility shims. Thanks @vincentkoc.
283
+ - Plugins/CLI: refresh the persisted registry after managed plugin files are removed so ClawHub uninstall cannot leave stale `plugins list` entries.
284
+ - Plugins/CLI: make plugin install and uninstall config writes conflict-aware, clear stale denylist entries on explicit reinstall/removal, and delete managed plugin files only after config/index commit succeeds.
285
+ - Plugins: fail `plugins update` when tracked plugin or hook updates error, keep bundled runtime-dependency repair behind restrictive allowlists, and reject package installs with unloadable extension entries.
286
+ - WebChat/Control UI: support non-video file attachments in chat uploads while preserving the existing image attachment path and MIME-sniff fallback for generic image uploads. (#70947) Thanks @IAMSamuelRodda.
287
+ - Skills/memory: restore Chokidar v5 hot reloads by watching concrete skill and memory roots with filters, including SKILL.md removals and deleted skill folders without broad workspace recursion. Fixes #27404, #33585, and #41606. Thanks @shelvenzhou, @08820048, and @rocke2020.
288
+ - Gateway/chat: keep duplicate attachment-backed `chat.send` retries with the same idempotency key on the documented in-flight path so aborts still target the real active run. Fixes #70139. Thanks @Feelw00.
289
+ - Gateway/session rows: report the same config-resolved thinking default that runtime sessions use, including global and per-agent defaults, so Control UI and TUI default labels stay aligned. (#71779, #70981, #71033, #70302) Thanks @chen-zhang-cs-code, @SymbolStar, and @cholaolu-boop.
290
+ - Plugins: share package entrypoint resolution between install and discovery, reject mismatched `runtimeExtensions`, and cache bundled runtime-dependency manifest reads during scans.
291
+ - WhatsApp/Web: keep quiet but healthy linked-device sessions connected by basing the watchdog on WhatsApp Web transport activity, while retaining a longer app-silence cap so frame activity cannot mask a stuck session forever. Fixes #70678; carries forward the focused #71466 approach and keeps #63939 as related configurable-timeout follow-up. Thanks @vincentkoc and @oromeis.
292
+ - Discord/gateway: count failed health-monitor restart attempts toward cooldown and hourly caps, and evict stale account lifecycle state during channel reloads so repeated Discord gateway recovery cannot loop on old status. Fixes #38596. (#40413) Thanks @jellyAI-dev and @vashquez.
293
+ - Cron/context engine: run isolated cron jobs under run-scoped context-engine session keys so prior runs of the same job are not inherited unless the job is explicitly session-bound. (#72292) Thanks @jalehman.
294
+ - Control UI: localize command palette labels, categories, skill shortcuts, footer hints, and connect-command copy labels while preserving localized command palette search matching. (#61130, #61119) Thanks @rubensfox20.
295
+ - Plugins/memory-lancedb: request float embedding responses from OpenAI-compatible servers so local providers that default SDK requests to base64 no longer return dimension-mismatched LanceDB vectors while preserving configured dimensions. Fixes #45982. (#59048, #46069, #45986) Thanks @deep-introspection, @xiaokhkh, @caicongyang, and @thiswind.
296
+ - Plugins/memory-lancedb: advance auto-capture cursors per session only after messages are processed or intentionally skipped, retry failed messages, survive compacted histories, and clear cursor state on session end. Fixes #71349; carries forward #42083. Thanks @as775116191.
297
+ - Plugins/memory-core: respect configured memory-search embedding concurrency during non-batch indexing so local Ollama embedding backends can serialize indexing instead of flooding the server. Fixes #66822. (#66931) Thanks @oliviareid-svg and @LyraInTheFlesh.
298
+ - Docker/update smoke: keep the package-derived update-channel fixture on package-shipped files and make its UI build stub create the asset the updater verifies. Thanks @vincentkoc.
299
+ - Gateway/models: repair legacy `models.providers.*.api = "openai"` config values to `openai-completions`, and skip providers with future stale API enum values during startup instead of bricking the gateway. Fixes #72477. (#72542) Thanks @JooyoungChoi14 and @obviyus.
300
+ - Gateway/skills: redact `apiKey` and secret-named `env` values from the `skills.update` RPC response to prevent leaking credentials into WebSocket traffic, client logs, or session transcripts. Config is still written to disk in full; only the response payload is redacted. (#69998) Thanks @Ziy1-Tan.
301
+
302
+ - Plugins/CLI: let flag-driven `openclaw channels add` install the selected channel plugin from its default source without opening an interactive prompt, fixing published npm Telegram setup in stdin-closed automation.
303
+ - Onboarding/setup: keep first-run config reads, plugin compatibility notices, and post-model sanity checks on cold metadata paths unless the user chooses to browse all models, avoiding full plugin/runtime catalog work between prompts. Thanks @shakkernerd.
304
+ - Onboarding/auth: run manifest-owned provider auth choices through scoped setup providers so selecting OpenAI Codex browser/device auth no longer loads every provider runtime before OAuth starts. Thanks @shakkernerd.
305
+ - Onboarding/auth: keep the post-auth default-model policy lookup on manifest/setup metadata so the next prompt appears without loading broad provider runtime. Thanks @shakkernerd.
306
+ - Onboarding/models: keep skip-auth and provider-scoped model picker prompts off the full global model catalog path, and cache provider catalog hook resolution so setup no longer stalls after auth on large plugin registries. Thanks @shakkernerd.
307
+ - Gateway/Bonjour: suppress known @homebridge/ciao cancellation and network assertion failures through scoped process handlers so malformed mDNS packets or restricted VPS networking disable/restart Bonjour instead of crashing the gateway. Fixes #67578. Thanks @zenassist26-create.
308
+ - Discord: keep late clicks on already-resolved exec approval buttons quiet when elevated mode auto-resolved the request, while still surfacing real approval submission failures. Fixes #66906. Thanks @rlerikse.
309
+ - Telegram: send a fresh final message for long-lived preview-streamed replies so the visible Telegram timestamp reflects completion time instead of the preview creation time. Thanks @rubencu.
310
+
CHANGELOG/2026.4.27.md ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.4.27
2
+
3
+ ### Highlights
4
+
5
+ - Codex Computer Use setup now ships with status/install commands, marketplace discovery, and fail-closed MCP checks for Codex-mode desktop control. Thanks @pash-openai.
6
+ - DeepInfra joins the bundled provider set with model discovery, media generation/editing, TTS, embeddings, and provider-owned onboarding policy. Thanks @ats3v.
7
+ - Tencent Yuanbao and QQBot support expand channel coverage with Yuanbao docs/catalog entries and QQBot group chat, streaming, media upload, and pipeline refactors. Thanks @loongfay and @cxyhhhhh.
8
+ - Plugin startup and model catalogs move toward manifest-first metadata, reducing Gateway boot work and making provider rows/aliases/suppressions easier to audit. Thanks @shakkernerd.
9
+ - Reliability fixes cover Telegram startup/sends, Slack socket/media stalls, gateway startup prewarm, session/history defaults, update sync, and Windows restart handoffs. Thanks @joerod26, @obviyus, @shivasymbl, @freerk, @bassboy2k, @jpreagan, @islandpreneur007, and @Thatgfsj.
10
+
11
+ ### Changes
12
+
13
+ - Sandbox/Docker: add opt-in `sandbox.docker.gpus` passthrough for Docker sandbox containers so local GPU workloads can run inside sandboxed agents when the host Docker runtime supports `--gpus`. Fixes #57976; carries forward #58124. Thanks @cyan-ember.
14
+ - iOS/Gateway: add an authenticated `node.presence.alive` protocol event and `node.list` last-seen fields so background iOS wakes can mark paired nodes recently alive without treating them as connected. Carries forward #63123. Thanks @ngutman.
15
+ - Android: publish authenticated `node.presence.alive` events after node connect and background transitions so paired Android nodes retain durable last-seen metadata after disconnects. Carries forward #63123. Thanks @ngutman.
16
+ - Gateway/chat: accept non-image attachments through `chat.send` by staging them as agent-readable media paths, while keeping unsupported RPC attachment paths explicit instead of silently dropping files. Fixes #48123. (#67572) Thanks @samzong.
17
+ - Security/networking: add opt-in operator-managed outbound proxy routing (proxy.enabled + proxy.proxyUrl/OPENCLAW_PROXY_URL) with strict http:// forward-proxy validation, loopback-only Gateway bypass, and cleanup of proxy env/dispatcher state on exit. (#70044) Thanks @jesse-merhi and @joshavant.
18
+ - Dependencies: refresh provider and tooling dependencies, including AWS SDK, PI runtime packages, AJV, Feishu SDK, Anthropic SDK, tokenjuice, and native TypeScript/oxlint tooling. Thanks @dependabot.
19
+ - Matrix/QA: add live Matrix approval scenarios for exec metadata, chunked fallback, plugin approvals, deny reactions, thread targeting, and `target: "both"` delivery, with redacted artifacts preserving safe approval summaries. Thanks @gumadeiras.
20
+ - Diagnostics/Codex: add owner-only core `/diagnostics` with a sensitive-data preamble, docs link, and explicit Gateway export approval guidance; Codex harness sessions also ask before uploading Codex feedback for the attached thread and print the matching `codex resume <thread-id>` inspection command after confirmed upload. Thanks @pashpashpash.
21
+ - Trajectory export: route `/export-trajectory` through per-run exec approval, send group-chat approval prompts and export results only to the owner privately, and add `openclaw sessions export-trajectory` for the approved command path. Thanks @pashpashpash.
22
+ - Codex: add Computer Use setup for Codex-mode agents, including `/codex computer-use status/install`, marketplace discovery, optional auto-install, and fail-closed MCP server checks before Codex-mode turns start. Fixes #72094. (#71842) Thanks @pash-openai.
23
+ - Apps: consume Peekaboo 3.0.0-beta4 and ElevenLabsKit 0.1.1, align Swabble on Commander 0.2.2, and refresh macOS/iOS SwiftPM resolutions against the released dependency graph. Thanks @Blaizzy.
24
+ - Plugin SDK: expose shared channel route normalization, parser-driven target resolution, raw-target compact keys, parsed-target types, and route comparison helpers through `openclaw/plugin-sdk/channel-route`, switch native approval origin matching onto that route contract with optional delivery and match-only target normalization, and retire the internal channel-route shim behind dated compatibility aliases for legacy key/comparable-target helpers. Thanks @vincentkoc.
25
+ - Docs/Codex: document how Codex Computer Use, direct `cua-driver mcp`, and OpenClaw.app's PeekabooBridge fit together so desktop-control setup choices are clearer. Thanks @pash-openai and @trycua.
26
+ - Matrix/streaming: stream tool-progress updates into live Matrix preview edits by default when preview streaming is active, with `streaming.preview.toolProgress: false` to keep answer previews while hiding interim tool lines. Thanks @gumadeiras.
27
+ - Plugins/models: wire manifest `modelCatalog.aliases` and `modelCatalog.suppressions` into model-catalog planning and built-in model suppression, with stale Spark and Qwen Coding Plan suppressions now declared in plugin manifests instead of runtime fallback hooks. Thanks @shakkernerd.
28
+ - Plugin SDK/models: add a shared manifest-backed provider catalog builder and move Qianfan, Xiaomi, NVIDIA, Cerebras, Mistral, Moonshot, DeepSeek, Tencent TokenHub, and StepFun provider catalogs onto their plugin manifest `modelCatalog` rows. Thanks @shakkernerd.
29
+ - Plugin SDK/models: move BytePlus and Volcano Engine standard and plan-provider catalogs into plugin manifest `modelCatalog` rows and remove the now-unused Volcengine-family shared catalog SDK subpath. Thanks @shakkernerd.
30
+ - CLI/models: move Fireworks and Together AI fixed provider catalogs into plugin manifest `modelCatalog` rows so provider-filtered listing can use manifest-backed static rows. Thanks @shakkernerd.
31
+ - CLI/models: move Groq's fixed text model catalog into the Groq plugin manifest and declare its setup auth env metadata so provider-filtered listing can use manifest-backed rows without deprecated auth metadata. Thanks @shakkernerd.
32
+ - CLI/models: move Venice's 41-row seed catalog into the Venice plugin manifest, derive runtime fallback rows from that manifest, and keep Venice API discovery as refreshable runtime work instead of a second hard-coded catalog. Thanks @shakkernerd.
33
+ - Channels/Yuanbao: register the Tencent Yuanbao external channel plugin (`openclaw-plugin-yuanbao`) in the official channel catalog, contract suites, and community plugin docs, with a new `docs/channels/yuanbao.md` quick-start guide for WebSocket bot DMs and group chats. (#72756) Thanks @loongfay.
34
+ - Channels/QQBot: add full group chat support (history tracking, @-mention gating, activation modes, per-group config, FIFO message queue with deliver debounce), C2C `stream_messages` streaming with a `StreamingController` lifecycle manager, unified `sendMedia` with chunked upload for large files, and refactor the engine into pipeline stages, focused outbound submodules, builtin slash-command modules, and explicit DI ports via `createEngineAdapters()`. (#70624) Thanks @cxyhhhhh.
35
+ - Plugins/startup: migrate bundled plugin manifests to explicit `activation.onStartup` declarations so Gateway startup imports only the bundled plugins that intentionally register startup-time runtime surfaces. Thanks @shakkernerd.
36
+ - Plugins/startup: add an opt-in future-mode gate for disabling deprecated implicit startup sidecar loading while preserving explicit startup and narrower activation triggers. Thanks @shakkernerd.
37
+ - Plugins/startup: add plugin compatibility warnings for deprecated implicit startup loading so authors can migrate to explicit `activation.onStartup` metadata. Thanks @shakkernerd.
38
+ - Plugins/runtime: load bundled agent tool-result middleware from manifest contracts on demand so tokenjuice stays startup-lazy without losing Pi/Codex tool-output compaction. Thanks @shakkernerd.
39
+ - Plugins/startup: add explicit `activation.onStartup` metadata so plugins can declare Gateway startup import behavior while the deprecated implicit sidecar fallback remains for legacy plugins. Thanks @shakkernerd.
40
+ - Gateway/startup: reuse lookup-table plugin manifests when loading startup plugins so Gateway boot avoids rebuilding plugin discovery and manifest metadata. Thanks @shakkernerd.
41
+ - CLI/models: declare fixed Qianfan, Xiaomi, NVIDIA, Cerebras, Mistral, Chutes, Kilo, OpenAI, and OpenCode Go model catalogs in refreshable plugin manifests, keep broad `models list --all` on raw registry and supplement rows without runtime normalization, and avoid duplicate supplement resolution. Thanks @shakkernerd.
42
+ - Gateway/runtime: reuse the current plugin metadata snapshot for provider discovery so repeated model-provider discovery avoids rebuilding plugin manifest metadata. Thanks @shakkernerd.
43
+ - Gateway/startup: pass the plugin metadata snapshot from config validation into plugin bootstrap so startup reuses one manifest product instead of rebuilding plugin metadata. Thanks @shakkernerd.
44
+ - Plugin SDK/testing: move core-only channel contract fixtures under the channel contract test tree and retire the old `test/helpers/channels` bridge directory so plugin tests stay on focused SDK surfaces. Thanks @vincentkoc.
45
+ - Plugin SDK/testing: expose native agent-runtime contract fixtures through `plugin-sdk/agent-runtime-test-contracts`, move sandbox config fixtures into the focused generic fixture subpath, and block extension tests from importing repo-only `test/helpers` bridges. Thanks @vincentkoc.
46
+ - Plugin SDK/testing: expose generic module reload, bundled-path, Node builtin mock, channel pairing/envelope, HTTP server, temp-home, replay-policy, and live STT helpers through focused SDK test subpaths so extension tests no longer depend on repo-only helper bridges. Thanks @vincentkoc.
47
+ - Plugin SDK: move maintained bundled channels off the deprecated `channel-config-schema-legacy` subpath, add an explicit bundled-channel schema SDK surface, and track both remaining legacy test/config compatibility barrels with dated removal windows. Thanks @vincentkoc.
48
+ - Plugin SDK/testing: expose media provider capability assertions and provider HTTP mocks through focused SDK test subpaths, and retire the repo-only media-generation test helper bridge. Thanks @vincentkoc.
49
+ - Plugin SDK/testing: promote bundled plugin/provider/channel contract helpers to focused SDK test subpaths and retire the repo-only `test/helpers/plugins` TypeScript bridge. Thanks @vincentkoc.
50
+ - Plugin SDK/testing: expose generic channel action, setup, status, and directory contract helpers through `plugin-sdk/channel-test-helpers` so bundled extension tests no longer import repo-only channel helper bridges. Thanks @vincentkoc.
51
+ - Plugin SDK/testing: add `plugin-sdk/channel-target-testing` for shared channel target-resolution cases, document channel reaction helpers on `plugin-sdk/channel-feedback`, and keep the old `plugin-sdk/test-utils` alias as compatibility-only. Thanks @vincentkoc.
52
+ - Plugin SDK/testing: add a focused generic fixture subpath for CLI capture, sandbox, skill, agent-message, system-event, terminal, chunking, auth-token, and typed-case helpers. Thanks @vincentkoc.
53
+ - Plugin SDK/testing: add focused plugin runtime and environment fixture subpaths so plugin tests can avoid the broad `plugin-sdk/testing` barrel for common setup helpers. Thanks @vincentkoc.
54
+ - Plugin SDK/testing: add a focused `plugin-sdk/plugin-test-api` helper subpath and move bundled plugin registration tests off the repo-only plugin API bridge. Thanks @vincentkoc.
55
+ - Plugin SDK: add generic host hooks for session state, next-turn context, trusted tool policy, UI descriptors, events, scheduler cleanup, and run-scoped plugin context. (#72287) Thanks @100yenadmin.
56
+ - Plugin SDK/testing: expose provider catalog, wizard, registry, manifest, public-artifact, outbound, and TTS contract helpers through documented SDK testing seams so bundled plugin tests no longer import repo `src/**` internals. Thanks @vincentkoc.
57
+ - Providers/DeepInfra: add a bundled DeepInfra provider with `DEEPINFRA_API_KEY` onboarding, dynamic OpenAI-compatible model discovery, image generation/editing, image/audio media understanding, TTS, text-to-video, memory embeddings, static catalog metadata, and provider-owned base URL policy. Carries forward #53805, #48088, #37576, #43896, #11533, and #2554. Thanks @ats3v.
58
+ - Matrix: attach versioned structured approval metadata to pending approval messages so capable Matrix clients can render richer approval UI while body text and reaction fallback keep working. (#72432) Thanks @kakahu2015.
59
+
60
+ ### Fixes
61
+
62
+ - Channels/QQBot: re-evaluate routing bindings against the current runtime config on every inbound message instead of the snapshot captured at gateway start, so peer-specific bindings added via the CLI take effect without restarting the gateway. Fixes #69546 via #73567. Thanks @statxc and @F32138.
63
+ - CLI/channel-setup: auto-skip the redundant "Install \<plugin\>?" confirmation when only one install source (npm or local) exists, show `download from <npm-spec>` hints for installable catalog channels in the picker, and suppress misleading npm hints for already-bundled channels. Fixes #73419. Thanks @sliverp.
64
+ - BlueBubbles: tighten DM-vs-group routing across the outbound session route (`chat_guid:iMessage;-;...` DMs no longer classified as groups), reaction handling (drop group reactions that arrive without any chat identifier instead of synthesizing a `"group"` literal peerId), inbound `chatGuid` fallback (no longer fall back to the sender's DM chatGuid when resolving a group whose webhook omits chatGuid+chatId+chatIdentifier), and short message id resolution (carry caller chat context so a numeric short id reused after a long group conversation cannot silently resolve to a message in a different chat, with the same cross-chat guard applied to full GUIDs so retries cannot bypass it). Thanks @zqchris.
65
+ - Gateway/sessions: clone cached session stores through the persisted JSON shape instead of `structuredClone`, reducing native-memory growth on the remaining #54155 Gateway RSS/session-accumulation path while keeping #54155 as the broader tracker and carrying forward the #45438 session-cache hypothesis. Thanks @vincentkoc and the #45438 reporters/commenters.
66
+ - Agents/approvals: fail restart-interrupted sessions whose transcript tail is still `approval-pending` instead of replaying stale exec approval IDs into the new Gateway process after restart. Fixes #65486. Thanks @mjmai20682068-create.
67
+ - CLI/Gateway: use method-specific least-privilege scopes for classified CLI Gateway calls while preserving legacy broad scopes for unclassified plugin methods, so read-only commands no longer create admin/write/pairing scope-upgrade prompts. Fixes #68634. Thanks @nightmusher.
68
+ - Gateway/sessions: align `chat.history` and `sessions.list` thinking defaults with owning-agent and catalog-aware resolution so Control UI session defaults match backend runtime state. (#63418) Thanks @jpreagan.
69
+ - Devices/pairing: recover array-shaped device and node pairing state files before persisting approvals, so UUID-keyed pending and paired entries no longer disappear after a malformed JSON store write. Fixes #63035. Thanks @sar618.
70
+ - Gateway/auth: clear reused stale device tokens and stop reconnecting on device-token mismatch in the Control UI and Node gateway clients, avoiding rate-limit loops after scope-upgrade or token-rotation handoffs. Fixes #71609. Thanks @ricksayhi.
71
+ - Gateway/approvals: treat duplicate same-decision approval resolves as idempotent during the resolved-entry grace window, including consumed `allow-once` approvals, while returning an explicit already-resolved error for conflicting repeats. Fixes #59162; refs #58479 and #65486. Thanks @wikithoughts, @sajazuniga7-coder, and @mjmai20682068-create.
72
+ - Channels/Telegram: honor `approvals.exec/plugin.targets[].accountId` when routing native approvals across multi-bot Telegram accounts while preserving unscoped Telegram targets for any account. Fixes #69916. Thanks @joerod26.
73
+ - Agents/exec: omit the internal session-resume fallback preface from successful async exec completion messages sent directly back to chat. Fixes #67181. Thanks @raistlin88.
74
+ - Agents/media: register detached `video_generate` and `music_generate` tool run contexts until terminal status, so Discord-backed provider jobs stay live in `/tasks` instead of becoming `lost` when the parent chat run context disappears. Thanks @vincentkoc.
75
+ - Agents/media: prefer OpenAI image and video providers when the default model uses the OpenAI Codex auth alias, so auto media generation no longer falls through to Fal before GPT Image or Sora. Thanks @vincentkoc.
76
+ - Tasks/media: infer agent ownership for session-scoped task records so `/tasks` agent-local fallback includes session-backed `video_generate` and other async media jobs even when the current chat session has no linked rows. Thanks @vincentkoc.
77
+ - Agents/media: keep long-running `video_generate` and `music_generate` tasks fresh while provider jobs are still pending, so task maintenance does not mark active Discord media renders lost before completion. Thanks @vincentkoc.
78
+ - CLI/status: treat scope-limited gateway probes as reachable-but-degraded in shared status scans, so `openclaw status --all` no longer reports a live gateway as unreachable after `missing scope: operator.read`. Fixes #49180; supersedes #47981. Thanks @openjay.
79
+ - Slack/Socket Mode: use a 15s Slack SDK pong timeout by default and add `channels.slack.socketMode.clientPingTimeout`, `serverPingTimeout`, and `pingPongLoggingEnabled` overrides so stale-websocket handling no longer depends on app-event health heuristics. Fixes #14248; refs #58519, #64009, and #63488. Thanks @shivasymbl and @freerk.
80
+ - Slack/media: bound private file and forwarded attachment downloads with idle and total timeouts while preserving placeholder fallback, so stalled Slack `file_share` media no longer wedges inbound message handling. Fixes #61850. Thanks @bassboy2k.
81
+ - Plugins/inspector: keep bundled plugin runtime capture quiet and config-tolerant for Codex, memory-lancedb, Feishu, Mattermost, QQBot, and Tlon so plugin-inspector JSON checks can validate the full bundled set. Thanks @vincentkoc.
82
+ - Slack/auto-reply: keep fully consumed text reset triggers such as `new session` out of `BodyForAgent` after directive cleanup, so configured Slack reset phrases do not leak into the fresh model turn. Fixes #73137. Thanks @neeravmakwana.
83
+ - Plugins/runtime deps: prune stale retained bundled runtime deps and keep doctor/secret channel contract scans on lightweight artifacts, so disabled bundled channels stop preserving old dependency trees or importing heavy plugin surfaces. Thanks @SymbolStar and @vincentkoc.
84
+ - Auto-reply: bound the post-run pending tool-result delivery drain with a progress-aware idle timeout, so a never-settling tool-result task no longer leaves the session active forever while slow healthy deliveries can keep draining. Fixes #53889; supersedes #64733 and #73434. Thanks @zijunl and @wujiaming88.
85
+ - Gateway/startup: start chat channels without waiting for primary model prewarm, keeping model warmup bounded in the background so Slack and other channels come online promptly when provider discovery is slow. Supersedes #73420. Thanks @dorukardahan.
86
+ - Gateway/install: carry env-backed config SecretRefs such as `channels.discord.token` into generated service environments when they are present only in the installing shell, while keeping gateway auth SecretRefs non-persisted. Fixes #67817; supersedes #73426. Thanks @wdimaculangan and @ztexydt-cqh.
87
+ - Auto-reply/commands: stop bare `/reset` and `/new` after reset hooks acknowledge the command, so non-ACP channels no longer fall through into empty provider calls while `/reset <message>` and `/new <message>` still seed the next model turn. Fixes #73367 and #73412. Thanks @hoyanhan, @wenxu007, and @amdhelper.
88
+ - Providers/DeepSeek: backfill DeepSeek V4 `reasoning_content` on plain assistant replay messages as well as tool-call turns, so thinking sessions with prior tool use no longer fail follow-up requests with missing reasoning content. Fixes #73417; refs #71372. Thanks @34262315716 and @Bartok9.
89
+ - Agents/gateway tool: strip full config payloads from `config.patch` and `config.apply` tool responses while preserving direct RPC responses, so config-heavy sessions no longer replay large redacted configs into transcript history. Fixes #47610; supersedes #73439. Thanks @HanenVit and @juan-flores077.
90
+ - Auto-reply: preserve voice-note media from silent turns while continuing to suppress text and non-voice media, so `NO_REPLY` TTS replies still deliver the requested audio bubble. (#73406) Thanks @zqchris.
91
+ - Channels/Mattermost: stop enqueueing regular inbound posts as system events, so Mattermost user messages reach the model only as user-role inbound-envelope content instead of also appearing as `System: Mattermost message...` directives. Fixes #71795. Thanks @juan-flores077.
92
+ - Agents/media: qualify bare `agents.defaults.imageModel` and `pdfModel` refs from unique configured image-capable providers, so Ollama vision models such as `moondream` and `qwen2.5vl:7b` do not fall through to the default provider. Fixes #38816; supersedes #73396. Thanks @alainasclaw and @vincentkoc.
93
+ - Agents/Anthropic: send implicit Anthropic beta headers only to direct public Anthropic endpoints, including OAuth, so custom Anthropic-compatible providers no longer mis-handle unsupported beta flags unless explicitly configured. Refs #73346. Thanks @byBrodowski.
94
+ - Skills: require explicit `skills.entries.coding-agent.enabled` before exposing the bundled coding-agent skill, so installs with Codex on PATH but no OpenAI auth do not silently offer Codex delegation. Fixes #73358. Thanks @LaFleurAdvertising and @Sanjays2402.
95
+ - Plugins/startup: treat manifestless Claude bundles as valid installed-plugin registry entries instead of stale missing manifests, so workspace bundles no longer force repeated derived registry rebuilds or noisy `plugins.entries.workspace` warnings during Gateway startup. Fixes #73433. Thanks @AnneVoss.
96
+ - Agents/subagents: preserve `sessions_yield` as a paused subagent state and ignore its wait text while freezing completion output, so parent sessions wait for the final post-compaction answer instead of receiving intermediate progress or `(no output)`. Fixes #73413. Thanks @Ask-sola.
97
+ - Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock and keep Docker bundled plugin runtime deps/mirrors in a Docker-managed volume instead of the Windows/WSL config bind mount, so cold starts avoid slow host-volume mirror writes. Fixes #73339. Thanks @1yihui.
98
+ - Plugins/runtime deps: refresh bundled runtime mirrors without deleting active import trees, so config-triggered restarts do not see transient missing plugin files during registration. Thanks @shakkernerd.
99
+ - Channels/LINE: persist inbound image, video, audio, and file downloads in `~/.openclaw/media/inbound/` instead of temporary files so agents can still read LINE media after `/tmp` cleanup. Fixes #73370. Thanks @hijirii and @wenxu007.
100
+ - CLI/plugins: keep bundled plugin installs out of `plugins.load.paths` while preserving install records, so install/inspect/doctor loops no longer warn about the current bundled plugin directory. Thanks @vincentkoc.
101
+ - CLI/plugins: scope `plugins inspect <id>` runtime loading to the matched plugin so single-plugin inspection does not load every plugin before checking the target. Thanks @shakkernerd.
102
+ - CLI/plugins: remove managed copied-path plugin directories during uninstall and plan uninstall from metadata instead of runtime-loading plugins, so plugin lifecycle commands avoid unnecessary bundled runtime-deps work. Thanks @shakkernerd.
103
+ - Cron tool: infer the creating session's agentId for `cron.add` jobs when `agentId` is omitted or passed as undefined, keeping scheduled agentTurn jobs routed to the session agent; #40571 identified the guard bug and supplied the focused regression coverage. Thanks @ChanningYul.
104
+ - Cron/Telegram: add `--thread-id` to `openclaw cron add` and `openclaw cron edit`, preserving Telegram forum topic delivery targets across scheduled announcements. Carries forward #51581, #60373, and #60890. Thanks @ChunHao-dev.
105
+ - Cron/Telegram: preserve session-derived Telegram topic thread IDs when isolated cron delivery explicitly targets the parent chat, keeping bare chat targets in the active forum topic without leaking stale topics to other chats. Carries forward #64708. Thanks @addelh.
106
+ - Memory/compaction: keep pre-compaction memory-flush prompts runtime-only so session transcripts and `chat.history` no longer expose them as normal user turns. Fixes #54408 and #58956; refs #43567. Thanks @markgong and @guoyuhang9.
107
+ - Control UI/WebChat: keep large attachment payloads out of Lit state and optimistic chat messages, using object URL previews plus send-time payload serialization so PDF/image uploads no longer trigger `RangeError: Maximum call stack size exceeded`. Fixes #73360; refs #54378 and #63432. Thanks @hejunhui-73, @Ansub, and @christianhernandez3-afk.
108
+ - Agents/Anthropic: cancel stalled Anthropic Messages SSE body reads when abort signals fire, so active-memory timeouts release transport resources instead of leaving hidden recall runs parked on `reader.read()`. Refs #72965 and #73120. Thanks @wdeveloper16.
109
+ - Control UI/WebChat: keep pending run and typing state attached to the active client run, so unowned inject/announce/side-result finals no longer unlock unrelated active runs while completed owned runs still clear promptly. Fixes #57795; carries forward the narrow diagnosis from #57887. Thanks @haoyu-haoyu.
110
+ - Sandbox/Docker: stop satisfying a missing default sandbox image by tagging plain Debian as `openclaw-sandbox:bookworm-slim`, preserving the Python tooling required by sandbox write/edit helpers and directing users to build the default image. Fixes #51185; refs #45108, #51099, #51609, and #57713. Thanks @dpalis, @Tin55FoilDev, @jbcohen2-coder, @macminihal-cyber, and @PraxoOnline.
111
+ - Control UI/WebChat: confirm toolbar New Session button resets before dispatching `/new` while leaving typed `/new` and `/reset` commands immediate. Fixes #45800; refs #27065, #56611, #54499, and #27110. Thanks @aethnova, @kosta228-huli, @adambezemek, and @xss925175263 (xianshishan).
112
+ - Agents/models: keep per-agent primary models strict when `fallbacks` is omitted, so probe-only custom providers are not tried as hidden fallback candidates unless the agent explicitly opts in. Fixes #73332. Thanks @haumanto.
113
+ - Gateway/models: add `models.pricing.enabled` so offline or restricted-network installs can skip startup OpenRouter and LiteLLM pricing-catalog fetches while keeping explicit model costs working. Fixes #53639. Thanks @callebtc, @palewire, and @rjdjohnston.
114
+ - Gateway/startup: warn when legacy `CLAWDBOT_*` or `MOLTBOT_*` environment variables are still present, pointing users to `OPENCLAW_*` names instead of failing silently. Fixes #53482; carries forward #53667. Thanks @lndyzwdxhs.
115
+ - Onboarding: pin interactive and non-interactive health checks to the just-configured setup token/password so stale `OPENCLAW_GATEWAY_TOKEN` or `OPENCLAW_GATEWAY_PASSWORD` values do not produce false gateway-token-mismatch failures after setup. Fixes #72203. Thanks @galiniliev.
116
+ - Doctor/state: require an interactive confirmation before archiving orphan transcript files, so `openclaw doctor --fix` no longer silently renames recoverable session history after upgrades regenerate `sessions.json`. Fixes #73106. Thanks @scottgl9.
117
+ - Cron/Telegram: preserve explicit `:topic:` delivery targets over stale session-derived thread IDs when isolated cron announces to Telegram forum topics. Carries forward #59069; refs #49704 and #43808. Thanks @roytong9.
118
+ - Build/runtime: write the runtime-postbuild stamp after `pnpm build` writes the build stamp, so the next CLI invocation does not re-sync runtime artifacts after a successful build. Fixes #73151. Thanks @bittoby.
119
+ - Build/runtime: preserve staged bundled-plugin runtime dependency caches across source-checkout tsdown rebuilds, so local CLI and gateway-watch rebuilds no longer recreate large plugin dependency trees before starting. Refs #73205. Thanks @SymbolStar.
120
+ - CLI/channels: list configured chat channel accounts from read-only setup metadata even when the standalone CLI has not loaded the runtime channel registry, so `openclaw channels list` shows Telegram accounts before auth providers. Fixes #73319 and #73322. Thanks @mlaihk.
121
+ - CLI/model probes: keep `infer model run --gateway` raw by skipping prior session transcript, bootstrap context, context-engine assembly, tools, and bundled MCP servers, so local backends can be tested without full agent-context overhead. Fixes #73308. Thanks @ScientificProgrammer.
122
+ - CLI/image describe: pass `--prompt` and `--timeout-ms` through `infer image describe` and `describe-many`, so custom vision instructions and slow local model budgets reach media-understanding providers such as Ollama, OpenAI, Google, and OpenRouter. Addresses #63700. Thanks @cedricjanssens.
123
+ - Providers/Ollama: reject long non-linguistic Kimi/GLM symbol runs as provider failures instead of storing them as successful visible assistant replies, so fallback or error handling can recover from garbled cloud output. Fixes #64262; refs #67019. Thanks @Kloz813 and @xiaomenger123.
124
+ - CLI/model probes: reject empty or whitespace-only `infer model run --prompt` values before calling local providers or the Gateway, so smoke checks do not spend provider calls on invalid turns. Fixes #73185. Thanks @iot2edge.
125
+ - Gateway/media: route text-only `chat.send` image offloads through media-understanding fields so `agents.defaults.imageModel` can describe WebChat attachments instead of leaving only an opaque `media://inbound` marker. Fixes #72968. Thanks @vorajeeah.
126
+ - Gateway/Windows: route no-listener restart handoffs through the Windows supervisor without leaving restart tokens in flight, so failed task scheduling can be retried and successful handoffs do not coalesce later restart requests. (#69056) Thanks @Thatgfsj.
127
+ - Gateway/model pricing: skip plugin manifest discovery during background pricing refreshes when `plugins.enabled: false`, so disabled-plugin setups do not keep rebuilding plugin metadata from the Gateway hot path. Fixes #73291. Thanks @slideshow-dingo and @fishgills.
128
+ - Ollama/thinking: validate `/think` commands against live Ollama catalog reasoning metadata and preserve explicit native `params.think`/`params.thinking`, so models whose `/api/show` capabilities include `thinking` expose `low`, `medium`, `high`, and `max` instead of being stuck on `off`. Fixes #73366. Thanks @cymise.
129
+ - Gateway/sessions: remove automatic oversized `sessions.json` rotation backups, deprecate `session.maintenance.rotateBytes`, and teach `openclaw doctor --fix` to remove the ignored key so hot session writes no longer copy multi-MB stores. Refs #72338. Thanks @midhunmonachan and @DougButdorf.
130
+ - Channels/Telegram: fail fast when Telegram rejects the startup `getMe` token probe with 401, so invalid or stale BotFather tokens are reported as token auth failures instead of misleading `deleteWebhook` cleanup failures. Fixes #47674. Thanks @samaedan-arch.
131
+ - ACPX: keep generated Codex and Claude ACP wrapper startup paths working when remote or special state filesystems reject chmod, since OpenClaw invokes the wrappers through Node instead of executing them directly. Fixes #73333. Thanks @david-garcia-garcia.
132
+ - CLI/onboarding: infer image input for common custom-provider vision model IDs, ask only for unknown models, and keep `--custom-image-input`/`--custom-text-input` overrides so vision-capable proxies do not get saved as text-only configs. Fixes #51869. Thanks @Antsoldier1974.
133
+ - Models/OpenAI Codex: stop listing or resolving unsupported `openai-codex/gpt-5.4-mini` rows through Codex OAuth, keep stale discovery rows suppressed with a clear API-key-route hint, and leave direct `openai/gpt-5.4-mini` available. Fixes #73242. Thanks @0xCyda.
134
+ - Plugin SDK: restore the root `stringEnum` and `optionalStringEnum` exports on both the published SDK entry and runtime root-alias bridge, so older external plugins can keep building and loading while migrating to focused SDK subpaths. Fixes #68279. Thanks @marzliak.
135
+ - Plugin SDK: restore the root-alias bridge for `registerContextEngine` and expose missing legacy compat helpers `normalizeAccountId` and `resolvePreferredOpenClawTmpDir` so older external plugins such as `openclaw-weixin` can keep loading while migrating to focused SDK subpaths. Fixes #53497. Thanks @alanxchen85.
136
+ - Auth profiles: make `openclaw doctor --fix` migrate legacy flat `auth-profiles.json` files such as `{ "ollama-windows": { "apiKey": "ollama-local" } }` to canonical provider default API-key profiles with a backup, so custom Ollama/OpenAI-compatible providers recover cleanly after upgrading. Fixes #59629; supersedes #59642. Thanks @Xsanders555 and @Linux2010.
137
+ - Memory/Dreaming: retry Dream Diary once with the session default when a configured dreaming model is unavailable, while leaving subagent trust and allowlist errors visible instead of silently masking configuration problems. Refs #67409 and #69209. Thanks @Ghiggins18 and @everySympathy.
138
+ - Feishu/inbound files: recover CJK filenames from plain `Content-Disposition: filename=` download headers when Feishu exposes UTF-8 bytes through Latin-1 header decoding, while leaving valid Latin-1 and JSON-derived names unchanged. (#48578, #50435, #59431) Thanks @alex-xuweilong, @lishuaigit, and @DoChaoing.
139
+ - Channels/Telegram: normalize accidental full `/bot<TOKEN>` Telegram `apiRoot` values at runtime and teach `openclaw doctor --fix` to remove the suffix, so startup control calls no longer 404 when direct Bot API curl commands work. Fixes #55387. Thanks @brendanmatthewjones-cmyk, @techfindubai-ux, and @Sivlerback-Chris.
140
+ - Zalo Personal: persist refreshed `zca-js` session cookies after QR login, session restore, and successful API calls so gateway restarts restore the freshest local session. (#73277) Thanks @darkamenosa.
141
+ - Logging/security: redact sensitive tokens (sk-\* keys, Bearer/Authorization values, etc.) at the subsystem console sink so `createSubsystemLogger().info/warn/error` output that bypasses the patched console-capture handler still applies the same redaction the file transport already does. Fixes #73284; refs #67953 and #64046. Thanks @edwin-rivera-dev.
142
+ - Plugins/runtime deps: reuse enclosing versioned cache roots when bundled plugins resolve from nested staged paths, so plugin-runtime-deps no longer mints `openclaw-unknown-*` directories or loops on `ENOTEMPTY`. Fixes #72956. (#73205) Thanks @SymbolStar.
143
+ - Agents/failover: classify CJK provider transport, quota, billing, auth, and overload error text so Chinese-language provider failures trigger fallback and user-facing transport copy instead of surfacing as unclassified raw errors. (#56242) Thanks @tomcatzh.
144
+ - Agents/failover: seed non-claude-cli fallback prompts with Claude Code session context when a claude-cli attempt fails, so fallback models do not restart cold after billing or quota failover. (#72069) Thanks @stainlu.
145
+ - Agents/CLI runner: transfer bundle-MCP tempDir cleanup from the per-turn runner finally to the Claude live-session lifecycle, so persistent Claude CLI sessions keep their `--mcp-config` directory until the live subprocess closes. Fixes #73244. Thanks @edwin-rivera-dev.
146
+ - Gateway/nodes: allow Windows companion nodes to use safe declared commands such as canvas, camera list, location, device info, and screen snapshot by default while keeping dangerous media commands opt-in. (#71884) Thanks @shanselman.
147
+ - Agents/cron: clarify agent-tool and CLI cron timezone guidance so supplied `tz` values use local wall-clock cron fields and omitted cron `tz` falls back to the Gateway host local timezone. Fixes #53669; carries forward #46177. (#73372) Thanks @chen-zhang-cs-code and @maranello-o.
148
+ - Providers/Qwen: allow explicitly configured `qwen/qwen3.6-plus` to resolve on Qwen Coding Plan endpoints while keeping the built-in catalog from advertising it there. Fixes #63654; carries forward #63987. Thanks @jepson-liu.
149
+ - Channels/Telegram: keep Bot API network fallbacks sticky after failed attempts and retry timed-out startup control calls once on the fallback route, so `deleteWebhook` IPv6 stalls no longer trigger slow multi-account retry storms. Fixes #73255. Thanks @ttomiczek and @sktbrd.
150
+ - Gateway/agents: accept heartbeat, cron, and webhook as internal channel hints for agent runs so `sessions_spawn` works from non-delivery parent sessions while unknown channel hints still fail closed. Fixes #73237. Thanks @KeWang0622.
151
+ - Gateway/models: merge explicit `models.providers.*.models` rows into the Gateway model catalog with normalized provider/model dedupe, and use normalized image-capability lookup so custom vision models keep native image attachments even when Pi discovery omits them or model ID casing differs. Fixes #64213 and #65165. Thanks @billonese and @202233a.
152
+ - Gateway/reload: publish canonical post-write source config to in-process reloaders so simple config saves no longer create phantom plugin diffs or trigger unnecessary Gateway restarts. (#73267) Thanks @szsip239.
153
+ - Gateway/Docker: keep config-triggered restarts in-process inside containers instead of spawning a detached child and exiting PID 1 cleanly, so Docker Swarm and other on-failure supervisors do not leave the service stuck at 0/1 replicas. Fixes #73178. Thanks @du-nguyen-IT007.
154
+ - CLI/tasks: ship the task-registry control runtime in npm packages so `openclaw tasks cancel` can load ACP/subagent cancellation helpers from published builds. Fixes #68997. Thanks @1OAKDesign.
155
+ - Channels/Telegram: preserve unsent generated media after partial reply streaming has already delivered the text, so `image_generate` outputs still reach Telegram as photos instead of being dropped from the final payload. Fixes #73253. Thanks @mlaihk.
156
+ - Memory-core/dreaming: cap detached Dream Diary narrative subagents across cron sweeps so multi-workspace dreaming no longer fans out unbounded subagent sessions, lock contention, and cascading narrative timeouts. Fixes #73198. (#73287) Thanks @KeWang0622.
157
+ - CLI/agents: close local one-shot Claude live stdio sessions and bundled MCP loopback resources after embedded `openclaw agent --local` runs, while keeping gateway-owned MCP loopback cleanup internal to the Gateway. Thanks @frankekn.
158
+ - Export/session: keep inline export HTML scripts and vendor libraries injected after template formatting so generated session exports open with the app code, markdown renderer, and syntax highlighter present. Fixes #41862 and #49957; carries forward #41861 and #68947. Thanks @briannewman, @martenzi, and @armanddp.
159
+ - Agents/ACPX: stage the patched Claude ACP adapter as an ACPX runtime dependency and route known Codex/Claude ACP commands through local wrappers, so Gateway runtime no longer depends on live `npx` adapter resolution. Fixes #73202. Thanks @joerod26.
160
+ - Memory/compaction: let pre-compaction memory flush use an exact `agents.defaults.compaction.memoryFlush.model` override such as `ollama/qwen3:8b` without inheriting the active session fallback chain, so local housekeeping can avoid paid conversation models. Fixes #53772. Thanks @limen96.
161
+ - macOS/update: stop managed Gateway services before package replacement and keep LaunchAgent service secrets out of world-readable plist metadata by loading them from owner-only env files. Fixes #72996. Thanks @Mathewb7.
162
+ - Google Meet: keep observe-only Chrome joins and setup checks from requiring BlackHole or audio bridge commands, avoid granting or selecting the microphone in observe-only mode, and make `test_speech` report fresh realtime output-byte verification instead of only confirming a queued utterance. Refs #72478. Thanks @DougButdorf.
163
+ - Gateway/hooks: route non-delivered hook completion and error summaries to the target agent's main session instead of the default agent session, preserving multi-agent hook isolation. Fixes #24693; carries forward #68667. Thanks @abersonFAC and @bluesky6868.
164
+ - Control UI/models: request the configured Gateway model-list view so dashboards with only `models.providers.*.models` show those configured models first instead of flooding the picker with the full built-in catalog. Fixes #65405. Thanks @wbyanclaw.
165
+ - CLI/models: keep default-model and allowlist pickers on explicit `models.providers.*.models` entries when `models.mode` is `replace` instead of loading the full built-in catalog. Fixes #64950. Thanks @mrozentsvayg.
166
+ - Media/security: tighten media-understanding MIME sanitization so parameterized MIME values stay end-anchored and malformed whitespace or suffix payloads are rejected before file-context handling. Fixes #9795; carries forward #68225 with related review/test context from #61016/#68456. Thanks @ymaxgit, @bluesky6868, and @shamsulalam1114.
167
+ - Discord: own the Carbon interaction listener and hand off Discord slash/component handling asynchronously, so compaction or long session locks no longer trip `InteractionEventListener` listener timeouts. Fixes #73204. Thanks @slideshow-dingo.
168
+ - Compaction/diagnostics: keep unknown compaction failure classifications stable while logging sanitized detail for unclassified provider errors such as missing Ollama provider adapters. Thanks @gzsiang.
169
+ - Models/fallbacks: record first-class `model.fallback_step` trajectory events with from/to models, failure detail, chain position, and final outcome so support exports preserve the primary model failure even when a later fallback also fails. Fixes #71744. Thanks @nikolaykazakovvs-ux.
170
+ - Gateway/agents: block agent `exec` from launching interactive `openclaw channels login` flows and abort active agent runs after invalid-config recovery restores last-known-good config, preventing known channel-login and reload paths from wedging replies. Refs #72338. Thanks @midhunmonachan.
171
+ - Gateway/diagnostics: emit payload-free liveness warnings with event-loop delay, event-loop utilization, CPU-core ratio, active-session counts, and OTEL warning metrics/spans so live-but-stalled Gateways capture CPU-spin context in stability bundles and telemetry. Refs #72338. Thanks @midhunmonachan and @DougButdorf.
172
+ - Gateway/startup: keep value-option foreground starts on the gateway fast path and skip proxy bootstrap unless proxy env is configured, reducing normal gateway startup RSS and avoiding full CLI graph loading. Thanks @vincentkoc.
173
+ - Heartbeat/models: show heartbeat model bleed guidance on context-overflow resets when the last runtime model matches configured `heartbeat.model`, so smaller local heartbeat models point users to `isolatedSession` or `lightContext` instead of only compaction-buffer tuning. Fixes #67314. Thanks @Knightmare6890.
174
+ - Subagents/models: persist `sessions_spawn.model` and configured subagent models as child-session model overrides before the first turn, so spawned subagents actually run on the requested provider/model instead of reverting to the target agent default. Fixes #73180. Thanks @danielzinhu99.
175
+ - Channels/Telegram: keep webhook-mode local listeners alive and retry Telegram `setWebhook` registration after recoverable startup network failures, so transient Bot API timeouts no longer leave reverse proxies pointing at a closed listener. Fixes #71834. Thanks @jinon86.
176
+ - Agents/ACPX: bundle the Codex ACP adapter and launch it from the isolated `CODEX_HOME` wrapper before falling back to npm, so Codex ACP startup no longer depends on live `npx` resolution or the stale `@zed-industries/codex-acp@^0.11.1` range. Fixes #72037; refs #73202. Thanks @jasonftl, @sazora, and @joerod26.
177
+ - Agents/ACPX: register the embedded ACP backend at Gateway startup through a lightweight ACP backend SDK path and without importing the heavy ACPX runtime until an ACP session or explicit startup probe needs it, reducing baseline Gateway RSS. Thanks @vincentkoc.
178
+ - CLI/update: keep restart health polling when the restarted Gateway is reachable but has not reported its version yet, so macOS service restarts do not fail early with `actual unavailable`. Thanks @ProspectOre.
179
+ - Backup: skip installed plugin `extensions/*/node_modules` dependency trees while keeping plugin manifests and source files in archives, so local backups avoid rebuildable npm payload bloat. Fixes #64144. Thanks @BrilliantWang.
180
+ - Cron/models: fail isolated cron runs closed when an explicit `payload.model` is not allowed or cannot be resolved, so scheduled jobs do not silently fall back to an unrelated agent default or paid route before configured provider proxies such as LiteLLM can run. Fixes #73146. Thanks @oneandrewwang.
181
+ - Memory/QMD: back off repeated chat-turn QMD open failures while still letting memory status and CLI probes recheck immediately, so a broken sidecar dependency cannot trigger active-memory or cron retry storms. Fixes #73188 and #73176. Thanks @leonlushgit and @w3i-William.
182
+ - Talk Mode: resolve `messages.tts.providers.<id>.apiKey` through the active runtime snapshot for `talk.config`, so Talk overlays can discover SecretRef-backed speech providers without falling back to local speech. Fixes #73109. (#73111) Thanks @omarshahine.
183
+ - Memory/Ollama: resolve `memorySearch.provider` custom provider ids through their configured `models.providers.<id>.api` owner, so multi-GPU Ollama setups can dedicate embeddings to providers such as `ollama-5080` without losing the Ollama adapter or local auth semantics. Fixes #73150. Thanks @oneandrewwang.
184
+ - CLI/memory: skip eager context-window warmup for `openclaw memory` commands so memory search does not race unrelated model metadata discovery. Fixes #73123. Thanks @oalansilva and @neeravmakwana.
185
+ - CLI/Telegram: route Telegram `message send` and poll actions through the running Gateway when available, so packaged installs use the staged `grammy` runtime deps and CLI sends return instead of hanging after the Telegram channel is active. Fixes #73140. Thanks @oalansilva.
186
+ - Plugins/runtime deps: prepare staged bundled plugin dependencies before loading packaged public surfaces, so OpenClaw's Telegram runtime/test facade loads resolve `grammy` from the managed runtime-deps stage without copying dependencies into the global package root. Refs #73140. Thanks @oalansilva.
187
+ - Agents/exec: emit `(no output)` for silent exec update and node-host result blocks so Anthropic-compatible providers no longer reject empty tool-result text after quiet commands. Fixes #73117. Thanks @pfrederiksen and @Sanjays2402.
188
+ - Cron/providers: preflight local Ollama and OpenAI-compatible provider endpoints before isolated cron agent turns, record unreachable local providers as skipped runs, and cache dead-endpoint probes so many jobs do not hammer the same stopped local server. Fixes #58584. Thanks @jpeghead.
189
+ - Gateway/config: let config reload continue in degraded mode when invalidity is scoped to plugin entries, so incompatible plugin configs can be skipped and the Gateway restart can still pick up the rest of the config after rollbacks. Fixes #73131. Thanks @Adam-Researchh.
190
+ - Doctor/channels: suppress disabled bundled-plugin blocker warnings when a trusted external plugin owns the configured channel, so Lark/Feishu installs no longer get Feishu repair noise after switching to `openclaw-lark`. Fixes #56794. Thanks @wuji-tech-dev.
191
+ - CLI/status: show skipped fast-path memory checks as `not checked` and report active custom memory plugin runtime status from `status --json --all` without requiring built-in `agents.defaults.memorySearch`, so plugins such as memory-lancedb-pro and memory-cms no longer look unavailable when their own runtime is healthy. Fixes #56968. Thanks @Tony-ooo and @aderius.
192
+ - Gateway/channels: record and log unexpected clean channel monitor exits so channels that return without throwing no longer appear stopped with no error. Fixes #73099. Thanks @balaji1968-kingler.
193
+ - Group/channel chats (all channels): keep group/channel replies private by default unless the agent explicitly uses the message tool, fall back to automatic visible replies when the message tool is unavailable, and have `openclaw doctor` warn about that policy mismatch; `messages.groupChat.visibleReplies: "automatic"` restores legacy auto-posting. (#73046) Thanks @scoootscooob.
194
+ - Plugins/package: force nested bundled-plugin runtime dependency installs out of inherited npm dry-run mode during prepack and package smoke checks, so packed installs materialize required plugin modules instead of reporting missing bundled files. Refs #73128. Thanks @Adam-Researchh.
195
+ - Discord: skip reaction events before REST channel fetch when notifications are off, guild reactions are disabled, or allowlist mode cannot match without channel overrides, reducing reconnect bursts that caused slow listener warnings. Fixes #73133. Thanks @isaacsummers.
196
+ - Channels/Telegram: centralize polling update tracking so accepted offsets remain durable across restarts, same-process handler failures can still retry, and slow offset writes cannot overwrite newer accepted watermarks. Refs #73115. Thanks @vdruts.
197
+ - Agents/models: classify empty, reasoning-only, and planning-only terminal agent runs before accepting a model fallback candidate, so invalid or incompatible models can advance to the next configured fallback instead of returning a 30-second terminal failure. Fixes #73115. Thanks @vdruts.
198
+ - Memory/LanceDB: let embedding config use provider-backed auth profiles, environment credentials, or provider config without a separate plugin `embedding.apiKey`, so OAuth-capable embedding providers can power auto-recall/capture. Fixes #68950. Thanks @malshaalan-ai.
199
+ - CLI/parents: invoking `openclaw <parent>` (memory, channels, plugins, approvals, devices, cron, mcp) without a subcommand now prints the parent's help and exits `0`, matching `<parent> --help` and the existing `agents` / `sessions` defaults so shell `&&` chains and pnpm wrappers no longer surface a misleading `ELIFECYCLE Command failed with exit code 1.` line. Fixes #73077. Thanks @hclsys.
200
+ - Plugins/hooks: time out never-settling `agent_end` observation hooks after 30 seconds and log the plugin failure, so hung embedding endpoints no longer leave memory capture silently pending forever. Fixes #65544. Thanks @ghoc0099.
201
+ - Gateway/config: serve runtime config schemas from the current plugin metadata snapshot and generated bundled channel schema metadata instead of rebuilding plugin channel config modules on every `config.get`/`config.schema`, preventing idle plugin-discovery CPU churn after upgrades. Fixes #73088. Thanks @sleitor and @geovansb.
202
+ - Memory/LanceDB: call OpenAI-compatible embedding endpoints through the raw SDK transport without sending `encoding_format`, then normalize float-array or base64 responses so providers such as ZhiPu and DashScope no longer fail recall with wrong vector dimensions or rejected parameters. Fixes #63655. Thanks @kinthaiofficial.
203
+ - Plugins/install: run dependency installs with npm error-level logging instead of silent mode so failed plugin or hook installs surface actionable npm errors such as EUNSUPPORTEDPROTOCOL instead of `npm install failed:` with no detail. (#73093) Thanks @sanctrl.
204
+ - Memory/LanceDB: bound memory recall embedding queries with a new `recallMaxChars` setting, prefer the latest user message over channel prompt metadata during auto-recall, and document the knob so small Ollama embedding models avoid context-length failures. Fixes #56780. Thanks @rungmc357 and @zak-collaborator.
205
+ - CLI/skills: resolve workspace-backed skills commands from `--agent`, then the current agent workspace, before falling back to the default agent, so multi-agent ClawHub installs, updates, and status checks stay scoped to the active workspace. Fixes #56161; carries forward #72726. Thanks @langbowang and @luyao618.
206
+ - Plugin SDK: fall back from partial bundled plugin directory overrides to package source public surfaces while preserving `OPENCLAW_DISABLE_BUNDLED_PLUGINS` as a hard disable. (#72817) Thanks @serkonyc.
207
+ - Agents/ACPX: stop forwarding Codex ACP timeout config controls that Codex rejects while preserving OpenClaw's run-timeout watchdog for ACP subagents. Fixes #73052. Thanks @pfrederiksen and @richa65.
208
+ - Memory Core: stream fallback vector search scoring with a bounded top-K result set so large indexes do not materialize every chunk embedding when sqlite-vec is unavailable. (#73069) Thanks @parkertoddbrooks.
209
+ - Memory Core: stream embedding-cache seeding during safe reindex so large local caches do not materialize every row into the V8 heap before the atomic rebuild. (#73067) Thanks @parkertoddbrooks.
210
+ - Memory/Ollama: add `memorySearch.remote.nonBatchConcurrency` for inline embedding indexing, default Ollama non-batch indexing to one request at a time, and keep batch concurrency separate from non-batch concurrency so local embedding backfills avoid timeout storms on smaller hosts. Carries forward #57733. Thanks @itilys.
211
+ - macOS app: update Peekaboo, ElevenLabsKit, and MLX TTS helper dependencies, make canvas file watching and config/exec-approval state writes reliable under concurrent app/test activity, and keep the app plus helper builds warning-free. Thanks @Blaizzy.
212
+ - iOS app: refresh SwiftPM/XcodeGen source hygiene, make app, extension, watch, and curated shared Swift files pass the prebuild SwiftFormat and SwiftLint checks, move relay registration off deprecated StoreKit receipt APIs, and keep simulator builds and logic tests warning-free. Thanks @ngutman.
213
+ - Agents/models: keep `models.json` readiness and provider-hook caches warm across repeated agent and subagent model resolution while preserving external `models.json` invalidation, reducing repeated provider-plugin loads on slower ARM64 hosts. Fixes #73075. Thanks @jochen.
214
+ - Docs/tools: clarify that `tools.profile: "messaging"` is intentionally narrow and that `tools.profile: "full"` is the unrestricted baseline for broader command/control access. Carries forward #39954. Thanks @posigit.
215
+ - Control UI/Agents: redact tool-call args, partial/final results, derived exec output, and configured custom secret patterns before streaming tool events to the Control UI, so tool output cannot expose provider or channel credentials. Fixes #72283. (#72319) Thanks @volcano303 and @BunsDev.
216
+ - Agents/sessions: keep `sessions_history` recall redaction enabled even when general log redaction is disabled, and clarify that safety-boundary UI/tool/diagnostic payloads still redact independently of `logging.redactSensitive`. Carries forward #72319. Thanks @volcano303 and @BunsDev.
217
+ - Providers/Codex: pass agent and workspace directories into provider stream wrappers so Codex native `web_search` activation can evaluate the correct auth context, and smoke-test the built status-message runtime by resolving the emitted bundle name. Carries forward #67843; refs #65909. Thanks @neilofneils404.
218
+ - Cron/models: keep `payload.model` as a per-job primary that can use configured fallbacks, while still letting `payload.fallbacks: []` make cron runs strict and avoid hidden agent-primary retries. Refs #73023. Thanks @pavelyortho-cyber.
219
+ - Models/fallbacks: treat user-selected session models as exact choices, so `/model ollama/...` and model-picker switches fail visibly when the selected provider is unreachable instead of answering from an unrelated configured fallback. Fixes #73023. Thanks @pavelyortho-cyber.
220
+ - Codex harness: keep ChatGPT subscription app-server runs from inheriting `CODEX_API_KEY` or `OPENAI_API_KEY`, and fall back to `CODEX_API_KEY` / `OPENAI_API_KEY` app-server login only when no Codex account is available. Fixes #73057. Thanks @holgergruenhagen and @pashpashpash.
221
+ - CLI/model probes: fail local `infer model run` probes when the provider returns no text output, so unreachable local providers and empty completions no longer look like successful smoke tests. Refs #73023. Thanks @pavelyortho-cyber.
222
+ - CLI/Ollama: run local `infer model run` through the lean provider completion path and skip global model discovery for one-shot local probes, so Ollama smoke tests no longer pay full chat-agent/tool startup cost or hang before the native `/api/chat` request. Fixes #72851. Thanks @TotalRes2020.
223
+ - Doctor/gateway services: ignore launchd/systemd companion services that only reference the gateway as a dependency, suppress inactive Linux extra-service warnings, and avoid rewriting a running systemd gateway command/entrypoint during doctor repair. Carries forward #39118. Thanks @therk.
224
+ - Daemon/service: only emit hard-coded version-manager paths such as `~/.volta/bin`, `~/.asdf/shims`, `~/.bun/bin`, and fnm/pnpm fallbacks into gateway and node service PATHs when the directories exist, so `openclaw doctor` no longer flags `gateway.path.non-minimal` against a PATH the daemon just wrote. Env-driven roots and stable user-bin dirs remain unconditional. Fixes #71944; carries forward #71964. Thanks @Sanjays2402.
225
+ - CLI/startup: disable Node's module compile cache automatically for live source-checkout launchers so in-place `pnpm build` updates are visible to the next `openclaw` CLI invocation. Fixes #73037. Thanks @LouisGameDev.
226
+ - Agents/group chat: keep silent-allowed empty and reasoning-only turns on the `NO_REPLY` path without injecting visible-answer retry prompts, and clarify the group prompt so agents use the exact silent token instead of prose. Thanks @vincentkoc.
227
+ - Agents/group chat: move `NO_REPLY` mechanics into channel-aware direct/group prompts and suppress the duplicate generic silent-reply section for auto-reply runs, so always-on group agents get one consistent stay-silent instruction. Thanks @vincentkoc.
228
+ - Providers/OpenAI: preserve encrypted empty-summary Responses reasoning items in WebSocket replay and request `reasoning.encrypted_content` on reasoning turns so GPT-5.4/GPT-5.5 sessions do not lose required `rs_*` state beside `msg_*` items. Fixes #73053. Thanks @odb36777.
229
+ - Gateway/startup: treat `plugins.enabled=false` as an early plugin fast path, skipping plugin auto-enable discovery, gateway plugin lookup/runtime-dependency staging, and stale-plugin cleanup warnings while preserving channel blocker warnings. (#73041) Thanks @WuKongAI-CMU.
230
+ - Channels/commands: make generated `/dock-*` commands switch the active session reply route through `session.identityLinks` instead of falling through to normal chat. Fixes #69206; carries forward #73033. Thanks @clawbones and @michaelatamuk.
231
+ - Providers/Cloudflare AI Gateway: strip assistant prefill turns from Anthropic Messages payloads when thinking is enabled, so Claude requests through Cloudflare AI Gateway no longer fail Anthropic conversation-ending validation. Fixes #72905; carries forward #73005. Thanks @AaronFaby and @sahilsatralkar.
232
+ - Gateway/startup: keep primary-model startup prewarm on scoped metadata preparation, let native approval bootstraps retry outside channel startup, and skip the global hook runner when no `gateway_start` hook is registered, so clean post-ready sidecar work stays off the critical path. Refs #72846. Thanks @RayWoo, @livekm0309, and @mrz1836.
233
+ - Gateway/channels: start bundled channel accounts with a lightweight `runtimeContexts` surface instead of importing the full reply/routing/session channel runtime before `startAccount`, so Discord, Telegram, Slack, Matrix, and QQBot startup no longer block on unrelated channel helper graphs. Refs #72846 and #72960. Thanks @mrz1836, @RayWoo, and @rollingshmily.
234
+ - Gateway/supervisor: exit cleanly when a supervised restart finds an existing healthy gateway and bound retries when the existing gateway stays unhealthy, so stale lock contention cannot loop indefinitely. Refs #72846. Thanks @azgardtek.
235
+ - Gateway/startup: scope primary-model provider discovery during channel prewarm to the configured provider owner and add split startup trace timings, so boot avoids staging unrelated bundled provider dependencies while setup discovery remains broad. Fixes #73002. Thanks @Schnup03.
236
+ - Plugins/runtime deps: declare retained staged bundled plugin dependencies in the npm staging manifest while installing only newly missing packages, so Gateway restarts avoid reinstalling the full retained dependency set when one runtime dependency is absent. Fixes #73055. Thanks @GCorp2026.
237
+ - CLI/status: keep default `openclaw status` off the heavyweight security audit, plugin compatibility, and memory-vector probes while still showing configured Telegram channels through setup metadata, so routine health checks stay fast and no longer render an empty Channels table. Fixes #72993. Thanks @comick1.
238
+ - Channels/Telegram: send a best-effort native typing cue immediately after an inbound message is accepted, so slow pre-dispatch turns show Telegram liveness before queueing, compaction, model, or tool work starts. Fixes #63759. Thanks @alessandropcostabr.
239
+ - Channels/Telegram: stop native approval startup auth failures from retrying every second, while still waiting through retryable Gateway auth handoffs, so Telegram approval setup problems no longer create a reconnect/log loop during channel startup. Refs #72846 and #72867. Thanks @kiranvk-2011 and @porly1985.
240
+ - Channels/Microsoft Teams: unwrap staged CommonJS JWT runtime dependencies before Bot Connector token validation so inbound Teams messages no longer 401 after the bundled runtime-deps move. Fixes #73026 and #73167. Thanks @kbrown10000 and @mikelavrik.
241
+ - Gateway/auth: allow local direct callers in trusted-proxy mode to use the configured gateway password as an internal fallback while keeping token fallback rejected. Fixes #17761. Thanks @dashed, @vincentkoc, and @jetd1.
242
+ - Gateway/auth: add explicit `trustedProxy.allowLoopback` support for same-host loopback reverse proxies while keeping loopback trusted-proxy auth fail-closed by default and preserving required-header and allowlist checks. Fixes #59167; carries forward #63379. Thanks @Matir, @jeremyakers, and @mrosmarin.
243
+ - Channels/sessions: prevent guarded inbound session recording from creating route-only phantom sessions while still allowing last-route updates for sessions that already exist. Carries forward #73009. Thanks @jzakirov.
244
+ - Cron: accept `delivery.threadId` in Gateway cron add/update schemas so scheduled announce delivery can target Telegram forum topics and other threaded channel destinations through the documented delivery path. Fixes #73017. Thanks @coachsootz.
245
+ - Plugins/runtime deps: stage bundled plugin dependencies imported by mirrored root dist chunks, so packaged memory and status commands do not miss `chokidar` or similar root-chunk dependencies after update. Fixes #72882 and #72970; carries forward #72992. Thanks @shrimpy8, @colin-chang, and @Schnup03.
246
+ - Plugins/runtime deps: reuse unchanged bundled plugin runtime mirrors instead of rebuilding plugin trees on every load, cutting avoidable writes and restart/reconnect I/O on slow storage. Fixes #72933. Thanks @jasonftl.
247
+ - Agents/runtime context: deliver hidden runtime context through prompt-local system context while keeping the transcript-only custom entry out of provider user turns, and strip stale copied runtime-context prefaces from user-facing replies. Fixes #72386; carries forward #72969. Thanks @jhsmith409.
248
+ - Channels/Telegram: skip the optional webhook-info API call during polling-mode status checks and startup bot-label probes so long-polling setups avoid an unnecessary Telegram round trip. Carries forward #72990. Thanks @danielgruneberg.
249
+ - CLI/message: resolve targeted `openclaw message` channels to their owning plugin before loading the registry, and fall back to configured channel plugins when the channel must be inferred, so scripted sends avoid full bundled plugin registry scans without assuming channel ids match plugin ids. Fixes #73006. Thanks @jasonftl.
250
+ - Plugins/startup: parse strict JSON plugin manifests with native JSON first and keep JSON5 as the compatibility fallback, reducing manifest registry CPU during Gateway boot and CLI startup. Fixes #73011. Thanks @jasonftl.
251
+ - CLI/models: keep route-first `models status --json` stdout reserved for the JSON payload by routing auth-profile and startup diagnostics to stderr. Fixes #72962. Thanks @vishutdhar.
252
+ - Gateway/runtime: keep dirty-tree status calls from rebuilding live `dist`, clear stale task and restart state across in-process restarts, retry transient Discord lazy imports, and let channel startup continue after slow model warmup so browser, Discord, and voice-call sidecars come online. Thanks @vincentkoc.
253
+ - Security/CodeQL: replace file SecretRef id gateway schema regex validation with segment-aligned predicates and set empty permissions on release summary/backfill jobs so the narrowed CodeQL profile stays clean. Thanks @vincentkoc.
254
+ - Sessions: ignore future-dated session activity timestamps during reset freshness checks and cap future `updatedAt` values at the merge boundary so clock-skewed messages cannot keep stale sessions alive forever. Fixes #72989. Thanks @martingarramon.
255
+ - Sessions: apply search, activity filters, and limits before gateway row enrichment so bounded session lists avoid scanning discarded transcripts. Carries forward #72978. Thanks @yeager.
256
+ - Sessions: remove trajectory runtime and pointer sidecars when session maintenance prunes, caps, or disk-evicts their owning session, while preserving sidecars still referenced by live rows. Fixes #73000. Thanks @jared-rebel.
257
+ - Plugins/CLI: allow managed plugin installs when the active extensions root is a symlink to a real state directory, while keeping nested target symlinks blocked and suppressing misleading hook-pack fallback errors for install-boundary failures. Fixes #72946. Thanks @mayank6136.
258
+ - Providers/Ollama: mark discovered Ollama catalog models as supporting streaming usage metadata so token accounting stays enabled for local models. (#72976) Thanks @sdeyang.
259
+ - Media understanding: reject malformed MIME values with trailing junk while preserving standard parameter tails before enrichment uses them. (#72914) Thanks @volcano303.
260
+ - WebChat: keep bare `/new` and `/reset` prompts from producing empty transcript text by inserting the hidden session marker when the visible tail is blank. (#72863) Thanks @mahopan.
261
+ - CLI/update: explain completion-cache refresh timeouts with manual refresh guidance instead of surfacing a raw low-level timeout. Fixes #72842. (#72850) Thanks @iot2edge.
262
+ - Memory-core/dreaming: give narrative generation a 60-second timeout so slower local or remote models can finish instead of timing out at 15 seconds. Fixes #72837. (#72852) Thanks @RayWoo.
263
+ - Plugins/hooks: inject each plugin's resolved config into internal hook event context without mutating the shared event object. (#72888) Thanks @jalapeno777.
264
+ - Agents/ACP: pass the resolved ACP agent directory into media understanding so per-agent media caches and config are used for ACP-dispatched image turns. (#72832) Thanks @luyao618.
265
+ - Gateway/Bonjour: truncate mDNS service names and host labels to the 63-byte DNS label limit at valid UTF-8 boundaries. (#72809) Thanks @luyao618.
266
+ - Feishu: treat groups explicitly configured under channels.feishu.groups as admitted even when groupAllowFrom is empty, while preserving groupPolicy: "disabled" as a hard group block and keeping groups.\* wildcard defaults non-admitting. Fixes #67687. (#72789) Thanks @MoerAI.
267
+ - Gateway/startup: keep hot Gateway boot paths on leaf config imports and add max-RSS reporting to the gateway startup bench so low-memory startup regressions are visible before release. Thanks @vincentkoc.
268
+ - WebChat: read `chat.history` from active transcript branches, drop stale streamed assistant tails once final history catches up, and coalesce duplicate in-flight Control UI submits, so rewritten prompts, completed replies, and rapid send events no longer render or process twice. Fixes #72975, #72963, and #72974. Thanks @dmagdici, @lhtpluto, and @Benjamin5281999.
269
+ - WebChat/TTS: persist automatic final-mode TTS audio as a supplemental audio-only transcript update instead of adding a second assistant message with the same visible text. Fixes #72830. Thanks @lhtpluto.
270
+ - Agents/LSP: terminate bundled stdio LSP process trees during runtime disposal and Gateway shutdown, so nested children such as `tsserver` do not survive stop or restart. Fixes #72357. Thanks @ai-hpc and @bittoby.
271
+ - Diagnostics/OTEL: capture privacy-safe model-call request payload bytes, streamed response bytes, first-response latency, and total duration in diagnostic events, plugin hooks, stability snapshots, and OTEL model-call spans/metrics without logging raw model content. Fixes #33832. Thanks @wwh830.
272
+ - Logging: write validated diagnostic trace context as top-level `traceId`, `spanId`, `parentSpanId`, and `traceFlags` fields in file-log JSONL records so traced requests and model calls are easier to correlate in log processors. Refs #40353. Thanks @liangruochong44-ui.
273
+ - Nextcloud-Talk: wire the existing reaction sender into the channel `actions` adapter so agents can react to messages via the shared `message` tool, instead of advertising the `reactions` capability without a dispatch path. Fixes #70110. Thanks @powerpaul17.
274
+ - Logging/sessions: apply configured redaction patterns to persisted session transcript text and accept escaped character classes in safe custom redaction regexes, so transcript JSONL no longer keeps matching sensitive text in the clear. Fixes #42982. Thanks @panpan0000.
275
+ - Providers/Ollama: honor `/api/show` capabilities when registering local models so non-tool Ollama models no longer receive the agent tool surface, and keep native Ollama thinking opt-in instead of enabling it by default. Fixes #64710 and duplicate #65343. Thanks @yuan-b, @netherby, @xilopaint, and @Diyforfun2026.
276
+ - Control UI/Agents: remount the Overview model controls when switching agents so the primary-model picker cannot retain stale per-agent selection. Fixes #39392; carries forward #39401, notes the duplicate #39495 approach, and keeps #46275/#54724 broader stabilization out of scope. Thanks @daijunyi002, @SergioChan, @aworki, and @wsyjh8.
277
+ - Auto-reply: poison inbound message dedupe after replay-unsafe provider/runtime failures so retries stay safe before visible progress but cannot duplicate messages after block output, tool side effects, or session progress. Fixes #69303; keeps #58549 and #64606 as duplicate validation. Thanks @martingarramon, @NikolaFC, and @zeroth-blip.
278
+ - Agents/model fallback: jump directly to a known later live-session model redirect instead of walking unrelated fallback candidates, while preserving the already-landed live-session/fallback loop guard. Fixes #57471; related loop family already closed via #58496. Thanks @yuxiaoyang2007-prog.
279
+ - Gateway/Bonjour: keep @homebridge/ciao cancellation handlers registered across advertiser restarts so late probing cancellations cannot crash Linux and other mDNS-churned gateways. Thanks @vincentkoc.
280
+ - Plugins/startup: load the default `memory-core` slot during Gateway startup when permitted so active-memory recall can call `memory_search` and `memory_get` without requiring an explicit `plugins.slots.memory` entry, while preserving `plugins.slots.memory: "none"`. Thanks @vincentkoc.
281
+ - Gateway/plugins: resolve `gateway_start` cron hooks from live Gateway runtime state before the legacy deps fallback, so memory-core dreaming cron reconciliation keeps working on installs where `deps.cron` is not populated during service startup. Fixes #72835. Thanks @RayWoo.
282
+ - Plugins/CLI: prefer native require for compiled bundled plugin JavaScript before jiti so read-only config, status, device, and node commands avoid unnecessary transform overhead on slow hosts. Fixes #62842. Thanks @Effet.
283
+ - Plugins/compat: inventory doctor-side deprecation migrations separately from runtime plugin compatibility so release sweeps preserve needed repairs while enforcing dated removal windows. Thanks @vincentkoc.
284
+ - Plugins/compat: add missing dated compatibility records for legacy extension-api, memory registration, provider hook/type aliases, runtime aliases, channel SDK helpers, and approval/test utility shims. Thanks @vincentkoc.
285
+ - Plugins/CLI: refresh the persisted registry after managed plugin files are removed so ClawHub uninstall cannot leave stale `plugins list` entries. Thanks @vincentkoc.
286
+ - Plugins/CLI: make plugin install and uninstall config writes conflict-aware, clear stale denylist entries on explicit reinstall/removal, and delete managed plugin files only after config/index commit succeeds. Thanks @vincentkoc.
287
+ - Plugins: fail `plugins update` when tracked plugin or hook updates error, keep bundled runtime-dependency repair behind restrictive allowlists, and reject package installs with unloadable extension entries. Thanks @vincentkoc.
288
+ - WebChat/Control UI: support non-video file attachments in chat uploads while preserving the existing image attachment path and MIME-sniff fallback for generic image uploads. (#70947) Thanks @IAMSamuelRodda.
289
+ - Skills/memory: restore Chokidar v5 hot reloads by watching concrete skill and memory roots with filters, including SKILL.md removals and deleted skill folders without broad workspace recursion. Fixes #27404, #33585, and #41606. Thanks @shelvenzhou, @08820048, and @rocke2020.
290
+ - Gateway/chat: keep duplicate attachment-backed `chat.send` retries with the same idempotency key on the documented in-flight path so aborts still target the real active run. Fixes #70139. Thanks @Feelw00.
291
+ - Gateway/chat: preserve repeated boundary characters while merging assistant chat stream deltas, including repeated digits, CJK characters, and markdown/table tokens. Fixes #63769; carries forward #63994 and #65457. Thanks @yon950905 and @mohuaxiao.
292
+ - Plugins: share package entrypoint resolution between install and discovery, reject mismatched `runtimeExtensions`, and cache bundled runtime-dependency manifest reads during scans. Thanks @vincentkoc.
293
+ - WhatsApp/Web: keep quiet but healthy linked-device sessions connected by basing the watchdog on WhatsApp Web transport activity, while retaining a longer app-silence cap so frame activity cannot mask a stuck session forever. Fixes #70678; carries forward the focused #71466 approach and keeps #63939 as related configurable-timeout follow-up. Thanks @vincentkoc and @oromeis.
294
+ - Discord/gateway: count failed health-monitor restart attempts toward cooldown and hourly caps, and evict stale account lifecycle state during channel reloads so repeated Discord gateway recovery cannot loop on old status. Fixes #38596. (#40413) Thanks @jellyAI-dev and @vashquez.
295
+ - TTS/BlueBubbles: pre-transcode synthesized MP3 audio to opus-in-CAF (mono, 24 kHz — validated against macOS 15.x Messages.app's native voice-memo CAF descriptor) on macOS hosts before handing the file to BlueBubbles, so iMessage renders the result as a native voice-memo bubble with proper duration and waveform UI instead of a plain file attachment. Adds an opt-in `tts.voice.preferAudioFileFormat` channel capability and a magic-byte sniff for the CAF container so the host-local-media validator (which uses `file-type` and didn't recognize CAF natively) can verify the pre-transcoded buffer. Channels that don't opt in are unaffected. (#72586) Fixes #72506. Thanks @omarshahine.
296
+ - Feishu: retry WebSocket startup failures with monitor-owned backoff while preserving SDK-local heartbeat defaults, so persistent-connection startup failures no longer leave the monitor hung. Fixes #68766; related #42354 and #55532. Thanks @alex-xuweilong, @120106835, @sirfengyu, and @tianhaocui.
297
+ - Cron: normalize isolated job tool allowlists before granting the narrow self-removal cron tool path, keeping scheduled jobs aligned with shared tool policy normalization. (#73028) Thanks @jalehman.
298
+
CHANGELOG/2026.4.5.md ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.4.5
2
+
3
+ ### Breaking
4
+
5
+ - Config: remove legacy public config aliases such as `talk.voiceId` / `talk.apiKey`, `agents.*.sandbox.perSession`, `browser.ssrfPolicy.allowPrivateNetwork`, `hooks.internal.handlers`, and channel/group/room `allow` toggles in favor of the canonical public paths and `enabled`, while keeping load-time compatibility and `openclaw doctor --fix` migration support for existing configs. (#60726) Thanks @vincentkoc.
6
+
7
+ ### Changes
8
+
9
+ - Agents/video generation: add the built-in `video_generate` tool so agents can create videos through configured providers and return the generated media directly in the reply.
10
+ - Agents/music generation: ignore unsupported optional hints such as `durationSeconds` with a warning instead of hard-failing requests on providers like Google Lyria.
11
+ - Providers/Arcee AI: add a bundled Arcee AI provider plugin with `ARCEEAI_API_KEY` onboarding, Trinity model catalog (mini, large-preview, large-thinking), OpenAI-compatible API support, and OpenRouter as an alternative auth path. (#62068) Thanks @arthurbr11.
12
+ - Providers/ComfyUI: add a bundled `comfy` workflow media plugin for local ComfyUI and Comfy Cloud workflows, including shared `image_generate`, `video_generate`, and workflow-backed `music_generate` support, with prompt injection, optional reference-image upload, live tests, and output download.
13
+ - Tools/music generation: add the built-in `music_generate` tool with bundled Google (Lyria) and MiniMax providers plus workflow-backed Comfy support, including async task tracking and follow-up delivery of finished audio.
14
+ - Providers: add bundled Qwen, Fireworks AI, and StepFun providers, plus MiniMax TTS, Ollama Web Search, and MiniMax Search integrations for chat, speech, and search workflows. (#60032, #55921, #59318, #54648).
15
+ - Providers/Amazon Bedrock: add bundled Mantle support plus inference-profile discovery and automatic request-region injection so Bedrock-hosted Claude, GPT-OSS, Qwen, Kimi, GLM, and similar routes work with less manual setup. (#61296, #61299) Thanks @wirjo.
16
+ - Control UI/multilingual: add localized control UI support for Simplified Chinese, Traditional Chinese, Brazilian Portuguese, German, Spanish, Japanese, Korean, French, Turkish, Indonesian, Polish, and Ukrainian. Thanks @vincentkoc.
17
+ - Plugins: add plugin-config TUI prompts to guided onboarding/setup flows, and add `openclaw plugins install --force` so existing plugin and hook-pack targets can be replaced without using the dangerous-code override flag. (#60590, #60544).
18
+ - Control UI/skills: add ClawHub search, detail, and install flows directly in the Skills panel. (#60134) Thanks @samzong.
19
+ - iOS/exec approvals: add generic APNs approval notifications that open an in-app exec approval modal, fetch command details only after authenticated operator reconnect, and clear stale notification state when the approval resolves. (#60239) Thanks @ngutman.
20
+ - Matrix/exec approvals: add Matrix-native exec approval prompts with account-scoped approvers, channel-or-DM delivery, and room-thread aware resolution handling. (#58635) Thanks @gumadeiras.
21
+ - Channels/context visibility: add configurable `contextVisibility` per channel (`all`, `allowlist`, `allowlist_quote`) so supplemental quote, thread, and fetched history context can be filtered by sender allowlists instead of always passing through as received.
22
+ - Providers/request overrides: add shared model and media request transport overrides across OpenAI-, Anthropic-, Google-, and compatible provider paths, including headers, auth, proxy, and TLS controls. (#60200) Thanks @vincentkoc.
23
+ - Providers/OpenAI: add forward-compat `openai-codex/gpt-5.4-mini`, an opt-in GPT personality, and provider-owned GPT-5 prompt contributions so Codex/GPT runs stay cache-stable and compatible with bundled catalog lag.
24
+ - Agents/Claude CLI: expose OpenClaw tools to background Claude CLI runs through a loopback MCP bridge and switch bundled runs to stdin + `stream-json` partial-message streaming so prompts stop riding argv, long replies show live progress, and final session/usage metadata still land cleanly. (#35676) Thanks @mylukin.
25
+ - ACPX/runtime: embed the ACP runtime directly in the bundled `acpx` plugin, remove the extra external ACP CLI hop, harden live ACP session binding and reuse, and add a generic `reply_dispatch` hook so bundled plugins like ACPX can own reply interception without hardcoded ACP paths in core auto-reply routing. (#61319).
26
+ - Agents/progress: add experimental structured plan updates and structured execution item events so compatible UIs can show clearer step-by-step progress during long-running runs.
27
+ - Providers/Anthropic: remove the Claude CLI backend and setup-token from new onboarding, keep existing configured legacy profiles runnable, and have `openclaw doctor` repair or remove stale `anthropic:claude-cli` state during migration.
28
+ - Tools/video generation: add bundled xAI (`grok-imagine-video`), Alibaba Model Studio Wan, and Runway video providers, plus live-test/default model wiring for all three.
29
+ - Memory/search: add Amazon Bedrock embeddings for Titan, Cohere, Nova, and TwelveLabs models, with AWS credential-chain auto-detection for `provider: "auto"` and provider-specific dimension controls. Thanks @wirjo.
30
+ - Providers/Amazon Bedrock Mantle: generate bearer tokens from the AWS credential chain so Mantle auto-discovery can use IAM auth without manually exporting `AWS_BEARER_TOKEN_BEDROCK`. Thanks @wirjo.
31
+ - Memory/dreaming (experimental): add weighted short-term recall promotion, a `/dreaming` command, Dreams UI, multilingual conceptual tagging, and doctor/status repair support, while refactoring dreaming from competing modes into three cooperative phases (light, deep, REM) with independent schedules and recovery behavior so durable memory promotion can run in the background with less manual setup. (#60569, #60697) Thanks @vignesh07.
32
+ - Memory/dreaming: add configurable aging controls (`recencyHalfLifeDays`, `maxAgeDays`) plus optional verbose logging so operators can tune recall decay and inspect promotion decisions more easily.
33
+ - Memory/dreaming: add REM preview tooling (`openclaw memory rem-harness`, `promote-explain`), surface possible lasting truths during REM staging, and make deep promotion replay-safe so reruns reconcile instead of duplicating `MEMORY.md` entries.
34
+ - Memory/dreaming: write dreaming trail content to top-level `dreams.md` instead of daily memory notes, update `/dreaming` help text to point there, and keep `dreams.md` available for explicit reads without pulling it into default recall. Thanks @davemorin.
35
+ - Memory/dreaming: add the Dream Diary surface in Dreams, simplify user-facing dreaming config to `enabled` plus optional `frequency`, treat phases as implementation detail in docs/UI, and keep the lobster animation visible above diary content. Thanks @vignesh07.
36
+ - Prompt caching: keep prompt prefixes more reusable across transport fallback, deterministic MCP tool ordering, compaction, embedded image history, normalized system-prompt fingerprints, `openclaw status --verbose` cache diagnostics, and the removal of duplicate in-band tool inventories from agent system prompts so follow-up turns hit cache more reliably. (#58036, #58037, #58038, #59054, #60603, #60691) Thanks @bcherny and @vincentkoc.
37
+ - Agents/cache: diagnostics: add prompt-cache break diagnostics, trace live cache scenarios through embedded runner paths, and show cache reuse explicitly in `openclaw status --verbose`. Thanks @vincentkoc.
38
+ - Agents/cache: stabilize cache-relevant system prompt fingerprints by normalizing equivalent structured prompt whitespace, line endings, hook-added system context, and runtime capability ordering so semantically unchanged prompts reuse KV/cache more reliably. Thanks @vincentkoc.
39
+ - Agents/tool prompts: remove the duplicate in-band tool inventory from agent system prompts so tool-calling models rely on the structured tool definitions as the single source of truth, improving prompt stability and reducing stale tool guidance.
40
+ - Config/schema: enrich the exported `openclaw config schema` JSON Schema with field titles and descriptions so editors, agents, and other schema consumers receive the same config help metadata. (#60067) Thanks @solavrc.
41
+ - Matrix/exec approvals: clarify unavailable-approval replies so Matrix no longer claims chat approvals are unsupported when native exec approvals are merely unconfigured. (#61424) Thanks @gumadeiras.
42
+ - Providers/OpenAI Codex: add forward-compat `openai-codex/gpt-5.4-mini` synthesis across provider runtime, model catalog, and model listing so Codex mini works before bundled Pi catalog updates land.
43
+ - Providers/OpenAI: add an opt-in GPT personality and move GPT-5 prompt tuning onto provider-owned system-prompt contributions so cache-stable guidance stays above the prompt cache boundary and embedded runner paths reuse the same provider-specific prompt behavior.
44
+ - Docs/IRC: replace public IRC hostname examples with `irc.example.com` and recommend private servers for bot coordination while listing common public networks for intentional use.
45
+ - Memory/dreaming: group nearby daily-note lines into short coherent chunks before staging them for dreaming, so one-off context from recent notes reaches REM/deep with better evidence and less line-level noise (#61583). Thanks @mbelinky.
46
+ - Memory/dreaming: drop generic date/day headings from daily-note chunk prefixes while keeping meaningful section labels, so staged snippets stay cleaner and more reusable. (#61597) Thanks @mbelinky.
47
+ - Plugins/Lobster: run bundled Lobster workflows in process instead of spawning the external CLI, reducing transport overhead and unblocking native runtime integration. (#61523) Thanks @mbelinky.
48
+ - Plugins/Lobster: harden managed resume validation so invalid TaskFlow resume calls fail earlier, and memoize embedded runtime loading per runner while keeping failed loads retryable. (#61566) Thanks @mbelinky.
49
+ - Agents/bootstrap: add opt-in `agents.defaults.contextInjection: "continuation-skip"` so safe continuation turns can skip workspace bootstrap re-injection, while heartbeat runs and post-compaction retries still rebuild context when needed. Fixes #9157. Thanks @cgdusek.
50
+
51
+ ### Fixes
52
+
53
+ - Control UI/chat: show `/tts` and other local audio-only slash replies in webchat by embedding local audio in the assistant message and rendering `<audio>` controls instead of dropping empty-text finals. Fixes #61564. (#61598) Thanks @neeravmakwana.
54
+ - Security: preserve restrictive plugin-only tool allowlists, require owner access for `/allowlist add` and `/allowlist remove`, fail closed when `before_tool_call` hooks crash, block browser SSRF redirect bypasses earlier, and keep non-interactive auth-choice inference scoped to bundled and already-trusted plugins. (#58476, #59836, #59822, #58771, #59120) Thanks @eleqtrizit and @pgondhi987.
55
+ - Providers/OpenAI: make GPT-5 and Codex runs act sooner with lower-verbosity defaults, visible progress during tool work, and a one-shot retry when a turn only narrates the plan instead of taking action.
56
+ - Providers/OpenAI and reply delivery: preserve native `reasoning.effort: "none"` and strict schemas where supported, add GPT-5.4 assistant `phase` metadata across replay and the Gateway `/v1/responses` layer, and keep commentary buffered until `final_answer` so web chat, session previews, embedded replies, and Telegram partials stop leaking planning text. Fixes #59150, #59643, #61282.
57
+ - Telegram: fix current-model checks in the model picker, HTML-format non-default `/model` confirmations, explicit topic replies, persisted reaction ownership across restarts, caption-media placeholder and `file_id` preservation on download failure, and upgraded-install inbound image reads. (#60384, #60042, #59634, #59207, #59948, #59971) Thanks @sfuminya, @GitZhangChi, @dashhuang, @samzong, @v1p0r, and @neeravmakwana.
58
+ - Telegram: restore DM voice-note preflight transcription so direct-message audio stops arriving as raw `<media:audio>` placeholders. (#61008) Thanks @manueltarouca.
59
+ - Telegram/reasoning: only create a Telegram reasoning preview lane when the session is explicitly `reasoning:stream`, so hidden `<think>` traces from streamed replies stop surfacing as chat previews on normal sessions. Thanks @vincentkoc.
60
+ - Telegram/native command menu: trim long menu descriptions before dropping commands so sub-100 command sets can still fit Telegram's payload budget and keep more `/` entries visible. (#61129) Thanks @neeravmakwana.
61
+ - Telegram/startup: bound `deleteWebhook`, `getMe`, and `setWebhook` startup requests while keeping the longer `getUpdates` poll timeout, so wedged Telegram control-plane calls stop hanging startup indefinitely. (#61601) Thanks @neeravmakwana.
62
+ - Agents/failover: classify Anthropic "extra usage" exhaustion as billing so same-turn model fallback still triggers when Claude blocks long-context requests on usage limits. (#61608) Thanks @neeravmakwana.
63
+ - Discord: keep REST, webhook, and monitor traffic on the configured proxy, preserve component-only media sends, honor `@everyone` and `@here` mention gates, keep ACK reactions on the active account, and split voice connect/playback timeouts so auto-join is more reliable. (#57465, #60361, #60345) Thanks @geekhuashan.
64
+ - Discord/reply tags: strip leaked `[[reply_to_current]]` control tags from preview text and honor explicit reply-tag threading during final delivery, so Discord replies stay attached to the triggering message instead of printing reply metadata into chat.
65
+ - Discord/replies: replace the unshipped `replyToOnlyWhenBatched` flag with `replyToMode: "batched"` so native reply references only attach on debounced multi-message turns while explicit reply tags still work.
66
+ - Discord/image generation: include the real generated `MEDIA:` paths in tool output, avoid duplicate plain-output media requeueing, and persist volatile workspace-generated media into durable outbound media before final reply delivery so generated image replies stop pointing at missing local files.
67
+ - Slack: route live DM replies back to the concrete inbound DM channel while keeping persisted routing metadata user-scoped, so normal assistant replies stop disappearing when pairing and system messages still arrive. (#59030) Thanks @afurm.
68
+ - WhatsApp: restore `channels.whatsapp.blockStreaming` and reset watchdog timeouts after reconnect so quiet chats stop falling into reconnect loops. (#60007, #60069) Thanks @MonkeyLeeT and @mcaxtr.
69
+ - Android/Talk Mode: cancel in-flight `talk.speak` playback when speech is explicitly stopped, and restore spoken replies on both node-scoped and gateway-backed sessions by keeping reply routing and embedded transport overrides aligned with the current playback path. (#60306, #61164, #61214).
70
+ - Voice-call/OpenAI: pass full plugin config into realtime transcription provider resolution so streaming calls can discover the bundled OpenAI realtime transcription provider again. Fixes #60936. Thanks @sliekens and @vincentkoc.
71
+ - Matrix/exec approvals: anchor seeded approval reactions to the primary Matrix prompt event, resolve them from event metadata instead of prompt text, and clean up chunked approval prompts correctly. (#60931) Thanks @gumadeiras.
72
+ - Matrix: recover more reliably when secret storage or recovery keys are missing by recreating secret storage during repair and backup reset, hold crypto snapshot locks during persistence, and surface explicit too-large attachment markers. (#59846, #59851, #60599, #60289) Thanks @al3mart, @emonty, and @efe-arv.
73
+ - Matrix/DM sessions: add `channels.matrix.dm.sessionScope`, shared-session collision notices, and aligned outbound session reuse so separate Matrix DM rooms can keep distinct context when configured. (#61373) Thanks @gumadeiras.
74
+ - Matrix: move legacy top-level `avatarUrl` into the default account during multi-account promotion and keep env-backed account setup avatar config persisted. (#61437) Thanks @gumadeiras.
75
+ - MS Teams: download inline DM images via Graph API and preserve channel reply threading in proactive fallback. (#52212, #55198) Thanks @Ted-developer and @hyojin.
76
+ - MS Teams: replace the deprecated Teams SDK HttpPlugin stub with `httpServerAdapter` so recurring gateway deprecation warnings stop firing and the Express 5 compatibility workaround stays on the supported SDK path. (#60939) Thanks @coolramukaka-sys.
77
+ - Control UI/chat: add a per-session thinking-level picker in the chat header and mobile chat settings, and keep the browser bundle on UI-local thinking/session-key helpers so Safari no longer crashes on Node-only imports before rendering chat controls.
78
+ - Sandbox/SSH: reject hardlinked files during cross-device rename fallback so EXDEV file copies preserve the same pinned file-boundary checks as direct reads.
79
+ - Control UI: keep Stop visible during tool-only execution, preserve pending-send busy state, and clear stale ClawHub search results as soon as the query changes. (#54528, #59800, #60267) Thanks @chziyue and @frankekn.
80
+ - Control UI/avatar: honor `ui.assistant.avatar` when serving `/avatar/:agentId` so Appearance UI avatar paths stop falling back to initials placeholders. (#60778) Thanks @hannasdev.
81
+ - Control UI/cron: highlight the Cron refresh button while refresh is in flight so the page's loading state stays visible even when prior data remains on screen. (#60394) Thanks @coder-zhuzm.
82
+ - Control UI/Overview: prevent gateway access token/password visibility toggle buttons from overlapping their inputs at narrow widths. (#56924) Thanks @bbddbb1.
83
+ - Auto-reply: unify reply lifecycle ownership across preflight compaction, session rotation, CLI-backed runs, and gateway restart handling so `/stop` and same-session overlap checks target the right active turn and restart-interrupted turns return the restart notice instead of being silently dropped. (#61267) Thanks @dutifulbob.
84
+ - Reply delivery: prevent duplicate block replies on `text_end` channels so providers that emit explicit text-end boundaries no longer double-send the same final message. (#61530).
85
+ - Gateway/startup: default `gateway.mode` to `local` when unset, detect PID recycling in gateway lock files on Windows and macOS, and show startup progress so healthy restarts stop getting blocked by stale locks. (#54801, #60085, #59843) Thanks @BradGroux and @TonyDerek-dot.
86
+ - Gateway/macOS: let launchd `KeepAlive` own in-process gateway restarts again, adding a short supervised-exit delay so rapid restarts avoid launchd crash-loop unloads while `openclaw gateway restart` still reports real LaunchAgent errors synchronously.
87
+ - Gateway/macOS: re-bootstrap the LaunchAgent if `launchctl kickstart -k` unloads it during restart so failed restarts do not leave the gateway unmanaged until manual repair.
88
+ - Gateway/macOS: recover installed-but-unloaded LaunchAgents during `openclaw gateway start` and `restart`, while still preferring live unmanaged gateways during restart recovery. (#43766) Thanks @HenryC-3.
89
+ - Gateway/Windows scheduled tasks: preserve Task Scheduler settings on reinstall, fail loudly when `/Run` does not start, and report fast failed restarts accurately instead of pretending they timed out after 60 seconds. (#59335) Thanks @tmimmanuel.
90
+ - Windows/restart: fall back to the installed Startup-entry launcher when the scheduled task was never registered, so `/restart` can relaunch the gateway on Windows setups where `schtasks` install fell back during onboarding. (#58943) Thanks @imechZhangLY.
91
+ - Windows/restart: clean up stale gateway listeners before Windows self-restart and treat listener and argv probe failures as inconclusive, so scheduled-task relaunch no longer falls into an `EADDRINUSE` retry loop. (#60480) Thanks @arifahmedjoy.
92
+ - Update/npm: prefer the npm binary that owns the installed global OpenClaw prefix so mixed Homebrew-plus-nvm setups update the right install. (#60153) Thanks @jayeshp19.
93
+ - Agents/music and video generation: add `tools.media.asyncCompletion.directSend` as an opt-in direct-delivery path for finished async media tasks, while keeping the legacy requester-session wake/model-delivery flow as the default.
94
+ - CLI/skills JSON: route `skills list --json`, `skills info --json`, and `skills check --json` output to stdout instead of stderr so machine-readable consumers receive JSON on the expected stream again. (#60914; fixes #57599; landed from contributor PR #57611 by @Aftabbs) Thanks @Aftabbs.
95
+ - CLI/Commander: preserve Commander-computed exit codes for argument and help-error paths, and cover the user-argv parse mode in the regression tests so invalid CLI invocations no longer report success when exits are intercepted. (#60923) Thanks @Linux2010.
96
+ - Cron: replay interrupted recurring jobs on the first gateway restart instead of waiting for a second restart. (#60583) Thanks @joelnishanth.
97
+ - Cron: send failure notifications through the job's primary delivery channel using the same session context as successful delivery when no explicit `failureDestination` is configured. (#60622) Thanks @artwalker.
98
+ - Exec/remote skills: stop advertising `exec host=node` when the current exec policy cannot route to a node, and clarify blocked exec-host override errors with both the requested host and allowed config path.
99
+ - Agents/Claude CLI/security: clear inherited Claude Code config-root and plugin-root env overrides like `CLAUDE_CONFIG_DIR` and `CLAUDE_CODE_PLUGIN_*`, so OpenClaw-launched Claude CLI runs cannot be silently pointed at an alternate Claude config/plugin tree with different hooks, plugins, or auth context. Thanks @vincentkoc.
100
+ - Agents/Claude CLI/security: clear inherited Claude Code provider-routing and managed-auth env overrides, and mark OpenClaw-launched Claude CLI runs as host-managed, so Claude CLI backdoor sessions cannot be silently redirected to proxy, Bedrock, Vertex, Foundry, or parent-managed token contexts. Thanks @vincentkoc.
101
+ - Agents/Claude CLI/security: force host-managed Claude CLI backdoor runs to `--setting-sources user`, even under custom backend arg overrides, so repo-local `.claude` project/local settings, hooks, and plugin discovery do not silently execute inside non-interactive OpenClaw sessions. Thanks @vincentkoc.
102
+ - Agents/Claude CLI: treat malformed bare `--permission-mode` backend overrides as missing and fail safe back to `bypassPermissions`, so custom `cliBackends.claude-cli.args` security config cannot accidentally consume the next flag as a bogus permission mode. Thanks @vincentkoc.
103
+ - Gateway/device pairing: require non-admin paired-device sessions to manage only their own device for token rotate/revoke and paired-device removal, blocking cross-device token theft inside pairing-scoped sessions. (#50627) Thanks @coygeek.
104
+ - Gateway/plugin routes: keep gateway-auth plugin runtime routes on write-only fallback scopes unless a trusted-proxy caller explicitly declares narrower `x-openclaw-scopes`, so plugin HTTP handlers no longer mint admin-level runtime scopes on missing or untrusted HTTP scope headers. (#59815) Thanks @pgondhi987.
105
+ - Build/types: fix the Node `createRequire(...)` helper typing so provider-runtime lazy loads compile cleanly again and `pnpm build` no longer fails in the Pi embedded provider error-pattern path.
106
+ - Gateway/security: scope loopback browser-origin auth throttling by normalized origin so one localhost Control UI tab cannot lock out a different localhost browser origin after repeated auth failures. Thanks @vincentkoc.
107
+ - Gateway/auth: serialize async shared-secret auth attempts per client so concurrent Tailscale-capable failures cannot overrun the intended auth rate-limit budget. Thanks @Telecaster2147.
108
+ - Device pairing/security: keep non-operator device scope checks bound to the requested role prefix so bootstrap verification cannot redeem `operator.*` scopes through `node` auth. (#57258) Thanks @jlapenna.
109
+ - Device pairing: reject rotating device tokens into roles that were never approved during pairing, and keep reconnect role checks bounded to the paired device's approved role set. (#60462) Thanks @eleqtrizit.
110
+ - Gateway/device auth: reuse cached device-token scopes only for cached-token reconnects, while keeping explicit `deviceToken` scope requests and empty-cache fallbacks intact so reconnects preserve `operator.read` without breaking explicit auth flows. (#46032) Thanks @caicongyang.
111
+ - Mobile pairing/security: fail closed for internal `/pair` setup-code issuance, cleanup, and approval paths when gateway pairing scopes are missing, and keep approval-time requested-scope enforcement on the internal command path. (#55996) Thanks @coygeek.
112
+ - Mobile pairing/bootstrap: keep QR bootstrap handoff tokens bounded to the mobile-safe contract so node handoff stays unscoped and operator handoff drops mixed `node.*`, `operator.admin`, and `operator.pairing` scopes. Thanks @vincentkoc.
113
+ - Mobile pairing/Android: tighten secure endpoint handling so Tailscale and public remote setup reject cleartext endpoints, private LAN pairing still works, merged-role approvals mint both node and operator device tokens, and bootstrap tokens survive node auto-pair until operator approval finishes. (#60128, #60208, #60221) Thanks @obviyus.
114
+ - Android/canvas security: require exact normalized A2UI URL matches before forwarding canvas bridge actions, rejecting query mismatches and descendant paths while still allowing fragment-only A2UI navigation.
115
+ - Synology Chat/security: default low-level HTTPS helper TLS verification to on so helper/API defaults match the shipped safe account default, and only explicit `allowInsecureSsl: true` opts out. Thanks @vincentkoc.
116
+ - Synology Chat/security: route webhook token comparison through the shared constant-time secret helper for consistency with other bundled plugins. Thanks @vincentkoc.
117
+ - Plugins/marketplace: block remote marketplace symlink escapes without breaking ordinary local marketplace install paths. (#60556) Thanks @eleqtrizit.
118
+ - Telegram/local Bot API: honor `channels.telegram.apiRoot` for buffered media downloads, add `channels.telegram.network.dangerouslyAllowPrivateNetwork` for trusted fake-IP setups, and require `channels.telegram.trustedLocalFileRoots` before reading absolute Bot API `file_path` values. (#59544, #60705) Thanks @SARAMALI15792 and @obviyus.
119
+ - Outbound/sanitizer: strip leaked `<tool_call>`, `<function_calls>`, and model special tokens from shared user-visible assistant text, including truncated tool-call streams, so internal scaffolding no longer bleeds into replies across surfaces. (#60619) Thanks @oliviareid-svg.
120
+ - Agents/errors: surface an explicit disk-full message when local session or transcript writes fail with `ENOSPC`/`disk full`, so those runs stop degrading into opaque `NO_REPLY`-style failures. Thanks @vincentkoc.
121
+ - Exec approvals: remove heuristic command-obfuscation gating from host exec so gateway and node runs rely on explicit policy, allowlist, and strict inline-eval rules only.
122
+ - Agents/tool results: cap live tool-result persistence and overflow-recovery truncation at 40k characters so oversized tool output stays bounded without discarding recent context entirely.
123
+ - Discord/video replies: split text-plus-video deliveries into a text reply followed by a media-only send, and let live provider auth checks honor manifest-declared API key env vars like `MODELSTUDIO_API_KEY`.
124
+ - Config/All Settings: keep the raw config view intact when sensitive fields are blank instead of corrupting or dropping the rendered snapshot. (#28214) Thanks @solodmd.
125
+ - Plugin SDK/facades: back-fill bundled plugin facade sentinels before plugin-id tracking re-enters config loading, so CLI/provider startup no longer crashes with `shouldNormalizeGoogleProviderConfig is not a function` or other empty-facade reads during bundled plugin re-entry. Thanks @adam91holt.
126
+ - Plugins/facades: back-fill facade sentinels before tracked-plugin resolution re-enters config loading, so facade exports stay defined during circular provider normalization. (#61180) Thanks @adam91holt.
127
+ - QA lab: restore typed mock OpenAI gateway config wiring so QA-lab config helpers compile cleanly again and `pnpm check` / `pnpm build` stay green.
128
+ - Discord/image generation: include the real generated `MEDIA:` paths in tool output and avoid duplicate plain-output media requeueing so Discord image replies stop pointing at missing local files (#61450). Thanks @gumadeiras.
129
+ - Slack: route live DM replies back to the concrete inbound DM channel while keeping persisted routing metadata user-scoped, so normal assistant replies stop disappearing when pairing and system messages still arrive. (#59030) Thanks @afurm.
130
+ - Discord/reply tags: strip leaked `[[reply_to_current]]` control tags from preview text and honor explicit reply-tag threading during final delivery, so Discord replies stay attached to the triggering message instead of printing reply metadata into chat.
131
+ - Telegram: fix current-model checks in the model picker, HTML-format non-default `/model` confirmations, explicit topic replies, persisted reaction ownership across restarts, caption-media placeholder and `file_id` preservation on download failure, and upgraded-install inbound image reads. (#60384, #60042, #59634, #59207, #59948, #59971) Thanks @sfuminya, @GitZhangChi, @dashhuang, @samzong, @v1p0r, and @neeravmakwana.
132
+ - Telegram: restore DM voice-note preflight transcription so direct-message audio stops arriving as raw `<media:audio>` placeholders. (#61008) Thanks @manueltarouca.
133
+ - Telegram/reasoning: only create a Telegram reasoning preview lane when the session is explicitly `reasoning:stream`, so hidden `<think>` traces from streamed replies stop surfacing as chat previews on normal sessions. Thanks @vincentkoc.
134
+ - Telegram/native command menu: trim long menu descriptions before dropping commands so sub-100 command sets can still fit Telegram's payload budget and keep more `/` entries visible. (#61129) Thanks @neeravmakwana.
135
+ - Feishu/reasoning: only expose streamed reasoning previews when the session is explicitly `reasoning:stream`, so hidden reasoning traces do not surface on normal streaming sessions. Thanks @vincentkoc.
136
+ - Discord: keep REST, webhook, and monitor traffic on the configured proxy, preserve component-only media sends, honor `@everyone` and `@here` mention gates, keep ACK reactions on the active account, and split voice connect/playback timeouts so auto-join is more reliable. (#57465, #60361, #60345) Thanks @geekhuashan.
137
+ - WhatsApp: restore `channels.whatsapp.blockStreaming` and reset watchdog timeouts after reconnect so quiet chats stop falling into reconnect loops. (#60007, #60069) Thanks @MonkeyLeeT and @mcaxtr.
138
+ - Browser/security: re-run SSRF safety checks after interaction-driven navigations and before snapshot reads so click, submit, keyboard, and current-page snapshot flows fail closed on disallowed destinations. (#62023) Thanks @eleqtrizit.
139
+ - Memory: keep `memory-core` builtin embedding registration on the already-registered path so selecting `memory-core` no longer recurses through plugin discovery and crashes during startup. (#61402) Thanks @ngutman.
140
+ - Agents/tool results: keep large `read` outputs visible longer, preserve the latest `read` output when older tool output can absorb the overflow budget, and fall back to Pi's normal overflow compaction/retry path before replacing a fresh `read` with a compacted stub. Thanks @vincentkoc.
141
+ - Memory/QMD: prefer modern `qmd collection add --glob`, accept newer single-line JSON hit metadata while keeping legacy line fields, refresh QMD docs/doctor install guidance and model-override guidance, and keep older QMD releases working. Thanks @vincentkoc.
142
+ - MS Teams: download inline DM images via Graph API and preserve channel reply threading in proactive fallback. (#52212, #55198) Thanks @Ted-developer and @hyojin.
143
+ - MS Teams: replace the deprecated Teams SDK HttpPlugin stub with `httpServerAdapter` so recurring gateway deprecation warnings stop firing and the Express 5 compatibility workaround stays on the supported SDK path. (#60939) Thanks @coolramukaka-sys.
144
+ - Matrix/exec approvals: anchor seeded approval reactions to the primary Matrix prompt event, resolve them from event metadata instead of prompt text, and clean up chunked approval prompts correctly. (#60931) Thanks @gumadeiras.
145
+ - Matrix: recover more reliably when secret storage or recovery keys are missing by recreating secret storage during repair and backup reset, hold crypto snapshot locks during persistence, and surface explicit too-large attachment markers. (#59846, #59851, #60599, #60289) Thanks @al3mart, @emonty, and @efe-arv.
146
+ - Android/Talk Mode: cancel in-flight `talk.speak` playback when speech is explicitly stopped, so stale replies stop starting after barge-in or manual stop. (#61164) Thanks @obviyus.
147
+ - Android/Talk Mode: restore spoken assistant replies on node-scoped sessions by keeping reply routing synced to the resolved node session key and pausing mic capture during reply playback. (#60306) Thanks @MKV21.
148
+ - Android/Talk Mode: restore voice replies on gateway-backed talk mode sessions by updating embedded runner transport overrides to the current agent transport API. (#61214) Thanks @obviyus.
149
+ - Voice-call/OpenAI: pass full plugin config into realtime transcription provider resolution so streaming calls can discover the bundled OpenAI realtime transcription provider again. Fixes #60936. Thanks @sliekens and @vincentkoc.
150
+ - Control UI/chat: add a per-session thinking-level picker in the chat header and mobile chat settings, and keep the browser bundle on UI-local thinking/session-key helpers so Safari no longer crashes on Node-only imports before rendering chat controls.
151
+ - Control UI: keep Stop visible during tool-only execution, preserve pending-send busy state, and clear stale ClawHub search results as soon as the query changes. (#54528, #59800, #60267) Thanks @chziyue and @frankekn.
152
+ - Control UI/avatar: honor `ui.assistant.avatar` when serving `/avatar/:agentId` so Appearance UI avatar paths stop falling back to initials placeholders. (#60778) Thanks @hannasdev.
153
+ - Control UI/cron: highlight the Cron refresh button while refresh is in flight so the page's loading state stays visible even when prior data remains on screen. (#60394) Thanks @coder-zhuzm.
154
+ - Control UI/Overview: prevent gateway access token/password visibility toggle buttons from overlapping their inputs at narrow widths. (#56924) Thanks @bbddbb1.
155
+ - CLI/skills JSON: route `skills list --json`, `skills info --json`, and `skills check --json` output to stdout instead of stderr so machine-readable consumers receive JSON on the expected stream again. (#60914; fixes #57599; landed from contributor PR #57611 by @Aftabbs) Thanks @Aftabbs.
156
+ - CLI/Commander: preserve Commander-computed exit codes for argument and help-error paths, and cover the user-argv parse mode in the regression tests so invalid CLI invocations no longer report success when exits are intercepted. (#60923) Thanks @Linux2010.
157
+ - Cron: replay interrupted recurring jobs on the first gateway restart instead of waiting for a second restart. (#60583) Thanks @joelnishanth.
158
+ - Cron: send failure notifications through the job's primary delivery channel using the same session context as successful delivery when no explicit `failureDestination` is configured. (#60622) Thanks @artwalker.
159
+ - Live model switching: only treat explicit user-driven model changes as pending live switches, so fallback rotation, heartbeat overrides, and compaction no longer trip `LiveSessionModelSwitchError` before making an API call. (#60266) Thanks @kiranvk-2011.
160
+ - Exec approvals: reuse durable exact-command `allow-always` approvals in allowlist mode so identical reruns stop prompting, and tighten Windows interpreter/path approval handling so wrapper and malformed-path cases fail closed more consistently. (#59880, #59780, #58040, #59182) Thanks @luoyanglang, @SnowSky1, and @pgondhi987.
161
+ - Node exec approvals: keep node-host `system.run` approvals bound to the prepared execution plan across async forwarding, so mutable script operands still get approval-time binding and drift revalidation instead of dropping back to unbound execution.
162
+ - Agents/exec approvals: let `exec-approvals.json` agent security override stricter gateway tool defaults so approved subagents can use `security: “full”` without falling back to allowlist enforcement again. (#60310) Thanks @lml2468.
163
+ - Agents/exec: restore `host=node` routing for node-pinned and `host=auto` sessions, while still blocking sandboxed `auto` sessions from jumping to gateway. (#60788) Thanks @openperf.
164
+ - Exec/heartbeat: use the canonical `exec-event` wake reason for `notifyOnExit` so background exec completions still trigger follow-up turns when `HEARTBEAT.md` is empty or comments-only. (#41479) Thanks @rstar327.
165
+ - Heartbeat: skip wake delivery when the target session lane is already busy so the pending event is retried instead of getting drained too early. (#40526) Thanks @lucky7323.
166
+ - Group chats/agent prompts: tell models to minimize empty lines and use normal chat-style spacing so group replies avoid document-style blank-line formatting. Thanks @vincentkoc.
167
+ - Providers/OpenAI GPT: treat short approval turns like `ok do it` and `go ahead` as immediate action turns, and trim overly memo-like GPT-5 chat confirmations so OpenAI replies stay shorter and more conversational by default. Thanks @vincentkoc.
168
+ - Providers/OpenAI Codex: split native `contextWindow` from runtime `contextTokens`, keep the default effective cap at `272000`, and expose a per-model `contextTokens` override on `models.providers.*.models[]`. Thanks @vincentkoc.
169
+ - Providers/OpenAI-compatible WS: compute fallback token totals from normalized usage when providers omit or zero `total_tokens`, so DashScope-compatible sessions stop storing zero totals after alias normalization. (#54940) Thanks @lyfuci.
170
+ - Agents/OpenAI: mark Claude-compatible file tool schemas as `additionalProperties: false` so direct OpenAI GPT-5 routes stop rejecting the `read` tool with invalid strict-schema errors. Thanks @vincentkoc.
171
+ - Agents/OpenAI: fall back to `strict: false` for native OpenAI tool calls when a tool schema is not strict-compatible, and normalize empty-object tool schemas to include `required: []`, so direct GPT-5 routes stop failing with invalid strict-schema errors like missing `path` in `required`.
172
+ - Agents/GPT: add explicit work-item lifecycle events for embedded runs, use them to surface real progress more reliably, and stop counting tool-started turns as planning-only retries. Thanks @vincentkoc.
173
+ - Plugins/OpenAI: enable `gpt-image-1` reference-image edits through `/images/edits` multipart uploads, and stop inferring unsupported resolution overrides when no explicit `size` or `resolution` is provided. Thanks @vincentkoc.
174
+ - Agents/replay: remove the malformed assistant-content canonicalization repair from replay history sanitization instead of extending that legacy repair path into replay validation.
175
+ - Plugins/OpenAI: tune the OpenAI prompt overlay for live-chat cadence so GPT replies stay shorter, more human, and less wall-of-text by default. Thanks @vincentkoc.
176
+ - Providers/compat: stop forcing OpenAI-only defaults on proxy and custom OpenAI-compatible routes, preserve native vendor-specific reasoning/tool/streaming behavior across Anthropic-compatible, Moonshot, Mistral, ModelStudio, OpenRouter, xAI, and Z.ai endpoints, and route GitHub Copilot Claude models through Anthropic Messages instead of OpenAI Responses. Thanks @vincentkoc.
177
+ - Providers/GitHub Copilot: send IDE identity headers on runtime model requests and GitHub token exchange so IDE-authenticated Copilot runs stop failing with missing `Editor-Version`. (#60641) Thanks @VACInc and @vincentkoc.
178
+ - Providers/OpenRouter failover: classify `403 “Key limit exceeded”` spending-limit responses as billing so model fallback continues instead of stopping on generic auth. (#59892) Thanks @rockcent.
179
+ - Providers/Anthropic: keep `claude-cli/*` auth on live Claude CLI credentials at runtime, avoid persisting stale bearer-token profiles, and suppress macOS Keychain prompts during non-interactive Claude CLI setup. (#61234) Thanks @darkamenosa.
180
+ - Providers/Anthropic: when Claude CLI auth becomes the default, write a real `claude-cli` auth profile so local and gateway agent runs can use Claude CLI immediately without missing-API-key failures. Thanks @vincentkoc.
181
+ - Memory/dreaming: make Dreams config reads and writes respect the selected memory slot plugin (including `doctor.memory.status` and Control UI fallback state) instead of always targeting `memory-core`. (#62275) Thanks @SnowSky1.
182
+ - Providers/Anthropic Vertex: honor `cacheRetention: “long”` with the real 1-hour prompt-cache TTL on Vertex AI endpoints, and default `anthropic-vertex` cache retention like direct Anthropic. (#60888) Thanks @affsantos.
183
+ - Agents/Anthropic: preserve native `toolu_*` replay ids on direct Anthropic and Anthropic Vertex paths so cache-sensitive history stops rewriting known-valid Anthropic tool-use ids. (#52612) Thanks @vincentkoc.
184
+ - Providers/Google: add model-level `cacheRetention` support for direct Gemini system prompts by creating, reusing, and refreshing `cachedContents` automatically on Google AI Studio runs. (#51372) Thanks @rafaelmariano-glitch.
185
+ - Google Gemini CLI auth: detect bundled npm installs by scanning packaged bundle files for the Gemini OAuth client config, so `npm install -g @google/gemini-cli` layouts work again. (#60486) Thanks @wzfmini01.
186
+ - Google Gemini CLI auth: detect personal OAuth mode from local Gemini settings and skip Code Assist project discovery for those logins, so personal Google accounts stop failing with `loadCodeAssist 400 Bad Request`. (#49226) Thanks @bobworrall.
187
+ - Google Gemini CLI auth: improve OAuth credential discovery across Windows nvm and Homebrew libexec installs, and align Code Assist metadata so Gemini login stops failing on packaged CLI layouts. (#40729) Thanks @hughcube.
188
+ - Google Gemini CLI models: add forward-compat support for stable `gemini-2.5-*` model ids by letting the bundled CLI provider clone them from Google templates, so `gemini-2.5-flash-lite` and related configured models stop showing up as missing. (#35274) Thanks @mySebbe.
189
+ - Google image generation: disable pinned DNS for Gemini image requests and honor explicit `pinDns` overrides in shared provider HTTP helpers so proxy-backed image generation works again. (#59873) Thanks @luoyanglang.
190
+ - Providers/Microsoft Foundry: preserve explicit image capability on normalized Foundry deployments, repair stale GPT/o-series text-only model metadata across gateway and runtime paths, and keep unknown fallback models from borrowing unrelated image support. Thanks @vincentkoc.
191
+ - Providers/Model Studio: preserve native streaming usage reporting for DashScope-compatible endpoints even when they are configured under a generic provider key, so streamed token totals stop sticking at zero. (#52395) Thanks @IVY-AI-gif.
192
+ - Providers/Z.AI: preserve explicitly registered `glm-5-*` variants like `glm-5-turbo` instead of intercepting them with the generic GLM-5 forward-compat shim. (#48185) Thanks @haoyu-haoyu.
193
+ - Amazon Bedrock/aws-sdk auth: stop injecting the fake `AWS_PROFILE` apiKey marker when no AWS auth env vars exist, so instance-role and other default-chain setups keep working without poisoning provider config. (#61194) Thanks @wirjo.
194
+ - Agents/Kimi tool-call repair: preserve tool arguments that were already present on streamed tool calls when later malformed deltas fail reevaluation, while still dropping stale repair-only state before `toolcall_end`. Thanks @vincentkoc.
195
+ - Plugins/Kimi Coding: parse tagged tool calls and keep Anthropic-native tool payloads so Kimi coding endpoints execute tools instead of echoing raw markup. (#60051, #60391) Thanks @obviyus and @Eric-Guo.
196
+ - Media understanding: auto-register image-capable config providers for vision routing, so custom GLM-style provider ids with image models stop failing with “no media-understanding provider registered”. (#51418) Thanks @xydt-610.
197
+ - Plugins/media understanding: enable bundled Groq and Deepgram providers by default so configured transcription models work without extra plugin activation config. (#59982) Thanks @yxjsxy.
198
+ - MiniMax/pricing: keep bundled MiniMax highspeed pricing distinct in provider catalogs and preserve the lower M2.5 cache-read pricing when onboarding older MiniMax models. (#54214) Thanks @octo-patch.
199
+ - MiniMax: advertise image input on bundled `MiniMax-M2.7` and `MiniMax-M2.7-highspeed` model definitions so image-capable flows can route through the M2.7 family correctly. (#54843) Thanks @MerlinMiao88888888.
200
+ - Models/MiniMax: honor `MINIMAX_API_HOST` for implicit bundled MiniMax provider catalogs so China-hosted API-key setups pick `api.minimaxi.com/anthropic` without manual provider config. (#34524) Thanks @caiqinghua.
201
+ - Usage/MiniMax: invert remaining-style `usage_percent` fields when MiniMax reports only remaining percentage data, so usage bars stop showing nearly-full remaining quota as nearly-exhausted usage. (#60254) Thanks @jwchmodx.
202
+ - Usage/MiniMax: let usage snapshots treat `minimax-portal` and MiniMax CN aliases as the same MiniMax quota surface, and prefer stored MiniMax OAuth before falling back to Coding Plan keys. Thanks @vincentkoc.
203
+ - Usage/MiniMax: prefer the chat-model `model_remains` entry and derive Coding Plan window labels from MiniMax interval timestamps so MiniMax usage snapshots stop picking zero-budget media rows and misreporting 4h windows as `5h`. (#52349) Thanks @IVY-AI-gif.
204
+ - Model picker/providers: treat bundled BytePlus and Volcengine plan aliases as their native providers during setup, and expose their bundled standard/coding catalogs before auth so setup can suggest the right models. (#58819) Thanks @Luckymingxuan.
205
+ - Tools/web_search (Kimi): when `tools.web.search.kimi.baseUrl` is unset, inherit native Moonshot chat `baseUrl` (`.ai` / `.cn`) so China console keys authenticate on the same host as chat. Fixes #44851. (#56769) Thanks @tonga54.
206
+ - Agents/Claude CLI: keep non-interactive `--permission-mode bypassPermissions` when custom `cliBackends.claude-cli.args` override defaults, including fallback resolution before the runtime plugin registry is active, so cron and heartbeat Claude CLI runs do not regress to interactive approval mode. (#61114) Thanks @cathrynlavery and @thewilloftheshadow.
207
+ - Agents/Claude CLI: persist explicit `openclaw agent --session-id` runs under a stable session key so follow-ups can reuse the stored CLI binding and resume the same underlying Claude session. Thanks @vincentkoc.
208
+ - Agents/Claude CLI: persist routed Claude session bindings, rotate them on `/new` and `/reset`, and keep live Claude CLI model switches moving across the configured Claude family so resumed sessions follow the real active thread and model. Thanks @vincentkoc.
209
+ - Agents/CLI backends: invalidate stored CLI session reuse when local CLI login state or the selected auth profile credential changes, so relogin and token rotation stop resuming stale sessions. Thanks @vincentkoc.
210
+ - Agents/Claude CLI/images: reuse stable hydrated image file paths and preserve shared media extensions like HEIC when passing image refs to local CLI runs, so Claude CLI image prompts stop thrashing KV cache prefixes and oddball image formats do not fall back to `.bin`. Thanks @vincentkoc.
211
+ - Agents/compaction: keep assistant tool calls and displaced tool results in the same compaction chunk so strict summarization providers stop rejecting orphaned tool pairs. (#58849) Thanks @openperf.
212
+ - Agents/failover: scope Anthropic `An unknown error occurred` failover matching by provider so generic internal unknown-error text no longer triggers retryable timeout fallback. (#59325) Thanks @aaron-he-zhu.
213
+ - Agents/subagents: honor allowlist validation, auth-profile handoff, and session override state when a subagent retries after `LiveSessionModelSwitchError`. (#58178) Thanks @openperf.
214
+ - Agents/runtime: make default subagent allowlists, inherited skills/workspaces, and duplicate session-id resolution behave more predictably, and include value-shape hints in missing-parameter tool errors. (#59944, #59992, #59858, #55317) Thanks @hclsys, @gumadeiras, @joelnishanth, and @priyansh19.
215
+ - Agents/pairing: merge completion announce delivery context with the requester session fallback so missing `to` still reaches the original channel, and include `operator.talk.secrets` in CLI default operator scopes for node-role device pairing approvals. (#56481) Thanks @maxpetrusenko.
216
+ - Agents/scheduling: steer background-now work toward automatic completion wake and treat `process` polling as on-demand inspection or intervention instead of default completion handling. (#60877) Thanks @vincentkoc.
217
+ - Agents/skills: skip `.git` and `node_modules` when mirroring skills into sandbox workspaces so read-only sandboxes do not copy repo history or dependency trees. (#61090) Thanks @joelnishanth.
218
+ - ACP/agents: inherit the target agent workspace for cross-agent ACP spawns and fall back safely when the inherited workspace no longer exists. (#58438) Thanks @zssggle-rgb.
219
+ - ACPX/Windows: preserve backslashes and absolute `.exe` paths in Claude CLI parsing, and fail fast on wrapper-script targets with guidance to use `cmd.exe /c`, `powershell.exe -File`, or `node <script>`. (#60689).
220
+ - Auth/failover: persist selected fallback overrides before retrying, shorten `auth_permanent` lockouts, and refresh websocket/shared-auth sessions only when real auth changes occur so retries and secret rotations behave predictably. (#60404, #60323, #60387) Thanks @extrasmall0 and @mappel-nv.
221
+ - Gateway/channels: pin the initial startup channel registry before later plugin-registry churn so configured channels stay visible and `channels.status` stops falling back to empty `channelOrder` / `channels` payloads after runtime plugin loads. Thanks @vincentkoc.
222
+ - Prompt caching: order stable workspace project-context files before `HEARTBEAT.md` and keep `HEARTBEAT.md` below the system-prompt cache boundary so heartbeat churn does not invalidate the stable project-context prefix. (#58979) Thanks @yozu and @vincentkoc.
223
+ - Prompt caching: route Codex Responses and Anthropic Vertex through boundary-aware cache shaping, and report the actual outbound system prompt in cache traces so cache reuse and misses line up with what providers really receive. Thanks @vincentkoc.
224
+ - Agents/cache: preserve the full 3-turn prompt-cache image window across tool loops, keep colliding bundled MCP tool definitions deterministic, and reapply Anthropic Vertex cache shaping after payload hook replacements so KV/cache reuse stays stable. Thanks @vincentkoc.
225
+ - Status/cache: restore `cacheRead` and `cacheWrite` in transcript fallback so `/status` keeps showing cache hit percentages when session logs are the only complete usage source. (#59247) Thanks @stuartsy.
226
+ - Status/usage: let `/status` and `session_status` fall back to transcript token totals when the session meta store stayed at zero, so LM Studio, Ollama, DashScope, and similar OpenAI-compatible providers stop showing `Context: 0/...`. (#55041) Thanks @jjjojoj.
227
+ - Mattermost/config schema: accept `groups.*.requireMention` again so existing Mattermost configs no longer fail strict validation after upgrade. (#58271) Thanks @MoerAI.
228
+ - Doctor/config: compare normalized `talk` configs by deep structural equality instead of key-order-sensitive serialization so `openclaw doctor --fix` stops repeatedly reporting/applying no-op `talk.provider/providers` normalization. (#59911) Thanks @ejames-dev.
229
+ - Anthropic CLI onboarding: rewrite migrated fallback model refs during non-interactive Claude CLI setup too, so onboarding and scripted setup no longer keep stale `anthropic/*` fallbacks after switching the primary model to `claude-cli/*`. Thanks @vincentkoc.
230
+ - Models/Anthropic CLI auth: replace migrated `agents.defaults.models` allowlists when `openclaw models auth login --provider anthropic --method cli --set-default` switches to `claude-cli/*`, so stale `anthropic/*` entries do not linger beside the migrated Claude CLI defaults. Thanks @vincentkoc.
231
+ - Doctor/Claude CLI: add dedicated Claude CLI health checks so `openclaw doctor` can spot missing local installs or broken auth before agent runs fail. Thanks @vincentkoc.
232
+ - Plugins/auth-choice: apply provider-owned auth config patches without recursively preserving replaced default-model maps, so Anthropic Claude CLI and similar migrations can intentionally swap model allowlists during onboarding and setup instead of accumulating stale entries. Thanks @vincentkoc.
233
+ - Plugins/onboarding: write dotted plugin uiHint paths like Brave `webSearch.mode` as nested plugin config so `llm-context` setup stops failing validation. (#61159) Thanks @obviyus.
234
+ - Plugins/install: preserve unsafe override flags across linked plugin and hook-pack probes so local `--link` installs honor the documented override behavior. (#60624) Thanks @JerrettDavis.
235
+ - Plugins/cache: inherit the active gateway workspace for provider, web-search, and web-fetch snapshot loads when callers omit `workspaceDir`, so compatible plugin registries and snapshot caches stop missing on gateway-owned runtime paths. (#61138) Thanks @jzakirov.
236
+ - Plugin SDK/context engines: export the missing context-engine result and subagent lifecycle types from `openclaw/plugin-sdk` so context engine plugins can type `ContextEngine` implementations without local workarounds. (#61251) Thanks @DaevMithran.
237
+ - Tasks/maintenance: reconcile stale cron and chat-backed CLI task rows against live cron-job and agent-run ownership instead of treating any persisted session key as proof that the task is still running. (#60310) Thanks @lml2468.
238
+ - Plugins: suppress trust-warning noise during non-activating snapshot and CLI metadata loads. (#61427) Thanks @gumadeiras.
239
+ - Agents/video generation: accept `agents.defaults.videoGenerationModel` in strict config validation and `openclaw config set/get`, so gateways using `video_generate` no longer fail to boot after enabling a video model.
240
+ - Matrix/streaming: add a quiet preview mode for streamed Matrix replies, keep legacy `partial` preview-first behavior, and finalize quiet media captions correctly so previews stop notifying early without dropping final text semantics. (#61450) Thanks @gumadeiras.
241
+ - Agents/compaction: skip redundant partial summarization when no messages were oversized, so the same transcript is not summarized twice after a full summarization failure. Fixes #61465. (#61603) Thanks @neeravmakwana.
242
+ - Gateway/shutdown: bound websocket-server shutdown even when no tracked clients remain, so gateway restarts stop hanging until the watchdog kills the process. (#61565) Thanks @mbelinky.
243
+ - Control UI/multilingual: localize the remaining shared channel, instances, nodes, and gateway-confirmation strings so the dashboard stops mixing translated UI with hardcoded English labels. Thanks @vincentkoc.
244
+ - Discord/media: raise the default inbound and outbound media cap to `100MB` so Discord matches Telegram more closely and larger attachments stop failing on the old low default.
245
+ - Matrix: keep direct transport requests on the pinned dispatcher by routing them through undici runtime fetch, so Matrix clients resume syncing on newer runtimes without dropping the validated address binding. (#61595) Thanks @gumadeiras.
246
+ - Plugins/facades: resolve globally installed bundled-plugin runtime facades from registry roots so bundled channels like LINE still boot when the winning plugin install lives under the global extensions directory with an encoded scoped folder name. (#61297) Thanks @openperf.
247
+ - Matrix: avoid failing startup when token auth already knows the user ID but still needs optional device metadata, retry transient auth bootstrap requests, and backfill missing device IDs after startup while keeping unknown-device storage reuse conservative until metadata is repaired. (#61383) Thanks @gumadeiras.
248
+ - Agents/exec: stop streaming `tool_execution_update` events after an exec session backgrounds, preventing delayed background output from hitting a stale listener and crashing the gateway while keeping the output available through `process poll/log`. (#61627) Thanks @openperf.
249
+ - Matrix: pass configured `deviceId` through health probes and keep probe-only client setup out of durable Matrix storage, so health checks preserve the correct device identity without rewriting `storage-meta.json` or related probe state on disk. (#61581) Thanks @MoerAI.
250
+ - Image generation/build: write stable runtime alias files into `dist/` and route provider-auth runtime lookups through those aliases so image-generation providers keep resolving auth/runtime modules after rebuilds instead of crashing on missing hashed chunk files (#57816). Thanks @ForestDengHK.
251
+ - Config/runtime: pin the first successful config load in memory for the running process and refresh that snapshot on successful writes/reloads, so hot paths stop reparsing `openclaw.json` between watcher-driven swaps (#57816). Thanks @ForestDengHK.
252
+ - Config/legacy cleanup: stop probing obsolete alternate legacy config names and service labels during local config/service detection, while keeping the active `~/.openclaw/openclaw.json` path canonical (#57816). Thanks @ForestDengHK.
253
+ - ACP/sessions_spawn: register ACP child runs for completion tracking and lifecycle cleanup, and make registration-failure cleanup explicitly best-effort so callers do not assume an already-started ACP turn was fully aborted. (#40885) Thanks @xaeon2026 and @vincentkoc.
254
+ - ACP/tasks: mark cleanly exited ACP runs as blocked when they end on deterministic write or authorization blockers, and wake the parent session with a follow-up instead of falsely reporting success (#57816). Thanks @ForestDengHK.
255
+ - ACPX/runtime: derive the bundled ACPX expected version from the extension package metadata instead of hardcoding a separate literal, so plugin-local ACPX installs stop drifting out of health-check parity after version bumps. (#49089) Thanks @jiejiesks and @vincentkoc.
256
+ - Gateway/auth: make local-direct `trusted-proxy` fallback require the configured shared token instead of silently authenticating same-host callers, while keeping same-host reverse proxy identity-header flows on the normal trusted-proxy path. Thanks @zhangning-agent and @vincentkoc.
257
+ - Memory/QMD: send MCP `query` collection filters as the upstream `collections` array instead of the legacy singular `collection` field, so mcporter-backed QMD 1.1+ searches still scope correctly after the unified `query` tool migration. (#54728) Thanks @armanddp and @vincentkoc.
258
+ - Memory/QMD: keep `qmd embed` active in `search` mode too, so BM25-first setups still build a complete index for later vector and hybrid retrieval. (#54509) Thanks @hnshah and @vincentkoc.
259
+ - Memory/QMD: point `QMD_CONFIG_DIR` at the nested `xdg-config/qmd` directory so per-agent collection config resolves correctly. (#39078) Thanks @smart-tinker and @vincentkoc.
260
+ - Memory/QMD: include deduplicated default plus per-agent `memorySearch.extraPaths` when building QMD custom collections, so shared and agent-specific extra roots both get indexed consistently. (#57315) Thanks @Vitalcheffe and @vincentkoc.
261
+ - Memory/session indexer: include `.jsonl.reset.*` and `.jsonl.deleted.*` transcripts in the memory host session scan while still excluding `.jsonl.bak.*` compaction backups and lock files, so memory search sees archived session history without duplicating stale snapshots. Thanks @hclsys and @vincentkoc.
262
+ - Agents/sandbox: honor `tools.sandbox.tools.alsoAllow`, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman.
263
+ - LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn .. --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels (#57816). Thanks @ForestDengHK.
264
+ - LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997.
265
+ - TTS/Microsoft: auto-switch the default Edge voice to Chinese for CJK-dominant text without overriding explicitly selected Microsoft voices. (#52355) Thanks @extrasmall0.
266
+ - Agents/context pruning: count supplementary-plane CJK characters with the shared code-point-aware estimator so context pruning stops underestimating Japanese and Chinese text that uses Extension B ideographs. (#39985) Thanks @Edward-Qiang-2024.
267
+ - Slack/status reactions: add a reaction lifecycle for queued, thinking, tool, done, and error phases in Slack monitors, with safer cleanup so queued ack reactions stay correct across silent runs, pre-reply failures, and delayed transitions. (#56430) Thanks @hsiaoa.
268
+ - macOS/local gateway: stop OpenClaw.app from killing healthy local gateway listeners after startup by recognizing the current `openclaw-gateway` process title and using the current `openclaw gateway` launch shape (#57816). Thanks @ForestDengHK.
269
+ - Gateway/OpenAI compatibility: accept flat Responses API function tool definitions on `/v1/responses` and preserve `strict` when normalizing hosted tools into the embedded runner, so spec-compliant clients like Codex no longer fail validation or silently lose strict tool enforcement. Thanks @malaiwah and @vincentkoc.
270
+ - Memory/QMD: resolve slugified `memory_search` file hints back to the indexed filesystem path before returning search hits, so `memory_get` works again for mixed-case and spaced paths. (#50313) Thanks @erra9x.
271
+ - OpenAI/Codex fast mode: map `/fast` to priority processing on native OpenAI and Codex Responses endpoints instead of rewriting reasoning settings, and document the exact endpoint and override behavior (#57816). Thanks @ForestDengHK.
272
+ - Memory/QMD: weight CJK-heavy text correctly when estimating chunk sizes, preserve surrogate-pair characters during fine splits, and keep long Latin lines on the old chunk boundaries so memory indexing produces better-sized chunks for CJK notes. (#40271) Thanks @AaronLuo00.
273
+ - Security/LINE: make webhook signature validation run the timing-safe compare even when the supplied signature length is wrong, closing a small timing side-channel. (#55663) Thanks @gavyngong.
274
+ - LINE/status: stop `openclaw status` from warning about missing credentials when sanitized LINE snapshots are already configured, while still surfacing whether the missing field is the token or secret. (#45701) Thanks @tamaosamu.
275
+ - Gateway/health: carry webhook-vs-polling account mode from channel descriptors into runtime snapshots so passive channels like LINE and BlueBubbles skip false stale-socket health failures. (#47488) Thanks @karesansui-u.
276
+ - Agents/MCP: reuse bundled MCP runtimes across turns in the same session, while recreating them when MCP config changes and disposing stale runtimes cleanly on session rollover. (#55090) Thanks @allan0509.
277
+ - Memory/QMD: honor `memory.qmd.update.embedInterval` even when regular QMD update cadence is disabled or slower by arming a dedicated embed-cadence maintenance timer, while avoiding redundant timers when regular updates are already frequent enough. (#37326) Thanks @barronlroth.
278
+ - Memory/QMD: add `memory.qmd.searchTool` as an exact mcporter tool override, so custom QMD MCP tools such as `hybrid_search` can be used without weakening the validated `searchMode` config surface. (#27801) Thanks @keramblock.
279
+ - Memory/QMD: keep reset and deleted session transcripts in QMD session export so daily session resets do not silently drop most historical recall from `memory_search`. (#30220) Thanks @pushkarsingh32.
280
+ - Memory/QMD: rebind collections when QMD reports a changed pattern but omits path metadata, so config pattern changes stop being silently ignored on restart. (#49897) Thanks @Madruru.
281
+ - Memory/QMD: warn explicitly when `memory.backend=qmd` is configured but the `qmd` binary is missing, so doctor and runtime fallback no longer fail as a silent builtin downgrade. (#50439) Thanks @Jimmy-xuzimo and @vincentkoc.
282
+ - Memory/QMD: pass a direct-session key on `openclaw memory search` so CLI QMD searches no longer get denied as `session=<none>` under direct-only scope defaults. (#43517) Thanks @waynecc-at and @vincentkoc.
283
+ - Memory/QMD: keep `memory_search` session-hit paths roundtrip-safe when exported session markdown lives under the workspace `qmd/` directory, so `memory_get` can read the exact returned path instead of failing on the generic `qmd/sessions/...` alias. (#43519) Thanks @holgergruenhagen and @vincentkoc.
284
+ - Agents/memory flush: keep daily memory flush files append-only during embedded attempts so compaction writes do not overwrite earlier notes. (#53725) Thanks @HPluseven.
285
+ - Web UI/markdown: stop bare auto-links from swallowing adjacent CJK text while preserving valid mixed-script path and query characters in rendered links. (#48410) Thanks @jnuyao.
286
+ - BlueBubbles/iMessage: coalesce URL-only inbound messages with their link-preview balloon again so sharing a bare link no longer drops the URL from agent context. Thanks @vincentkoc.
287
+ - Sandbox/browser: install `fonts-noto-cjk` in the sandbox browser image so screenshots render Chinese, Japanese, and Korean text correctly instead of tofu boxes. Fixes #35597. Thanks @carrotRakko and @vincentkoc.
288
+ - Memory/FTS: add configurable trigram tokenization plus short-CJK substring fallback so memory search can find Chinese, Japanese, and Korean text without breaking mixed long-and-short queries. Thanks @carrotRakko.
289
+ - Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
290
+ - TUI/chat: keep optimistic outbound user messages visible during active runs by deferring local-run binding until the first gateway chat event reveals the real run id, preventing premature history reloads from wiping pending local sends. (#54722) Thanks @seanturner001.
291
+ - TUI/model picker: keep searchable `/model` and `/models` input mode from hijacking `j`/`k` as navigation keys, and harden width bounds under `m`-filtered model lists so search no longer crashes on long rows. (#30156) Thanks @briannicholls.
292
+ - Agents/Kimi: preserve already-valid Anthropic-compatible tool call argument objects while still clearing cached repairs when later trailing junk exceeds the repair allowance. (#54491) Thanks @yuanaichi.
293
+ - Docker/setup: force BuildKit for local image builds (including sandbox image builds) so `./docker-setup.sh` no longer fails on `RUN --mount=...` when hosts default to Docker's legacy builder. (#56681) Thanks @zhanghui-china.
294
+ - Control UI/agents: auto-load agent workspace files on initial Files panel open, and populate overview model/workspace/fallbacks from effective runtime agent metadata so defaulted models no longer show as `Not set`. (#56637) Thanks @dxsx84.
295
+ - Control UI/slash commands: make `/steer` and `/redirect` work from the chat command palette with visible pending state for active-run `/steer`, correct redirected-run tracking, and a single canonical `/steer` entry in the command menu. (#54625) Thanks @fuller-stack-dev.
296
+ - Exec/runtime: default implicit exec to `host=auto`, resolve that target to sandbox only when a sandbox runtime exists, keep explicit `host=sandbox` fail-closed without sandbox, and show `/exec` effective host state in runtime status/docs (#57816). Thanks @ForestDengHK.
297
+ - Exec: fail closed when the implicit sandbox host has no sandbox runtime, and stop denied async approval followups from reusing prior command output from the same session. (#56800) Thanks @scoootscooob.
298
+ - Exec/approvals: infer Discord and Telegram exec approvers from existing owner config when `execApprovals.approvers` is unset, extend the default approval window to 30 minutes, and clarify approval-unavailable guidance so approvals do not appear to silently disappear (#57816). Thanks @ForestDengHK.
299
+ - Exec/node: stop gateway-side workdir fallback from rewriting explicit `host=node` cwd values to the gateway filesystem, so remote node exec approval and runs keep using the intended node-local directory. (#50961) Thanks @openperf.
300
+ - Plugins/ClawHub: sanitize temporary archive filenames for scoped package names and slash-containing skill slugs so `openclaw plugins install @scope/name` no longer fails with `ENOENT` during archive download. (#56452) Thanks @soimy.
301
+ - Telegram/polling: keep the watchdog from aborting long-running reply delivery by treating recent non-polling API activity as bounded liveness instead of a hard stall. (#56343) Thanks @openperf.
302
+ - Memory/FTS: keep provider-less keyword hits visible at the default memory-search threshold, so FTS-only recall works without requiring `--min-score 0`. (#56473) Thanks @opriz.
303
+ - Memory/LanceDB: resolve runtime dependency manifest lookup from the bundled `extensions/memory-lancedb` path (including flattened dist chunks) so startup no longer fails with a missing `@lancedb/lancedb` dependency error. (#56623) Thanks @LUKSOAgent.
304
+ - Tools/web_search: localize the shared search cache to module scope so same-process global symbol lookups can no longer inspect or mutate cached web-search responses. Thanks @vincentkoc.
305
+ - Agents/silent turns: fail closed on silent memory-flush runs so narrated `NO_REPLY` self-talk cannot stream or finalize into external replies even when block streaming is enabled. (#52593) Thanks @ForestDengHK.
306
+ - Browser/plugins: auto-enable the bundled browser plugin when browser config or browser tool policy already references it, and show a clearer CLI error when `plugins.allow` excludes `browser` (#57816). Thanks @ForestDengHK.
307
+ - Matrix/plugin loading: ship and source-load the crypto bootstrap runtime sidecar correctly so current `main` stops warning about failed Matrix bootstrap loads and `matrix/index` plugin-id mismatches on every invocation. (#53298) thanks @keithce.
308
+ - iOS/Live Activities: mark the `ActivityKit` import in `LiveActivityManager.swift` as `@preconcurrency` so Xcode 26.4 / Swift 6 builds stop failing on strict concurrency checks. (#57180) Thanks @ngutman.
309
+ - Plugins/Matrix: mirror the Matrix crypto WASM runtime dependency into the root packaged install and enforce root/plugin dependency parity so bundled Matrix E2EE crypto resolves correctly in shipped builds. (#57163) Thanks @gumadeiras.
310
+ - Plugins/CLI: add descriptor-backed lazy plugin CLI registration so Matrix can keep its CLI module lazy-loaded without dropping `openclaw matrix ...` from parse-time command registration. (#57165) Thanks @gumadeiras.
311
+ - Plugins/CLI: collect root-help plugin descriptors through a dedicated non-activating CLI metadata path so enabled plugins keep validated config semantics without triggering runtime-only plugin registration work, while preserving runtime CLI command registration for legacy channel plugins that still wire commands from full registration. (#57294) thanks @gumadeiras.
312
+ - Anthropic/OAuth: inject `/fast` `service_tier` hints for direct `sk-ant-oat-*` requests so OAuth-authenticated Anthropic runs stop missing the same overload-routing signal as API-key traffic. Fixes #55758. Thanks @Cypherm and @vincentkoc.
313
+ - Anthropic/service tiers: support explicit `serviceTier` model params for direct Anthropic requests and let them override `/fast` defaults when both are set. (#45453) Thanks @vincentkoc.
314
+ - Auto-reply/fast: accept `/fast status` on the directive-only path, align help/status text with the documented `status|on|off` syntax, and keep current-state replies consistent across command surfaces. Fixes #46095. Thanks @weissfl and @vincentkoc.
315
+ - Telegram/native commands: prefix native command menu callback payloads and preserve `CommandSource: "native"` when Telegram replays them through callback queries, so `/fast` and other native command menus keep working even when text-command routing is disabled. Thanks @vincentkoc.
316
+ - Docs/anchors: fix broken English docs links and make Mint anchor audits run against the English-source docs tree. (#57039) thanks @velvet-shark.
317
+ - Cron/announce: preserve all deliverable text payloads for announce mode instead of collapsing to the last chunk, so multi-line cron reports deliver in full to Telegram forum topics (#57816). Thanks @ForestDengHK.
318
+ - Harden async approval followup delivery in webchat-only sessions (#57359) Thanks @joshavant.
319
+ - Status: fix cache hit rate exceeding 100% by deriving denominator from prompt-side token fields instead of potentially undersized totalTokens. Fixes #26643 (#57816). Thanks @ForestDengHK.
320
+ - Config/update: stop `openclaw doctor` write-backs from persisting plugin-injected channel defaults, so `openclaw update` no longer seeds config keys that later break service refresh validation. (#56834) Thanks @openperf.
321
+ - Agents/Anthropic failover: treat Anthropic `api_error` payloads with `An unexpected error occurred while processing the response` as transient so retry/fallback can engage instead of surfacing a terminal failure. (#57441) Thanks @zijiess and @vincentkoc.
322
+ - Agents/compaction: keep late compaction-retry rejections handled after the aggregate timeout path wins without swallowing real pre-timeout wait failures, so timed-out retries no longer surface an unhandled rejection on later unsubscribe. (#57451) Thanks @mpz4life and @vincentkoc.
323
+ - Matrix/delivery recovery: treat Synapse `User not in room` replay failures as permanent during startup recovery so poisoned queued messages move to `failed/` instead of crash-looping Matrix after restart. (#57426) thanks @dlardo.
324
+ - Plugins/facades: guard bundled plugin facade loads with a cache-first sentinel so circular re-entry stops crashing `xai`, `sglang`, and `vllm` during gateway plugin startup. (#57508) Thanks @openperf.
325
+ - Agents/MCP: dispose bundled MCP runtimes after one-shot `openclaw agent --local` runs finish, while preserving bundled MCP state across in-run retries so local JSON runs exit cleanly without restarting stateful MCP tools mid-run (#57816). Thanks @ForestDengHK.
326
+ - Gateway/OpenAI HTTP: restore default operator scopes for bearer-authenticated requests that omit `x-openclaw-scopes`, so headless `/v1/chat/completions` and session-history callers work again after the recent method-scope hardening. (#57596) Thanks @openperf.
327
+ - Gateway/attachments: offload large inbound images without leaking `media://` markers into text-only runs, preserve mixed attachment order for model input/transcripts, and fail closed when model image capability cannot be resolved. (#55513) Thanks @Syysean.
328
+ - Agents/subagents: fix interim subagent runtime display so `/subagents list` and `/subagents info` stop inflating short runtimes and show second-level durations correctly. (#57739) Thanks @samzong.
329
+ - Diffs/config: preserve schema-shaped plugin config parsing from `diffsPluginConfigSchema.safeParse()`, so direct callers keep `defaults` and `security` sections instead of receiving flattened tool defaults. (#57904) Thanks @gumadeiras.
330
+ - Diffs: fall back to plain text when `lang` hints are invalid during diff render and viewer hydration, so bad or stale language values no longer break the diff viewer. (#57902) Thanks @gumadeiras.
331
+ - Doctor/plugins: skip false Matrix legacy-helper warnings when no migration plans exist, and keep bundled `enabledByDefault` plugins in the gateway startup set. (#57931) Thanks @dinakars777.
332
+ - Matrix/CLI send: start one-off Matrix send clients before outbound delivery so `openclaw message send --channel matrix` restores E2EE in encrypted rooms instead of sending plain events. (#57936) Thanks @gumadeiras.
333
+ - xAI/Responses: normalize image-bearing tool results for xAI responses payloads, including OpenResponses-style `input_image.source` parts, so image tool replays no longer 422 on the follow-up turn. (#58017) Thanks @neeravmakwana.
334
+ - Cron/isolated sessions: carry the full live-session provider, model, and auth-profile selection across retry restarts so cron jobs with model overrides no longer fail or loop on mid-run model-switch requests. (#57972) Thanks @issaba1.
335
+ - Matrix/direct rooms: stop trusting remote `is_direct`, honor explicit local `is_direct: false` for discovered DM candidates, and avoid extra member-state lookups for shared rooms so DM routing and repair stay aligned. (#57124) Thanks @w-sss.
336
+ - Agents/sandbox: make remote FS bridge reads pin the parent path and open the file atomically in the helper so read access cannot race path resolution. Thanks @AntAISecurityLab and @vincentkoc.
337
+ - Tools/web_fetch: add an explicit trusted env-proxy path for proxy-only installs while keeping strict SSRF fetches on the pinned direct path, so trusted proxy routing does not weaken strict destination binding. (#50650) Thanks @kkav004.
338
+ - Exec/env: block Python package index override variables from request-scoped host exec environment sanitization so package fetches cannot be redirected through a caller-supplied index. Thanks @nexrin and @vincentkoc.
339
+ - Telegram/audio: transcode Telegram voice-note `.ogg` attachments before the local `whisper-cli` auto fallback runs, and keep mention-preflight transcription enabled in auto mode when `tools.media.audio` is unset (#65984). Thanks @mbelinky.
340
+ - Matrix/direct rooms: recover fresh auto-joined 1:1 DMs without eagerly persisting invite-only `m.direct` mappings, while keeping named, aliased, and explicitly configured rooms on the room path. (#58024) Thanks @gumadeiras.
341
+ - TTS: Restore 3.28 schema compatibility and fallback observability. (#57953) Thanks @joshavant.
342
+ - Telegram/forum topics: restore reply routing to the active topic and keep ACP `sessions_spawn(..., thread=true, mode="session")` bound to that same topic instead of falling back to root chat or losing follow-up routing. (#56060) Thanks @one27001.
343
+ - Config/SecretRef + Control UI: harden SecretRef redaction round-trip restore, block unsafe raw fallback (force Form mode when raw is unavailable), and preflight submitted-config SecretRefs before config write RPC persistence. (#58044) Thanks @joshavant.
344
+ - Config/Telegram: migrate removed `channels.telegram.groupMentionsOnly` into `channels.telegram.groups["*"].requireMention` on load so legacy configs no longer crash at startup. (#55336) thanks @jameslcowan.
345
+ - Gateway/SecretRef: resolve restart token drift checks with merged service/runtime env sources and hard-fail unsupported mutable SecretRef plus OAuth-profile combinations so restart warnings and policy enforcement match runtime behavior. (#58141) Thanks @joshavant.
346
+ - Telegram/outbound chunking: use static markdown chunking when Telegram runtime state is unavailable so long outbound Telegram messages still split correctly after cold starts. (#57816) Thanks @ForestDengHK.
347
+ - Update/Corepack: disable interactive Corepack download prompts during update preflight install unless `COREPACK_ENABLE_DOWNLOAD_PROMPT` is already explicitly set, so `openclaw update` can fetch the repo-pinned pnpm version non-interactively. (#61456) Thanks @p6l-richard.
348
+
CHANGELOG/2026.5.19.md ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.5.19
2
+
3
+ ### Changes
4
+
5
+ - Agents: clarify that fixes should default to clean bounded refactors, lean internals, and explicit plugin SDK/API deprecation paths.
6
+ - Agents/tools: normalize Swagger/OpenAPI refs and OpenAPI schema annotations when preparing tool parameter schemas.
7
+ - Dependencies: update `@openclaw/proxyline` to 0.3.3.
8
+ - Dependencies: update Pi packages to 0.75.1 and raise the minimum supported Node.js 22 line to 22.19.
9
+ - Docker/Podman: add `OPENCLAW_IMAGE_APT_PACKAGES` as the runtime-neutral image build arg for extra apt packages while keeping `OPENCLAW_DOCKER_APT_PACKAGES` as a legacy fallback. (#62431) Thanks @urtabajev.
10
+ - Gateway/ACPX: attribute startup probe, config, runtime, and resource-count costs in restart traces without changing readiness behavior. (#83300) Thanks @samzong.
11
+ - Gateway: overlap startup logging and plugin-service startup with channel sidecars to reduce restart ready latency while preserving `/readyz` sidecar gating. (#83301) Thanks @samzong.
12
+ - Plugins/admin-http-rpc: allow trusted admin HTTP RPC clients to start and wait for web QR login flows. (#83259) Thanks @liorb-mountapps.
13
+ - Mac app: redesign Settings pages with consistent card layouts, cached navigation, cleaner permissions/voice/skills/cron/exec/debug panes, and steadier spacing around the native sidebar.
14
+ - Mac app: refine Voice & Talk recognition-language and wake-phrase settings so they use the same compact card rows as the rest of Settings.
15
+ - Skills: rename the repo-local Codex closeout review skill and helper to `autoreview` while preserving the Codex-first fallback behavior.
16
+ - Skills: add a meme-maker skill for curated template search, local SVG/PNG rendering, Imgflip hosted rendering, and Know Your Meme provenance links.
17
+ - Skills CLI: allow `openclaw skills install` and `openclaw skills update` to target shared managed skills with `--global`. (#74466) Thanks @Marvae.
18
+ - Browser: surface pending and recently handled modal dialogs in snapshots, return `blockedByDialog` when an action opens a modal, and allow `browser dialog --dialog-id` to answer pending dialogs.
19
+ - Browser CLI: add `openclaw browser evaluate --timeout-ms` so long-running page functions can extend both the evaluate action and request timeout budgets. (#83447) Thanks @eefreenyc.
20
+ - Codex app-server: scope OpenClaw prompt guidance by runtime surface so native Codex keeps Codex-owned base/personality instructions while OpenClaw contributes only runtime context, delivery guidance, and explicitly scoped command hints. (#83454) Thanks @100yenadmin.
21
+ - Docker/Podman: add `OPENCLAW_IMAGE_PIP_PACKAGES` for opt-in Python package installation in local image builds. (#83771) Thanks @stephenredmond-straiteis.
22
+ - Agents/tools: shorten built-in tool descriptions and schema hints across media, messaging, sessions, cron, Gateway, web, image/PDF, TTS, nodes, and plan tools while preserving routing guardrails.
23
+ - Skills: add node inspector debugging, fused diagram generation, and throwaway spike workflow skills.
24
+ - CLI/plugins: add `defineToolPlugin` plus `openclaw plugins build`, `validate`, and `init` for typed simple tool plugins with generated manifest metadata, optional tool declarations, and context factories.
25
+ - Agents/skills: tighten bundled skill prompts and metadata, quote skill descriptions, refresh current CLI/API guidance, and update embedded sherpa-onnx runtime downloads.
26
+ - Skills: update the Obsidian skill to target the official `obsidian` CLI and require its registered binary instead of the third-party `obsidian-cli`.
27
+ - Skills: add a Python debugging skill for pdb, breakpoint(), post-mortem inspection, and debugpy remote attach.
28
+ - Codex: add `/codex plugins list`, `enable`, and `disable` for managing configured native Codex plugins from chat without editing config by hand.
29
+ - Plugins/messages: add presentation capability limits for channel renderers, adapt rich message controls before native rendering, and mark legacy `interactive`/Slack directive producer APIs as deprecated.
30
+ - Plugins/subagents: store channel delivery routes as canonical session metadata and deprecate ad hoc subagent hook delivery-origin fields in favor of core route projection.
31
+ - Proxy: support HTTPS managed forward-proxy endpoints and scoped `proxy.tls.caFile` CA trust for proxy endpoint TLS. (#79171) Thanks @jesse-merhi.
32
+ - QA-Lab: add first-hour 20-turn and optional 100-turn runtime parity scenarios, with tier metadata for standard and soak QA gates. Fixes #80338; refs #80337. Thanks @100yenadmin.
33
+ - QA-Lab: add `openclaw qa suite --runtime-parity-tier` and wire the standard Codex-vs-Pi tier into release checks separately from optional/live-only/soak lanes. Fixes #80337. Thanks @100yenadmin.
34
+ - QA-Lab: add a live-only Codex Pi-shaped Read vocabulary canary so runtime parity catches native workspace-read prompt compatibility drift. (#80323) Thanks @100yenadmin.
35
+ - QA-Lab: add live-only harness self-health scenarios for plugin hook crashes, manifest contract errors, and WebChat direct-reply self-message routing. (#80323) Thanks @100yenadmin.
36
+ - QA-Lab: add runtime tool fixture scenarios and coverage reporting for Codex-native workspace tools, OpenClaw dynamic tools, and optional plugin-backed tools. Fixes #80173. Thanks @100yenadmin.
37
+ - QA-Lab: expose runtime tool fixture coverage through `openclaw qa coverage --tools`, with optional suite-summary evaluation for parity gate artifacts. Thanks @100yenadmin.
38
+ - QA-Lab: schedule a live-frontier Codex-vs-Pi runtime token-efficiency artifact lane in the all-lanes QA workflow. Fixes #80175. Thanks @100yenadmin.
39
+ - QA-Lab: hard-gate required OpenClaw dynamic runtime-tool drift in the standard Codex-vs-Pi tier with a blocking release-check verifier and publish the tool coverage report artifact. Fixes #80339; refs #80319. Thanks @100yenadmin.
40
+ - QA-Lab: add the personal-agent approval-denial scenario so the benchmark pack verifies denied local reads stop cleanly without tool progress or fixture leaks. (#83150) Thanks @iFiras-Max1.
41
+ - QA-Lab: extend the personal-agent benchmark pack with a local task followthrough scenario for proof-backed pending, blocked, and done status reporting. Thanks @iFiras-Max1.
42
+ - QA-Lab: add a report-only dreaming shadow-trial scenario so candidate memory promotion can be evaluated without mutating `MEMORY.md`. Thanks @iFiras-Max1.
43
+ - Gateway/performance: add `pnpm test:restart:gateway` benchmark tooling for repeated restart readiness, downtime, trace, and resource-slope evidence. (#83299) Thanks @samzong.
44
+ - Android: switch Talk Mode to realtime Gateway relay voice sessions with streaming mic input, realtime audio playback, tool-result bridging, and on-screen transcripts. (#83130) Thanks @sliekens.
45
+ - Gateway/config: expose config lookup reload metadata so tools can distinguish restart-required, hot-reloadable, and no-op fields before applying config edits. Fixes #81409. (#81612) Thanks @LLagoon3.
46
+ - Telegram: add allowlisted native DM draft previews for transient tool progress while keeping final answers on the normal persistent delivery path. (#83622) Thanks @akrimm702.
47
+ - QA-Lab: add a personal-agent share-safe diagnostics artifact scenario so support handoffs keep useful status while omitting raw personal content. Thanks @iFiras-Max1.
48
+ - QA-Lab: add a personal-agent no-fake-progress scenario so completion claims stay tied to local evidence instead of unsupported external progress. (#83824) Thanks @iFiras-Max1.
49
+
50
+ ### Fixes
51
+
52
+ - Agents/exec approvals: return approved WebChat gateway exec output inline after native approval instead of leaving the model waiting for an async follow-up. (#82019) Thanks @Zac-W.
53
+ - CLI/node: reject invalid explicit `node run --port` values instead of silently falling back to the configured or default port. Fixes #83923. Thanks @davinci282828.
54
+ - CLI: reject explicit port numbers above 65535 before they reach Gateway or Node bind paths. Fixes #83900. (#84008) Thanks @hclsys.
55
+ - Codex app-server: preserve plugin tool auth profiles when Codex owns model transport so OpenClaw dynamic tools can resolve their provider credentials. (#83603) Thanks @rubencu.
56
+ - Memory/search: scan the JS-side fallback vector path (used when the sqlite-vec index is unavailable or has a mismatched dimension) in bounded rowid batches and yield to the event loop between batches so large chunk tables can no longer pin the Node.js main thread for multi-second windows. Also keeps the SQL prepared statement rooted in a local so node:sqlite cannot finalize it mid-scan under heap pressure. Fixes #81172. Thanks @dev23xyz-oss.
57
+ - Telegram: preserve inbound bold, italic, code, preformatted, strikethrough, underline, spoiler, and text-link entities as markdown in the agent-facing prompt body. Fixes #52859.
58
+ - Backup: dereference hardlinks during archive creation and reject unsafe hardlink targets during verification so archives that pass `backup verify` do not fail broad extraction on macOS tar. Fixes #54242. Thanks @jason-allen-oneal.
59
+ - Memory Wiki: preserve fs-safe diagnostics when bridge source page writes fail for non-symlink filesystem safety reasons, so directory collisions are reported with the underlying error code. (#83776) Thanks @TurboTheTurtle.
60
+ - Telegram: keep forum topics from blocking sibling topic traffic by routing inbound serialization, media/text buffers, and account API queues on topic-aware lanes. (#83829)
61
+ - Telegram: keep queued forum-topic follow-up messages from inheriting superseded source abort signals, so later same-topic user turns can still run and reply after an active turn is replaced. (#83827) Thanks @VACInc.
62
+ - CLI/update: bypass npm freshness filters consistently during managed package and plugin installs so freshly published release plugins remain installable. Thanks @jalehman.
63
+ - CLI/update: guide root-owned npm install EACCES recovery by stopping the managed Gateway before manual package replacement, then reinstalling and restarting the service. Fixes #83747. (#83757) Thanks @brokemac79.
64
+ - Twitch: register refreshing chat tokens with Twurple's chat intent so automatic token refresh keeps chat access available. (#83750) Thanks @TurboTheTurtle.
65
+ - Agents/subagents: keep collect-mode announce queues batching unresolved-origin items with compatible same-route messages and resume collection after a true cross-channel drain when a later compatible batch remains. Fixes #83577.
66
+ - CLI/config: preserve numeric-looking record keys such as Discord guild IDs when creating missing config containers with `config set`. (#83769) Thanks @TurboTheTurtle.
67
+ - Skills: refresh existing session skill snapshots when watched skill roots change, so changed extra skill directories take effect without starting a new session. Fixes #83782. (#83800) Thanks @hclsys.
68
+ - Providers/Anthropic: preserve native image input for current Claude model rows when stale local catalog data marks them text-only. (#83756) Thanks @TurboTheTurtle.
69
+ - Providers/Anthropic: preserve Claude 4 image capability when configured model refs resolve through a stale local catalog row. (#83756) Thanks @TurboTheTurtle.
70
+ - Providers/DeepSeek: normalize MCP tool schemas with `anyOf`/`oneOf` unions before normal and compaction requests reach DeepSeek, preventing union-shaped parameters from being rejected. (#83766) Thanks @TurboTheTurtle.
71
+ - Control UI: render live tool progress from session-scoped `session.tool` Gateway events so externally started runs show their tool cards in the active session. (#83734) Thanks @TurboTheTurtle.
72
+ - Outbound: resolve send-capable channel plugins from the active runtime registry when the pinned startup registry only has setup metadata. (#83733) Thanks @TurboTheTurtle.
73
+ - Discord: preserve streamed reply previews when recovered tool-warning finals are delivered before or after the assistant's final reply. (#84169) Thanks @neeravmakwana.
74
+ - Control UI: keep the chat delete confirmation popover clamped inside the visible viewport on small screens. (#83804) Thanks @ThiagoCAltoe.
75
+ - Browser: enforce current-tab URL allowlist checks for `/act` evaluate/batch actions and `/highlight` routes while leaving tab-management actions unblocked. (#78523)
76
+ - CI: require real-behavior-proof verdict markers to come from the ClawSweeper GitHub App before accepting exact-head proof. (#83692)
77
+ - Models: show the effective OpenAI/Codex auth profile in `/models` provider headers instead of falling back to the OpenAI env-key label. (#83697) Thanks @yu-xin-c.
78
+ - CLI: include active bundled loopback MCP tools in CLI system prompts and reset provider-side CLI sessions when that prompt-visible tool surface changes. (#83785) Thanks @TurboTheTurtle.
79
+ - Browser: keep a profile `cdpPort` when its `cdpUrl` omits a port, while still letting explicitly written URL ports win. (#82166) Thanks @Marvae.
80
+ - Agents/image generation: allow distinct `image_generate` prompts to start separate session-backed background tasks while same-prompt retries still return the active task status. (#83614) Thanks @Elarwei001.
81
+ - Gateway/WebChat: honor configured `channels.webchat.textChunkLimit` and `chunkMode` overrides when chunking WebChat replies. (#83713)
82
+ - Control UI: stop the chat reading indicator from sticking after an assistant response finishes. (#83515) Thanks @njuboy11.
83
+ - Skills: reject empty or whitespace-only skill names and descriptions during quick validation. (#27061)
84
+ - Sessions: skip trailing custom transcript entries when checking tail assistant replies so embedded CLI gap-fill does not duplicate canonical assistant output. (#83635) Thanks @yaoyi1222.
85
+ - Memory Wiki: keep `wiki_lint` tool output path-safe by reporting vault-internal lint reports as relative paths in tool text and details while preserving absolute report paths for CLI/file callers. (#83439) Thanks @LLagoon3.
86
+ - Telegram: keep verbose tool progress visible without mirroring non-final progress into active session transcripts, preventing embedded provider replies from aborting mid-run. (#83631) Thanks @kurplunkin.
87
+ - Telegram: log successful outbound text and media deliveries with account, chat, message, operation, thread, reply, silent, and chunk metadata while keeping message bodies out of logs. Fixes #83196. (#83247) Thanks @jrwrest.
88
+ - Cron: link isolated scheduled task runs to their stable cron session so task status and cleanup can follow the backing agent run. (#83606) Thanks @jai.
89
+ - Codex app-server: mark Codex-native subagent task mirrors terminal when blocked or failed spawn-agent calls arrive with stale initializing child state, preventing task registry entries from staying running. Fixes #83852. (#83945) Thanks @joshavant.
90
+ - CLI: enforce the documented Node.js 22.19 runtime floor in the source launcher.
91
+ - Release stability: repair broad-gate regressions in requester-agent completion handoff, QA-Lab mock spawn attribution, Slack monitor test isolation, plugin uninstall peer fixtures, and Node-floor launcher contract coverage.
92
+ - Agents/replies: persist queued follow-up user messages and assistant error stubs only once across model-fallback retries, preventing repeated provider rejections from corrupted same-role session transcripts. Fixes #83404. (#83417) Thanks @yetval.
93
+ - Telegram: preserve reply-target context for bare mention replies on runtime-only turns so the model sees the replied-to message body. Fixes #83767. (#83953) Thanks @joshavant.
94
+ - ClawHub: preserve configured base URL path prefixes when building API request URLs, so self-hosted ClawHub instances mounted under a subpath keep routing correctly. (#83982) Thanks @ThiagoCAltoe.
95
+ - Slack: persist delivered inbound message IDs and fail closed when same-channel thread replies lose their thread context, preventing delayed duplicate replies and accidental channel-root posts. Fixes #83521. Thanks @shannon0430.
96
+ - Codex app-server: complete OpenClaw dynamic tool diagnostics at the request boundary so successful, failed, timed out, aborted, and blocked tool calls do not leave active tool state behind. Fixes #83474. Thanks @rozmiarD.
97
+ - Doctor/Codex: warn when Linux host policy blocks the Codex bwrap user or network namespace path used by sandboxed app-server turns, with Ubuntu/AppArmor repair guidance. Refs #83018.
98
+ - Gateway/config: keep config writes from failing on unrelated unresolved auth-profile SecretRefs while preserving live auth-profile runtime snapshots.
99
+ - Gateway/sessions: clear stored CLI provider resume bindings on non-subagent `/reset` so the next turn starts a fresh provider-side CLI conversation instead of resuming old context. (#83448) Thanks @jasonyliu.
100
+ - Doctor: preserve legacy whole-agent Claude CLI intent by moving matching Anthropic model selections to model-scoped runtime policy before removing stale runtime pins. Fixes #83491. Thanks @danielcrick.
101
+ - Discord/OpenAI: keep realtime Discord voice sessions hearing follow-up turns with OpenAI realtime and prebuffer assistant playback to avoid choppy starts. (#80505) Thanks @Solvely-Colin.
102
+ - LM Studio: resolve env-template API keys like `${LMSTUDIO_API_KEY}` through the standard SecretInput path instead of sending the raw template as the bearer token, and preserve header-auth and discovery-key precedence when the template is unset. Fixes #80495. (#80568) Thanks @MonkeyLeeT.
103
+ - Discord/subagents: route the initial reply from thread-bound delegated sessions into the bound Discord thread instead of the parent channel. Fixes #83170. (#83172) Thanks @100menotu001.
104
+ - Gateway/sessions: rotate failed agent sessions when their transcript file is missing instead of wedging per-channel lanes. Fixes #83488. (#83553) Thanks @LLagoon3.
105
+ - Agents: refresh final-delivery routing from fresh session state before declaring a no-send failure, keeping recovered runs on the normal durable delivery path. (#83835) Thanks @joshavant.
106
+ - Agents: guard final-delivery fresh session routing against mismatched logical sessions before reusing recovered delivery context. (#83928) Thanks @joshavant.
107
+ - Media: prevent image metadata probing from invoking external decoder delegates on unrecognized image bytes, and stop fallback chaining after real processing errors.
108
+ - Media: install Sharp with the root package and fall back to sips, Windows native imaging, ImageMagick, GraphicsMagick, or ffmpeg for image resizing/conversion when Sharp is unavailable. Fixes #83401. Thanks @scotthuang.
109
+ - Channels/bundled: append `openclaw doctor --fix` guidance to the bundled-channel load warnings emitted on `ERR_MODULE_NOT_FOUND` / `MODULE_NOT_FOUND` (including those wrapped on `.cause` by the native-require loader), so users hitting unstaged plugin runtime deps (e.g. `nostr-tools`) see an actionable repair hint instead of a bare module-not-found warning. (#76974) Thanks @BSG2000.
110
+ - Telegram: deliver generated media completions back into forum topics by preserving topic IDs across requester-agent handoff. (#83556) Thanks @fuller-stack-dev.
111
+ - Gateway: defer update-check startup until after readiness so package update checks no longer block sidecar-ready startup, while preserving update broadcasts and shutdown cleanup. (#83520) Thanks @samzong.
112
+ - Telegram: keep `/btw` and read-only status commands from aborting active runs, and avoid retaining raw update payloads in timed-out spool tombstones. Refs #83272.
113
+ - Agents: log strict-agentic execution contract diagnostics only when the planning-only retry path actually triggers.
114
+ - Agents: stop embedded session takeover and session write-lock errors from consuming model fallbacks while preserving provider fallback metadata. Fixes #83510. Thanks @luyao618.
115
+ - Agents/video: hide `video_generate` reference-audio parameters unless a registered video provider supports audio inputs.
116
+ - Plugins: fall back to npm for official ClawHub updates when artifact downloads are unavailable, including beta-to-default fallback and dry-run version reporting.
117
+ - Plugins/xAI: echo PKCE challenge fields during OAuth authorization-code token exchange for xAI token-endpoint compatibility. (#83499) Thanks @fuller-stack-dev.
118
+ - Codex app-server: hydrate current inbound image attachments before queued runs so Responses-backed agents receive Discord and other channel images as native vision input. Fixes #83466. Thanks @iannwu.
119
+ - Codex app-server: keep native code mode available without forcing code-mode-only so OpenClaw dynamic tool turns complete through the app-server tool bridge. Fixes #83109. Thanks @daswass.
120
+ - Codex app-server: expose OpenClaw's sandbox-routed shell as `sandbox_exec`/`sandbox_process` for non-Docker sandbox backends so SSH sandbox agents keep a correctly routed shell path without shadowing Codex native shell. Fixes #80322. Thanks @keramblock.
121
+ - Release stability: recover stale session diagnostics and Codex OAuth fallback state so stuck runs and reused refresh tokens clear without blocking follow-up work. (#83503) Thanks @100yenadmin.
122
+ - Messages/TTS: apply TTS directives before message-tool sends reach core, gateway, or plugin delivery so opt-in message-tool rooms and proactive sends attach voice notes instead of leaking raw tags. Fixes #81598. Thanks @CG-Intelligence-Agent-Jack and @CoronovirusG10.
123
+ - Messages/Codex: keep Codex direct/source chats on message-tool visible delivery by default while documenting and testing `messages.visibleReplies: "automatic"` as the old-mode opt-out; channel wildcard model overrides now apply to direct chats before harness delivery defaults.
124
+ - Memory/QMD: keep archived session transcript hits visible after QMD export while preserving normal `.md` session ids that only resemble archive names. (#83518; fixes #83506) Thanks @tanshanshan.
125
+ - Codex app-server: preserve network access for sandboxed Codex code-mode turns when the OpenClaw sandbox allows outbound egress. Fixes #83347. Thanks @YusukeIt0.
126
+ - Codex app-server: honor writable Docker bind mounts for sandboxed workspace-write turns while disabling native Code Mode when container-path aliases or read-only bind shadows cannot be represented safely host-side. Fixes #83737. (#83849) Thanks @joshavant.
127
+ - QA-Lab: keep the OTLP smoke decoder independent of removed OpenTelemetry generated-root internals.
128
+ - Messages: default group/channel visible replies to automatic final delivery again, keeping `message_tool` opt-in for ambient/shared rooms and tool-reliable models.
129
+ - CLI/TUI: force standalone `/exit` runs to terminate after `runTui` returns so onboarding-launched TUI children do not stay alive invisibly. (#83501) Thanks @fuller-stack-dev.
130
+ - Agents/code mode: honor per-agent code-mode config in schema, runtime catalog activation, and model payload filtering. Fixes #83388. Thanks @Kaspre.
131
+ - Agents/code mode: preserve agent, session, run, and channel context in `before_tool_call` hooks for top-level `exec`/`wait` dispatches. Fixes #83387.
132
+ - QQBot: shorten C2C typing indicators to a 10-second window renewed every 5 seconds, capped to keep a final passive-reply slot available. (#83469)
133
+ - Replies: keep final payload delivery after live preview updates so channels can finalize or send the completed answer instead of losing preview-only drafts. (#83468)
134
+ - Discord: deliver final replies in progress-mode preview streams instead of deduplicating the final visible message. (#83443) Thanks @compoodment.
135
+ - Providers/Xiaomi: replay MiMo Anthropic-compatible `reasoning_content` as provider-required thinking blocks even when OpenClaw thinking is disabled, fixing follow-up tool turns for `mimo-v2-flash`. Fixes #83407. Thanks @Xgenious7.
136
+ - Agents/exec approvals: forward approval-runtime credentials on agent-owned Gateway approval calls so approved async commands complete through the existing runtime path instead of stalling on unauthenticated follow-up calls. Thanks @IWhatsskill, @Patrick-Erichsen, and @jesse-merhi.
137
+ - Gateway/skills: preflight remote macOS skill-bin refreshes with a WebSocket connectivity check so stale node sessions skip quickly instead of logging slow `system.which` timeout warnings.
138
+ - CLI/config: keep broken discovered plugins that are not referenced by active config from failing `openclaw config validate`, while preserving fatal errors for explicitly configured plugin entries.
139
+ - GitHub Copilot: drop unsafe native Responses reasoning replay items with non-replayable IDs before dispatch, preventing affected Copilot sessions from failing with `invalid_request_body`. Fixes #83220. Thanks @galiniliev.
140
+ - Agents/Codex: fail closed when an explicitly requested Codex harness is not registered instead of silently trying configured model fallbacks. Fixes #83349. Thanks @r2-vibes.
141
+ - QA-Lab: make runtime tool coverage fail on missing required tool exercise instead of treating pass/pass parity envelope drift as missing coverage.
142
+ - Core/plugins: harden clawpatch-reported edge cases across gateway auth cleanup, Claude session id paths, plugin activation policy, apply-patch hunk handling, diagnostic redaction, and plugin metadata validation.
143
+ - UI: show reasoning choices as plain labels instead of leaking internal override wording in session and chat pickers.
144
+ - Mac app: avoid repeating the Configuration heading inside channel quick settings.
145
+ - Mac app: keep the Settings sidebar always visible and remove the redundant titlebar hide/show control.
146
+ - Mac app: normalize Settings pane content margins so pages share the same left and right rail.
147
+ - Mac app: prefer explicit private/Tailscale/LAN Gateway endpoints over SSH tunnels, preserve legacy loopback tunnel configs, persist transport choices, and show captured SSH stderr when tunneling really fails.
148
+ - Gateway/sessions: keep ACP/acpx and runtime child sessions visible in configured-only session lists when their owner or parent session belongs to a configured agent.
149
+ - Mac app: keep app-level menu commands and Dashboard failure states reachable when the remote Gateway is disconnected.
150
+ - Mac app: allow longer Gateway and Context errors to wrap in the menu instead of truncating the useful failure detail.
151
+ - Mac app: tighten remote Gateway fields in Settings so the Connection pane keeps readable labels and full action button text.
152
+ - Mac app: keep custom Settings card rows left-aligned and full-width so Discovery and status sections no longer appear centered or detached.
153
+ - Mac app: align Location permission controls to the same trailing column as the rest of Settings.
154
+ - Mac app: add Dashboard, Chat, Canvas, and Settings shortcuts to the Dock icon menu.
155
+ - Mac app: replace the Settings window's native split-view sidebar with an explicit layout so page content keeps its leading gutter when the sidebar is shown or hidden.
156
+ - Mac app: render channel quick config as aligned Settings rows and hide schema-only variants that cannot be edited safely from the quick pane.
157
+ - Gateway/webchat: hide internal runtime-context and other `display: false` transcript messages from Chat history and live message events. Fixes #83216. Thanks @EmpireCreator.
158
+ - CLI/help: keep `gateway`, `doctor`, `status`, and `health` help registration out of action/runtime imports so subcommand `--help` stays lightweight in constrained terminals. Fixes #83228. Thanks @dfguerrerom.
159
+ - CLI/help: show plugin-owned command help based on the active memory slot so LanceDB memory users see `ltm` instead of unavailable `memory` commands. Fixes #83745. (#83841) Thanks @joshavant.
160
+ - Cron/Discord: keep explicit announce runs in message-tool-only source-reply mode so scheduled agent turns post once instead of also echoing through automatic visible replies. Fixes #83261. Thanks @Theralley.
161
+ - Telegram: preserve forum-topic origin targets in inbound, audio-preflight, and skipped-message hook contexts so follow-up delivery stays bound to the originating topic. Fixes #83302. Thanks @M00zyx.
162
+ - Telegram: retry HTTP 421 Misdirected Request send failures on a fresh fallback transport so transient edge-node routing errors no longer drop outbound replies. Fixes #48892. (#48908) Thanks @MarsDoge.
163
+ - Telegram: fail topic sends closed when Telegram reports `message thread not found` instead of retrying without `message_thread_id` into the base chat. Refs #83302.
164
+ - Config/subagents: remove ignored agent-model `timeoutMs` keys, keep subagent model config to primary/fallback selection, and clean shipped stale config through doctor. Fixes #83291. Thanks @giodl73-repo.
165
+ - Mac app: align the Sessions settings pane with the standard Settings page gutter and row spacing.
166
+ - OpenAI/Codex: stop rejecting available `openai-codex` GPT-5.1, GPT-5.2, and GPT-5.3 model refs during config validation, while keeping removed Spark aliases suppressed. Fixes #83303.
167
+ - Plugins/xAI: complete OAuth-backed xAI login and sidecar auth fixes, including guarded loopback callback CORS handling, video generation polling/defaults, and native-host User-Agent attribution. (#83322) Thanks @Jaaneek.
168
+ - Codex app-server: preserve streamed native command output in mirrored transcripts and trajectory exports when final snapshots omit aggregated output. (#83200) Thanks @rozmiarD.
169
+ - Codex app-server: fail closed when chat or sender policy denies tools, disabling native code, app, environment, and user MCP surfaces for restricted turns. (#82374) Thanks @VACInc.
170
+ - Codex app-server: keep recent context-engine messages when oversized projected history is truncated, so short follow-ups in long channel sessions do not fall back to stale earlier turns. (#83127) Thanks @VACInc.
171
+ - Codex app-server: keep OpenClaw session spawning searchable while steering Codex-native delegation through native subagents, avoiding duplicate direct subagent surfaces. (#83329) Thanks @fuller-stack-dev.
172
+ - Codex app-server: recover stale childless Codex-native subagent task mirrors during maintenance and allow their registry rows to be cancelled without an OpenClaw child session. (#82836) Thanks @yshimadahrs-ship-it and @joshavant.
173
+ - Feishu: return bound subagent delivery origins from session thread setup so Feishu subagent completions route back to the same DM or topic. (#83190) Thanks @100menotu001.
174
+ - CLI/update: tailor post-update Gateway recovery hints by platform, showing systemd, LaunchAgent, Scheduled Task, or generic service-manager guidance instead of macOS-only recovery text. (#83096) Thanks @rubencu.
175
+ - Plugins: apply a default 15-second timeout to legacy `before_agent_start` hooks so hung plugin handlers no longer block agent startup. Fixes #48534. (#83136) Thanks @therahul-yo.
176
+ - Feishu: refresh inbound session delivery context for DM, group, and broadcast turns so later replies do not inherit stale WebChat routing. Fixes #78274.
177
+ - Agents/subagents: require the initial subagent registry save before reporting spawn accepted, returning a spawn error instead of losing an untracked run when the registry write fails. (#83146) Thanks @yetval.
178
+ - QA-Lab/qa-channel: attach redacted agent tool-start traces to outbound `QaBusMessage` records so scenarios can assert actual tool use instead of relying only on reply text. Fixes #67637. Thanks @100yenadmin.
179
+ - QA-Lab: fail live runtime parity reports when assistant-message usage is missing, preventing `0 vs 0` live token rows from being reported as passing proof. Fixes #80411. Thanks @100yenadmin.
180
+ - QA-Lab: add a runtime token-efficiency sidecar report that classifies Codex savings separately from regressions and fails only positive Codex-over-Pi live token deltas above threshold. Fixes #81093. Thanks @100yenadmin.
181
+ - QA-Lab: fail Codex-backed OpenAI live runtime-pair runs before launching isolated workers when no portable Codex auth is available, while staging API-key fallbacks and configured Codex keys for isolated QA agents. Fixes #80412. Thanks @100yenadmin.
182
+ - QA-Lab: refresh parity gates, mock frontier fixtures, model scenarios, and workflow artifact lanes to compare GPT-5.5 against Claude Opus 4.7. Fixes #74262. Thanks @100yenadmin.
183
+ - QA-Lab: make mock parity dispatch provider-aware for source discovery and subagent scenarios so OpenAI and Anthropic lanes no longer share identical canned plans. Fixes #64879. Thanks @100yenadmin.
184
+ - QA-Lab: stop returning Control UI bearer tokens from unauthenticated bootstrap payloads and bind Docker harness ports to loopback-only host addresses. (#66355) Thanks @pgondhi987.
185
+ - Mac app: avoid a SwiftUI metadata crash when rendering the Cron Jobs settings pane.
186
+ - Agents/subagents: preserve run-mode keep subagent registry entries past the session sweep TTL, so kept subagent runs remain visible after cleanup completes. Fixes #83132. (#83168) Thanks @yetval.
187
+ - Agents/OpenAI streams: yield via `setTimeout(0)` instead of `setImmediate` between bursty Responses chunks so abort timers can fire during the yield, keeping cancel-on-timeout responsive on hot streams. Refs #82462.
188
+ - Agents/Codex: keep legacy `oauthRef`-backed OAuth profiles usable while `openclaw doctor --fix` migrates them back to inline credentials, without creating new sidecar credentials. (#83312) Thanks @joshavant.
189
+ - Agents/Codex: load the selected provider owner alongside the Codex harness runtime so `openai-codex` models resolve when plugin allowlists scope runtime loading. Fixes #83380. (#83519) Thanks @joshavant.
190
+ - Telegram: fail stalled isolated-ingress handlers into tombstones and abort same-lane reply work before restarting, so later same-chat updates drain after a hung turn. Fixes #83272. (#83505) Thanks @joshavant.
191
+ - CLI/config: send SecretRef diagnostics to stderr so JSON command stdout remains parseable.
192
+ - CLI/doctor: seed Control UI allowed origins when migrating legacy non-loopback gateway bind host aliases like `0.0.0.0`. Fixes #83286. Thanks @giodl73-repo.
193
+ - CLI/plugins: ship the bundled memory CLI as a package entry so package-installed `openclaw memory` commands register correctly.
194
+ - CLI/update: defer doctor-time plugin package installs during package swaps and seed post-core repair from the updated install registry, preventing duplicate reinstall failures.
195
+ - CLI/update: preserve old-parent-readable config metadata during legacy package handoffs, fall back only to official `@openclaw/*` npm plugin packages when ClawHub plugin artifacts are unavailable, and keep managed service package roots authoritative during updates.
196
+ - Feishu: detect SecretRef top-level credentials as a configured default account instead of treating object-backed app secrets as missing.
197
+ - Gateway/restart: keep ordinary unmanaged SIGUSR1/config restarts in-process instead of detach-spawning an orphaned child, preserving custom supervisor PID tracking while leaving update restarts on the fresh-process path. Fixes #65668.
198
+ - CLI/completion: resolve concrete PowerShell profile paths and reload commands during setup and doctor completion installation. Fixes #44296. (#83059) Thanks @yu-xin-c.
199
+ - Telegram: keep isolated long polling below the hard `getUpdates` request guard so idle bot accounts with high `timeoutSeconds` do not false-disconnect and restart-loop. Fixes #83264. Thanks @riccodecarvalho.
200
+ - Providers/Google: preserve and recover Gemini 3 tool-call thought signatures during native replay so function-calling turns no longer fail with missing `thought_signature` 400s. Fixes #72879. (#80358) Thanks @abnershang.
201
+ - Telegram: skip transcript-only delivery mirrors and gateway-injected rows when resolving latest assistant text, preventing retained previews from replacing final replies with stale fragments. Fixes #83159. (#83362) Thanks @joshavant.
202
+ - Memory/QMD: keep lexical search on raw hyphenated queries while normalizing semantic QMD sub-searches, avoiding fallback to the builtin index for dashed identifiers and dates. Fixes #81328.
203
+ - Memory-core: distinguish sqlite-vec load failures from missing semantic vector embeddings in degraded `memory index` warnings, so vector recall diagnostics point at unresolved dimensions instead of blaming sqlite-vec when the store is ready. Fixes #75624. (#83056) Thanks @xuruiray and @Noah3521.
204
+ - Agents/subagents: preserve sandbox-peer controller ownership while routing completion announcements back to the originating run session, keeping subagent control and completion delivery scoped correctly. Fixes #80201. (#80242) Thanks @Jerry-Xin.
205
+ - Gateway: continue restarting remaining channels when one hot-reload channel restart fails, while still reporting aggregate reload failure and rolling back plugin pre-replace stops. Fixes #83054. Thanks @zqchris.
206
+ - Gateway/plugins: bind admin HTTP RPC dispatch to the accepting gateway instance so multi-gateway processes cannot execute plugin HTTP control-plane calls against another live gateway. Fixes #83486. (#83487) Thanks @coygeek.
207
+ - Telegram: keep hot-reload restarts from marking polling accounts manually stopped and restart isolated ingress cleanly after worker shutdown, preserving Telegram replies across config reloads. Fixes #83008. (#83410) Thanks @joshavant.
208
+ - Telegram/Ollama: pass current Telegram image attachments into native PI/Ollama vision turns so live photo prompts reach Ollama as native images. Fixes #83023. (#83516) Thanks @joshavant.
209
+ - Gateway/secrets: split the lightweight secrets runtime state and auth-store cache from the full secrets runtime and take a startup fast path when the gateway startup config has no SecretRef values, speeding up secrets startup while preserving cleanup and refresh semantics.
210
+ - Codex app-server: rotate oversized native Codex threads before resume and cap dynamic tool-result text entering native Codex sessions, preventing stale oversized context from surviving OpenClaw compaction. (#82981) Thanks @hansolo949.
211
+ - Gateway/restart: drain pending replies and active chat runs during restart shutdown before sockets and channels close, aborting timed-out chat runs through the normal cleanup path. (#69121) Thanks @alexlomt.
212
+ - Agents/Codex: use the Codex runtime context window for OpenAI-model preflight compaction and memory flush checks, so GPT-5.5 Codex sessions compact before hitting the smaller native context limit. Fixes #82982. Thanks @vliuyt.
213
+ - QA-Lab: clean orphaned gateway temp roots when a suite parent exits and wait on gateway plus transport readiness after config restarts, reducing stale `qa-channel` noise from interrupted runs. Fixes #65506. Thanks @100yenadmin.
214
+ - QA-Lab: wake qa-bus long polls that arrive with stale future cursors after a bus restart, preserving reconnect readiness for harness clients. (#67142) Thanks @hxy91819.
215
+ - QA-Lab: stage Multipass transfer scripts under OpenClaw's preferred temp root instead of raw OS temp paths, keeping the VM runner inside temp-path guardrails. (#64098) Thanks @ImLukeF.
216
+ - Agents/replies: keep surviving reply media and append a warning when other media references fail, so partial media normalization no longer drops failures silently. Thanks @Jerry-Xin.
217
+ - Config/models: accept `thinkingFormat: "together"` in model compat config so Together routes can opt into the Together-specific thinking response shape.
218
+ - Plugins/tokenjuice: bump the bundled tokenjuice runtime to 0.7.1, bringing Codex hook approval compatibility, pre-tool command wrapping fixes, and Rolldown/Vitest output compaction improvements into the OpenClaw plugin.
219
+ - Agents/OpenAI: stop post-processing GPT-5 final replies with hardcoded brevity caps, preserving full channel responses instead of appending synthetic ellipses, and log when strict-agentic GPT-5 execution activates. Fixes #82910.
220
+ - Mac app: refine the Settings General and Connection panes with cleaner status panels, card rows, and a single native titlebar sidebar toggle.
221
+ - Agents/media: deliver failed async image, music, and video generation completions directly when requester-session completion handoff fails, so channel users see provider errors instead of silent fallback stalls.
222
+ - Browser/CDP: keep loopback proxy bypass active across both `NO_PROXY` casings and redact home-relative Chrome MCP profile paths in attach-failure diagnostics.
223
+ - Agents/music: steer song, jingle, beat, anthem, and instrumental requests toward `music_generate` audio creation instead of lyric-only replies, and reserve `lyrics` for exact sung words.
224
+ - Codex app-server: record native Codex tool calls and results into trajectory artifacts so debug/trajectory exports capture the full Codex-native tool history, not just OpenClaw-bridged turns. Thanks @vyctorbrzezowski.
225
+ - Codex/app-server: keep bound conversation sessions on the owning agent runtime so native Codex control and follow-up turns do not fall back to the default agent client. Fixes #82954. (#82993)
226
+ - CLI/infer: run gateway model probes in fresh explicit sessions so one-shot provider checks do not inherit default agent transcript state. (#82861) Thanks @Kaspre.
227
+ - Providers/Together: send video-generation requests to Together's v2 video API even when shared text-model config still points at the v1 base URL. (#82992)
228
+ - Browser CLI: preserve browser-level options on nested commands, skip option values during lazy command registration, and keep long-running wait/download/dialog hooks open for their advertised wait window.
229
+ - CLI/sessions: accept `openclaw sessions list` as an alias for `openclaw sessions`, matching other list-style commands. Fixes #81139. (#81163) Thanks @YB0y.
230
+ - Channels/stream previews: widen compact progress draft lines and cut prose at word boundaries while preserving command/path suffixes, with `streaming.progress.maxLineChars` for channel-specific tuning.
231
+ - CLI/plugins: have `openclaw plugins doctor` warn when a configured runtime needs a missing owner plugin, sharing the same install mapping as `openclaw doctor --fix`. Fixes #81326. (#81674) Thanks @Zavianx.
232
+ - Agents/Codex: route OpenAI runs that resolve to `openai-codex` through the Codex provider and bootstrap OpenClaw's stored OAuth profile into the Codex harness when the harness owns transport, so `openai/*` model refs no longer fail with `No API key found for openai-codex` despite an existing Codex OAuth profile. (#82864) Thanks @ragesaq.
233
+ - Agents/ACP: distinguish prompt-submitted and runtime-active child stalls from true interactive waits, including redacted proxy-env diagnostics for Codex ACP no-output runs. Fixes #44810.
234
+ - Agents/memory: explain that memory-triggered compaction exposes only `read` and append-only `write` when configured core tools are unavailable in `tools.allow` warnings. Fixes #82941. Thanks @galiniliev.
235
+ - Agents/OpenAI: preserve deterministic tool payload ordering for prompt-cache reuse across OpenAI Responses and chat completions calls. (#82940) Thanks @galiniliev.
236
+ - ACP/Codex: honor terminal ACP turn results so failed Codex/acpx runs are not recorded as successful after only progress text. Fixes #79522. Thanks @dudaefj.
237
+ - Telegram: warn when a media group drops photos that fail to download, including albums where every photo is skipped. Fixes #55216. (#82987) Thanks @eldar702.
238
+ - Agents/diagnostics: treat repeated same-handle embedded-run cleanup as idempotent while preserving true replacement-handle mismatch diagnostics. Fixes #82959. (#82960) Thanks @galiniliev.
239
+ - Agents/subagents: preserve high-priority `AGENTS.md` policy in bootstrap context when oversized files are trimmed, and warn agents to read the full policy file before relying on scoped rules. Fixes #82920. (#82921) Thanks @galiniliev.
240
+ - Agents/skills: apply the full effective tool policy pipeline to inline `command-dispatch: tool` skill dispatch before owner-only filtering, preserving configured allow, deny, sandbox, sender, group, and subagent restrictions. (#78525)
241
+ - Codex: avoid spawning native hook relay subprocesses for post-tool/finalize events with no registered hook handlers while preserving pre-tool safety and approval relays. Fixes #76552. (#78004) Thanks @evgyur.
242
+ - Channel accounts: keep top-level default channel accounts visible when named accounts are added alongside default credential material, so mixed legacy/new account configs keep resolving `default` instead of silently dropping it.
243
+ - Agents/CLI: reject empty successful CLI subprocess replies as `empty_response` and keep them out of shared auth-profile health, so blank Claude CLI results no longer become green no-payload turns. Fixes #83231. (#83421) Thanks @joshavant.
244
+ - Codex/Telegram: synthesize native Codex tool progress from final turn snapshots so Telegram `/verbose` stays visible when command events arrive only at completion.
245
+ - Codex/Telegram: deliver Codex verbose tool summaries in direct message-tool-only turns while suppressing message-send and activity-log noise. (#83186) Thanks @kurplunkin.
246
+ - Mac app: make Channels settings open faster by deferring config-schema work, avoiding startup channel probes, caching decoded channel status rows, and showing only compact quick settings instead of the full generated channel schema.
247
+ - Control UI: include the Control UI and Gateway protocol versions in protocol-mismatch errors so stale app/dashboard pairings identify which side needs rebuilding or restarting.
248
+ - Gateway/protocol: restore Gateway WS protocol v4 and keep `message.action` room-event metadata on the existing `inboundTurnKind` wire field while preserving internal inbound-event classification.
249
+ - Agents/tools: prefer non-webchat session-key routes when the message tool has stale webchat context, so message-tool-only replies keep delivering to the originating channel. Fixes #82911. (#83004) Thanks @joshavant.
250
+ - Channels: keep direct-message last-route writes on isolated `per-channel-peer` sessions instead of contaminating the agent main session with channel delivery context. Fixes #36614. Thanks @aspenas.
251
+ - Mac app: move the Settings sidebar toggle into the native titlebar and tighten the General pane width.
252
+ - Mac app: keep visited Settings panes mounted so switching tabs no longer blanks and reloads their content.
253
+ - Mac app: make Config settings open from shallow schema lookups and load selected paths on demand instead of fetching and rendering the full generated config schema up front.
254
+ - Codex: sanitize inline image payloads before Codex app-server and OpenAI Responses replay, and clear poisoned Codex thread bindings after invalid image errors. Fixes #82878.
255
+ - Providers/GitHub Copilot: request identity-encoded Copilot API responses across token exchange, catalog, model calls, usage, and embeddings so compressed Business-account error payloads no longer reach JSON parsers as gzip bytes. Fixes #82871. Thanks @tonyfe01.
256
+ - Telegram: redact nested raw-update identifiers and user metadata before verbose raw update logging, preserving useful update/message ids without exposing chat, user, command, or profile details. (#82945) Thanks @galiniliev and @joshavant.
257
+ - Telegram: preserve replied-to bot messages, captions, and media metadata in group reply chains so follow-up replies understand what the user is reacting to. (#82863)
258
+ - Providers/Together: update PI runtime packages to 0.74.1 and emit Together-style `reasoning.enabled`/`max_tokens` controls for reasoning-capable OpenAI-completions models.
259
+ - Agents/diagnostics: split slow embedded-run `attempt-dispatch` startup summaries into workspace, prompt, runtime-plan, and final dispatch subspans so traces identify the delayed setup phase. Fixes #82782. (#82783) Thanks @galiniliev.
260
+ - Agents/Codex: flatten nested tool-result middleware blocks into bounded text so successful message sends are no longer replaced with `Tool output unavailable due to post-processing error`. Fixes #82912. Thanks @joeykrug.
261
+ - CLI/media: accept HTTP(S) URLs in `openclaw infer image describe --file`, fetching remote images through the guarded media path instead of treating URLs as local files. Fixes #82837. (#82854) Thanks @neeravmakwana.
262
+ - Agents/subagents: keep session-backed parent runs active when the child wait call times out before the child session has actually settled, so late subagent completions are reconciled instead of being lost. Fixes #82787. Thanks @ramitrkar-hash.
263
+ - Control UI: advertise shared Gateway protocol constants in browser connect frames, fixing protocol mismatch handshakes after protocol constant drift. Fixes #82882. Thanks @galiniliev.
264
+ - Gateway: add rollback protocol-mismatch diagnostics, including client protocol ranges in Gateway logs and deep status/doctor hints for stale client processes. Fixes #82841. (#82908)
265
+ - Agents/subagents: keep successful keep-mode completion payloads pending after final-delivery retry exhaustion, so requester recovery no longer loses final subagent results. Fixes #82583. (#82999) Thanks @joshavant.
266
+ - Gateway/auth: allow same-host trusted-proxy callers to use the documented local direct `gateway.auth.password` fallback after revisiting the #78684 fail-closed policy, while keeping token fallback rejected and forwarded-header requests on the trusted-proxy path. Fixes #82607. (#82953) Thanks @joshavant.
267
+ - Agents/subagents: wait for queued completion handoffs to reach the parent transcript before marking them announced, preventing busy parent runs from cleaning up before observing child results. Fixes #82913. (#83039) Thanks @joshavant.
268
+ - Agents/subagents: route group/channel subagent completions through message-tool-only handoffs when required and keep active-requester wake failures from dropping completion delivery. Fixes #82803. Thanks @galiniliev, @yozakura-ava, and @moeedahmed.
269
+ - Memory-core: scan persisted memory source sessions on startup, comparing on-disk transcripts against the index and marking only missing/newer/resized files dirty for incremental sync. Fixes #82341. (#82341) Thanks @giodl73-repo.
270
+ - Telegram: keep the top-level default account in the account list when named accounts or bindings are added alongside top-level credentials, preserving default polling while still letting named-only configs resolve to a single account. Fixes #82794. (#82794) Thanks @giodl73-repo.
271
+ - CLI/models: reuse command-scoped plugin metadata across model listing, provider catalog, auth, and synthetic-auth checks, restoring fast `openclaw models` runs for plugin-heavy installs. Fixes #82881. (#83033) Thanks @joshavant.
272
+ - CLI/channels: show configured official external channels such as Discord in `openclaw channels list` when their plugin package is missing, including the install and doctor repair command instead of reporting no configured channels. Fixes #82813.
273
+ - Signal: preserve mixed-case group IDs through routing and session persistence so group auto-replies keep delivering after updates. Fixes #82827.
274
+ - Agents/tools: keep the `message` tool available in embedded runs when it is explicitly allowed through `tools.alsoAllow` or runtime tool allowlists, so channel plugins with custom reply delivery can still use configured message sends. Fixes #82833. Thanks @cn1313113.
275
+ - WhatsApp: honor forced document delivery for outbound image, GIF, and video media so `forceDocument`/`asDocument` sends preserve original media bytes instead of using compressed media payloads. (#79272) Thanks @itsuzef.
276
+ - WhatsApp: reject symlinked Web credential files across auth checks and socket startup so unsafe `creds.json` paths cannot be read through. Thanks @mcaxtr.
277
+ - WhatsApp: name outbound document attachments from their MIME type when no filename is provided, so PDF and CSV sends arrive as `file.pdf` and `file.csv` instead of an extensionless `file`. Thanks @mcaxtr.
278
+ - Process/diagnostics: report active lane blockers in lane wait warnings so `queueAhead=0` no longer hides commands waiting behind active work. Fixes #82791. (#82792) Thanks @galiniliev.
279
+ - Process/diagnostics: stop counting the active processing turn as queued backlog in liveness warnings so transient max-only event-loop spikes do not surface as gateway warnings.
280
+ - Agents/replies: classify provider conversation-state rejections and return a clear message-channel error instead of auto-resetting or falling back to a generic runner failure. (#82616) Thanks @dutifulbob.
281
+ - Browser plugin: trust managed Chrome CDP diagnostics when launch HTTP probes race cold-start readiness, avoiding false startup failures. Fixes #82904. (#82986) Thanks @kmanan and @hclsys.
282
+ - Android: prompt before replacing a changed Gateway TLS thumbprint, showing the old and new SHA-256 fingerprints so users can accept expected certificate rotations instead of hard failing on pin mismatch. (#83077) Thanks @sliekens.
283
+ - CLI/status: render extra gateway-like service diagnostics as warning/info output instead of error output. Fixes #46930. (#82922) thanks @giodl73-repo.
284
+ - Agents/failover: classify Moonshot/Kimi exhausted-balance HTTP 429 payloads as billing instead of generic rate limits, preserving billing guidance and fallback behavior. Fixes #43447. (#83079) Thanks @leno23.
285
+ - Plugin SDK: bundle `openclaw/plugin-sdk/zod` into the published package artifact and verify the packed zod subpath stays self-contained, so pnpm global installs can register plugins without a package-local `zod` symlink. Fixes #78398. (#78515) Thanks @ggzeng.
286
+ - Providers/Google: drop compaction-truncated Gemini thought signatures before replay so malformed Base64 no longer aborts the next assistant turn. (#82995) Thanks @wAngByg.
287
+ - Gateway/mobile: allow paired iOS and Android clients to refresh same-family OS metadata on authenticated reconnect instead of requiring a new approval. (#83490) Thanks @ngutman.
288
+ - WhatsApp: treat `upload-file` as a supported media send intent by lowering path/URL uploads through the channel's normal send-media transport. (#81883) Thanks @ngutman.
289
+ - iOS: end Live Activities when OpenClaw is connected, idle, or disconnected, and show compact attention states for approval-required reconnects. (#83597) Thanks @ngutman.
290
+ - Control UI: hide child nav items when collapsing the active sidebar group. Fixes #42167. (#42223) Thanks @Aroool.
291
+ - CI/proof: skip the real-behavior-proof gate for private org maintainers by minting a least-privilege (`members: read`) GitHub App token and checking active membership in the `maintainer` team, instead of treating `author_association=CONTRIBUTOR` as definitively external. (#83418) Thanks @RomneyDa.
292
+
CHANGELOG/2026.5.28.md ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.5.28
2
+
3
+ ### Highlights
4
+
5
+ - Agent and Codex runtime recovery is steadier: subagents keep cwd/workspace separation, hook context stays prompt-local, session locks release on timeout abort while live OpenClaw locks survive cleanup, stale restart continuations are avoided, and Codex app-server/helper failures no longer tear down shared runtime state. (#87218, #86875, #87409, #87399, #87375, #88129)
6
+ - Channel delivery and session identity got safer across outbound plugin hooks, Matrix room ids, iMessage reactions/approvals, Slack final replies, Discord recovered tool warnings, runtime-config message actions, WhatsApp profile auth roots, Telegram polling, and Microsoft Teams service URL trust checks. (#73706, #75670, #87366, #87451, #87334, #84535, #82492, #83304, #87160)
7
+ - Mobile and chat surfaces got a broader refresh: the iOS Pro UI, hosted push relay default, realtime Talk tab playback, Gateway chat transport, onboarding, Talk permissions, WebChat reconnect delivery, and session picker behavior now preserve more state across reconnects and empty searches. (#87367, #87531, #87682, #88096, #88105) Thanks @ngutman and @BunsDev.
8
+ - Browser, channel, and automation inputs are stricter: Browser tool timeouts, viewport/tab indices, Gateway ports, cron retry handling, Discord component ids, schema array refs, Telegram callback pages, and channel progress callbacks now reject malformed values earlier and preserve the intended delivery context. (#82887)
9
+ - Provider, media, and document coverage expands with Claude Opus 4.8, Fal Krea image schemas, NVIDIA featured models, MiniMax streaming music responses, encrypted PDF extraction, voice model catalogs, GitHub Copilot agent runtime support, and a Codex Supervisor plugin path for delegated Codex workflows. (#87845, #87890, #80775, #84764, #87751, #87794)
10
+ - CLI, auth, doctor, and provider paths fail faster and recover more clearly: malformed numeric/version options are rejected, workspace dotenv provider credentials are ignored, heartbeat defaults, OAuth/token lifetimes, and local service startup requests are bounded, agent auth health labels are clearer, legacy `api_key` auth profiles migrate to canonical form, and restart guidance is actionable. (#87398, #86281, #87361, #88133, #83655, #87559, #88088, #85924) Thanks @vincentkoc and @giodl73-repo.
11
+ - Plugin and Gateway hot paths do less repeated work while preserving cache correctness for install records, config JSON parsing, tool search catalogs, session stores, manifest model rows, auto-enabled plugin config, browser tokens, viewer assets, and release-split external plugin packages. (#86699)
12
+ - Release, QA, and E2E validation now bound more log, artifact, harness, and cross-OS waits so failing lanes produce proof instead of hanging or false-greening.
13
+
14
+ ### Changes
15
+
16
+ - Status: show active subagent details in status output.
17
+ - Diffs: split the default language pack and expand default Diffs language coverage while keeping the host floor aligned. (#87370, #87372) Thanks @RomneyDa.
18
+ - ClawHub: add plugin display names plus skill verification and trust surfaces. (#87354, #86699) Thanks @thewilloftheshadow and @Patrick-Erichsen.
19
+ - iOS: refresh the dev app with Pro Command, Chat, Agents, Settings, hosted push relay defaults, and realtime Talk playback wired to gateway sessions, diagnostics, chat, and realtime Talk. (#87367, #88096, #88105) Thanks @Solvely-Colin and @ngutman.
20
+ - Docs: clarify Codex computer-use setup, paste-token stdin auth setup, macOS gateway sleep troubleshooting, native Codex hook relay recovery, container model auth, install deployment cards, device-token admin gating, CLI setup flow compatibility, Notte cloud browser CDP setup, and backport targets. (#87313, #63050, #87685) Thanks @bdjben, @liaoandi, and @thewilloftheshadow.
21
+ - PDF/tools: use ClawPDF for PDF extraction, support encrypted PDF extraction, and surface MCP structured content in agent tool results. (#87670, #87751)
22
+ - Providers: add Claude Opus 4.8 support, Fal Krea image model schemas, NVIDIA featured model catalogs, MiniMax streaming music responses, and provider-backed voice model catalogs. (#87845, #87890, #80775, #84764, #87794) Thanks @eleqtrizit and @vincentkoc.
23
+ - Codex/GitHub: add the GitHub Copilot agent runtime and the Codex Supervisor plugin package.
24
+ - Plugins: externalize GitHub Copilot and Tokenjuice as official install-on-demand plugins with npm and ClawHub publish metadata.
25
+ - Workboard: add agent coordination tools for tracking and handing off active agent work.
26
+ - Discord: show commentary in progress drafts so live Discord runs expose useful in-progress context. (#85200)
27
+ - Plugin SDK: add a reply payload sending hook for plugins that need to deliver channel-owned replies and flatten package types for SDK declarations. (#82823, #87165) Thanks @piersonr and @RomneyDa.
28
+ - Policy: add policy comparison, ingress-channel conformance, and sandbox-posture conformance checks. (#85572, #85744, #86768)
29
+
30
+ ### Fixes
31
+
32
+ - Agents: fall back to local config pruning when the optional `agents delete` Gateway probe cannot authenticate, so offline installs can still delete agents without removing shared workspaces.
33
+ - Tighten phone-control mutation authorization [AI]. (#87150) Thanks @pgondhi987.
34
+ - Clarify directive persistence authorization policy [AI]. (#86369) Thanks @pgondhi987.
35
+ - Agents/Codex: keep spawned agent cwd/workspace state separated, forward ACP spawn attachments, keep hook context prompt-local, release session locks on timeout abort and runtime teardown without deleting live OpenClaw-owned locks during cleanup, avoid session event queue self-wait, clean up exec abort listeners, stream assistant deltas incrementally, recover raw missing-thread compaction failures, preserve rotated compaction session identity, keep compaction-timeout snapshots continuable, preserve shared app-server state across startup or helper failures, keep native hook relay alive across restarts and prune stale bridge files, close native hook relay replacement races, keep Claude live tool progress visible for watchdog recovery, suppress abandoned requester completion handoff, route workspace memory through tools, resolve Codex runtime models first, report quarantined dynamic tools, format `skills` command output, bind node auto-review to prepared plans, retry Claude CLI transcript probes, and bound compaction/steering retries. (#87218, #86875, #86123, #88129, #87399, #87375, #72574, #87383, #87400, #83022, #87671, #87738, #87747, #87706, #87546, #87541, #81048) Thanks @mbelinky, @Alix-007, @luoyanglang, @yetval, @sjf, @joshavant, @benjamin1492, @c19354837, @fuller-stack-dev, @pfrederiksen, and @dodge1218.
36
+ - Codex Supervisor: keep real-home app-server MCP session listing on the loaded state path, bound stored history scans, and close WebSocket probes cleanly.
37
+ - Channels: thread canonical session keys into outbound hooks, preserve Matrix room-id case, keep fallback tool warnings mention-inert, retain delivered Slack final replies during late cleanup, continue iMessage polling after denied reactions, suppress duplicate native exec approvals, resolve Gateway message actions against the active runtime config, preserve Telegram SecretRef prompt config and polling keepalives, preserve WhatsApp profile auth roots, QR display, document filenames, and plugin hook config, suppress Discord recovered tool warnings, preserve the Discord voice outbound helper, cap Discord/Signal/Zalo channel request and container timeouts, and block untrusted Teams service URLs while keeping TeamsSDK patterns aligned. (#73706, #75670, #87366, #87451, #87465, #87334, #84535, #76262, #83304, #82492, #87581, #77114, #86426, #85529, #87160) Thanks @zeroaltitude, @lukeboyett, @jarvis-mns1, @xiaotian, @funmerlin, @joshavant, @eleqtrizit, @heyitsaamir, @amittell, @lidge-jun, @liorb-mountapps, @masatohoshino, @bladin, and @giodl73-repo.
38
+ - CLI/auth/doctor/providers: reject malformed numeric/timeout/subcommand-version inputs, ignore workspace dotenv provider credentials, wait for respawn child shutdown, bound heartbeat defaults plus Codex, GitHub Copilot, OpenAI, Anthropic, Google, Feishu, LM Studio, MiniMax, Xiaomi TTS, and local-provider OAuth/token/model requests, harden Codex auth probes, label auth health by agent, preserve explicit agentRuntime pins during Codex model migration, warm provider auth off the main thread, honor Codex response timeouts, stop migrating current Claude Haiku 4.5 profiles to Sonnet, bound local service startup, resolve GPT-5.5 without cached catalog, migrate legacy memory auto-provider config, rewrite non-canonical `api_key` auth profiles, and make doctor restart follow-ups actionable. (#87398, #86281, #87361, #88133, #83655, #87559, #87719, #88088, #85924, #84362) Thanks @Patrick-Erichsen, @samzong, @giodl73-repo, @alkor2000, @mmaps, @nxmxbbd, and @vincentkoc.
39
+ - Gateway/security/session state: expire browser tokens after auth rotation, scope assistant idempotency dedupe, drain probe client closes, avoid stale restart continuation reuse, preserve retry-after fallbacks and stale rate-limit cooldown probes, bound webchat image and artifact transcript scans, include seconds in inbound metadata timestamps, clear completed session active runs, clear stale chat stream buffers, and evict current plugin-state namespaces at row caps. (#87810, #87833, #75089) Thanks @joshavant and @litang9.
40
+ - Config/parsing/network: reject partial numeric parsing, parse provider/Discord retry headers and dates strictly, honor IPv6 and bare IPv6 `no_proxy` entries, preserve empty plugin allowlists, canonicalize secret target array indexes, and reject malformed media content lengths, inspected TCP ports, marketplace content lengths, cron epochs, sandbox stat fields, unsafe duration values, empty config path segments, noncanonical schema array refs, unsafe Telegram callback pages, and invalid Teams attachment-fetch DNS targets. (#87883) Thanks @zhangguiping-xydt.
41
+ - Browser/input hardening: reject invalid tab indexes, excessive viewport resizes, explicit zero CDP ports, malformed geolocation options, unsafe screenshot or permission-grant timeouts, loose response-body limits, invalid cookie expiries, and non-finite Browser tool delays/timeouts.
42
+ - Cron/automation: retry recurring jobs after transient model rate limits before waiting for the next scheduled slot, and preflight model fallbacks before skipping scheduled work. (#82887) Thanks @chen-zhang-cs-code.
43
+ - Auto-reply/directives: respect provider and relayed channel metadata during directive persistence so channel-originated decisions keep their intended context. (#87683)
44
+ - WhatsApp: resolve the auth directory from the active profile so profile-scoped WhatsApp installs do not drift to the wrong credential root. (#82492) Thanks @lidge-jun.
45
+ - Gateway/session state: clear completed session active runs, avoid cold-loading providers for MCP inventory, cache single-session child indexes, cap handshake timers, and bound preauth, auth-guard, media, transcript, readiness, and port options.
46
+ - Channels/replies: preserve channel-owned progress callbacks when verbose output is off, keep group-room progress suppression intact, prefer external session delivery context, escape Discord component id delimiters, force final TUI chat repaints, show Slack reasoning previews, and normalize Discord/Matrix/Mattermost channel numeric options. (#87476, #87423)
47
+ - Agents/tool args: harden smart-quoted argument repair for edit arrays and exact escaped arguments so model-produced tool calls recover without corrupting valid input. (#86611) Thanks @ferminquant.
48
+ - Providers/agents: preserve seeded Anthropic signatures, preserve signed thinking payloads, concatenate signature-delta chunks, preserve DeepSeek `reasoning_content` replay across tier suffixes, apply OpenRouter strict9 ids to Mistral routes, promote Ollama plain-text tool calls, load NVIDIA featured model catalogs, stream MiniMax music generation responses, and recover empty preflight compaction. (#87593, #87493, #80775, #84764) Thanks @Pluviobyte and @eleqtrizit.
49
+ - Media/images: skip CLI image cache refs when resolving generated images, allow trusted generated HTML attachments, and bound generated video downloads so stale refs and slow providers fail cleanly. (#87523, #87982)
50
+ - File transfer: handle late tar stdin pipe errors after archive validation or unpacking has already settled.
51
+ - Performance: trust install-record caches between reloads, prefer native JSON parsing, reuse unchanged tool-search catalogs, reuse gateway session and plugin metadata paths, skip unchanged store serialization, patch single-entry session writes, add precomputed session patch writers, reduce store clone allocations, cache manifest model catalog rows and auto-enabled plugin config, avoid full session snapshots for entry reads, defer configured Slack full startup, prefer bundled plugin dist entries, and slim current metadata identity caches. (#87760)
52
+ - Docker/release/QA: package runtime workspace templates, stream cross-OS served artifacts, preserve sparse Crabbox run artifacts, isolate npm plugin installs per package, reject incompatible package plugin API installs, drop the leftover root Sharp dependency from package manifests after the Rastermill migration, bound OpenClaw instance logs, plugin gauntlet relay logs, MCP channel buffers, kitchen-sink scans, agent-turn assertions, QA-Lab credential broker calls, QA Matrix substrate requests, and release scenario logs, and keep release/google live guards current. (#87647, #87477) Thanks @rohitjavvadi and @vincentkoc.
53
+ - Release/CI: bound manual git fetches, ClawHub verifier responses, ClawHub owner metadata, dependency-guard error bodies, Parallels limits, startup/test/memory budget parsing, and diffs viewer build warnings so release lanes fail with useful proof instead of hanging. (#87839)
54
+
CHANGELOG/2026.5.4.md ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.5.4
2
+
3
+ ### Highlights
4
+
5
+ - Google Meet/Voice Call: make Twilio dial-in joins speak through the realtime Gemini voice bridge with paced audio streaming, backpressure-aware buffering, barge-in queue clearing, and no TwiML fallback during realtime speech, giving Meet participants a much snappier OpenClaw voice agent. (#77064) Thanks @scoootscooob.
6
+
7
+ ### Changes
8
+
9
+ - Gateway/Windows: bind the default loopback gateway listener only to `127.0.0.1` on Windows so libuv's dual-stack `::1` behavior cannot wedge localhost HTTP requests. (#69701, fixes #69674) Thanks @SARAMALI15792.
10
+ - Plugins/migration: emit catalog-backed install hints when `plugins.entries` or `plugins.allow` references an official external plugin that is not installed, so upgraded configs point operators to `openclaw plugins install <spec>` instead of telling them to remove valid plugin config. (#77483) Thanks @hclsys.
11
+ - OpenAI/Codex media: advertise Codex audio transcription in runtime and manifest metadata and route active Codex chat models to the OpenAI transcription default instead of sending chat model ids to audio transcription. Thanks @vincentkoc.
12
+ - Dependencies: refresh runtime and provider packages including Pi 0.73.0, ACPX adapters, OpenAI, Anthropic, Slack, and TypeScript native preview, while keeping the Bedrock runtime installer override pinned below the Windows ARM Node 24 npm resolver failure.
13
+ - Agents/performance: pass the resolved workspace through BTW, compaction, embedded-run model generation, and PDF model setup so explicit agent-dir model refreshes can reuse the current workspace-scoped plugin metadata snapshot instead of falling back to cold plugin metadata scans. (#77519, #77532)
14
+ - Plugins/performance: let unscoped model catalog and manifest-contract readers reuse the current workspace-compatible plugin metadata snapshot, avoiding repeated cold plugin metadata scans on hot control-plane paths while preserving env/config/workspace compatibility checks. (#77519, #77532)
15
+ - Config/plugin auto-enable: prefer the claiming plugin manifest id over a built-in channel alias when auto-allowlisting a configured channel, so WeCom/Yuanbao-style aliases resolve to the installed plugin id. Thanks @Beandon13.
16
+ - Secrets/apply: preserve auth-profile `keyRef` and `tokenRef` fields when scrubbing provider-target secrets, so the canonical SecretRef metadata survives `secrets apply` without keeping plaintext values. Thanks @Beandon13.
17
+ - Plugins/active-memory: skip session-store channel entries that contain `:` when resolving the recall subagent's channel, so QQ c2c agent IDs (e.g. `c2c:10D4F7C2…`) and other scoped conversation IDs do not reach bundled-plugin `dirName` validation and crash the recall run. The same guard already applied to explicit `channelId` params (#76704); this extends it to store-derived channels. (#77396) Thanks @hclsys.
18
+ - Secrets/external channel contracts: also look in `<rootDir>/dist/` when resolving the `secret-contract-api` sidecar, so npm-published externalized channel plugins (e.g. `@openclaw/discord` since 2026.5.2) whose compiled artifacts live under `dist/` actually contribute their channel SecretRef contracts to the runtime snapshot. Without this, env-backed `channels.discord.token` SecretRefs silently failed to resolve at gateway start on 2026.5.3, leaving the channel `not configured` even though #76449 had landed the generic external-contract loader. Thanks @mogglemoss.
19
+ - Models/auth: add `openclaw models auth list [--provider <id>] [--json]` so users can inspect saved per-agent auth profiles without dumping secrets or hitting the old “too many arguments” path. Thanks @vincentkoc.
20
+ - Control UI/header: show the active agent name in dashboard breadcrumbs without adding the current session key, keeping non-chat views oriented without crowding the topbar.
21
+ - Control UI/cron: make the New Job sidebar collapsible so the jobs list can reclaim space while keeping the form one click away. Thanks @BunsDev.
22
+ - Gateway/startup: keep model-catalog test helpers, run-session lookup code, QR pairing helpers, and TypeBox memory-tool schema construction out of hot startup import paths, reducing default gateway benchmark plugin-load and memory pressure.
23
+ - Control UI/performance: record browser long animation frame or long task entries in the debug event log when supported, making slow dashboard renders easier to attribute from the UI.
24
+ - Slack/streaming: add `streaming.progress.render: "rich"` for Block Kit progress drafts backed by structured progress line data.
25
+ - Slack/streaming: keep the newest rich progress lines when Block Kit limits trim long progress drafts. Thanks @vincentkoc.
26
+ - Channels/streaming: cap progress-draft tool lines by default so edited progress boxes avoid jumpy reflow from long wrapped lines.
27
+ - Agents/verbose: use compact explain-mode tool summaries for `/verbose` and progress drafts by default, with `agents.defaults.toolProgressDetail: "raw"` and per-agent overrides for debugging raw command/detail output.
28
+ - Control UI/chat: add an agent-first filter to the chat session picker, keep chat controls/composer responsive across phone/tablet/desktop widths, keep desktop chat controls on one row, avoid duplicate avatar refreshes during initial chat load, and hide that row while scrolling down the transcript. Thanks @BunsDev.
29
+ - Control UI/chat: collapse consecutive duplicate text messages into one bubble with a count so no-op heartbeat acknowledgements stay compact without hiding nearby context.
30
+ - Agents/subagents: preserve every grouped child result when direct completion fallback has to bypass the requester-agent announce turn. Thanks @vincentkoc.
31
+ - TTS/telephony: honor provider voice/model overrides in telephony synthesis providers so Google Meet agent speech logs match the backend that actually produced the audio. Thanks @vincentkoc.
32
+ - Voice Call/realtime: bound the paced Twilio audio queue and close overloaded realtime streams before provider audio can pile up behind the websocket backpressure guard. Thanks @vincentkoc.
33
+ - Docs: clarify that IRC uses raw TCP/TLS sockets outside operator-managed forward proxy routing, so direct IRC egress should be explicitly approved before enabling IRC. Thanks @jesse-merhi.
34
+ - Gateway/performance: defer non-readiness sidecars until after the ready signal, avoid hot-path channel plugin barrel imports, and fast-path trusted bundled plugin metadata during Gateway startup.
35
+ - Gateway/performance: avoid importing `jiti` on native-loadable plugin startup paths, so compiled bundled plugin surfaces do not pay source-transform loader cost unless fallback loading is actually needed.
36
+ - Gateway/diagnostics: add startup phase spans, active work labels, stale terminal bridge markers, and default sync-I/O tracing in `pnpm gateway:watch` so slow Gateway turns are easier to attribute from logs and stability diagnostics.
37
+ - Plugins/loader: preserve real compiled plugin module evaluation errors on the native fast path instead of treating every thrown `.js` module as a source-transform fallback miss. Thanks @vincentkoc.
38
+ - QA/Mantis: add `pnpm openclaw qa mantis slack-desktop-smoke` to run Slack live QA inside a Crabbox VNC desktop, open Slack Web, and capture desktop screenshots beside the Slack QA artifacts.
39
+ - QA/Mantis: pass the runtime env through desktop-browser Crabbox and artifact-copy child commands, so embedded Mantis callers can provide Crabbox credentials without mutating the parent process. Thanks @vincentkoc.
40
+ - QA/Mantis: return the copied Slack desktop screenshot path even when remote Slack QA fails, so the CLI still prints the failure screenshot artifact. Thanks @vincentkoc.
41
+ - QA/Mantis: accept Blacksmith Testbox `tbx_...` lease ids from desktop smoke warmup, so provider overrides do not fail before inspect/run. Thanks @vincentkoc.
42
+ - QA/Codex harness: add targeted live Docker/Testbox diagnostics, auth preflight checks, cache mount fixes, and app-server protocol checkout discovery so maintainer harness failures are easier to reproduce. Thanks @vincentkoc.
43
+ - Plugins/update: treat official externalized bundled npm migrations and ClawHub-to-npm fallbacks as trusted source-linked installs, so prerelease-only official plugin packages can migrate from bundled builds without being rejected as unsafe prerelease resolutions. Thanks @vincentkoc.
44
+ - Plugins/update: move ClawHub-preferred externalized plugin installs back to ClawHub after an earlier npm fallback once the ClawHub package becomes available. Thanks @vincentkoc.
45
+ - Plugins/update: clean stale bundled load paths for already-externalized pinned npm and ClawHub plugin installs, so release-channel sync does not leave removed bundled paths ahead of the installed external package. Thanks @vincentkoc.
46
+ - Telegram: accept plugin-owned numeric forum-topic targets in the agent message tool and keep reply-dispatch provider chunks behind a real stable runtime alias during in-place package updates. Fixes #77137. Thanks @richardmqq.
47
+ - Google Meet: preserve `realtime.introMessage: ""` so realtime Chrome joins can stay silent instead of restoring the default spoken intro. Thanks @vincentkoc.
48
+ - Plugins/SDK: add bounded `before_agent_finalize` retry instructions so workflow plugins can request one more model pass. Thanks @100yenadmin.
49
+ - Discord/status: add degraded Discord transport and gateway event-loop starvation signals to `openclaw channels status`, `openclaw status --deep`, and fetch-timeout logs so intermittent socket resets do not look like a healthy running channel. (#76327) Thanks @joshavant.
50
+ - Providers/OpenRouter: add opt-in response caching params that send OpenRouter's `X-OpenRouter-Cache`, `X-OpenRouter-Cache-TTL`, and cache-clear headers only on verified OpenRouter routes. Thanks @vincentkoc.
51
+ - Providers/OpenRouter: expand app-attribution categories so OpenClaw advertises coding, programming, writing, chat, and personal-agent usage on verified OpenRouter routes. Thanks @vincentkoc.
52
+ - Providers/OpenRouter: add inbound audio STT support to media-understanding via OpenRouter's JSON `/audio/transcriptions` contract, including default audio model metadata and auto-selection priority. (#77490) Thanks @remdev.
53
+ - Plugins/update: make package upgrades swap pnpm/npm-prefix installs cleanly, keep legacy plugin install runtime chunks working, and on the beta channel fall back default-line npm plugins to default/latest when plugin beta releases are missing or fail install validation. Thanks @vincentkoc and @joshavant.
54
+ - Channels/WhatsApp: support explicit WhatsApp Channel/Newsletter `@newsletter` outbound message targets with channel session metadata instead of DM routing. Fixes #13417; carries forward the narrow outbound target idea from #13424. Thanks @vincentkoc and @agentz-manfred.
55
+ - Exec approvals: add a tree-sitter-backed shell command explainer for future approval and command-review surfaces. (#75004) Thanks @jesse-merhi.
56
+ - Agents/sandbox: store sandbox container and browser registry entries as per-runtime shard files, reducing unrelated session lock contention while `openclaw doctor --fix` migrates legacy monolithic registry files. (#74831) Thanks @luckylhb90.
57
+ - Plugins/ClawHub: annotate 429 errors from ClawHub with the reset window from `RateLimit-Reset`/`Retry-After` and append a `Sign in for higher rate limits.` hint when the request was unauthenticated, so users can see when downloads will recover and how to lift the cap. Thanks @RomneyDa.
58
+ - Plugins/runtime state: add `registerIfAbsent` for atomic keyed-store dedupe claims that return whether a plugin successfully claimed a key without overwriting an existing live value. Thanks @amknight.
59
+ - Plugin SDK: add plugin-owned `SessionEntry` slot projection and scoped trusted-policy session extension reads. (#75609; replaces part of #73384/#74483) Thanks @100yenadmin.
60
+ - Sandbox/Windows: accept drive-absolute Docker bind sources while keeping sandbox blocked-path and allowed-root policy comparisons Windows-case-insensitive. (#42174) Thanks @6607changchun.
61
+
62
+ ### Fixes
63
+
64
+ - Browser/chrome-mcp: read Chrome DevTools MCP screenshot output from the extension-suffixed path, fixing ENOENT on screenshot capture. Fixes #77222. (#74685) Thanks @barbarhan.
65
+
66
+ - Agents/OpenAI: honor `compat.supportsTools: false` for OpenAI Completions models so chat-only compatible endpoints do not receive `tools`, `tool_choice`, or tool-history fallback payloads. Fixes #74664. Thanks @yelog.
67
+ - macOS/launchd: set generated Gateway LaunchAgent plists to `ProcessType=Interactive` so the gateway keeps timely execution during idle periods. Fixes #58061; refs #62294 and closed duplicate #66992. (#62308) Thanks @bryanpearson and @zssggle-rgb.
68
+ - Plugins/install: honor the beta update channel for onboarding and doctor-managed plugin installs by requesting floating npm and ClawHub specs with `@beta` while keeping persistent install records on the catalog default. Thanks @vincentkoc.
69
+ - WhatsApp/onboarding: canonicalize setup and pairing allowlist entries to WhatsApp's digit-only phone ids while still accepting E.164, JID, and `whatsapp:` inputs, so personal-phone allowlists match WhatsApp Web sender ids after setup. Thanks @vincentkoc.
70
+ - Gateway/startup: load provider plugins that own explicitly configured image, video, or music generation defaults so generation tools become live after gateway restart instead of remaining catalog-only. Fixes #77244. Thanks @buyuangtampan, @Nikoxx99, and @vincentkoc.
71
+ - Slack/subagents: keep resumed parent `message.send` calls in the originating Slack thread when ambient session thread context is present, and suppress successful silent child completion rows from follow-up findings. Thanks @bek91.
72
+ - Slack/mentions: record thread participation for successful visible threaded Slack sends, including message-tool and media delivery paths, so unmentioned replies in bot-participated threads can bypass mention gating as documented. Fixes #77648. Thanks @bek91.
73
+ - Infra/Windows: skip the POSIX `/tmp/openclaw` preferred path on Windows in `resolvePreferredOpenClawTmpDir` so log files, TTS temp files, and other writes land in `%TEMP%\openclaw-<uid>` instead of `C:\tmp\openclaw`. Fixes #60713. Thanks @juan-flores077.
74
+ - Media/Windows: open saved attachment temp files read/write before fsync so Windows WebChat and `chat.send` media offloads no longer fail with EPERM during durability flush. (#76593) Thanks @qq230849622-a11y.
75
+ - Agents/tools: honor narrow runtime tool allowlists when constructing embedded-runner tool families and bundled MCP/LSP runtimes, so cron/subagent runs that request tools such as `update_plan`, `browser`, `x_search`, channel login tools, or `group:plugins` no longer start with missing tools or unrelated bootstrap work. (#77519, #77532)
76
+ - Codex plugin: mirror the experimental upstream app-server protocol and format generated TypeScript before drift checks, keeping OpenClaw's `experimentalApi` bridge compatible with latest Codex while preserving formatter gates.
77
+ - Telegram/media: derive no-caption inbound media placeholders from saved MIME metadata instead of the Telegram `photo` shape, so non-image and mixed attachments no longer reach the model as `<media:image>`. Fixes #69793. Thanks @aspalagin.
78
+ - Telegram/streaming: reuse the active preview as the first chunk for long text finals, so multi-chunk replies no longer create a transient extra bubble that appears and then disappears. Thanks @vincentkoc.
79
+ - Agents/cache: keep per-turn runtime context out of ordinary chat system prompts while still delivering hidden current-turn context, restoring prompt-cache reuse on chat continuations. Fixes #77431. Thanks @Udjin79.
80
+ - Gateway/startup: include resolved thinking and fast-mode defaults in the `agent model` startup log line, defaulting unset startup thinking to `medium` without mixing in reasoning visibility.
81
+ - Gateway/update: resolve local gateway probe auth from the installed config during post-update restart verification, so token/device-authenticated VPS gateways are not misreported as unhealthy port conflicts after a package swap. Thanks @vincentkoc.
82
+ - Agents/Tools: add post-compaction loop guard in `pi-embedded-runner` that arms after auto-compaction-retry and aborts the run with `compaction_loop_persisted` when the agent emits the same `(tool, args, result)` triple `windowSize` times (default 3) within that window. Disable via existing `tools.loopDetection.enabled`; tune via `tools.loopDetection.postCompactionGuard.windowSize`. Targets the failure mode where context-overflow + compaction does not break a tool-call loop. Refs #77474; carries forward #21597. Thanks @efpiva.
83
+ - Gateway/watch: suppress sync-I/O trace output during `pnpm gateway:watch --benchmark` unless explicitly requested, so CPU profiling no longer floods the terminal with stack traces.
84
+ - Gateway/watch: when benchmark sync-I/O tracing is explicitly enabled, tee trace blocks to the benchmark output log and filter them from the terminal pane while keeping normal Gateway logs visible.
85
+ - Plugins/runtime-deps: include `json5` in the memory-core plugin runtime dependency set so packaged `memory_search` sandboxes can resolve generated OpenClaw runtime chunks that parse JSON5 config. Fixes #77461.
86
+ - Plugins/Windows: show a Git install hint when npm plugin installation fails with `spawn git ENOENT`, and document the WhatsApp plugin's Git-on-PATH requirement for Baileys/libsignal installs.
87
+ - Codex harness: preserve app-server usage-limit reset details and deliver OpenClaw-owned runtime failure notices through tool-only source-reply mode, so Telegram and other chat channels tell users when Codex subscription limits or API failures block a turn instead of going silent. (#77557) Thanks @pashpashpash.
88
+ - Agents/OpenAI: default direct OpenAI Responses models to the SSE transport instead of WebSocket auto-selection, preventing pi runtime chat turns from hanging on servers where the WebSocket path stalls while the OpenAI HTTP stream works. Thanks @vincentkoc.
89
+ - Plugins/update: repair missing plugin-local `openclaw` peer links before skipping unchanged npm plugin updates, so current external Codex installs can recover `openclaw/plugin-sdk/*` resolution during OTA repair. (#77544) Thanks @ProspectOre.
90
+ - Discord/replies: treat failed final reply delivery as a failed turn instead of counting it as a delivered automatic visible reply, so guild/channel turns no longer show done when the final message was dropped. Fixes #77520. Thanks @Patrick-Erichsen.
91
+ - Discord: prefer IPv4 for Discord REST and gateway WebSocket startup paths so IPv4-only networks no longer stall before Gateway READY and inbound message dispatch. Fixes #77398; refs #77526. Thanks @Beandon13.
92
+ - Channels/plugins: key bundled package-state probes, env/config presence, and read-only command defaults by channel id instead of manifest plugin id, preserving setup and native-command detection for channel plugins whose package id differs from the channel alias. Thanks @vincentkoc.
93
+ - Docker: prune package-excluded plugin dist directories from runtime images unless the build explicitly opts that plugin in, so official external plugins such as Feishu stay install-on-demand instead of shipping partial metadata without compiled runtime output. Fixes #77424. Thanks @vincentkoc.
94
+ - Model switching: include the exact additive allowlist repair command when `/model ... --runtime ...` targets a blocked model, and make Telegram's model picker say that it changes only the session model while leaving the runtime unchanged. Thanks @vincentkoc.
95
+ - Mattermost: clarify that the model picker only changes the session model and that runtime switches require `/oc_model <provider/model> --runtime <runtime>`. Thanks @vincentkoc.
96
+ - Doctor/config: keep active `auth.profiles` metadata intact when `doctor --fix` strips stale secret fields from configs, repairing legacy `<provider>:default` API-key profile metadata when model fallbacks or explicit `model@profile` refs still depend on it. Fixes #77400.
97
+ - Doctor/plugins: include `plugins.allow`-only official plugin ids in the release configured-plugin repair set, so `doctor --fix` installs official external plugins that are configured but not yet loaded instead of removing them as stale allow entries. Fixes #77155. Thanks @hclsys.
98
+ - Doctor/sessions: clear auto-created stale session routing state from the sessions store when `doctor --fix` sees plugin-owned model/runtime/auth/session bindings outside the current configured route, while leaving explicit user model choices for manual review. Refs #68615.
99
+ - CLI/update: disable and skip plugins that fail package-update plugin sync, so a broken npm/ClawHub/git/marketplace plugin cannot turn a successful OpenClaw package update into a failed update result. Thanks @vincentkoc.
100
+ - CLI/update: use an absolute POSIX npm script shell during package-manager updates, so restricted PATH environments can still run dependency lifecycle scripts while updating from `--tag main`. Fixes #77530. Thanks @PeterTremonti.
101
+ - Diagnostics: grant the internal diagnostics event bus to official installed diagnostics exporter plugins, so npm-installed `@openclaw/diagnostics-prometheus` can emit metrics without broadening the capability to arbitrary global plugins. Fixes #76628. Thanks @RayWoo.
102
+ - Browser: enforce strict SSRF current-URL checks before existing-session screenshots, matching existing-session snapshot handling. Thanks @vincentkoc.
103
+ - Active Memory: give timeout partial transcript recovery enough abort-settle headroom so temporary recall summaries are returned before cleanup. Thanks @vincentkoc.
104
+ - Gateway/chat: clear the active reply-run guard before draining queued same-session follow-up turns, so sequential `chat.send` calls no longer trip `ReplyRunAlreadyActiveError` every other request. Fixes #77485. Thanks @bws14email.
105
+ - Agents/media: avoid sending generated image, video, and music attachments twice when streamed reply text arrives before the final `MEDIA:` directive.
106
+ - CLI/sessions: cap `openclaw sessions` output to the newest 100 rows by default and add `--limit <n|all>` plus JSON pagination metadata, so repeated machine polling of large session stores cannot fan out into unbounded per-row enrichment/output work. Fixes #77500. Thanks @Kaotic3.
107
+ - Doctor/config: restore legacy group chat config migrations for `routing.allowFrom`, `routing.groupChat.*`, and `channels.telegram.requireMention` so upgrades keep WhatsApp, Telegram, and iMessage group mention gates and history settings instead of leaving configs invalid or silently blocked. Thanks @scoootscooob.
108
+ - CLI/update: make package-update follow-up processes write completion results and exit explicitly, so Windows packaged upgrades do not hang after the new package finishes post-core plugin work. Thanks @vincentkoc.
109
+ - Release validation: skip Slack live QA unless Slack credentials are explicitly configured, so release gates can keep proving non-Slack surfaces while Slack is still local and credential-gated. Thanks @vincentkoc.
110
+ - Plugins/update: treat OpenClaw CalVer correction versions like `2026.5.3-1` as satisfying base plugin API ranges, so correction builds can install plugins that require the base runtime API. Fixes #77293. (#77450) Thanks @p3nchan.
111
+ - Discord/Gateway startup: retry Discord READY waits with backoff, defer startup `sessions.list` and native approval readiness failures until sidecars recover, and preserve component-only Discord payloads when final reply scrubbing removes all text. (#77478) Thanks @NikolaFC.
112
+ - CLI/launcher: forward termination signals to compile-cache respawn children, so killing a wrapper process no longer leaves the security audit worker orphaned. Fixes #77458. Thanks @jaikharbanda.
113
+ - Plugins/registry: recover managed-npm external plugins from the owned npm root when a stale persisted registry would otherwise hide them after package-manager upgrades. Fixes #77266. Thanks @p3nchan.
114
+ - fix(gateway): clamp unbound websocket auth scopes [AI]. (#77413) Thanks @pgondhi987.
115
+ - Diffs plugin: accept `defaults.ttlSeconds` as a plugin-wide artifact lifetime default, so LAN-viewable diff links can keep their configured six-hour TTL without doctor quarantining the plugin entry. (#77456) Thanks @VACInc.
116
+ - Gate zalouser startup name matching [AI]. (#77411) Thanks @pgondhi987.
117
+ - Active Memory: send a bounded latest-message search query to the recall worker so channel/runtime metadata does not become the memory search string. Fixes #65309. Thanks @joeykrug, @westley3601, @pimenov, and @tasi333.
118
+ - Memory/QMD: report missing or invalid agent workspace directories as workspace probe failures in doctor/QMD availability checks instead of sending operators toward binary-install fixes. Fixes #63158. Thanks @sercada.
119
+ - fix(device-pair): require pairing scope for pair command [AI]. (#76377) Thanks @pgondhi987.
120
+ - Providers/OpenRouter: keep DeepSeek V4 `reasoning_effort` on OpenRouter-supported values, mapping stale `max` thinking overrides to `xhigh` so `openrouter/deepseek/deepseek-v4-pro` no longer fails with OpenRouter's invalid-effort 400. Fixes #77350. (#77423) Thanks @krllagent, @mushuiyu886, and @sallyom.
121
+ - fix(qqbot): keep private commands off framework surface [AI]. (#77212) Thanks @pgondhi987.
122
+ - Claude CLI: honor non-off `/think` levels by passing Claude Code's session-scoped `--effort` flag through the CLI backend seam, so chat bridges no longer show an inert thinking control. Fixes #77303. Thanks @Petr1t.
123
+ - Agents/subagents: refresh deferred final-delivery payloads when same-session completion output changes, so retried parent notifications use the final child summary instead of stale progress text. Thanks @vincentkoc.
124
+ - Agents/media: route async music and video completion results back through the requester agent, preserving automatic replies while requiring the message tool only for message-tool-only group/channel delivery.
125
+ - active-memory: skip the memory sub-agent gracefully instead of logging a confusing allowlist error when no memory plugin (`memory-core` or `memory-lancedb`) is loaded, so active-memory with no memory backend no longer produces misleading "No callable tools remain" warnings in the gateway log. Fixes #77506. Thanks @hclsys.
126
+ - Memory/wiki: preserve representation from both corpora in `corpus=all` searches while backfilling unused result capacity, so memory hits are not starved by numerically higher wiki integer scores. Fixes #77337. Thanks @hclsys.
127
+ - Docker/compose: pin container-side `OPENCLAW_CONFIG_DIR` and `OPENCLAW_WORKSPACE_DIR` on both gateway and CLI services so the host paths written into `.env` by `scripts/docker/setup.sh` (used as Compose bind-mount sources) cannot leak into runtime code via the `env_file` import. Fixes regressions on macOS Docker setups where the first agent reply died with `EACCES: permission denied, mkdir '/Users'` because the host-style workspace path got persisted into `agents.defaults.workspace`. Fixes #77436. Thanks @lonexreb.
128
+ - Telegram: clean up tool-only draft previews after assistant message boundaries so transient `Surfacing...` tool-status bubbles do not linger when no matching final preview arrives. Thanks @BunsDev.
129
+ - Telegram: cool down repeatedly failing Bot API transport fallbacks so long polling stops hammering a blackholed Telegram route. Fixes #77900. Thanks @bryce-d-greybeard.
130
+ - Slack: report `unknown error` instead of `undefined` in socket-mode startup retry logs and label the retry reason explicitly.
131
+ - Telegram: let explicit forum-topic `requireMention` settings override persisted `/activate` and `/deactivate` state, so per-topic mention gates work consistently. Fixes #49864. Thanks @Panniantong.
132
+ - Cron: surface failed isolated-run diagnostics in `cron show`, status, and run history when requested tools are unavailable, so blocked cron runs report the actual tool-policy failure instead of a misleading green result. Fixes #75763. Thanks @RyanSandoval.
133
+ - TUI/escape abort: track the in-flight runId after `chat.send` resolves so pressing Esc during the gap before the first gateway event aborts the run instead of repeatedly printing `no active run`. Fixes #1296. Thanks @Lukavyi and @RomneyDa.
134
+ - TUI/render: stop the long-token sanitizer from injecting literal spaces inside inline code spans, fenced code blocks, table borders, and bare hyphenated/dotted identifiers, so copied package names, entity IDs, and shell line-continuations stay byte-for-byte intact while narrow-terminal protection still chunks unidentifiable long prose tokens. Fixes #48432, #39505. Thanks @DocOellerson, @xeusoc, @CCcassiusdjs, @akramcodez, @brokemac79, @RomneyDa.
135
+ - Plugin skills: publish plugin-declared skills through the generated plugin skills directory (`~/.openclaw/plugin-skills/`) while keeping direct prompt loading intact, so agent file-based discovery paths find plugin skill `SKILL.md` files and inactive plugin links are cleaned up. Fixes #77296. (#77328) Thanks @zhangguiping-xydt.
136
+ - Gateway/status: label Linux managed gateway services as `systemd user`, making status output explicit about the user-service scope instead of implying a system-level unit. Thanks @vincentkoc.
137
+ - Plugins/install: remove the previous managed plugin directory when a reinstall switches sources, so stale ClawHub and npm copies no longer keep duplicate plugin ids in discovery after the new install wins. Thanks @vincentkoc.
138
+ - Plugins/install: let official plugin reinstall recovery repair source-only installed runtime shadows, so `openclaw plugins install npm:@openclaw/discord --force` can replace the bad package instead of stopping at stale config validation. Thanks @vincentkoc.
139
+ - CLI/update: stage pnpm-detected npm-layout global package updates through a clean npm prefix swap, keep plugin install runtime imports behind a stable alias, and ship legacy install-runtime aliases back to `2026.3.22`, preventing stale overlay chunks from breaking plugin post-update sync. Thanks @vincentkoc.
140
+ - Plugins/commands: allow the official ClawHub Codex plugin package to keep reserved `/codex` command ownership, matching the existing npm-managed Codex package behavior. Thanks @vincentkoc.
141
+ - Auth/OpenAI Codex: rewrite invalidated per-agent Codex auth-order and session profile overrides toward a healthy relogin profile, so revoked OAuth accounts do not stay pinned after signing in again. Thanks @BunsDev.
142
+ - Plugins/commands: scope QQBot framework slash commands to the QQBot channel so `/bot-*` command handlers and native specs do not leak onto unrelated chat surfaces. Thanks @vincentkoc.
143
+ - fix: harden backend message action gateway routing [AI]. (#76374) Thanks @pgondhi987.
144
+ - Gate QQBot streaming command auth [AI]. (#76375) Thanks @pgondhi987.
145
+ - Plugins/discovery: ignore managed npm plugin packages that only expose TypeScript source entries without compiled runtime output, so stale/broken installs cannot hide a working bundled or reinstallable channel plugin during setup. Thanks @vincentkoc.
146
+ - CLI/update: treat OpenClaw stable correction versions like `2026.5.3-1` as newer than their base stable release, so package updates no longer ask for downgrade confirmation. Thanks @vincentkoc.
147
+ - Plugins/install: suppress dangerous-pattern scanner warnings for trusted official OpenClaw npm installs, so installing `@openclaw/discord` no longer prints credential-harvesting warnings for the official package. Thanks @vincentkoc.
148
+ - Plugins/commands: suppress dangerous-pattern scanner warnings for trusted catalog npm installs from owner-gated `/plugins install` commands, so chat-driven installs match the CLI install trust path. Thanks @vincentkoc.
149
+ - Plugins/release: make the published npm runtime verifier reject blank `openclaw.runtimeExtensions` entries instead of treating them as absent and passing via inferred outputs. Thanks @vincentkoc.
150
+ - Plugins/security: ignore inline and block comments when matching source-rule context in plugin install scans, so comment-only `fetch`/`post` references near environment defaults do not block clean plugins. Thanks @vincentkoc.
151
+ - Doctor/plugins: remove stale managed install records for bundled plugins even when the bundled plugin is not explicitly configured, so doctor cleanup cannot leave orphaned install metadata behind. Thanks @vincentkoc.
152
+ - Web fetch: scope provider fallback cache entries by the selected fetch provider so config reloads cannot reuse another provider's cached fallback payload. Thanks @vincentkoc.
153
+ - Web search: honor late-bound `tools.web.search.enabled: false` during tool execution so config reloads cannot leave an already-created `web_search` tool runnable. Thanks @vincentkoc.
154
+ - Plugins/packages: reject inferred built runtime entries that exist but fail package-boundary checks instead of falling back to TypeScript source for installed packages. Thanks @vincentkoc.
155
+ - Plugins/loader: do not retry native-loaded JavaScript plugin modules through the source transformer after native evaluation has already reached a missing dependency, avoiding duplicate top-level side effects. Thanks @vincentkoc.
156
+ - Plugins/packages: reject blank `openclaw.runtimeExtensions` entries instead of silently ignoring them and falling back to inferred TypeScript runtime entries. Thanks @vincentkoc.
157
+ - Doctor/plugins: remove stale managed npm plugin shadow entries from the managed package lock as well as `package.json` and `node_modules`, so future npm operations do not keep referencing repaired bundled-plugin shadows. Thanks @vincentkoc.
158
+ - Plugins/runtime state: keep the key being registered when namespace eviction runs in the same millisecond as existing entries, so `register` and `registerIfAbsent` do not report success while evicting their own fresh value. Thanks @vincentkoc.
159
+ - Plugins/providers: make bundled provider discovery honor restrictive `plugins.allow` by default for new configs, while doctor migrates legacy restrictive allowlist configs to `plugins.bundledDiscovery: "compat"` to preserve upgrade behavior. Thanks @dougbtv.
160
+ - Control UI/Talk: make failed Talk startup errors dismissable and clear the stale Talk error state when dismissed, so missing realtime voice provider configuration does not leave a permanent chat banner. Fixes #77071. Thanks @ijoshdavis.
161
+ - Control UI/Talk: stop and clear failed realtime Talk sessions when dismissing runtime error banners, so the next Talk click starts a fresh session instead of only stopping the stale one. Thanks @vincentkoc.
162
+ - Control UI/Talk: retry from a failed realtime Talk session on the next Talk click instead of requiring a separate stale-session stop click first. Thanks @vincentkoc.
163
+ - Canvas host: preserve the Gateway TLS scheme in browser canvas host URLs and startup mount logs, so direct HTTPS gateways do not advertise insecure canvas links. Thanks @vincentkoc.
164
+ - WhatsApp/login: route login success and failure messages through the injected runtime, so setup/onboarding surfaces capture all login output instead of only the QR. Thanks @vincentkoc.
165
+ - Google Chat: create an isolated Google auth transport per auth client, so google-auth-library interceptor mutations do not accumulate across webhook verification and access-token clients. Thanks @vincentkoc.
166
+ - Doctor/plugins: remove orphaned or recovered managed npm copies of bundled `@openclaw/*` plugins during `doctor --fix`, so stale package manifests cannot shadow the current bundled plugin config schema.
167
+ - Control UI/performance: cap long-task and long-animation-frame diagnostics in the shared event log, so slow-render telemetry does not evict gateway/plugin events from the Debug and Overview views. Thanks @vincentkoc.
168
+ - Gateway/startup: log the canvas host mount only after the HTTP server has bound, so startup logs no longer report the canvas host as mounted before it can serve requests.
169
+ - Control UI/i18n: render the Sessions active filter tooltip with the configured minute count in every locale and make the i18n check reject placeholder drift. Thanks @BunsDev.
170
+ - Web fetch: late-bind `web_fetch` config and provider fallback metadata from the active runtime snapshot, matching `web_search` so long-lived tools do not use stale fetch provider settings. Thanks @vincentkoc.
171
+ - Discord: clear stale startup probe bot/application status when the async bot probe throws, not just when it returns a degraded probe result. Thanks @vincentkoc.
172
+ - Web search: scope explicit bundled `web_search` provider runtime loading through manifest ownership, so selecting DuckDuckGo/Gemini/etc. does not import unrelated bundled providers or log their optional dependency failures. Thanks @vincentkoc.
173
+ - Plugins/discovery: demote the source-only TypeScript runtime check on already-installed `origin: "global"` plugin packages from a config-blocking error to a warning and let the runtime fall through to the TypeScript source via jiti, so a single broken installed package no longer blocks `plugins install` for unrelated plugins; install-time rejection of newly-installed source-only packages is unchanged. Thanks @RomneyDa.
174
+ - Providers/OpenAI Codex: stop the OAuth progress spinner before showing the manual redirect paste prompt, so callback timeouts do not spam `Browser callback did not finish` across terminals.
175
+ - Providers/OpenAI Codex: fail closed on malformed `/codex` control commands and diagnostics confirmations before changing bindings, permissions, model overrides, active turns, or feedback uploads. Thanks @vincentkoc.
176
+ - Providers/OpenAI Codex: sanitize Codex app-server command readouts, failure replies, approval prompts, elicitation prompts, and `request_user_input` text before posting them back into chat. Thanks @vincentkoc.
177
+ - Providers/OpenAI Codex: preserve local bound-turn image paths, reject stale same-thread turn notifications, enforce option-only user input prompts, and return failed dynamic tool results to Codex as unsuccessful tool calls. Thanks @vincentkoc.
178
+ - Providers/DeepSeek: expose DeepSeek V4 `xhigh` and `max` thinking levels through the lightweight provider-policy surface, so Control UI `/think` pickers keep showing the max reasoning options when the runtime plugin registry is not active. Fixes #77139. Thanks @bittoby.
179
+ - Release/beta smoke: resolve the dispatched Telegram beta E2E run from `gh run list` when `gh workflow run` returns no run URL, so the maintainer helper does not fail immediately after dispatch. Thanks @vincentkoc.
180
+ - Media/images: keep HEIC/HEIF attachments fail-closed when optional Sharp conversion is unavailable instead of sending originals that still need conversion. Thanks @vincentkoc.
181
+ - Google Meet: fork the caller's current agent transcript into agent-mode meeting consultant sessions, so Meet replies inherit the context from the tool call that joined the meeting.
182
+ - iOS/mobile pairing: reject non-loopback `ws://` setup URLs before QR/setup-code issuance and let the iOS Gateway settings screen scan QR codes or paste full setup-code messages. Thanks @BunsDev.
183
+ - Control UI: keep Gateway Access inputs and locale picker contained inside the card at narrow and tablet widths.
184
+ - Agents/trajectory: bound runtime trajectory capture and yield queued sidecar writes so oversized traces stop recording instead of monopolizing Gateway cleanup. Fixes #77124. Thanks @loyur.
185
+ - Telegram/streaming: sanitize tool-progress draft preview backticks before shared compaction, so long backtick-heavy progress text still renders inside the safe code-formatted preview instead of collapsing to an ellipsis.
186
+ - UI/chat: remove the unsupported `line-clamp` declaration from the chat queue text rule to eliminate Firefox console noise without changing visible truncation behavior. Thanks @ZanderH-code.
187
+ - Control UI: add explicit feedback for repeated actions by announcing session switches, flashing the active session selector, showing inline Save/Apply/Update progress, and distinguishing filtered-empty session lists from genuinely empty session stores. Thanks @BunsDev.
188
+ - Agents/Pi: suppress persistence for synthetic mid-turn overflow continuation prompts, so transcript-retry recovery does not write the "continue from transcript" prompt as a new user turn. Thanks @vincentkoc.
189
+ - Agents/tools: strip reasoning text from visible rich presentation titles, blocks, buttons, and select labels before message-tool sends, so structured channel payloads cannot leak hidden planning. Thanks @vincentkoc.
190
+ - Telegram: keep reply-dispatch lazy provider runtime chunks behind stable dist names and delete `/reasoning stream` previews after final delivery so package updates and live reasoning drafts do not leave Telegram turns broken or noisy. Thanks @BunsDev.
191
+ - Discord: start the gateway monitor without waiting for the startup bot/application probe, so WSL2 hosts with a slow `/users/@me` REST path still bring the channel online while status enrichment finishes asynchronously. Fixes #77103. Thanks @Suited78.
192
+ - Exec approvals: detect `env -S` split-string command-carrier risks when `-S`/`-s` is combined with other env short options, so approval explanations do not miss split payloads hidden behind `env -iS...`. Thanks @vincentkoc.
193
+ - Google Meet: log the concrete agent-mode TTS provider, model, voice, output format, and sample rate after speech synthesis, so Meet logs show which voice backend spoke each reply.
194
+ - Voice Call: mark realtime calls completed when the realtime provider closes normally, so Twilio/OpenAI/Google realtime stop events do not leave active call records behind. Thanks @vincentkoc.
195
+ - Gateway/update: keep the shutdown close path behind a stable runtime chunk and ship compatibility aliases for recent `server-close-*` hashes, so manual npm package replacement cannot leave an already-running Gateway unable to shut down cleanly. Fixes #77087. Thanks @westlife219.
196
+ - Control UI/media: mint short-lived scoped tickets for assistant media fetches and render ticketed URLs instead of exposing long-lived auth tokens in chat image URLs. Fixes #70830 and #77097. Thanks @hclsys.
197
+ - Exec approvals: treat POSIX `exec` as a command carrier for inline eval, shell-wrapper, and eval/source detection, so approval explanations and command-risk checks do not miss payloads hidden behind `exec`. Thanks @vincentkoc.
198
+ - Google Meet: log the resolved audio provider model when starting Chrome and paired-node Meet talk-back bridges, so agent-mode joins show the STT model and bidi joins show the realtime voice model.
199
+ - Diagnostics: handle missing session-tail files in cron recovery context without tripping extension test typecheck. Thanks @vincentkoc.
200
+ - QA/Slack: update the Slack dispatch preview fallback test SDK mock for structured progress draft helpers, so the rich progress draft regression suite covers the new imports instead of failing before assertions run. Thanks @vincentkoc.
201
+ - Release validation: allow focused QA live reruns to select Matrix and Telegram without running Slack, so known Slack credential-pool outages do not block non-Slack live proof. Thanks @vincentkoc.
202
+ - Plugins/loader: keep bundled plugin package `test-api.js` aliases behind private QA mode, so source transforms do not expose test-only public surfaces during normal plugin loading. Thanks @vincentkoc.
203
+ - Gateway/startup: start cron and record the post-ready memory trace even when deferred maintenance timers fail after readiness, so a non-fatal timer setup issue does not silently leave scheduled jobs idle. Thanks @vincentkoc.
204
+ - Exec approvals: unwrap BSD/macOS `env -P <path>` carrier commands before approval-command and strict inline-eval checks, so `/approve` shell execution and inline interpreter payloads are still blocked behind that env form.
205
+ - Agents/session status: keep semantic `session_status({ sessionKey: "current" })` on the live run session even before that run has a persisted session-store entry, instead of falling back to the sandbox policy key. Thanks @vincentkoc.
206
+ - QA/Slack: resolve bundled official plugin public-surface package aliases during source-mode QA runs, so release Slack live validation can load `@openclaw/slack/api.js` without workspace symlinks. Thanks @vincentkoc.
207
+ - Codex: pass the live run session key into app-server dynamic tools when sandbox policy uses a separate session key, so `session_status({ sessionKey: "current" })` reports the active run instead of the sandbox policy key. Thanks @vincentkoc.
208
+ - Web search: keep first-class assistant `web_search` auto-detect and configured runtime providers visible when active runtime metadata or the active plugin registry is incomplete. Fixes #77073. Thanks @joeykrug.
209
+ - Plugins/tools: mark manifest-optional sibling tools as optional even when they come from a shared non-optional factory, so cached/status/MCP metadata keeps opt-in tool policy accurate. Thanks @vincentkoc.
210
+ - Matrix: keep `streaming.progress.toolProgress` scoped to progress draft mode, so partial and quiet Matrix previews do not lose tool progress unless `streaming.preview.toolProgress` is disabled. Thanks @vincentkoc.
211
+ - Gateway/validation: isolate gateway server validation files, ignore unrelated startup logs in request-trace coverage, and fail fast on stuck shared-auth sockets, reducing false main-branch CI failures for contributors. Thanks @amknight.
212
+ - Channels/streaming: keep `streaming.progress.toolProgress` scoped to progress draft mode, so disabling compact progress lines does not silence partial/block preview tool updates. Thanks @vincentkoc.
213
+ - Plugins/update: treat OpenClaw stable correction versions like `2026.5.3-1` as stable releases for npm installs, plugin updates, and bundled-version comparisons, so `latest` can advance official plugins without prerelease opt-in. Thanks @vincentkoc.
214
+ - Control UI: point the Appearance tweakcn browse action and docs at the live tweakcn editor route instead of the removed `/themes` page. Fixes #77048.
215
+ - Control UI: render Dream Diary prose through the sanitized markdown pipeline, so diary bold/italic/header markdown no longer appears as literal source text. Fixes #62413.
216
+ - Control UI: render tool results whose output arrives as text-block arrays and give expanded tool output a scrollable block, so read/exec output remains visible in WebChat. Fixes #77054.
217
+ - MCP: include serialized conversation/message payloads in the primary text content for `conversations_list` and `messages_read`, while preserving `structuredContent` for capable clients. Fixes #77024.
218
+ - Media: treat `EPERM` from the post-write media fsync step as best-effort, allowing WebChat and channel uploads to finish on Windows filesystems that reject `fsync` after a successful write. Fixes #76844.
219
+ - Media/Telegram: send in-limit original images when optional image optimization is unavailable, so Telegram MEDIA replies and message-tool image sends do not fail just because `sharp` is missing. Fixes #77081. (#77117) Thanks @pfrederiksen.
220
+ - Diagnostics: include last progress, cron job/run ids, stopped cron job name, and the last assistant transcript snippet in stalled-session and stuck-session recovery logs so cron stalls show what was stopped.
221
+ - Streaming channels: add `streaming.preview.commandText: "status"` / `streaming.progress.commandText: "status"` to hide command/exec text in preview progress lines while keeping the released raw command text default. Fixes #77072.
222
+ - Agents/cron: let explicit cron `timeoutSeconds` drive both CLI no-output and embedded LLM idle watchdogs instead of being capped by resume defaults. Fixes #76289.
223
+ - Plugins/catalog: suppress missing `channelConfigs` compatibility diagnostics for external channel plugins that are disabled, denied, or outside a restrictive allowlist. Fixes #76095.
224
+ - Diagnostics: keep webhook/message OTEL attributes and Prometheus delivery labels low-cardinality and omit raw chat/message IDs from spans, so progress-draft and message-tool modes do not leak high-cardinality messaging identifiers.
225
+ - Google Meet: stop advertising legacy `mode: "realtime"` to agents and config UIs, while keeping it as a hidden compatibility alias for `mode: "agent"`, so new joins use the STT -> OpenClaw agent -> TTS path instead of selecting the direct realtime voice fallback.
226
+ - Google Meet: add `chrome.audioBufferBytes` for generated command-pair SoX audio commands and lower the default buffer from SoX's 8192 bytes to 4096 bytes to reduce Chrome talk-back latency.
227
+ - Google Meet: split realtime provider config into agent-mode transcription and bidi-mode voice providers, and migrate legacy Gemini Live bidi configs with `doctor --fix`, so Gemini Live can back direct bidi fallback without breaking the default OpenClaw agent talk-back path.
228
+ - Google Meet: keep waiting for the Meet microphone to unmute during join intro readiness instead of permanently skipping talk-back when Meet briefly reports the local mic as muted.
229
+ - Google Meet: expose `voiceCall.postDtmfSpeechDelayMs` in the plugin manifest schema and setup hints, so manifest-based config editing accepts the runtime-supported Twilio delay key. Thanks @vincentkoc.
230
+ - Google Meet: keep explicit non-Google `realtime.provider` values as the transcription provider compatibility fallback when `realtime.transcriptionProvider` is unset. Thanks @vincentkoc.
231
+ - Google Meet: make Twilio setup status require an enabled `voice-call` plugin entry instead of treating a missing entry as ready. Thanks @vincentkoc.
232
+ - Telegram: render shared interactive reply buttons in reply delivery so plugin approval messages show inline keyboards. (#76238) Thanks @keshavbotagent.
233
+ - Cron/sessions: keep cron metadata rows without an on-disk transcript non-resumable until a transcript exists, so doctor and `sessions cleanup --fix-missing` no longer report or prune pre-transcript cron rows as broken sessions. Refs #77011.
234
+ - OpenAI Codex: recreate missing bound app-server threads once when a stale `/codex bind` sidecar survives a restart, preserving the selected auth profile and turn overrides before retrying the inbound turn. (#76936) Thanks @keshavbotagent.
235
+ - Agents/cli-runner: drop a saved `claude-cli` resume sessionId at preparation time when its on-disk transcript no longer exists in `~/.claude/projects/`, so a stale binding from a half-installed `update.run` cannot trap follow-up runs (auto-reply / Telegram direct) in a `claude --resume` timeout loop; the run starts fresh and the new sessionId is written back through the existing post-run flow. (#77030; refs #77011) Thanks @openperf.
236
+ - Release validation: install the cross-OS TypeScript harness through Windows-safe Node/npm shims so native Windows package checks reach the OpenClaw smoke suites instead of exiting before artifact capture. Thanks @vincentkoc.
237
+ - Release validation: let Windows packaged-upgrade checks continue after the shipped 2026.5.2 updater hits its native-module swap cleanup fallback, verifying the fallback-installed candidate through package metadata and downstream smoke instead of crashing on the immediate update-status probe. Thanks @vincentkoc.
238
+ - Doctor/plugins: skip channel-derived official plugin installs when another configured plugin is the effective owner for the same channel, so `doctor --repair` does not reinstall `feishu` while `openclaw-lark` handles `channels.feishu`. Fixes #76623. Thanks @fuyizheng3120.
239
+ - Gateway/sessions: memoize repeated thinking-option enrichment and skip unused cost fallback checks while listing sessions, reducing per-row work on large multi-agent stores. Fixes #76931.
240
+ - Gateway/sessions: bound default `sessions.list` RPC responses and report truncation metadata, preventing Slack-heavy long-lived stores from forcing unbounded Gateway row construction. Fixes #77062.
241
+ - Agents/tools: use config-only runtime snapshots for plugin tool registration and live runtime config getters, avoiding expensive full secrets snapshot clones on the core-plugin-tools prep path. Fixes #76295.
242
+ - Agents/tools: honor the effective tool denylist before constructing optional PDF/media tool factories, so `tools.deny: ["pdf"]` skips PDF setup before later policy filtering. Fixes #76997.
243
+ - MCP/plugin tools: apply global `tools.profile`, `tools.alsoAllow`, and `tools.deny` policy while exposing plugin tools over the standalone MCP bridge, so ACP clients do not see policy-hidden plugin tools or miss opt-in optional tools. Thanks @vincentkoc.
244
+ - Plugin tools: honor explicit tool denylists while selecting plugin tool runtimes, so denied plugin tools are not materialized for direct command or gateway surfaces before later policy filtering. Thanks @vincentkoc.
245
+ - Plugin tools: filter factory-returned tools by manifest per-tool optional policy, so optional sibling tools from a shared runtime factory stay hidden unless explicitly allowed. Thanks @vincentkoc.
246
+ - Agents/transcripts: retry context-overflow compaction from the current transcript only after the inbound user turn was actually persisted, and keep WebChat agent-run live delivery from writing duplicate Pi-managed assistant turns. Fixes #76424. (#77033)
247
+ - Agents/bootstrap: keep pending `BOOTSTRAP.md` and bootstrap truncation notices in system-prompt Project Context instead of copying setup text or raw warning diagnostics into WebChat user/runtime context. Fixes #76946.
248
+ - Gateway/install: keep `.env`-managed values in the macOS LaunchAgent env file while still tracking `OPENCLAW_SERVICE_MANAGED_ENV_KEYS`, so regenerated services do not boot without managed auth/provider keys. Fixes #75374.
249
+ - Gateway/restart: verify listener PIDs by argv when `lsof` reports only the Node process name, so stale gateway cleanup can find macOS `cnode` listeners. Fixes #70664.
250
+ - Gateway/logging: expand leading `~` in `logging.file` before creating the file logger, preventing startup crash loops for home-relative log paths. Fixes #73587.
251
+ - Channels/CLI: keep `openclaw channels list --json` usable when provider usage fetching fails, and report per-provider usage errors without aborting the channel list. Refs #67595.
252
+ - Doctor/plugins: do not treat `plugins.allow` entries as configured plugins during missing-plugin repair, so restrictive allowlists no longer install allowed-but-unused plugins. Thanks @vincentkoc.
253
+ - Agents/messaging: deliver distinct final commentary after same-target `message` tool sends while still deduping text/media already sent by the tool, so short closing remarks are no longer silently dropped. Fixes #76915. Thanks @hclsys.
254
+ - Agents/messaging: preserve string thread IDs when matching message-tool reply dedupe routes, avoiding precision loss on numeric-looking topic IDs before channel plugin comparison. Thanks @vincentkoc.
255
+ - Channels/streaming: honor `agents.defaults.toolProgressDetail: "raw"` in Slack, Discord, Telegram, Matrix, and Microsoft Teams progress drafts, so tool-start lines include raw command/detail output when debugging. Thanks @vincentkoc.
256
+ - Channels/streaming: strip unmatched inline-code backticks from compacted raw progress draft lines, avoiding stray markdown markers after long command details are shortened. Thanks @vincentkoc.
257
+ - Discord/Slack/Mattermost: align draft preview tool-progress config help with the runtime behavior that hides interim tool updates when `streaming.preview.toolProgress` is false. Thanks @vincentkoc.
258
+ - Feishu: use the shared channel progress formatter for streaming-card tool status lines, including raw command/detail output and message-tool filtering. Thanks @vincentkoc.
259
+ - Mattermost: use the shared progress draft formatter for tool status previews, including raw command/detail output when `agents.defaults.toolProgressDetail: "raw"` is enabled. Thanks @vincentkoc.
260
+ - Mattermost: suppress standalone default tool-progress messages while draft previews are active, including when draft tool lines are disabled. Thanks @vincentkoc.
261
+ - Telegram: deliver button-only interactive replies by sending the shared fallback button-label text with the inline keyboard instead of dropping the reply as empty. Thanks @vincentkoc.
262
+ - OpenAI Codex: honor `auth.order.openai-codex` when starting app-server clients without an explicit auth profile, so status/model probes and implicit startup use the configured Codex account instead of falling back to the default profile. Thanks @vincentkoc.
263
+ - OpenAI Codex: let SSRF-guarded provider requests inherit OpenClaw's undici IPv4/IPv6 fallback policy, so ChatGPT-backed Codex runs recover on IPv4-working hosts when DNS still returns unreachable IPv6 addresses. Fixes #76857. Thanks @jplavoiemtl and @SymbolStar.
264
+ - Plugin updates: do not short-circuit trusted official npm updates as unchanged when the default/latest spec still resolves to an already-installed prerelease that the installer should replace with a stable fallback. Thanks @vincentkoc.
265
+ - Plugin updates: clean stale bundled load paths for already-externalized npm installs whose legacy install record only preserved the resolved package name. Thanks @vincentkoc.
266
+ - Plugin tools: keep auth-unavailable optional tools hidden even when another default tool from the same plugin is available and `tools.alsoAllow` names the optional tool. Thanks @vincentkoc.
267
+ - Realtime transcription: report socket closes before provider readiness as closed-before-ready failures instead of mislabeling them as connection timeouts for OpenAI, xAI, and Deepgram streaming transcription. Thanks @vincentkoc.
268
+ - OpenAI/Google Meet: fail realtime voice connection attempts when the socket closes before `session.updated`, avoiding stuck Meet joins waiting on a bridge that never became ready. Thanks @vincentkoc.
269
+ - Google Meet: avoid treating repeated participant words as multiple assistant-overlap matches when suppressing realtime echo transcripts. Thanks @vincentkoc.
270
+ - Google Meet: make `mode: "agent"` the default Chrome talk-back path, using realtime transcription for input and regular OpenClaw TTS for speech output, while keeping direct realtime voice answers available as `mode: "bidi"` and accepting `mode: "realtime"` as an agent-mode compatibility alias.
271
+ - Codex harness: keep `codex_app_server.*` telemetry publication owned by the harness instead of republishing the same callback event from core runners. Thanks @vincentkoc.
272
+ - Slack/Discord: suppress standalone tool-progress chatter when partial preview streaming has `streaming.preview.toolProgress: false`, matching the documented quiet-preview behavior. Thanks @vincentkoc.
273
+ - Matrix: bind native approval reaction targets before publishing option reactions, so fast approver reactions on threaded prompts are not dropped while the approval handler finishes setup. Thanks @vincentkoc.
274
+ - Google Meet: make realtime talk-back agent-driven by default with `realtime.strategy: "agent"`, keep the previous direct bidirectional model behavior available as `realtime.strategy: "bidi"`, route the Meet tab speaker output to `BlackHole 2ch` automatically for local Chrome realtime joins, coalesce nearby speech transcript fragments before consulting the agent, and avoid cutting off agent speech from server VAD or stale playback pipe errors.
275
+ - Google Meet: suppress queued assistant playback and assistant-like transcript echoes from the realtime input path, so the meeting does not hear the agent's own speech as a new user turn and loop or cut itself off.
276
+ - Google Meet: keep Chrome realtime transport tests hermetic on Linux prerelease shards while preserving the macOS-only runtime guard. Thanks @vincentkoc.
277
+ - QA/Matrix: let the live tool-progress preview and error checks verify progress replacement events without depending on the preview saying `Working`, `tool: read`, an unlabelled/pathless `read from`, or the original draft root being observed. Thanks @vincentkoc.
278
+ - QA/Matrix: keep the target=both approval scenario focused on channel and DM metadata delivery by resolving the accepted approval through the gateway after both Matrix events are observed. Thanks @vincentkoc.
279
+ - QA/Matrix: wait for live approval reactions to echo before starting the threaded approval decision timeout. Thanks @vincentkoc.
280
+ - QA/Matrix: reuse the primed driver sync stream when confirming approval reaction echoes, avoiding missed self-reactions in live release runs. Thanks @vincentkoc.
281
+ - Channels/WhatsApp: apply the shared group/channel visible-reply mode during inbound dispatch so group replies stay message-tool-only by default without overriding direct-chat harness defaults. Refs #75178 and #67394. Thanks @scoootscooob.
282
+ - Plugins/Codex: preserve Codex-native OAuth routing for `/codex bind` app-server turns so bound sessions keep the selected Codex auth profile instead of falling back to public OpenAI credentials. (#76714) Thanks @keshavbotagent.
283
+ - Telegram: keep status checks pointed at the active chat so asking for the current session no longer reports an old direct-message conversation. (#76708) Thanks @amknight.
284
+ - Gateway/install: prefer supported system Node over nvm/fnm/volta/asdf/mise when regenerating managed gateway services, so `gateway install --force` no longer recreates service definitions that doctor immediately flags as version-manager-backed. Fixes #76339. Thanks @brokemac79 and @BunsDev.
285
+ - Google Chat: normalize Google auth certificate response headers before google-auth-library reads cache-control, so inbound webhook auth no longer rejects with `res?.headers.get is not a function`. Fixes #76880. Thanks @donbowman.
286
+ - WhatsApp: route terminal login QR output through the active runtime for initial and restart sockets, so `openclaw channels login --channel whatsapp` does not lose the QR behind direct stdout writes. Fixes #76213. Thanks @dougvk.
287
+ - Proxy/debugging: disable debug proxy direct upstream forwarding for proxy requests and CONNECT tunnels while managed proxy mode is active unless `OPENCLAW_DEBUG_PROXY_ALLOW_DIRECT_CONNECT_WITH_MANAGED_PROXY=1` is explicitly set for approved local diagnostics. Thanks @jesse-merhi and @mjamiv.
288
+ - Direct APNs: route direct HTTP/2 delivery through the active managed proxy with redacted proxy diagnostics, so push requests honor configured egress controls and `openclaw proxy validate --apns-reachable` can prove APNs is reachable through the proxy before deployment. (#74905) Thanks @jesse-merhi.
289
+ - Agents/subagents: detect prefix-only completion announce replies and fall back to the captured child result so requester chats no longer lose most of long sub-agent reports silently. Fixes #76412. Thanks @inxaos and @davemorin.
290
+ - TUI: replace the stale-response watchdog notice with plain user-facing copy so stalled replies no longer surface backend or streaming internals. (#77120) Thanks @davemorin.
291
+ - Security/Windows: validate `SystemRoot`/`WINDIR` env values through the Windows install-root validator and add them to the dangerous-host-env policy when resolving `icacls.exe`/`whoami.exe` for `openclaw security audit`, so workspace `.env` overrides and bare command names cannot redirect Windows ACL helpers to attacker-controlled binaries. (#74458) Thanks @mmaps.
292
+ - Security/Windows: pin Windows registry-probe `reg.exe` resolution to the canonical Windows install root in install-root probing, so `SystemRoot`/`WINDIR` env overrides cannot redirect registry queries during Windows host detection. (#74454) Thanks @mmaps.
293
+ - QQBot: preserve the framework command authorization decision when converting framework command contexts into engine slash command contexts, so downstream slash handlers see `commandAuthorized` matching the channel's resolved `isAuthorizedSender` instead of a hardcoded `true`. (#77453) Thanks @drobison00.
294
+ - Security/Windows: block `LOCALAPPDATA` from workspace `.env` and resolve Windows update-flow portable Git path prepends from the trusted process-local `LOCALAPPDATA` only, so workspace-supplied values cannot redirect `git` discovery during `openclaw update`. (#77470) Thanks @drobison00.
295
+ - Browser/SSRF: enforce the existing current-tab URL navigation policy before tab-scoped debug, export, and read routes (console, page errors, network requests, trace start/stop, response body, screenshot, snapshot, storage, etc.) collect from an already-selected tab, so blocked tabs return a policy error instead of being read first and redacted only at response time. (#75731) Thanks @eleqtrizit.
296
+ - Security/Windows: route the `.cmd`/`.bat` process wrapper through the shared Windows install-root resolver instead of `process.env.ComSpec`, so workspace dotenv-blocked `SystemRoot`/`WINDIR` overrides and unsafe values like UNC paths or path-lists cannot redirect `cmd.exe` selection on Windows. (#77472) Thanks @drobison00.
297
+ - Agents/bootstrap: honor `BOOTSTRAP.md` content injected by `agent:bootstrap` hooks when deciding whether bootstrap is pending, so hook-provided required setup instructions are included in the system prompt. (#77501) Thanks @ificator.
298
+
CHANGELOG/2026.5.5.md ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.5.5
2
+
3
+ ### Fixes
4
+
5
+ - Telegram/Codex: generate DM topic labels with Codex-compatible simple-completion requests so auto-created private topics can be renamed instead of staying `New Chat`.
6
+ - Doctor/Codex OAuth: preserve working `openai-codex/*` PI routes during `doctor --fix`, recover 2026.5.5-rewritten `openai/*` GPT-5 routes when only Codex OAuth auth is available, and warn without rewriting mixed Codex OAuth plus direct OpenAI PI routes, so update repair does not break subscription-auth setups. Fixes #78407. Thanks @shakkernerd.
7
+ - Plugins/runtime fetch: drop third-party symbol metadata from plain request header dictionaries before passing them into native `fetch` or `Headers`, so SDK and guarded/proxy fetch paths do not reject otherwise valid plugin requests. Fixes #77846. Thanks @shakkernerd.
8
+ - Web fetch: bound guarded dispatcher cleanup after request timeouts so timed-out fetches return tool errors instead of leaving Gateway tool lanes active. (#78439) Thanks @obviyus.
9
+ - Mattermost/setup: prompt for and persist the server base URL after the bot token in `openclaw setup --wizard`, instead of failing validation before `--http-url` is collected. Fixes #76670. Thanks @jacobtomlinson.
10
+ - Gate Slack startup user allowlist resolution [AI]. (#77898) Thanks @pgondhi987.
11
+ - OpenAI/Codex: suppress stale `openai-codex` GPT-5.1/5.2/5.3 model refs that ChatGPT/Codex OAuth accounts now reject, keeping model lists, config validation, and forward-compat resolution on current 5.4/5.5 routes. Fixes #67158. Thanks @drpau.
12
+ - CLI/update: keep pnpm package updates on the running custom global install root and pass pnpm's `--global-dir` so `openclaw update` does not create a second default-prefix install when `OPENCLAW_HOME` or the shell points at a custom OpenClaw directory. Fixes #78377. Thanks @amknight.
13
+ - Google Meet/Voice Call: wait longer before playing PIN-derived Twilio DTMF for Meet dial-in prompts and retire stale delegated phone sessions instead of reusing completed calls.
14
+ - PDF/Codex: include extraction-fallback instructions for `openai-codex/*` PDF tool requests so Codex Responses receives its required system prompt. Fixes #77872. Thanks @anyech.
15
+ - Gateway/startup: keep the Gateway running when a configured optional plugin-owned capability such as a web_search provider or channel points at a known installable plugin that is currently unavailable; startup now logs a config warning and leaves `openclaw doctor --fix` to install or enable the plugin. (#78642) Thanks @joshavant.
16
+ - Onboard/channels: recover externalized channel plugins from stale `channels.<id>` config by falling back to `ensureChannelSetupPluginInstalled` via the trusted catalog when the plugin is missing on disk, so leftover `appId`/token entries no longer dead-end onboard with "<channel> plugin not available." (#78328) Thanks @sliverp.
17
+ - Codex/app-server: forward the OpenClaw workspace bootstrap block through Codex `developerInstructions` instead of `config.instructions`, so persona/style guidance reaches the behavior-shaping app-server lane. Fixes #77363. Thanks @lonexreb.
18
+ - MS Teams: route proactive channel sends with stored thread roots through the configured threaded reply path instead of forcing every CLI/message-tool send into a new top-level post. Fixes #78298. Thanks @amknight.
19
+ - CLI/infer: pass minimal instructions to local `openai-codex/*` model probes and surface provider error details when `infer model run` returns no text. Fixes #76464. Thanks @lilesjtu.
20
+ - Dependencies: override transitive `ip-address` to `10.2.0` so the runtime lockfile no longer includes the vulnerable `10.1.0` build flagged by Dependabot alert 109. Thanks @vincentkoc.
21
+ - Plugins/install: apply OpenClaw's npm security overrides inside managed external plugin npm roots so hoisted plugin dependencies inherit the host package hardening. Thanks @vincentkoc.
22
+ - Plugins/install: skip npm peer resolution in managed plugin roots so installing peer-based plugins such as Opik cannot pull a stale registry `openclaw` copy beside Codex/Discord/WhatsApp and trigger `ERESOLVE`. Thanks @vincentkoc.
23
+ - Plugins/uninstall: run managed npm cleanup even when a plugin package directory is already missing, preventing stale package manifests from reinstalling removed plugins on the next npm install.
24
+ - Feishu: hydrate missing native topic starter thread IDs before session routing so first turns and follow-ups stay in the same topic session. Fixes #78262. Thanks @joeyzenghuan.
25
+ - LINE: reject `dmPolicy: "open"` configs without wildcard `allowFrom` so webhook DMs fail validation instead of being acknowledged and silently blocked before inbound processing. Fixes #78316.
26
+ - Telegram/Codex: keep message-tool-only progress drafts visible and render native Codex tool progress once per tool instead of duplicating item/tool draft lines. Fixes #75641. (#77949)
27
+ - Providers/xAI: stop sending OpenAI-style reasoning effort controls to native Grok Responses models, so `xai/grok-4.3` no longer fails live Docker/Gateway runs with `Invalid reasoning effort`.
28
+ - Providers/xAI: clamp the bundled xAI thinking profile to `off` so live Gateway runs cannot send unsupported reasoning levels to native Grok Responses models.
29
+ - Matrix/approvals: retry approval delivery up to 3 times with a short backoff so transient Matrix send failures do not strand pending approval prompts. (#78179) Thanks @Patrick-Erichsen.
30
+ - Discord/gateway: measure heartbeat ACK timeouts from the actual heartbeat send, preventing late initial heartbeats from triggering false reconnect loops while the channel is still awaiting readiness. Fixes #77668. (#78087) Thanks @bryce-d-greybeard and @NikolaFC.
31
+ - Discord/guilds: route plain text control commands such as `/steer` through the normal authorization and mention gate instead of silently dropping them before an agent session can see them. Fixes #78080. Thanks @ramitrkar-hash.
32
+ - Control UI/Sessions: make the compaction count a compact `N Checkpoint(s)` disclosure and show expanded session-level details with modern checkpoint history cards across responsive table layouts. Thanks @BunsDev.
33
+ - Control UI/performance: keep chat and channel tabs responsive while history payloads and channel probes are slow, label partial channel status, and record slow chat/config render timings in the event log. Thanks @BunsDev.
34
+ - Control UI/sessions: fire the documented `/new` command and lifecycle hooks only for explicit Control UI session creation, restoring session-memory and custom hook capture without changing SDK parent-session creates. Fixes #76957. Thanks @BunsDev.
35
+ - Exec approvals: fall back to a guarded copy when Windows rejects rename-overwrite for `exec-approvals.json`, while preserving symlink, hard-link, and owner-only permission safeguards. Fixes #77785. (#77907) Thanks @Alex-Alaniz and @MilleniumGenAI.
36
+ - Status/session store: derive `totalTokens` for CLI backends from `agentMeta.lastCallUsage` (and set it on Claude CLI runs) so `/status` context usage is not shown as `?` while cache/token lines are populated. Fixes #78194. Thanks @neeravmakwana.
37
+ - Slack: preserve Socket Mode SDK error context and structured Slack API fields in reconnect logs, so startup failures no longer collapse to a bare `unknown error`.
38
+ - iOS pairing: allow setup-code and manual `ws://` connects for private LAN and `.local` gateways while keeping Tailscale/public routes on `wss://`, and prefer explicit gateway passwords over stale bootstrap tokens in mixed-auth reconnects. Fixes #47887; carries forward #65185. Thanks @draix and @BunsDev.
39
+ - Plugins/diagnostics: make source-only TypeScript package warnings actionable by explaining that missing compiled runtime output is a publisher packaging issue and pointing users to update/reinstall or disable/uninstall the plugin. Fixes #77835. Thanks @googlerest.
40
+ - Control UI/chat: keep persisted assistant progress text visible when the same transcript turn also contains tool-use metadata, so chat.history reloads no longer make those replies vanish after the next user message. Fixes #77374. Thanks @BunsDev.
41
+ - Cron: repair persisted future `nextRunAtMs` values that no longer line up with the cron schedule, so daily timezone-aware jobs do not stay jumped to stale future dates. Fixes #77867. Thanks @hongfangsong.
42
+ - TUI: skip the generic CLI respawn wrapper for interactive launches, exit cleanly on terminal loss, and refuse to restore heartbeat sessions as the remembered chat session, preventing stale heartbeat history and orphaned `openclaw-tui` processes on first boot. Thanks @vincentkoc.
43
+ - Doctor/sessions: move heartbeat-poisoned default main session store entries to recovery keys and clear stale TUI restore pointers, so `doctor --fix` can repair instances already stuck on `agent:main:main` heartbeat history. Thanks @vincentkoc.
44
+ - Agents/context engines: keep hidden OpenClaw runtime-context custom messages out of context-engine assemble, afterTurn, and ingest hooks so transcript reconstruction plugins only see conversation messages. Thanks @vincentkoc.
45
+ - Gateway/shutdown: cancel delayed post-ready maintenance during close and suppress maintenance/cron startup after quick restarts, preventing orphaned background timers. Thanks @vincentkoc.
46
+ - Agents/generated media: treat attachment-style message tool actions as completed chat sends, preventing duplicate fallback media posts when generated files were already uploaded.
47
+ - Control UI/sessions: show each session's agent runtime in the Sessions table and allow filtering by runtime labels, matching the Agents panel runtime wording. Thanks @vincentkoc.
48
+ - Discord/streaming: show live reasoning text in progress drafts instead of a bare `Reasoning` status line.
49
+ - Gateway/status: avoid marking fast repeated health/status samples as event-loop degraded from CPU/utilization alone until the Gateway has accumulated a sustained sampling window. Thanks @shakkernerd.
50
+ - Plugins/update: keep installed official npm and ClawHub plugins such as Codex, Discord, WhatsApp, and diagnostics plugins synced during host updates even when disabled or previously exact-pinned, while preserving third-party plugin pins. Thanks @vincentkoc.
51
+ - Doctor/status: warn when `OPENCLAW_GATEWAY_TOKEN` would shadow a different active `gateway.auth.token` source for local CLI commands, while avoiding false positives when config points at the same env token. Fixes #74271. Thanks @yelog.
52
+ - Gateway/HTTP: avoid loading managed outgoing-image media handlers for unrelated requests, so disabled OpenAI-compatible routes return 404 without waiting on lazy media sidecars. Thanks @vincentkoc.
53
+ - Gateway/OpenAI-compatible: send the assistant role SSE chunk as soon as streaming chat-completion headers are accepted, so cold agent setup cannot leave `/v1/chat/completions` clients with a bodyless 200 response until their idle timeout fires.
54
+ - Agents/media: avoid direct generated-media completion fallback while the announce-agent run is still pending, so async video and music completions do not duplicate raw media messages. (#77754)
55
+ - WebChat/Codex media: stage Codex app-server generated local images into managed media before Gateway display, so Codex-home image paths no longer hit `LocalMediaAccessError` while keeping Codex home out of the display allowlist. Thanks @frankekn.
56
+ - TUI/sessions: bound the session picker to recent rows and use exact lookup-style refreshes for the active session, so dusty stores no longer make TUI hydrate weeks-old transcripts before becoming responsive. Thanks @vincentkoc.
57
+ - Doctor/gateway: report recent supervisor restart handoffs in `openclaw doctor --deep`, using the installed service environment when available so service-managed clean exits are visible in guided diagnostics. Thanks @shakkernerd.
58
+ - Gateway/status: show recent supervisor restart handoffs in `openclaw gateway status --deep`, including JSON details, so clean service-managed restarts are reported as restart handoffs instead of opaque stopped-service diagnostics. Thanks @shakkernerd.
59
+ - Providers/Fireworks: expose Kimi models as thinking-off-only and keep K2.5/K2.6 requests on `thinking: disabled`, so manual model switches do not send Fireworks-rejected `reasoning*` parameters. Refs #74289. Thanks @frankekn.
60
+ - WhatsApp responsiveness: stop only verified stale local TUI clients when they degrade the Gateway event loop and delay replies. Thanks @vincentkoc.
61
+ - Plugins/update: repair stale managed npm-root `openclaw` peer packages before plugin installs, so beta-channel official plugin updates are not downgraded by old core package-lock state. Thanks @vincentkoc.
62
+ - Plugins/install: reassert managed npm plugin `openclaw` peer links after shared-root npm installs, updates, and uninstalls, so mutating one plugin does not leave previously installed SDK-using plugins unable to resolve `openclaw/plugin-sdk/*`.
63
+ - Hooks/session-memory: add collision suffixes to fallback memory filenames so repeated `/new` or `/reset` captures in the same minute do not overwrite the earlier session archive. Thanks @vincentkoc.
64
+ - Agents/config: remove the ambiguous legacy `main` agent dir helper from runtime paths; model, auth, gateway, bundled plugin, and test helpers now resolve default/session agent dirs through `agents.list`/agent-scope helpers while plugin SDK keeps a deprecated compatibility export.
65
+ - CLI/status: show the selected agent runtime/harness in `openclaw status` session rows so terminal status matches the `/status` runtime line. Thanks @vincentkoc.
66
+ - CLI/sessions: prune old unreferenced transcript, compaction checkpoint, and trajectory artifacts during normal `sessions cleanup`, so gateway restart or crash orphans do not accumulate indefinitely outside `sessions.json`. Fixes #77608. Thanks @slideshow-dingo.
67
+ - Doctor/Codex: repair legacy `openai-codex/*` routes in primary models, fallbacks, heartbeat/subagent/compaction overrides, hooks, channel overrides, and stale session pins to canonical `openai/*`, selecting `agentRuntime.id: "codex"` only when the Codex plugin is installed, enabled, contributes the `codex` harness, and has usable OAuth; otherwise select `agentRuntime.id: "pi"`. Thanks @vincentkoc.
68
+ - Plugins/update: keep installed official npm and ClawHub plugins such as Codex, Discord, WhatsApp, and diagnostics plugins synced during host updates even when disabled or previously exact-pinned, while preserving third-party plugin pins. Thanks @vincentkoc.
69
+ - Video generation: accept provider-specific aspect-ratio and resolution hints at the tool boundary, normalize `720P` to MiniMax's supported `768P`, and stop sending Google `generateAudio` on Gemini video requests so provider fallback can recover from model-specific parameter differences. Thanks @vincentkoc.
70
+ - Status: show compact Gateway process uptime and host system uptime in `/status`, making restart and host-lifetime checks visible from chat. Thanks @vincentkoc.
71
+ - WhatsApp responsiveness: stop only verified stale local TUI clients when they degrade the Gateway event loop and delay replies. Thanks @vincentkoc.
72
+ - Hooks/session-memory: run reset memory capture off the command reply path and make model-generated memory filename slugs opt-in with `llmSlug: true`, so `/new` and `/reset` no longer block WhatsApp and other message-channel reset replies on hook housekeeping or a nested model call. Thanks @vincentkoc.
73
+ - CLI/gateway: pause non-TTY stdin after full CLI command completion and stop `openclaw agent` from falling back to embedded mode after gateway request/auth failures, so parent help commands exit cleanly and scoped delivery probes surface the real Gateway error immediately. Thanks @vincentkoc.
74
+ - Gateway/model catalog: cache empty read-only model catalog results until reload, so TUI and control-plane refresh loops cannot hammer plugin metadata reads when no usable models are currently discovered. Thanks @vincentkoc.
75
+ - Hooks/session-memory: add collision suffixes to fallback memory filenames so repeated `/new` or `/reset` captures in the same minute do not overwrite the earlier session archive. Thanks @vincentkoc.
76
+ - TUI/sessions: bound the session picker to recent rows and use exact lookup-style refreshes for the active session, so dusty stores no longer make TUI hydrate weeks-old transcripts before becoming responsive. Thanks @vincentkoc.
77
+ - Agents/context engines: keep hidden OpenClaw runtime-context custom messages out of context-engine assemble, afterTurn, and ingest hooks so transcript reconstruction plugins only see conversation messages. Thanks @vincentkoc.
78
+ - TUI: skip the generic CLI respawn wrapper for interactive launches, exit cleanly on terminal loss, and refuse to restore heartbeat sessions as the remembered chat session, preventing stale heartbeat history and orphaned `openclaw-tui` processes on first boot. Thanks @vincentkoc.
79
+ - Doctor/sessions: move heartbeat-poisoned default main session store entries to recovery keys and clear stale TUI restore pointers, so `doctor --fix` can repair instances already stuck on `agent:main:main` heartbeat history. Thanks @vincentkoc.
80
+ - Gateway/shutdown: report structured shutdown warnings and HTTP close timeout warnings through `ShutdownResult` while preserving lifecycle hook hardening. Carries forward #41296. Thanks @edenfunf.
81
+ - CLI/update: make dev-channel preflight lint opt-in and constrained when enabled, so `openclaw update --channel dev` no longer walks back otherwise-good main commits when Ubuntu hosts OOM-kill or fail parallel oxlint shards. Thanks @vincentkoc.
82
+ - CLI/channels: skip config, proxy, channel-option catalog, banner-config, and plugin startup bootstrap for the bare `openclaw channels` parent-help command, so it exits promptly after printing help instead of loading configured channel plugins. Thanks @vincentkoc.
83
+ - Gateway/shutdown: cancel delayed post-ready maintenance during close and suppress maintenance/cron startup after quick restarts, preventing orphaned background timers. Thanks @vincentkoc.
84
+ - CLI/status: show the selected agent runtime/harness in `openclaw status` session rows so terminal status matches the `/status` runtime line. Thanks @vincentkoc.
85
+ - Sessions CLI: show the selected agent runtime in the `openclaw sessions` table so terminal output matches the runtime visibility already present in JSON/status surfaces. Thanks @vincentkoc.
86
+ - Control UI/sessions: show each session's agent runtime in the Sessions table and allow filtering by runtime labels, matching the Agents panel runtime wording. Thanks @vincentkoc.
87
+ - Docker/Gateway: harden the gateway container by dropping `NET_RAW` and `NET_ADMIN` capabilities and enabling `no-new-privileges` in the bundled `docker-compose.yml`. Thanks @VintageAyu.
88
+ - OpenAI/Gateway: flush the initial chat stream chunk correctly so first-token streaming is visible instead of being delayed behind later chunks.
89
+ - Gateway/media: skip media sidecar handling for unrelated HTTP routes so non-media requests do not pay the media route behavior.
90
+ - Discord: show reasoning text in progress drafts so streaming replies expose useful thinking/progress instead of blank draft updates.
91
+ - Auth profiles: avoid putting providers on cooldown for format-level rejections, so fallback profiles can still be tried when a model name is unsupported.
92
+ - Update/plugins: tolerate corrupt managed plugin records during update so core package updates can still complete and report the plugin repair path.
93
+ - Update: stop dev-channel updates cleanly after a fetch failure instead of continuing into later update steps.
94
+ - Agents/generated media: treat attachment-style message tool actions as completed chat sends, preventing duplicate fallback media posts when generated files were already uploaded.
95
+
CHANGELOG/2026.6.2.md ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.6.2
2
+
3
+ ### Highlights
4
+
5
+ - **Governed plugin and skill installs:** the old dangerous-code scanner gives way to an operator install policy with clearer doctor, CLI, ClawHub, package, archive, source, upload, and marketplace recovery paths. (#89516) Thanks @joshavant and @vincentkoc.
6
+ - **Safer channel delivery:** Telegram, Feishu, Discord, WhatsApp, and outbound sends now handle transcript mirroring, streamed finals, admin writeback, approval allowlists, poll modifiers, and setup state without corrupting delivery. (#88973, #89626, #89812, #89035, #89814, #89813, #89601) Thanks @pgondhi987, @Petru2224, @zhangguiping-xydt, @ppmuzyk, @codezz, @takhoffman, @vincentkoc, @harjothkhara, @obviyus, @glenn-agent, @kesslerio, and @leiJack-lo.
7
+ - **Steadier chat and operator UI:** visible stream text, completed sends, Workboard keyboard navigation, dialog accessibility, lazy usage views, and Android companion flows retain their state through normal interaction. (#89801, #89777) Thanks @vincentkoc.
8
+ - **Stricter safety checks:** config, policy, shell snapshots, exec prechecks, script limits, and Gateway startup reject malformed or unsafe input before it becomes runtime state. (#89701, #87074, #81488, #87056, #89480) Thanks @RomneyDa, @giodl73-repo, @mmaps, @drobison00, @vincentkoc, and @q1387154-spec.
9
+ - **More reliable Gateway and model sessions:** session locks, abandoned Codex startup, ACP handoffs, custom-provider fanout, provider aliases, prompt caching, and memory checks recover without leaving a run wedged. (#89811, #89244) Thanks @RomneyDa, @takhoffman, @spencer2211, and @vincentkoc.
10
+
11
+ ### Changes
12
+
13
+ - Plugins/security: replace dangerous-code scanner enforcement with operator install policy, install-policy context, doctor checks, install/update CLI wiring, ClawHub metadata paths, and package/archive/source/upload lifecycle coverage. (#89516) Thanks @joshavant and @vincentkoc.
14
+ - Policy: add data-handling conformance checks and reject unsupported policy keys. (#87056, #87074) Thanks @giodl73-repo.
15
+ - Telegram/channels: show commentary and reasoning in progress drafts, share progress draft compositors across channel plugins, and keep Telegram polling stop/reset boundaries cheaper and more reliable.
16
+ - UI/mobile: add Workboard keyboard movement controls, tighten Workboard card operations, and improve Android companion-first shell UX. Thanks @vincentkoc.
17
+
18
+ ### Fixes
19
+
20
+ - Channels/outbound: keep channel sends durable when transcript mirroring fails, stop schema-padded poll modifiers from blocking normal sends, preserve WebChat `sessions_send` handoffs, preserve Discord channel-label suppression while hiding internal agent failure traces, match Discord libopus error shapes, and sanitize Discord tool progress scaffolding. (#89626, #89812, #89601) Thanks @Petru2224, @codezz, @takhoffman, @harjothkhara, and @vincentkoc.
21
+ - Telegram/Feishu: require admin rights for Telegram target writeback, keep Telegram DM exec approval allowlists working with `ask:off`, prevent Telegram preview duplication across streaming modes, isolate verbose status after streamed finals, cancel clean restart stop timers, slow polling restart storms, and wire Feishu setup runtime setters. (#88973, #89035, #89813, #89814) Thanks @pgondhi987, @zhangguiping-xydt, @ppmuzyk, @takhoffman, @vincentkoc, @obviyus, @kesslerio, @glenn-agent, and @leiJack-lo.
22
+ - Feishu: preserve full streaming card content by sending the merged text on each update instead of only the latest delta, so card readers see complete output when intermediate frames are missed. (#90181) Thanks @mushuiyu886.
23
+ - Chat/UI/Gateway: preserve visible chat stream text, clear stale stream buffers before terminal commits, reconcile completed sends, scroll pending sends into view, harden Workboard dialog accessibility, stabilize WebChat prompt-cache affinity, overlap chat catalog startup, render chat history incrementally, lazy-load usage dashboard, and report gateway health auth diagnostics. (#89337) Thanks @RomneyDa and @vincentkoc.
24
+ - Agents/Codex/providers/models: release session write locks when prompt-release fence reads fail, retire abandoned Codex app-server startups, keep stream-to-parent ACP spawns registered, close Codex startup clients on timeout, recover bundled provider aliases, avoid custom-provider runtime fanout, preserve provider prompt-cache boundaries, forward Gemini stop sequences, and strip Kimi-incompatible Anthropic cache markers. (#89811) Thanks @takhoffman, @spencer2211, and @vincentkoc.
25
+ - Memory/build/update: warn after startup watcher pressure checks, externalize optional Baileys image backends, restore and pin Canvas A2UI compatibility assets, keep plugin repair fetch failures nonblocking, restore Skill Workshop view switching, and keep the current chat toggle active after awaited session switches. (#89244) Thanks @RomneyDa and @vincentkoc.
26
+ - Plugins/auth: keep Hermes migration reports pointed at SQLite auth-profile stores.
27
+ - Plugins/CLI: avoid importing the runtime plugin loader only to clear in-process caches after short-lived plugin install, enable, disable, update, and uninstall commands refresh registry metadata.
28
+ - Security/config/tooling: reject corrupt shell snapshots, suspicious gateway startup configs, malformed numeric limits, oversized audit responses, unsafe exec precheck env, and invalid pending-agent SQLite scaffold denials. (#89701, #89705, #89480, #81488) Thanks @RomneyDa, @mmaps, @drobison00, @vincentkoc, and @q1387154-spec.
29
+
30
+ ### Complete contribution record
31
+
32
+ This audited record covers the complete v2026.6.1..v2026.6.2-beta.1 history: 57 merged PRs. The generation manifest also supplies direct commits as editorial input; the grouped notes above prioritize user impact.
33
+
34
+ #### Pull requests
35
+
36
+ - **PR #88922** fix(google): forward stop sequences to Gemini generationConfig. Thanks @coder999999999.
37
+ - **PR #89460** fix(models): preserve provider prompt cache boundaries. Related #89386. Thanks @Enominera.
38
+ - **PR #89478** fix: restore Skill Workshop view switcher. Thanks @shakkernerd.
39
+ - **PR #76741** fix(kimi): strip anthropic cache markers. Related #76612. Thanks @BryanTegomoh and @vliuyt.
40
+ - **PR #89480** fix: recover suspicious gateway startup configs. Related #89331. Thanks @q1387154-spec.
41
+ - **PR #87056** Policy: add data handling conformance checks. Thanks @giodl73-repo.
42
+ - **PR #81488** Harden node exec approval precheck env [AI]. Thanks @mmaps and @drobison00.
43
+ - **PR #89356** Add accessible Workboard movement controls. Thanks @BunsDev.
44
+ - **PR #87074** fix(policy): reject unsupported policy keys. Thanks @giodl73-repo.
45
+ - **PR #89601** fix(outbound): stop schema-padded poll modifiers from blocking send. Thanks @codezz and @Takhoffman.
46
+ - **PR #88963** perf(telegram): avoid broad reset-boundary scan. Thanks @MonkeyLeeT.
47
+ - **PR #89125** Suppress internal agent failure traces before channel delivery. Thanks @fuller-stack-dev.
48
+ - **PR #89701** fix(exec): reject corrupt shell snapshots. Thanks @RomneyDa.
49
+ - **PR #89705** fix: allowlist pending agent sqlite scaffold. Thanks @RomneyDa.
50
+ - **PR #89704** Share channel progress draft compositor. Thanks @obviyus.
51
+ - **PR #89708** perf(control-ui): coalesce chat metadata startup. Thanks @vincentkoc.
52
+ - **PR #89337** fix: report gateway health auth diagnostics. Related #89711. Thanks @RomneyDa.
53
+ - **PR #88685** Render dashboard chat history incrementally. Related #87345. Thanks @alexzhu0 and @2xmncvcx92-dotcom.
54
+ - **PR #89740** fix(gateway): stabilize webchat prompt cache affinity. Related #89139. Thanks @vincentkoc and @Enominera.
55
+ - **PR #89191** fix(webchat): show sessions_send handoffs as forwarded. Related #89161. Thanks @849261680 and @Xj49688-lgtm.
56
+ - **PR #89723** fix(auto-reply): surface fatal channel errors. Thanks @fuller-stack-dev.
57
+ - **PR #89727** fix #87699: [Bug]: [BUG] UI shows agent "running" after conversation ends — requires manual page refresh every time. Thanks @zhangguiping-xydt and @csck-luoy.
58
+ - **PR #88786** fix #71992: [Bug]: Control UI webchat duplicates every assistant reply on 2026.4.21 — regression from #5964/#39469. Thanks @zhangguiping-xydt and @rzhnrhjr6j-cloud and @astoreyai and @kAIborg24.
59
+ - **PR #89530** fix(ui): preserve visible chat stream text. Related #67035. Thanks @osolmaz and @q7793527.
60
+ - **PR #87072** feat(telegram): opt-in interleaved progress lane. Thanks @anagnorisis2peripeteia.
61
+ - **PR #89771** perf(ui): start chat refresh before bootstrap. Thanks @vincentkoc.
62
+ - **PR #89777** perf(ui): label delayed chat sends in telemetry. Thanks @vincentkoc.
63
+ - **PR #89786** perf(gateway): overlap chat catalog startup. Thanks @vincentkoc.
64
+ - **PR #89793** test(ui): cover control chat send timing phases. Thanks @vincentkoc.
65
+ - **PR #89801** perf(ui): surface chat ACK server timing. Thanks @vincentkoc.
66
+ - **PR #89355** Harden Workboard modal and drawer accessibility. Thanks @BunsDev.
67
+ - **PR #89802** docs(web): document chat ACK timing metadata. Thanks @vincentkoc.
68
+ - **PR #89391** fix(android): improve companion-first shell UX. Thanks @Tosko4.
69
+ - **PR #89811** fix(agents): release session write lock if fence read throws on prompt release. Thanks @Takhoffman and @spencer2211.
70
+ - **PR #89808** perf(ui): trace chat send server milestones. Thanks @vincentkoc.
71
+ - **PR #89813** fix(telegram): isolate verbose status after streamed finals. Related #89540. Thanks @Takhoffman and @kesslerio.
72
+ - **PR #89814** fix(feishu): wire setup runtime setter. Related #88024. Thanks @Takhoffman and @glenn-agent and @leiJack-lo.
73
+ - **PR #85961** fix #85807: retain Telegram preview after generation race. Thanks @zhangguiping-xydt and @samson1357924.
74
+ - **PR #89035** fix #88773: [Bug]: Telegram DM exec requires approval despite allowlist + ask:off — works in webchat, not in Telegram. Thanks @zhangguiping-xydt and @obviyus and @ppmuzyk.
75
+ - **PR #88634** fix(telegram): prevent preview duplication in partial and block streaming modes. Related #87624. Thanks @jmao0001 and @tuckyapps.
76
+ - **PR #89812** fix(outbound): keep channel send durable when transcript mirror fails (#89626). Thanks @Takhoffman and @harjothkhara and @Petru2224.
77
+ - **PR #88973** fix(telegram): require admin for target writeback [AI]. Thanks @pgondhi987 and @vincentkoc.
78
+ - **PR #89449** refactor(gateway): share duplicated test helpers. Thanks @vincentkoc.
79
+ - **PR #88832** fix(telegram): slow polling restart storms. Thanks @TurboTheTurtle.
80
+ - **PR #89960** test(channels): fix guardrail regex lint. Thanks @RomneyDa.
81
+ - **PR #89244** fix(memory): warn after startup watcher pressure check. Thanks @RomneyDa.
82
+ - **PR #89516** Add operator install policy and remove dangerous-code install scanners. Thanks @joshavant.
83
+ - **PR #90024** chore(release): update appcast for 2026.6.1.
84
+ - **PR #89613** docs: document auth profile failure policy contract.
85
+ - **PR #89548** fix(agents): classify read-only shell commands as non-mutating. Thanks @Glucksberg.
86
+ - **PR #89939** fix: keep stream-to-parent spawns registered. Thanks @scotthuang.
87
+ - **PR #88964** fix(agents): repair context-engine tool-result pairing. Related #88561. Thanks @MonkeyLeeT and @Finn-jiejie.
88
+ - **PR #82219** fix(codex): accept first-party OpenAI plugin marketplaces (bundled and primary-runtime). Related #82216. Thanks @yaanfpv.
89
+ - **PR #89998** revert(codex): revert first-party marketplace allowlist. Thanks @kevinslin.
90
+ - **PR #89176** fix(browser): honor tab timeout for Chrome MCP. Related #88213. Thanks @MonkeyLeeT and @lamkan0210.
91
+ - **PR #90043** fix: restore Skill Workshop current chat toggle. Thanks @shakkernerd.
92
+ - **PR #81422** fix(update): surface plugin channel fallbacks. Thanks @BKF-Gitty.
93
+
CHANGELOG/2026.6.6.md ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.6.6
2
+
3
+ ### Highlights
4
+
5
+ - **Tighter security boundaries:** transcript, sandbox, MCP, browser, channel, and exec-approval paths now fail closed around unsafe access, timed-out approvals, and malformed boundary input. (#91529, #91618, #91741, #91750, #89938) Thanks @joshavant, @pgondhi987, @mmaps, @eleqtrizit, @drobison00, @vincentkoc, and @devinkuhn.
6
+ - **Reliable Telegram delivery:** account-scoped topics route to the correct agent, streamed text survives tool calls, callbacks and draft chunks stay coherent, and unauthorized DM text does not enter cache or prompt context. (#91189, #88682, #90212, #91478, #91915) Thanks @codysai001, @alexzhu0, @snowzlm, @obviyus, @sallyom, @AbdelftahZowail, @producedbysavant, @shakkernerd, @vincentkoc, and @BSG2000.
7
+ - **iMessage stays connected:** always-on inbound recovery, durable echo markers, block streaming, idle approval discovery, and outbound transport now survive restarts and idle periods. (#91335, #91449, #88969, #91783) Thanks @omarshahine, @jmissig, @dwonshin, @colmbrogan, @vincentkoc, and @TurboTheTurtle.
8
+ - **Better browser and MCP connectivity:** existing browser sessions, CDP/WebSocket discovery, default-profile URLs, OAuth/SSE transport, and tool schemas now connect through clearer, safer paths. (#91422, #89851, #91736, #91451) Thanks @pgondhi987, @anagnorisis2peripeteia, @eleqtrizit, @LiuwqGit, @lifuyue, @marcusbsorensen, @cursoragent, @vincentkoc, @849261680, and @mgrandau.
9
+ - **Faster first replies:** Control UI startup no longer waits on broad model loading, while cached metadata, lazy slash-command work, and first-event tracing make slow initial responses visible. (#91531, #91538, #91568, #91583) Thanks @vincentkoc and @BSG2000.
10
+ - **Broader provider support:** OpenRouter OAuth and Claude Fable 5 land alongside correct Codex compaction ownership, local-model execution, normalized tool progress, and Gemma 4 reasoning replay. (#91830, #91882, #91590, #88630, #91696) Thanks @Patrick-Erichsen, @joshavant, @bdjben, @Coder-Wangyankun, @vincentkoc, @bfox55, @shakkernerd, and @NOVA-Openclaw.
11
+
12
+ ### Changes
13
+
14
+ - CLI progress: emit Claude CLI commentary progress events and bridge inter-tool commentary into channel progress without exposing internal protocol scaffolding. (#89834, #90883) Thanks @anagnorisis2peripeteia, @AbdelftahZowail, @kentuscn, and @vincentkoc.
15
+ - Observability: allow trusted diagnostics channels to capture tool input/output content, add first-assistant-event traces, and warn on slow initial replies. (#91256, #91568, #91583) Thanks @amknight, @mjunaidca, and @vincentkoc.
16
+ - Plugins/ClawHub: dogfood reusable package publishing, let dry runs skip publish approval, allow declared installed trusted hooks, report managed plugin version drift, and warn instead of failing on retired Skill Workshop configuration. (#91574, #91591, #90004, #90927, #90838) Thanks @Patrick-Erichsen, @brokemac79, @lonexreb, @rogerallen1, @vincentkoc, and @ryanhelms.
17
+ - Memory/providers: move the local llama.cpp runtime into its provider plugin, batch embeddings across files, persist the agent model catalog cache, and keep QMD JSON search one-shot while filtering stale REM recall previews. (#91324, #89138, #90457, #91837, #91851) Thanks @osolmaz, @mushuiyu886, @ai-hpc, @TurboTheTurtle, @jalehman, @hartmark, @vincentkoc, @rudi193-cmd, @Peilsender, and @xpysgdhr.
18
+ - Channels/mobile: add the QQBot group mention toggle, improve iPad and iPhone control surfaces, and expose the active connection host in the TUI footer. (#91423, #91557, #89909) Thanks @cxyhhhhh, @Solvely-Colin, @baskduf, @joshavant, @sliverp, and @deuxksy.
19
+ - Performance: prewarm TUI runtime plugins, deduplicate plugin auto-enable fanout, trim dense text-delta snapshots, and reuse prepared startup model metadata. (#90782, #89978, #91580, #91531) Thanks @RomneyDa, @ai-hpc, @vincentkoc, and @JakeBiggs.
20
+
21
+ ### Fixes
22
+
23
+ - Agent/session recovery: drop stale approval follow-ups after session rebind, remove drained reply-queue items by identity, recover stale main and visible replies, preserve Codex context-engine compaction ownership, lower the default compaction timeout to 180 seconds while respecting explicit configuration, and keep provider-failure terminal lifecycle state correct. (#85679, #91450, #91566, #91840, #91590, #91361, #91895) Thanks @openperf, @yetval, @joshavant, @wangmiao0668000666, @TurboTheTurtle, @two3pro, @velvet-shark, @sallyom, @849261680, @vincentkoc, @Tony-ooo, @Jerry-Xin, @olveww-dot, and @nikhilmaddirala.
24
+ - User-visible content boundaries: suppress Codex/Harmony protocol artifacts, neutralize browser and LanceDB memory media directives, redact transcript images, and preserve native `/compact` replies through source suppression. (#89151, #91422, #91425, #91529, #90212) Thanks @joelnishanth, @pgondhi987, @joshavant, @snowzlm, @reslp, @vincentkoc, and @devinkuhn.
25
+ - Channel delivery: keep WhatsApp captured replies attached to the successor controller after restart, retry Feishu rate limits, preserve Mattermost thread replies, canonicalize LINE webhook paths, restore Discord reply hydration and runtime timeout exports, and show OpenAI Realtime WebRTC assistant transcripts. (#85823, #89659, #91684, #91649, #90263, #91686, #90426) Thanks @itsuzef, @ladygege, @jacobtomlinson, @fuller-stack-dev, @shushushv, @mcaxtr, @AxelHu, @vincentkoc, @marshallm-create, @sliverp, and @dahifi.
26
+ - Cron: cancel active task runs cleanly, preserve terminal timeout/cancel state, and recover no-deliver tool warnings instead of silently losing the outcome. (#90666, #90678) Thanks @ai-hpc.
27
+ - Gateway/config/auth: share the approval runtime socket token, replace arrays explicitly in `config.patch`, skip the deleted-agent guard only for valid ACP harness sessions, surface headless LaunchAgent state, verify SQLite auth migration before cleanup, and arm QMD startup maintenance. (#87105, #91551, #91219, #91614, #91740, #91978) Thanks @fuller-stack-dev, @scotthuang, @joshavant, @velvet-shark, @vincentkoc, and @dahifi.
28
+ - Providers/Codex: clarify quota errors, restore the Codex synthetic usage line, canonicalize Codex protocol assets, require API-key auth for realtime voice, normalize ACP model refs, preserve Gemma 4 `reasoning_content`, and avoid guardian review for local models. (#91390, #91709, #91507, #91567, #88630, #91696) Thanks @hxy91819, @brokemac79, @RomneyDa, @joshavant, @Coder-Wangyankun, @vincentkoc, @bfox55, @shakkernerd, and @sergiopesch.
29
+ - Updates/builds: recover package Gateway restarts after refresh failure, expose plugin convergence repair, fall back to Corepack in PATH-less pnpm environments, seed the correct Docker store packages, and keep ClawHub dry-run and publish paths reusable. (#91581, #91599, #91547, #91591) Thanks @fuller-stack-dev, @sallyom, @Patrick-Erichsen, @vincentkoc, and @laurenceputra.
30
+ - UI: require explicit user intent before opening chat sessions and drain restored chat queues after session switches. (#91480) Thanks @TurboTheTurtle, @Takhoffman, and @zdwalter.
31
+ - Android: avoid the `dataSync` foreground-service type for persistent nodes. (#80082) Thanks @davelutztx.
32
+ - Native hooks: bound relay lifetimes so abandoned native hook connections cannot linger indefinitely. (#91550) Thanks @joshavant and @clem-git.
33
+
34
+ ### Complete contribution record
35
+
36
+ This audited record covers the complete v2026.6.5..v2026.6.6 history: 198 merged PRs. The generation manifest also supplies direct commits as editorial input; the grouped notes above prioritize user impact.
37
+
38
+ #### Pull requests
39
+
40
+ - **PR #91335** fix(imessage): always-on inbound recovery and dedupe. Related #89237. Thanks @omarshahine and @vincentkoc and @dwonshin.
41
+ - **PR #91189** fix(telegram): route account-scoped topic agents. Thanks @codysai001.
42
+ - **PR #88682** Preserve Telegram streamed text blocks between tool calls. Related #87326. Thanks @alexzhu0 and @AbdelftahZowail.
43
+ - **PR #91390** fix: clarify provider quota errors. Thanks @hxy91819.
44
+ - **PR #90883** fix(cli): bridge inter-tool commentary events to channel progress. Thanks @anagnorisis2peripeteia.
45
+ - **PR #91419** docs: preserve channel brand terms in Chinese i18n. Thanks @hxy91819.
46
+ - **PR #87105** fix(gateway): share approval runtime socket token. Thanks @fuller-stack-dev.
47
+ - **PR #80082** fix(android): avoid dataSync FGS for persistent node. Thanks @davelutztx.
48
+ - **PR #91442** docs: preserve LINE across localized docs glossaries. Thanks @hxy91819.
49
+ - **PR #88768** fix(codex): normalize dynamic tool progress results. Thanks @bdjben.
50
+ - **PR #91422** fix(browser): neutralize media directives in browser output [AI]. Thanks @pgondhi987.
51
+ - **PR #89834** feat(cli): emit commentary progress events from Claude CLI parser. Related #87326. Thanks @anagnorisis2peripeteia and @AbdelftahZowail.
52
+ - **PR #85679** fix(agents): drop stale exec approval followups after session rebind. Related #59349. Thanks @openperf and @two3pro.
53
+ - **PR #91450** fix(reply-queue): remove the drained item by reference instead of front index. Thanks @yetval.
54
+ - **PR #89151** fix(delivery): suppress Codex/Harmony internal protocol artifacts from user-facing channels. Related #88128. Thanks @joelnishanth and @reslp.
55
+ - **PR #90678** fix(cron): recover no-deliver tool warnings. Thanks @ai-hpc.
56
+ - **PR #91449** fix(imessage): honor block streaming config. Thanks @jmissig and @omarshahine.
57
+ - **PR #91508** Revert "docs: add maturity scorecard mirror". Thanks @kevinslin.
58
+ - **PR #91364** build(deps): bump github.com/steipete/peekaboo from 3.3.0 to 3.4.0 in /apps/macos in the swift-deps group.
59
+ - **PR #91368** build(deps): bump actions/github-script from 8 to 9.
60
+ - **PR #91512** chore: add taxonomy file. Thanks @kevinslin.
61
+ - **PR #91369** build(deps): bump actions/cache from 4 to 5.
62
+ - **PR #91367** build(deps): bump the actions group with 2 updates.
63
+ - **PR #91365** build(deps): bump the android-deps group in /apps/android with 3 updates.
64
+ - **PR #91496** chore: bump Codex app-server to 0.137.0. Thanks @RomneyDa.
65
+ - **PR #90666** fix(cron): cancel active cron task runs. Thanks @ai-hpc.
66
+ - **PR #90927** fix(doctor): report managed plugin version drift. Related #90891. Thanks @brokemac79.
67
+ - **PR #91531** perf(control-ui): reuse startup model metadata. Thanks @vincentkoc.
68
+ - **PR #91538** perf(control-ui): avoid startup catalog wait. Thanks @vincentkoc.
69
+ - **PR #91507** feat: canonicalize Codex protocol JSON asset ordering. Thanks @RomneyDa.
70
+ - **PR #91550** fix: bound native hook relay lifetime. Related #90993. Thanks @joshavant and @clem-git.
71
+ - **PR #89588** fix(telegram): restore /compact on generic message ingress. Related #89525. Thanks @joelnishanth and @cursoragent and @bomberluke37-prog.
72
+ - **PR #91529** Fix transcript image redaction. Related #90760. Thanks @joshavant and @devinkuhn.
73
+ - **PR #91551** Fix config.patch explicit array replacement. Thanks @joshavant.
74
+ - **PR #91568** perf(control-ui): trace first assistant event. Thanks @vincentkoc.
75
+ - **PR #85823** fix(whatsapp): route captured replies through successor controller after restart. Thanks @itsuzef and @mcaxtr.
76
+ - **PR #91574** feat: dogfood reusable ClawHub package publish. Thanks @Patrick-Erichsen.
77
+ - **PR #91583** perf(control-ui): warn on slow first replies. Thanks @vincentkoc.
78
+ - **PR #89659** fix(feishu): retry on send rate-limit errors (230020/230006). Related #70879. Thanks @ladygege and @marshallm-create and @sliverp and @AxelHu.
79
+ - **PR #91547** Fix Docker store seed target packages. Related #91035. Thanks @sallyom and @laurenceputra.
80
+ - **PR #91578** fix: make docs i18n frontmatter translation resilient. Thanks @hxy91819.
81
+ - **PR #91567** fix(openai): require api-key auth for realtime voice. Related #90456. Thanks @joshavant and @sergiopesch.
82
+ - **PR #91591** fix: let ClawHub dry runs skip publish approval. Thanks @Patrick-Erichsen.
83
+ - **PR #91598** perf(control-ui): lazy load slash commands. Thanks @vincentkoc.
84
+ - **PR #91580** fix(agents): trim dense text delta snapshots. Related #86599. Thanks @vincentkoc and @JakeBiggs.
85
+ - **PR #91425** fix(memory-lancedb): guard memory recall output [AI]. Thanks @pgondhi987.
86
+ - **PR #88969** fix(imessage): persist echo markers before send. Thanks @colmbrogan.
87
+ - **PR #91566** Fix stale main session startup recovery. Related #90525. Thanks @joshavant and @Tony-ooo.
88
+ - **PR #91324** fix(memory): move local llama.cpp runtime to provider plugin. Related #88705. Thanks @osolmaz and @Peilsender.
89
+ - **PR #91637** docs: include plugin prerelease in release validation approval.
90
+ - **PR #91649** fix(line): canonicalize trailing-slash webhook paths.
91
+ - **PR #91423** feat(qqbot): add /bot-group-allways command to toggle mention requirement. Thanks @cxyhhhhh and @sliverp.
92
+ - **PR #91642** fix(docs): continue partial i18n batches after file errors. Thanks @hxy91819.
93
+ - **PR #91661** chore(plugin-sdk): refresh API baseline hash.
94
+ - **PR #91665** docs: fix release CI Android dispatch guidance.
95
+ - **PR #89138** fix #88009: [Feature]: batched memory embedding should batch over files. Thanks @mushuiyu886 and @jalehman and @hartmark.
96
+ - **PR #91679** fix(plugin-sdk): align Discord component edit facade types. Thanks @vincentkoc.
97
+ - **PR #91686** fix(discord): restore runtime timeout compatibility exports. Thanks @vincentkoc.
98
+ - **PR #90212** fix(agents): deliver native /compact replies through source suppression. Thanks @snowzlm.
99
+ - **PR #91618** fix: expand unsafe host env denylist. Thanks @pgondhi987.
100
+ - **PR #91615** fix: block rustup toolchain env overrides [AI]. Thanks @pgondhi987.
101
+ - **PR #89851** fix(gateway): support Streamable HTTP MCP transport on loopback server. Thanks @anagnorisis2peripeteia.
102
+ - **PR #91619** fix: block git protocol env controls [AI]. Thanks @pgondhi987.
103
+ - **PR #91684** fix(mattermost): keep default replies in existing threads. Thanks @jacobtomlinson.
104
+ - **PR #90457** fix(models): persist agent catalog cache. Thanks @ai-hpc.
105
+ - **PR #91709** fix(status): restore Codex synthetic usage line. Related #91694. Thanks @brokemac79.
106
+ - **PR #89909** fix(tui): show connection host in footer. Related #56276. Thanks @baskduf and @deuxksy.
107
+ - **PR #89978** perf(config): dedupe plugin auto-enable fanout work. Thanks @ai-hpc.
108
+ - **PR #91219** fix(gateway): skip deleted-agent guard for ACP harness session keys. Thanks @scotthuang.
109
+ - **PR #90782** perf(tui): prewarm runtime plugins before first send. Thanks @RomneyDa.
110
+ - **PR #90838** fix(config): warn for retired skill-workshop plugin entry instead of failing validation (#90244). Thanks @lonexreb and @rogerallen1.
111
+ - **PR #91753** docs: clarify Matrix plugin upgrade repair. Thanks @RomneyDa.
112
+ - **PR #91755** docs: align Feishu DM policy defaults. Thanks @RomneyDa.
113
+ - **PR #91745** fix(discord): require sender for moderation actions [AI]. Thanks @eleqtrizit.
114
+ - **PR #85950** docs: clarify trusted-proxy Control UI scope behavior. Related #80063. Thanks @nielskaspers and @longstoryscott.
115
+ - **PR #91746** fix(msteams): require admin for group actions. Thanks @eleqtrizit.
116
+ - **PR #91256** feat(diagnostics-otel): capture tool input/output content via trusted channel. Thanks @amknight.
117
+ - **PR #91749** fix(gateway): restrict non-owner loopback tools. Thanks @eleqtrizit.
118
+ - **PR #91748** fix(elevated): reject group ids as senders. Thanks @eleqtrizit.
119
+ - **PR #91752** fix(codex): guard sandbox http requests. Thanks @eleqtrizit.
120
+ - **PR #91763** fix: require ACP metadata for deleted-agent bypass. Thanks @shakkernerd.
121
+ - **PR #91751** fix(mcp): harden stdio env filtering. Thanks @eleqtrizit.
122
+ - **PR #91765** Clarify env-var executable behavior reports in SECURITY.md. Thanks @jacobtomlinson.
123
+ - **PR #91480** fix(ui): require user intent for chat sessions. Related #89760. Thanks @TurboTheTurtle and @Takhoffman and @zdwalter.
124
+ - **PR #91777** docs: remove superpowers spec draft. Thanks @Patrick-Erichsen.
125
+ - **PR #91773** fix(mcp): lowercase SSE event-source header keys to prevent duplicate Authorization (401). Thanks @Takhoffman.
126
+ - **PR #91741** Validate sandbox bind parent paths [AI]. Thanks @mmaps.
127
+ - **PR #88530** fix(imessage): skip idle approval discovery scans. Thanks @colmbrogan and @omarshahine.
128
+ - **PR #91780** fix(ui): drain restored chat queue after session switch. Thanks @tmimmanuel.
129
+ - **PR #91750** fix(search): enforce native web search tool policy. Thanks @eleqtrizit.
130
+ - **PR #91757** fix(config): clarify retired skill workshop plugin warning. Thanks @RomneyDa.
131
+ - **PR #91787** fix(doctor): keep TTS legacy migration on supported paths.
132
+ - **PR #91783** fix(imessage): harden outbound send transport. Related #84329. Thanks @omarshahine and @TurboTheTurtle.
133
+ - **PR #91785** fix(imessage): surface inbound startup diagnostics. Thanks @omarshahine.
134
+ - **PR #91590** Fix context-engine compaction ownership for Codex sessions. Thanks @joshavant.
135
+ - **PR #91557** Improve iPad and iPhone control surfaces. Thanks @Solvely-Colin and @joshavant.
136
+ - **PR #91666** chore(deps): bump useblacksmith/setup-docker-builder from 1.8.0 to 1.9.0 in the actions group.
137
+ - **PR #91819** docs: link ClawHub plugin validation fixes guide. Thanks @Patrick-Erichsen.
138
+ - **PR #88630** fix(codex): avoid guardian review for local models. Thanks @vincentkoc.
139
+ - **PR #91830** feat: add OpenRouter OAuth to onboarding. Thanks @Patrick-Erichsen.
140
+ - **PR #91842** fix(plugin-sdk): refresh API baseline hash.
141
+ - **PR #91614** fix(gateway): surface headless LaunchAgent state. Thanks @fuller-stack-dev.
142
+ - **PR #91851** fix(memory-core): filter stale recall entries in REM harness preview. Thanks @vincentkoc.
143
+ - **PR #91859** fix(ci): disable memory slot in release smoke config. Thanks @vincentkoc.
144
+ - **PR #90004** [plugin sdk] Allow declared installed trusted hooks. Related #87735. Thanks @brokemac79 and @ryanhelms.
145
+ - **PR #91837** fix(memory-core): keep QMD JSON search one-shot. Related #91821. Thanks @TurboTheTurtle and @xpysgdhr.
146
+ - **PR #91871** Remove bundled channel contract fallbacks. Thanks @obviyus.
147
+ - **PR #91879** fix(ci): include ACPX in shared live-test image.
148
+ - **PR #91840** Fix stale visible reply recovery. Related #90535. Thanks @joshavant and @Jerry-Xin.
149
+ - **PR #91876** Fix Telegram callback API handling. Thanks @obviyus.
150
+ - **PR #91874** Share channel draft chunking resolver. Thanks @obviyus.
151
+ - **PR #91599** fix(update): expose plugin convergence repair. Thanks @fuller-stack-dev.
152
+ - **PR #91581** fix(update): recover package gateway restart after refresh failure. Thanks @fuller-stack-dev.
153
+ - **PR #91904** fix(telegram): use SDK dispatch dedupe. Thanks @obviyus.
154
+ - **PR #90263** fix(discord): hydrate reply context metadata. Thanks @fuller-stack-dev.
155
+ - **PR #91478** block unauthorized Telegram DM text from prompt context. Related #91209. Thanks @sallyom and @producedbysavant.
156
+ - **PR #91915** fix(telegram): audit follow-ups — block-mode chunk config, dedupe bucket cleanup, grammy contract trust. Thanks @obviyus.
157
+ - **PR #91361** fix(compaction): lower default timeout from 900s to 180s, preserve explicit config. Related #91358. Thanks @wangmiao0668000666 and @velvet-shark and @olveww-dot.
158
+ - **PR #91791** fix(sandbox): use materialized skill paths in startup prompts. Related #91761. Thanks @brokemac79 and @vincentkoc and @gbb-netizen.
159
+ - **PR #91736** Support existing-session browser CDP endpoints. Related #56118. Thanks @lifuyue and @mgrandau.
160
+ - **PR #91747** fix(browser): validate discovered CDP websocket URLs. Thanks @eleqtrizit.
161
+ - **PR #91882** feat(anthropic): support Claude Fable 5 adaptive thinking. Related #91805. Thanks @NOVA-Openclaw.
162
+ - **PR #91884** fix(memory): keep ignored-name QMD roots watchable. Thanks @vincentkoc.
163
+ - **PR #91740** fix(auth): verify SQLite auth migration before cleanup. Thanks @fuller-stack-dev and @velvet-shark.
164
+ - **PR #91451** fix(mcp): repair OAuth redirect, errors, and unicode schema patterns. Related #91433. Thanks @LiuwqGit and @cursoragent and @vincentkoc and @marcusbsorensen.
165
+ - **PR #91978** fix(gateway): arm qmd startup maintenance. Thanks @vincentkoc.
166
+ - **PR #90426** fix(talk): show OpenAI Realtime WebRTC assistant transcripts. Thanks @shushushv and @vincentkoc.
167
+ - **PR #91696** fix(agents): preserve reasoning_content replay for Gemma 4 openai-completions models. Related #91645. Thanks @Coder-Wangyankun and @bfox55.
168
+ - **PR #89938** Fail closed on exec approval timeout. Thanks @drobison00.
169
+ - **PR #91895** fix(webchat): finalize provider failure lifecycle. Related #91730. Thanks @TurboTheTurtle and @sallyom and @nikhilmaddirala.
170
+ - **PR #80143** fix(browser): honor cdpUrl for user default profile. Related #48042. Thanks @HemantSudarshan and @Max-Resilient.
171
+ - **PR #91688** fix(cron): reject cron expressions that have no reachable run time. Thanks @yetval and @vincentkoc.
172
+ - **PR #91737** fix(cron): use final-call usage for session token totals. Related #91716. Thanks @MonkeyLeeT and @vincentkoc and @yetval.
173
+ - **PR #89605** fix(process): return timeout code for killed commands. Thanks @ai-hpc.
174
+ - **PR #80013** perf(usage-cost-cache): throttle full-cache rewrites during refresh. Thanks @zeroaltitude.
175
+ - **PR #76731** Fix mobile Control UI chat layout. Thanks @Solvely-Colin.
176
+ - **PR #83738** fix(cron): capture originating session/agent on the cron wake tool call. Thanks @anagnorisis2peripeteia.
177
+ - **PR #85196** Redact tool output secrets. Thanks @amknight.
178
+ - **PR #92007** fix(security): block build tool env overrides. Thanks @eleqtrizit.
179
+ - **PR #91891** fix: preserve non-oneOf protocol schema array order. Thanks @RomneyDa.
180
+ - **PR #91754** fix(macos): hide unsupported Voice Wake controls. Related #89575. Thanks @RomneyDa and @cwhyhy.
181
+ - **PR #92049** test(ci): restore upgrade survivor session fixture. Thanks @vincentkoc.
182
+ - **PR #89670** fix: keep skill toggles keyed by skill identity. Related #89661. Thanks @s-moffett.
183
+ - **PR #91934** fix(state): tolerate chmod failures when opening the state database. Related #91919. Thanks @truffle-dev and @david-garcia-garcia.
184
+ - **PR #92051** fix(fal): parse raw completed queue results. Related #91989. Thanks @harjothkhara and @oswaldyeo.
185
+ - **PR #92047** fix(agents): prefer explicit sessions_send keys. Related #64699. Thanks @vincentkoc and @sunxq1017-hash.
186
+ - **PR #92020** fix(memory-core): check SQLite plugin state for dreaming ingestion audit after JSON migration (fixes #92017). Thanks @zenglingbiao and @JUMPUNDER.
187
+ - **PR #92032** fix(mcp): always log channel-bridge notification failures. Thanks @hansraj316.
188
+ - **PR #92033** fix(gateway): log swallowed background-task finalization errors. Thanks @hansraj316.
189
+ - **PR #92022** fix(sessions): derive channel from direct-chat session keys in send-policy. Thanks @hansraj316.
190
+ - **PR #91163** fix(xai): clarify x_search query guidance. Thanks @rubencu.
191
+ - **PR #90121** fix(memory): write dream fallback without subagent runtime. Thanks @a-m-a-r-a.
192
+ - **PR #91215** fix(ui): show prompt progress while sending. Related #91199. Thanks @zhangguiping-xydt and @vincentkoc and @Monniasza.
193
+ - **PR #92029** fix(tools): surface unsupported-signal in anyOf availability. Thanks @hansraj316.
194
+ - **PR #92034** perf(agents): memoize XML attribute regex in DSML stream parser. Thanks @hansraj316.
195
+ - **PR #92026** perf(agents): sanitize compaction messages once for token estimation. Thanks @hansraj316.
196
+ - **PR #91351** fix(opencode-go): add qwen plus tiered pricing. Related #91238. Thanks @849261680 and @vincentkoc and @samson910022.
197
+ - **PR #92027** fix(gateway): recover config hot-reload after watcher errors. Thanks @hansraj316.
198
+ - **PR #91471** feat(cron): add readable ISO time fields to `cron runs` JSON output. Thanks @FMLS and @cursoragent.
199
+ - **PR #91711** :bug: fix(agents): classify harness provider mismatch as format error (#91710). Thanks @a-tokyo.
200
+ - **PR #91292** fix(models): keep bundled provider catalog when configured base URL is blank (#91270). Thanks @yetval and @vincentkoc and @resYuto.
201
+ - **PR #91720** :bug: fix(openai): remove chatgpt-responses transport override from gpt-5.3-codex catalog entry. Related #91710. Thanks @a-tokyo.
202
+ - **PR #91305** fix(control-ui): make Control UI bootstrap config endpoint base-path-relative (#66946). Thanks @Alix-007 and @vincentkoc and @yndwx01.
203
+ - **PR #92056** fix(exec): honor state dir approvals. Thanks @vincentkoc.
204
+ - **PR #91897** fix(memory): self-heal missing index identity by initializing provider during sync. Thanks @xydt-tanshanshan and @vincentkoc.
205
+ - **PR #91802** fix(diagnostics): release wedged session lane when stuck-session recovery aborts a run with queued session work. Related #91700. Thanks @openperf and @infocus13.
206
+ - **PR #92030** fix(cron): structural top-of-hour match in stagger heuristic. Thanks @hansraj316 and @vincentkoc.
207
+ - **PR #92055** fix(media): resolve state-relative inbound attachments. Thanks @sercada and @vincentkoc.
208
+ - **PR #91962** fix(agent): dampen Discord stale thread replies. Thanks @RomneyDa.
209
+ - **PR #90912** fix(agents): honor configured CLI resume timeouts. Thanks @ai-hpc and @vincentkoc.
210
+ - **PR #91296** fix: hand off supervised git updates. Thanks @abnershang.
211
+ - **PR #91950** fix(web_fetch): sanitize URL whitespace from LLM tool call arguments (fixes #91651). Thanks @zenglingbiao and @vincentkoc and @akang1798.
212
+ - **PR #77367** fix(discord): scope command-deploy cache by application id. Related #77359. Thanks @lonexreb and @sallyom and @igmarketing.
213
+ - **PR #91976** feat(auto-reply): durable inter-tool commentary via verbose standalone progress (supersedes #89850/#89890). Thanks @anagnorisis2peripeteia.
214
+ - **PR #90128** fix(sessions): preserve user /model override across daily/idle session rollover (#90119). Thanks @Marvinthebored.
215
+ - **PR #92084** fix(clickclack): allow explicit enable through plugin allowlist.
216
+ - **PR #92092** fix(auto-reply): stop dropping claude-cli narration when commentary lane is off. Thanks @ragesaq.
217
+ - **PR #92123** #92109: [Bug]: EmbeddedAttemptSessionTakeoverError caused by Btrfs ctimeNs instability. Thanks @lzyyzznl and @vincentkoc and @recruits.
218
+ - **PR #92136** fix(feishu): reply inside P2P direct-message threads. Thanks @LiaoyuanNing and @vincentkoc.
219
+ - **PR #92121** fix(memory): preserve live SQLite index during swaps. Related #91216. Thanks @xydt-tanshanshan and @chrisreddington.
220
+ - **PR #90173** fix(agents): stabilize a2a prompt cache context. Thanks @Sunjae-k and @sunjae-1.
221
+ - **PR #91974** fix(cli-runner): scope claude-cli queue to live-session owner identity (#91946). Thanks @wangwllu.
222
+ - **PR #92053** fix(thinking): apply Claude profile to anthropic-messages catalog rows. Related #91975. Thanks @wangwllu.
223
+ - **PR #41991** Google: show detailed Gemini CLI OAuth extraction failures. Thanks @bgmbgm94.
224
+ - **PR #92074** fix(qqbot): flush tool output before silent non-streaming final. Thanks @sliverp.
225
+ - **PR #89508** fix(models): clarify provider model registration hint. Related #89192. Thanks @sweetcornna and @aaajiao.
226
+ - **PR #89085** fix(agents): keep migrated session entry ids unique on v1 upgrade. Thanks @yetval.
227
+ - **PR #89552** fix(discord): clean migrated thread binding state. Thanks @SYU8384.
228
+ - **PR #89448** fix(cron): reject durations that overflow to a non-finite value. Related #83906. Thanks @Alix-007 and @davinci282828.
229
+ - **PR #89319** fix(doctor): warn on unsupported hook entry loaders. Related #89309. Thanks @leno23 and @vincentkoc and @CameronWeller.
230
+ - **PR #91966** fix(config): stop config.patch replacePaths index suffix from widening array consent. Thanks @yetval and @vincentkoc.
231
+ - **PR #92127** fix(plugins): rescan storm in "/models" call (regression shipped since v2026.5.18). Thanks @obuchowski.
232
+ - **PR #91657** fix(ollama): use provider thinking default in SDK session factory. Related #91428. Thanks @openperf and @vincentkoc and @anijatsu.
233
+ - **PR #91742** fix(memory): abort orphaned embedding work when memory_search times out. Related #91718. Thanks @dreamhunter2333 and @vincentkoc and @NOVA-Openclaw.
234
+ - **PR #89091** fix(memory-core): retry narrative message reads. Thanks @bennewell35.
235
+ - **PR #92150** fix(release): gate beta publish on plugin verification. Thanks @vincentkoc.
236
+ - **PR #92158** fix(cli): validate gateway RPC timeout inputs. Thanks @ruanrrn and @comeran.
237
+ - **PR #91911** fix(agents): retry same model across short rate-limit windows. Thanks @lanzhi-lee.
238
+
CHANGELOG/2026.8.1.md ADDED
The diff for this file is too large to render. See raw diff
 
CHANGELOG/2026.9.1.md ADDED
@@ -0,0 +1,1291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 2026.9.1
2
+
3
+ ### Highlights
4
+
5
+ - **Diagrams in every chat:** Mermaid blocks now render as diagrams in the Control UI and in the native macOS, iOS, and Android apps, with enlarge previews and a retry when a diagram fails to render on mobile. (#134913, #135746, #135470, #135342)
6
+ - **From install to chat in one prompt:** fresh installs (including `npx openclaw@latest`) get a quick-start lane that detects existing Claude Code or Codex logins and API keys, verifies them live, and opens the web dashboard from a foreground Gateway; the full wizard stays available as Custom setup. (#134221) Thanks @fuller-stack-dev.
7
+ - **Personal skill libraries on shared Gateways:** keep your own skills beside the workspace set with `openclaw skills library`, import them from ZIP archives, and share or publish them per identity on team Gateways. Related #133602. (#134068)
8
+ - **Updates that leave you working:** `openclaw update` now rolls back the npm candidate when the post-update Doctor fails, preserves your configuration and secret references across a failed upgrade, hands failures to a built-in triage agent, waits for plugin readiness before restarting, accepts npm 12 local archives, lets agent-launched updates finish outside the Gateway process tree, and proceeds without a Gateway service instead of refusing when no service manager exists (users on 2026.8.2 without a service manager should run `openclaw update --no-restart` once). Related #134204, #135655. (#135462, #134490, #134865, #134699, #134663, #136316, #135701) Thanks @fuller-stack-dev, @Patrick-Erichsen, @vyctorbrzezowski, @jalehman, @devzeroLL, and @obviyus.
9
+ - **A Gateway that stays up:** startup recovers under load and with large agent rosters, malformed legacy cron rows are quarantined instead of blocking boot, migration warnings degrade the Gateway instead of refusing to start, local model servers become the preferred OOM victims, and Windows Gateways stay online after an agent restart. Related #135743, #134458, #135150, #136275, #120134, #134851. (#132186, #135773, #134704, #135713, #136276, #134549, #134853) Thanks @galiniliev, @LiuwqGit, @obviyus, @609NFT, @Nielsh82, @Zak-Finance, @vincentkoc, @nerclid, @w1130150306, and @cmanrav.
10
+ - **Codex approvals that stick:** "Allow Always" is durable for MCP tools on OpenClaw-configured servers, tool approvals follow the session's posture, and approvals granted to an active Codex placement are reused instead of asked again. Related #132369. (#136019, #135812, #132370) Thanks @jalehman.
11
+ - **Approvals reach your chat:** when a delegated system agent proposes a config change or Gateway restart, the approval card is delivered to the originating channel (Telegram topics included) with the requester's title, instead of stalling silently. Related #136083. (#134670, #136091) Thanks @obviyus.
12
+ - **Android catches up with the web UI:** the chat screen, sidebar navigation, and appearance settings match the Control UI, the composer grows to six lines, and recording is offered when dictation is unavailable. Related #125322. (#134939, #135923, #135794) Thanks @IWhatsskill, @obviyus, @BennyAI2, and @RaviTharuma.
13
+
14
+ ### Changes
15
+
16
+ - **Personal GitHub accounts:** connect "My GitHub" beside the system account in your profile, publish pull requests under an explicit personal identity, and switch sessions without re-verifying credentials every time. (#133799, #136223)
17
+ - **Model setup that shows how you are signed in:** Model Setup lists account versus API-key access with the runtime-reported email for Codex and Claude candidates, the catalog "+" opens the native Codex or Claude CLI in a terminal, and the provider defaults page autosaves with a simpler model picker footer. Related #136068. (#136521, #136230, #134813, #136160) Thanks @Patrick-Erichsen.
18
+ - **New models and usage:** support Anthropic Fable 5.1 from shared model metadata, show SuperGrok usage in `openclaw models` and the usage panel, keep GPT-5.6 Ultra selected across runtime boundaries and configured native accounts, and keep Sonnet 5 pricing current on Vertex. Related #135623, #135991, #135664. (#135638, #135766, #135397, #136061, #135761)
19
+ - **Local and configured providers:** discover llama.cpp models behind web-app endpoints, keep local Ollama routes selectable, auto-enable the Google provider plugin when it is configured, retain externally authenticated providers in the model menu, and refresh model catalogs after auth or config changes without a restart. Related #135361, #134731, #135221, #134516. (#135445, #135095, #135239, #135046, #134361, #135063, #135353) Thanks @gaoanze888, @Bloodis94, @obviyus, @BoatAngle, @Solvely-Colin, and @goslingmanagment.
20
+ - **Agent working directories and worktrees:** set `agents.defaults.cwd` or a per-agent `cwd` and describe directory roles in the prompt, configure a global `worktreeRoot`, and run up to 100 managed checkouts without sessions being rejected at the limit. Related #134848, #135852. (#135080, #134872, #135885, #135989)
21
+ - **Configuration controls:** `cron.skipMissedJobs` skips missed recurring jobs at startup, `blockedHostnames` on the SSRF policy blocks configured hosts for browser, web fetch, and webhooks, `config set` accepts `--expect-current-json`, `--expect-current-absent`, `--dry-run`, and `--strict-json`, and `channels.<id>.enabled: false` no longer loads that channel plugin. (#135071, #135097, #136137, #136211) Thanks @vincentkoc.
22
+ - **Memory maintenance:** `openclaw memory reset` rebuilds derived indexes without deleting sessions, and recall outcomes from active memory are surfaced to the model. Related #135086. (#135653, #135193) Thanks @Alix-007, @obviyus, and @wave-workflow.
23
+ - **Codex plugin:** preserve native history in supervised message forks, let idle chats resume while other chats run, stop interrupting long quiet native turns, keep configured MCP tools on native fallback, honor the user's time zone, keep replies working after sub-plugin config changes, keep inbound audio for automatic voice replies, hide saved reasoning unless enabled, hide node exec when no capable node is connected, keep Codex streams connected under load, and move managed installs to Codex 0.152.1. Related #134552, #136143, #99272, #122233, #136015, #135618, #135571, #135978, #135966, #135338. (#134660, #136196, #134755, #134941, #136172, #135863, #135884, #136008, #136000, #135577, #136184, #136705) Thanks @obviyus, @ernestrolfson-design, @itsuzef, @NianJiuZst, @davidcittadini, @vyctorbrzezowski, @hyper-sdn, and @tzlwn1.
24
+ - **Session naming and switching:** new-session names are prepared after idle typing, switching sessions in large lists is faster with lighter sidebar work and payloads, sidebar catalog groups are capped at five sessions, and New Session and Chat do less startup work. Related #133702. (#133724, #135021, #135332, #135574, #136362, #136021, #136040) Thanks @fuller-stack-dev.
25
+ - **Control UI layout:** sidebar controls move into the agent header, session context menus are regrouped, the accent color lives with the theme, dashboard widgets fill mobile width, the shared Agent picker and settings pages are streamlined with loading skeletons, hovercards show participants, and Gateway suspension appears in account footers. Related #135506, #135535, #132147. (#134365, #135526, #135547, #133814, #134680, #134684, #134659, #134636, #136542, #136220, #134478, #134764) Thanks @vyctorbrzezowski, @MoerAI, and @Patrick-Erichsen.
26
+ - **Chat polish:** compaction gets a folding shimmer, the composer has a stable accessible name and accepts the next prompt right after send, Escape closes the visible session popover, dropdowns reopened stay dismissible, keyboard navigation survives submenu switches, and chat errors can be copied without expanding them. Related #136441, #134997, #135613, #136070. (#135988, #133829, #135345, #136445, #135006, #135629, #136085) Thanks @aniruddhaadak80 and @Takhoffman.
27
+ - **macOS app:** a streamlined native chat composer, a browser sidebar that can expand past half width, lower idle menu-bar CPU, notifications when background sessions finish, a Talk overlay that survives quick toggles, and speech recognizers reused between voice captures. Related #136157, #136562, #127645. (#135608, #136168, #136572, #136519, #135731, #135401) Thanks @HuzaifaChaudary and @Colton-Harris.
28
+ - **iOS and watchOS:** open session dashboards without nested Control UI, disclose model targets and enforce availability, show the latest output when reopening chats, keep Watch and realtime voice replies with their own turn, and explain when a spoken reply times out. Related #134306, #135251, #135693. (#132983, #135044, #135429, #135697, #135795, #135765) Thanks @fuller-stack-dev and @goslingmanagment.
29
+ - **Linux desktop:** first-run Gateway choices work without the CLI installed. Related #135565. (#135650)
30
+ - **Channel controls:** Feishu and Matrix deliver the buttons an agent reply offers, and LINE tells the agent which buttons it renders, keeps the words a quick-reply prompt leaves behind, lets a group mention reach the turn that answers it, and gives up a card LINE would refuse instead of losing the reply. Related #132739, #134975, #135187, #135228. (#135255, #133268, #134915, #134976, #135195, #135229) Thanks @edenfunf and @ml12580.
31
+ - **CLI:** `channels add/login/logout/remove/resolve` accept `--agent`, `mcp --json` emits a JSON failure envelope, zsh and Bash completions escape descriptions and option values correctly, heartbeat status shows Gateway ages, `sessions tail` reports recorded trajectory outcomes, secrets JSON failures stay machine-readable, device approval hints keep their profile context, and TUI picker cancellation routes through shared input. Related #132347, #46932, #136089, #136406, #136632, #136556, #135089, #133128. (#135505, #132379, #64490, #135114, #136557, #136093, #136407, #136142, #136633, #133151) Thanks @wangmiao0668000666, @EdenKangdw, @walker1211, @jeffrey4341, @qingminglong, @obviyus, and @aniruddhaadak80.
32
+ - **Plugins and skills:** renamed official plugins migrate by their legacy npm package name, plugin updates require capability consent when prior acceptance is stale while verified first-party plugins are exempt, plugin uninstalls persist across a Gateway restart and keep channel settings, and skill Workshop content, read-only diagnosis, and channel setup are corrected. Related #134076, #135726, #136591. (#130894, #134172, #134933, #135460, #135729, #134759, #134871, #136600, #136615) Thanks @cxyhhhhh, @RileyJJY, @beastyrabbit, @devzeroLL, @obviyus, and @Patrick-Erichsen.
33
+ - **Uninstall and backups:** `openclaw uninstall` defaults to removing only the service and keeps user data, Claude migration backups preserve overwritten generated skills, and backups exclude disabled workspaces, tolerate ACPX-generated symlinks, find managed schedules by identity, keep Git failure diagnostics, and retain the active config through volatile filtering. Related #125694, #127403, #135218, #136159, #136118, #136665. (#134299, #134302, #135830, #136215, #136133, #135455, #136666) Thanks @PollyBot13, @kodi, @obviyus, @ericcaiwx-star, @jarvismazz, @LiuwqGit, @wave-workflow, @ly85206559, and @devzeroLL.
34
+
35
+ ### Fixes
36
+
37
+ - **Ingress and token safety:** bound concurrent pre-auth reads on SMS webhooks, stop delivering commands to a Watch node after its device is revoked, require the per-process nonce on the Copilot Azure BYOK proxy, isolate webhook rate limits by client, let authorized scoped node tokens be managed by their owner, and reject oversized A2A JSON-RPC batches and responses. (#136504, #135904, #134781, #134622, #135617, #134603) Thanks @drobison00 and @eleqtrizit.
38
+ - **Secrets stay redacted:** `config.get` no longer returns unredacted pre-migration snapshots, blank sensitive fields stay editable without being treated as secrets, systemd unit backups no longer leak Gateway tokens, and iOS omits deep-link URLs from logs. Related #135649, #131781. (#134940, #135798, #131786, #134738) Thanks @SunnyShu0925, @markhaines, @vyctorbrzezowski, and @eleqtrizit.
39
+ - **Browser and filesystem boundaries:** Chrome MCP `--browserUrl` endpoints obey the CDP reachability policy, unreviewed Chrome MCP upgrades are blocked, unsupported fill field keys are rejected, sanitized temp file names reject dot segments, and memory sync skips symlinked or non-regular workspace files instead of aborting. Related #135845, #131231, #134967. (#135857, #136121, #131400, #135803, #135042) Thanks @LiuwqGit, @obviyus, @srb11e, @teddytennant, @ruel225, and @kiranvk-2011.
40
+ - **Authority boundaries:** agent creation stops after delegated authority closes, system-agent setup stops writing after its run closes, unauthorized native `/compact` returns no response, clients are paused when a role requires a verified identity, guest coding works again in private sandboxes, and dashboard GitHub Actions reads use the agent identity. Related #136081, #136219, #133955, #37634, #135515. (#136090, #136247, #131408, #136420, #135702, #135740) Thanks @igs-rogenlo, @obviyus, @aoclaw-glitch, and @whyuds.
41
+ - **Upgrade data safety:** identical session event replays migrate cleanly, nested session writes keep their order, schema-17 session repair is atomic, legacy cron rows keep their enabled state and delivery intent, credentials replaced during cleanup are preserved, workspaces survive legacy agent-list upgrades, and per-agent memory search survives Doctor. Related #134455, #131113, #135634, #135635, #135637, #135656, #134256. (#134568, #131456, #134272, #132868, #135736, #134892, #134760) Thanks @shakkernerd, @gaoanze888, @Grynn, @RileyJJY, @obviyus, @CanReader, and @vdruts.
42
+ - **Doctor converges:** `doctor --fix` finishes on empty workspace attestations, stale workspace setup loops resolve, incomplete exec-approvals migrations fail loudly and empty stubs retire, contaminated device-pairing records no longer crash migration, Windows Doctor and retained workspace recovery are unblocked, and legacy transcript checks are left to migration. Related #134445, #134331, #135437, #135968, #134340, #131770. (#134641, #135755, #135454, #135981, #134483, #136578, #134765) Thanks @leilei3167, @obviyus, @rosssaunders, @jjjhenriksen, @Deregtx, @qdivan, @samson1357924, @LiuwqGit, and @sblindt.
43
+ - **Doctor keeps your config:** authored values and agents survive repair, official plugin config is preserved before install, external plugin payloads are kept, keyed multi-agent rosters and explicit agent rosters are repaired and persisted, and stale npm plugins are recovered during update repair. Related #134407, #134353, #135450. (#135671, #134717, #135791, #134706, #134758, #135451) Thanks @fuller-stack-dev, @obviyus, @abacha, and @xiaomijituan.
44
+ - **Shared credential migration:** interrupted migrations recover, stranded credentials are restored, redundant relocation subsets converge, relocation conflicts are diagnosed with forced-login owners cleared, relogin repairs stale profile order, custom provider SecretRefs survive Doctor, the durable auth order is published on a running Gateway, and shared auth health reports without a default agent. Related #132605, #132979, #135385, #135140, #135079, #135734. (#134952, #135346, #135096, #134808, #135739, #135355, #135357, #135847, #135443, #134902, #133978) Thanks @jodok, @fuller-stack-dev, @Deregtx, @obviyus, @mattcbianco, @dannevang, @yetval, @josephbergvinson, and @lzhan011.
45
+ - **Credential writes and state directories:** CLI and Gateway state-directory split-brain is detected before credential writes, Model Setup no longer leaves a stale claude-cli key after Claude CLI activation, retained device-auth files and unconfigured agent databases are explained, and stale OpenAI doctor route pins are repaired. Related #134379, #101672. (#134403, #134779, #135295, #102180) Thanks @obviyus, @leilei3167, @aaajiao, @PollyBot13, @849261680, and @cpwilhelmi.
46
+ - **Subagent completions arrive:** completions are delivered after busy parent turns, background completion rejections are caught, empty completions are recorded, unsent completions are no longer reported as delivered, rejected requester wakes settle, model reroutes are shown in the completion including rerouted siblings in batched requester wakes, and Swarm completion guidance matches delivery. Related #134186, #131734, #135633, #135692, #133517. (#136423, #131735, #135846, #136524, #136033, #135531, #133660) Thanks @svdwalt007, @ruel225, @louisfy, @obviyus, @jakestenger, @Grynn, and @MertBasar0.
47
+ - **Compaction and context accounting:** native compaction is serialized with session writers and kept in one persistent transcript marker, cached usage is no longer overcounted, context usage survives Anthropic proxies that omit usage or cache fields, and status keeps context usage after tool-only turns. Related #99843, #126436, #135530. (#135441, #136169, #99864, #126473, #135467, #134634, #135580) Thanks @jjjhenriksen, @obviyus, @LZY3538, @ayaangazali, @jrex-jooni, @Oliverbot26, @Yigtwxx, @goslingmanagment, @dmsrg399, and @VACInc.
48
+ - **Fallback and retries:** Anthropic refusals are terminal instead of resending, model fallback no longer cycles every provider when the session already holds a turn claim, transient LLM retries have one failover owner that surfaces a persistent outage sooner, and failover counters appear in console logs. Related #134968, #134187. (#134980, #134219, #134281, #135844) Thanks @Marvinthebored, @obviyus, @tzlwn1, and @svdwalt007.
49
+ - **Replies that reach the conversation:** stale session dispatches are refreshed so a message after a reset is not dropped, deliveries mirrored into another session are no longer silently lost, an interrupted user message stays in context after restart recovery, fenced code survives duplicate-block collapse byte for byte, commentary progress shows before final validation with unique segment identities, and requester sessions recover after a settle-wake timeout. Related #135149, #132766, #135881, #135882, #135949, #134971, #135231. (#135154, #134083, #136023, #135945, #135954, #135081, #135854) Thanks @brettdaman, @obviyus, @edenfunf, @VACInc, @abacha, @leilei3167, @yetval, @ruel225, @JosephNatsu, @akagifreeez, @tomroberts78, and @nikolascostello.
50
+ - **Cron and heartbeat:** cancelled runs settle without ending cron retries early, `NO_REPLY` is preserved after tool calls, proven-not-sent announce deliveries retry, failure-alert outcomes persist, one-shot retries survive startup recovery, immediate main-session wakes are admitted with heartbeat disabled, the first-alert preamble shows once for isolated routes, the configured watchdog timeout is honored, scheduled replies bind to the published runtime, and ISO dates honor time zones. Related #133743, #133785, #135658, #135836, #134500, #135205, #135435, #136225. (#133747, #135919, #135985, #135516, #135083, #134648, #135849, #135925, #136256, #135973, #135155) Thanks @shakkernerd, @leilei3167, @obviyus, @Frank683456, @gaoanze888, @yetval, @RayWangyangMa, @rwinkelman, @sunlit-deng, @LowCode191, @virtexvirtuoso, @xialonglee, @jaxonparrott, @Famyoff, and @brettdaman.
51
+ - **Cron configuration:** kind changes work without an env object, isolated `cron add` no longer hides a fail-closed announce route, jobs are created with configured Codex app-server auth, legacy shorthand schedules validate their payload, history pages after visibility filtering, Doctor refuses store rewrites another process committed after its snapshot, and account identities are no longer suggested as recipients. Related #134895, #136042. (#134639, #135003, #134525, #135829, #136043, #127999, #134368) Thanks @solomonneas, @ericcaiwx-star, @obviyus, @sjf-oa, @sjf, @teddytennant, @yetval, and @BryanTegomoh.
52
+ - **Sessions and runs:** a cyclic session parent chain no longer hangs context building, the Gateway survives late CLI output after a timeout, restart drain counts as ingress idle, stale CLI recovery cannot replace newer sessions, yielded CLI spawns settle, BOOT.md runs when a previous boot session exists, oversized transcript archives stream instead of loading whole, queued cold turns keep CLI continuity, and worktree restore capacity advice is correct. Related #132581, #124343, #134748. (#131567, #132582, #133871, #135168, #134974, #134831, #136171, #136283, #136644, #136717) Thanks @igs-rogenlo, @TARSDrakon, @SebTardif, @obviyus, @mushuiyu886, @VACInc, @aoclaw-glitch, @miguelarios, @efe-arv, @liri-ha, and @anyech.
53
+ - **Tool results and edits:** `oc-path` patches preserve exact edit bytes, PDF and image analysis survive Code Mode, plugin tools are kept for namespaced allowlist entries, exec selects capable nodes and reports its target, failed `/bash` commands show their real exit status, `sessions_spawn` declares its completion-message expectation, and failed-exec warnings are preserved on silent turns. Related #136530, #132750, #131765, #133385, #75848, #135068. (#136531, #135037, #132753, #131767, #133414, #136570, #135119, #132756) Thanks @tommyjoseph, @vyctorbrzezowski, @MoerAI, @alfredjbclaw, and @sashankh.
54
+ - **Markdown, links, and media:** task lists survive chunk boundaries, literal Markdown and balanced image URLs are preserved, complete markdown links are parsed and link preprocessing is cancelled with the reply, UTF-8 attachments are recognized across sniff boundaries and keep their types when chunks are reused, inferred text is preserved while downloads are rejected promptly, fractional storage limits are reported accurately, and attachment failure cards stay out of durable model history. Related #127637, #135921, #135922, #136397, #135061, #136139, #136140. (#135200, #136228, #135341, #132883, #135227, #135232, #135928, #136402, #135185, #136685) Thanks @teddytennant, @obviyus, @SunnyShu0925, @ly85206559, @gaoanze888, and @snls1994.
55
+ - **Slack:** replies to existing threads send again, Agent View DMs keep their per-root sessions after a Gateway restart in HTTP mode and collapse correctly when Slack rejects the prompts probe, short replies arrive before progress finalization, quiet previews stay on the latest preamble, socket and relay accounts start without an unused signing secret, pre-dispatch rejections are surfaced, agent RPC messages use the configured identity, and Enterprise thread lookups are not duplicated. Related #126872. (#136566, #136559, #136510, #136460, #134716, #136092, #134827, #132723, #122078, #121598) Thanks @danielduerr, @pash-openai, @zhangguiping-xydt, @Iskam31, and @mikasa0818.
56
+ - **Discord:** webhook reply limits and delivery outcomes are preserved, missing response IDs no longer falsely confirm delivery, model picker choices apply without false failures, session-busy notices honor reply visibility, error replies stop after a voice consult is cancelled, and voice capture is bound to one lifecycle owner. Related #135946, #135947, #133550, #135442, #135340. (#135963, #136620, #135382, #135444, #135380, #135870) Thanks @Call44 and @LZY3538.
57
+ - **Telegram and WhatsApp:** durably queued Telegram callbacks are acknowledged promptly, WhatsApp replies are sent unquoted when the quote cache misses so bubbles never render blank, and Doctor reports shadowed ack emoji and lossy acknowledgement scope migrations. Related #133294, #127948, #119317, #112796. (#133379, #127959, #119501, #129825) Thanks @zhangguiping-xydt, @Paeddy87, @Finn763, @markswitch83, @harjothkhara, @obviyus, @abacha, and @yetval.
58
+ - **iMessage, Signal, Google Chat, Teams, Matrix, Feishu, Twitch, Tlon:** iMessage sends fail fast after the private-API bridge dies and remote attachments survive materialization, Signal publishes terminal status on permanent SSE rejection and rejects invalid UTF-8, Google Chat labeled links keep labels with spaces, Teams keeps thread context without a replyToId and Doctor can load its state checker after a source build, Matrix keeps code out of spoiler collisions and persists its sync cache after schema upgrades, Feishu validates document API outcomes, Twitch names the `accessToken` key in setup errors, and Tlon preserves parser progress. Related #135860, #136104, #129344, #136499, #134741. (#134572, #136301, #136178, #121569, #136151, #129345, #136509, #128453, #134742, #135106, #135421, #135181) Thanks @omarshahine, @zhangguiping-xydt, @obviyus, @zqchris, @masatohoshino, @sunlit-deng, @ericcaiwx-star, @KimOckHyun, @amalysh, @ruel225, @w9n, and @ly85206559.
59
+ - **Channel accounts and delivery:** HTTP routes are scoped to account lifetimes, rejected webhook connections are released after answering, failed plugin accounts appear in status and health, explicitly disabled manifest accounts are omitted, explicit channels resolve through the scoped plugin registry, capability timeout reports stay alive, message plugin cleanup runs before the CLI exits, and the selected agent is preserved during plugin discovery. Related #132886, #126808, #135573, #136538, #136563. (#133297, #126818, #135082, #135417, #135831, #136539, #136564, #135505) Thanks @zhangguiping-xydt, @goffern, @edenfunf, @obviyus, @ly85206559, @ruel225, and @TailsProwerWorks.
60
+ - **Voice, Talk, and TTS:** voice-call setup reports missing agent ownership, diagnostic logs stream with backpressure, host cancellation is not reported as a tool failure, Talk consults keep their owner across clients and internal prompts out of later context, narration requests are cancelled when their turn ends, terminal command replies stay text-only, local CLI synthesis inherits request timeouts, OpenAI OAuth audio transcription works again, and Volcengine TTS rejects invalid UTF-8. Related #132619, #134883, #126730, #82582, #96135. (#134739, #134838, #134924, #135031, #135423, #134839, #134802, #135222, #135855, #134010) Thanks @felixboenkost-droid, @sunlit-deng, @astra-openclaw, @najef1979-code, @yungchentang, @kAIborg24, and @ZengWen-DT.
61
+ - **Web chat and Control UI recovery:** replies to attachment-only assistant messages are restored, replies appear above queued prompts and hydrated replies reconcile by identity during recovery, WebChat reset denials show without admin scope, Ask OpenClaw connections and conversation recovery are restored, and cloud sessions can be restored from Chat or kept stopped with cleanup failures surfaced. Related #135507, #135533, #134679, #134727, #134506. (#135393, #135568, #135715, #134696, #134776, #135024, #135583) Thanks @starship863, @jalehman, @rwinkelman, and @obviyus.
62
+ - **Control UI chat:** new-session sends show immediately under load, sent images stay visible during history handoff and stable on hover, loaded images survive Gateway outages, late dictation transcripts are kept after Stop, the selected agent stays on native progress cards and dashboards, interrupted runs retire from history, commentary reconciles by run and item identity, and speaker names survive conversation exports. Related #135026, #135156, #136437. (#136069, #136072, #136440, #136446, #135109, #135426, #134714, #134457, #134888, #136439) Thanks @obviyus, @jadabreu, @rwinkelman, @Synthetic2802, and @RomneyDa.
63
+ - **Control UI details:** wildcard tool policies display correctly, compact output token counts return, tool-only model auth rows are ignored, the model picker discloses its write scope, unavailable agent harnesses and trusted-proxy login failures are explained, OAuth errors are concise, heartbeat scratch loads read-only, automation links open beyond the loaded page, equivalent Usage filters clear from their menus, format-constrained settings stay editable, the chat face switch gets icons and embedded visibility with standardized menu options, sharing member lists load with skeletons, model provider refresh status is aligned, Inbox stays out of Settings, and the macOS composer shows the Default permission icon. Related #134306, #134304, #135556, #134916, #135457, #135948, #136568, #135720, #134409. (#135786, #135951, #134218, #134473, #135118, #135584, #135112, #135508, #136012, #136569, #136603, #136602, #136646, #136647, #136648, #136649, #136650, #136609, #135722, #134413) Thanks @wantosure, @fuller-stack-dev, @goslingmanagment, @shakkernerd, @gaoanze888, @obviyus, @itsuzef, @Patrick-Erichsen, and @vyctorbrzezowski.
64
+ - **Control UI sessions and boards:** pinned sessions show a filled neutral pin without repeating titles, group titles stay on one marquee line, question sessions stay on one line, rewind hides while the agent works, task reviews fill the side panel, tall task progress scrolls, board widgets replace on put and Remove matches its menu, Workboard task links recover after cursor rejection, and free-text questions are separated from optionless ones. Related #134647, #135705, #134477. (#134775, #134651, #135523, #136606, #135521, #135926, #134517, #135748, #136637, #136619, #134598, #135524, #136607, #136614, #136527, #136668, #134631, #134732, #134860, #134791, #134653, #134833, #135008, #135681, #135957, #134686, #135724, #136002) Thanks @Patrick-Erichsen, @obviyus, @asgeirtj, @VACInc, @teddytennant, @shakkernerd, @goslingmanagment, and @Grynn.
65
+ - **Pull request tooling in the Control UI:** an explicitly approved replacement head is recovered, merge receipts finish when main advances, merges invoked from another checkout are recovered, ignored files are preserved, quota failures are distinguished from authentication errors, and GitHub sign-in works when anonymous API quota is exhausted with the CLI preflighted before device authorization. Related #135496, #135335, #134701, #135872, #135028. (#135622, #135396, #134778, #135500, #135657, #135873, #134724, #135301) Thanks @mushuiyu886, @obviyus, and @jadabreu.
66
+ - **macOS app fixes:** confirmed setup cancellation and onboarding failures are shown clearly with retry actions, misleading AI setup progress is replaced, computer input works after execution, bounded native pipe reads and final drains are centralized, native notifications are cancelled before side effects, and completions are delivered before notification status arrives. Related #134573, #134800, #134621, #136206, #136207, #136208. (#134654, #134756, #135041, #134689, #136214, #135796, #136683) Thanks @gaoanze888 and @wbstrbt.
67
+ - **iOS fixes:** assistant text selection survives long press, duplicate replies after a history refresh are gone, and dependency resolution works with WebRTC 152. Related #135217, #135249. (#135430, #135789, #134942) Thanks @Solvely-Colin and @nikolaihack.
68
+ - **Android fixes:** sends are prevented with auth-unavailable models, credentials are masked with correct secret input behavior, reconnect cleanup stays scoped to its connection, reconnected and queued messages no longer get stuck or reappear after deletion, health is rechecked when Refresh races a queued send, chat choices and live appearance updates are preserved, narrow composers stay readable, the settings conversation keeps its position, and refresh results and progress stay current. Related #135874, #136047, #128926, #136638. (#134884, #136352, #135878, #135961, #136161, #136413, #136173, #135432, #136550, #136643) Thanks @fuller-stack-dev and @aniruddhaadak80.
69
+ - **Installers:** Windows installs tolerate npm stderr warnings, use basic parsing for MinGit downloads, and keep the portable Node extraction fallback; the installer no longer points at a log that does not exist; Docker images install `libgomp1` for managed llama.cpp and build the explicitly selected WhatsApp plugin; and source installs use isolated linking to reduce filesystem churn. Related #134638, #134439, #120659. (#134385, #134412, #135410, #134655, #134532, #120660, #135728) Thanks @ly85206559, @mohamedelrefaiy, @chelsealong, @henrique-simoes, and @fr-meyer.
70
+ - **Plugins:** orphan installs recover without weakening ownership, ambiguous copied plugin paths are rejected, build stamps are ignored in registry freshness, plugin load errors survive retries, complete config errors are reported, the Weixin package compatible with the current SDK is selected, scoped ClawHub specs match in uninstall warnings, optional hook-pack dependencies install, inspection diagnostics and configured policy are preserved, lifecycle-less prepared channel turns run, packaged setup and TSX build artifacts resolve, the persisted registry refreshes when source mounts change, and plugin updates rediscover restored payloads instead of reusing stale cached facts after a reinstall. Related #134321, #136357, #134657, #136046, #136594, #136595, #114020, #135990, #136455, #136456, #136516. (#134590, #136373, #134780, #134874, #93842, #134854, #135749, #136050, #136596, #135179, #135993, #135245, #135820, #134719, #136458, #136517) Thanks @MoerAI, @obviyus, @abacha, @LiuwqGit, @Dresch63, @RayWangyangMa, @axiom-ncis, @bladin, @teddytennant, @nissl24, @fridaylans2000-art, @jooey, and @ssaade01.
71
+ - **Memory:** automatic reindex recovers after concurrent writes, orphaned workspace locks recover after PID reuse, deep consolidation works with explicit ownership, clean transient CLI searches stay off the reindex path, index identity mismatches are attributed, unnecessary full rebuilds are avoided, replies stay responsive during legacy index repair, cache overflow is prevented during forced reindex, embeddings honor the environment proxy and resolve profile auth, and embedded session recall returns the newest history. Related #134332, #134999, #134687, #135459, #135641, #134337, #135414, #136656. (#134333, #135051, #135166, #135520, #135864, #136064, #135062, #135373, #134858, #134744, #135415, #136657, #131329) Thanks @TheAngryPit, @obviyus, @pengzh1, @gru-10k, @zhangguiping-xydt, @giangthb, @gaoanze888, @LifeViwer, @yetval, @CjTruHeart, @ThatGuySizemore, @Artemeey, @0x-Parzival, and @hartmark.
72
+ - **Model harness selection:** model selections without an activatable harness are rejected with the reason explained, utility completions route through the selected runtime, scheduled model aliases stay scoped to the selected agent, `models status` skips refresh with a matching agent directory override, harness policy reasons are preserved, and the Claude subprocess keeps its failure diagnostics. Related #134305, #134304, #135566, #134690. (#134837, #135118, #135711, #135069, #134862, #135206, #134794) Thanks @fuller-stack-dev and @goslingmanagment.
73
+ - **Providers and CLIs on PATH:** claude-cli connects through PATH shims, Windows bare commands resolve through PATHEXT with empty entries dropped, native Claude login is preserved for blank node-host credentials, the CLI-backend run honors the fallback delegation gate, Responses continuations survive admitted tool arguments and exclude tools from the request-equality check, zai honors `thinkingLevelMap`, DeepInfra refreshes estimates from native pricing, Model Studio cache defaults are honored, Bedrock rejects malformed embedding encodings, and OpenCode Go sends the required User-Agent. Related #135013, #134960, #134709, #135538. (#135124, #135138, #135807, #134932, #115405, #134423, #132951, #132697, #134893, #135093, #133716, #135588) Thanks @zhangguiping-xydt, @obviyus, @masatokawano, @gaoanze888, @Vasanthdev2004, @teddytennant, @MertBasar0, @hartmark, @wangjx-xydt, @RileyJJY, and @VACInc.
74
+ - **Local models:** llama.cpp context-size-exceeded is treated as overflow, managed release archives extract safely, managed server inspection responses are bounded, and local providers are stopped before Gateway shutdown. Related #133860, #134649. (#133888, #129035, #132490, #134890) Thanks @gokay-ai, @cursoragent, @Areson, @pgondhi987, @RileyJJY, @MoerAI, @obviyus, and @Deregtx.
75
+ - **MCP and tools:** OAuth-authenticated MCP requests carry a Content-Length, MCP runtimes are not evicted during requester resolution, MCP lint finishes when SQLite is busy, `agents_wait` deadlines stay monotonic, approval grant lifetimes are validated, terminal summaries release after nested calls settle, and tool-loop warnings are bucketed. Related #136204, #134605. (#136234, #134819, #134698, #135292, #133806, #135398, #135103) Thanks @LiuwqGit, @obviyus, @Volevanius, @qingminglong, and @pfrederiksen.
76
+ - **Browser tool:** tab enumeration has its own budget separate from the CDP handshake timeout, messages are no longer interrupted during startup recovery, standalone routing errors and cropped screenshots are avoided, native action cancellation is isolated by page, and stale group updates no longer revoke valid commands. Related #132452, #136464, #135176. (#135219, #135016, #135315, #136465, #135186) Thanks @SunnyShu0925, @obviyus, @alexph-dev, @jesse-merhi, and @rwinkelman.
77
+ - **Nodes and devices:** the `openclaw node` worker exits after a stop even with stdin held open, same-install CLI nodes are classified correctly, ambiguity between current node clients is preserved, recently connected nodes stay in list age filters, remote exec hides without an executable node, node wakes retry after clock rollback, presence expires after rollback and stays bounded, and the rejected setup-code warning is clarified. Related #135661, #136593, #136635, #135914. (#135665, #136604, #136636, #135936, #136067, #133354, #134737, #134740, #134795, #136654) Thanks @Colton-Harris, @obviyus, @qingminglong, @lzhan011, @0x-Parzival, and @eleqtrizit.
78
+ - **Gateway networking:** SSH tunnel stop awaits process reaping, probe tunnels are abortable and handle SIGINT/SIGTERM, listener timeouts ignore clock skew, long temp paths no longer break desktop tunnels, socat forwards that name OpenClaw are distinguished, the tailscale sudo fallback names the operator fix, Accept media-range precedence is honored, invalid `utcOffset` values are rejected, and usage peak hours survive daylight saving. Related #127457, #127591, #135706, #124567. (#133847, #135182, #135281, #135456, #133381, #135825, #118197, #124568, #135153, #125378) Thanks @aniruddhaadak80, @obviyus, @xialonglee, @ly85206559, @ericcaiwx-star, @cursoragent, @pengzh1, @ezimerman, @peterolkhov, @zyw02, @qdivan, and @tzlwn1.
79
+ - **Gateway serving and status:** encoded and symlinked Control UI assets load, stale 304 responses are avoided, retained asset memory is bounded, APNG icons go through the shared image policy, small hosts no longer get false RSS critical alerts, delivery queue warnings appear in detailed status, active probes are reported when the preferred account is unconfigured, wait deadlines are not reported as draining, terminal timeout reasons stay in the sidebar, session-observer failures show their real cause, and method discovery no longer loads session storage. Related #136442, #136443, #135606, #136106, #136415, #136640, #136598, #135305. (#136444, #135619, #136108, #136417, #136641, #134889, #136599, #135797, #135466, #135337, #135102, #135105, #136676) Thanks @vincentkoc, @LiuwqGit, and @Cobblestone-Digital1.
80
+ - **Sessions store and CLI selectors:** strict transcript appends work without a storePath, configured transcript stores are honored, incognito sessions stay in their explicit environment, ACP metadata comes from the selected agent, blank `--session-id`, `--session-key`, `--store`, `--every`, suspend wait, dead-letter, and agent selectors are rejected instead of silently defaulted, `--target-file` reads are bounded, and deeply nested config mutations are stack-safe. Related #136198, #136217, #136224, #136662, #129734. (#136201, #136221, #136227, #136280, #134797, #134799, #135742, #135270, #135848, #136664, #134957, #129918, #135273, #136574) Thanks @marmar9615-cloud, @obviyus, @qingminglong, @masatohoshino, @xialonglee, @SunnyShu0925, @hpyhandsome, and @SebTardif.
81
+ - **Skills and worktrees:** one profile cannot exhaust pending ZIP imports, Doctor reports plugin version drift after upgrades and blocked workspace migration cleanup, tailscale-managed ingress recovers after upgrades, concurrent session starts avoid duplicate Git work, canonical skill names survive discovery and the Workshop, and ordinary replies no longer trigger eager skill discovery. Related #135587, #136493. (#135593, #134719, #134751, #135394, #134912, #136495, #136716) Thanks @jjjhenriksen.
82
+ - **Config and terminal output:** the CLI suggests `config patch --file` when a shell strips JSON quotes, `openclaw config` shows approval allowlist no-op snapshots, tabbed table columns stay aligned, authored hyperlink destinations survive in the TUI, duration quantities are preserved across locales, and console trace stacks are kept without duplicate errors. Related #135164, #136573, #136468. (#135203, #136574, #136312, #136469, #133224, #136066, #135932) Thanks @SunnyShu0925 and @wojtek76.
83
+ - **Lower memory:** reduce memory spikes in catalogs, resets, and chat, release retained Gateway and Activity memory, cut memory held by transcript scans and chat views, decode and capture large command output with less memory, drop repeated JSON copies of Code Mode results, release completed log payloads and evicted queue entries, and reduce memory used by HTTP response bodies and CodeMode between tool calls. (#134722, #134891, #135326, #135189, #135894, #135898, #135918, #136030, #136416, #134954, #135040, #135183, #136567, #136340)
84
+ - **Faster replies and streaming:** less overhead for concurrent and long streaming replies, faster concurrent final-answer streaming, pending streamed replies kept current, reduced work during streaming event delivery, faster CodeMode and session lookup, reduced turn preparation, cheaper session metadata reads, lower approval tracking across concurrent runs, and faster fence-aware message chunking. Related #135750. (#136350, #135751, #136180, #136041, #136249, #136296, #134935, #134673, #136391, #136205, #135208, #135220, #135204, #135034, #135014, #134930, #135626, #135607, #136428, #136425, #136099, #135464, #135390, #135504, #135514) Thanks @quangtran88.
85
+ - **Faster startup and plugins:** faster Doctor bootstrap and cold Doctor in built checkouts, CLI-only code no longer loads at startup, plugin catalogs and known contribution owners avoid full scans, catalog worker imports are scoped, verified metadata is reused during startup, unused discovery is skipped for read-only catalog queries, the Claude Code catalog refresh no longer re-scans the projects tree on every poll, and shell commands have shorter cold starts. Related #136385, #135893, #134864. (#136500, #136399, #133105, #135260, #135953, #135486, #136141, #136020, #136222, #136124, #134581, #134807, #134812, #134814, #135169, #135172, #135091, #135073, #135121, #136063, #136080, #136162, #136398, #136401, #136403, #136381, #135998, #135986, #135942, #135931, #135077, #135048, #135007) Thanks @ly85206559, @zeroaltitude, @obviyus, @LiuwqGit, and @rwinkelman.
86
+ - **Faster UI, terminal, and logging:** cheaper Control UI history and config processing, side panel and Workboard styles load with their owners, Settings English is deferred, attributed Markdown and reply formatting allocate less, terminal tables and SGR handling avoid character arrays, and log formatting, payload counting, and tail trimming do less work. (#135851, #136186, #135131, #135950, #135725, #136014, #136389, #135473, #136451, #136479, #136380, #135602, #135625, #136435, #134979, #135974, #136028, #136393, #136404, #135476, #135477, #135475, #135597, #136450, #136457, #136418, #135094, #135107, #135116, #135162, #134978, #134988, #134998, #136377, #135135, #134746) Thanks @arpe1618 and @vincentkoc.
87
+ - **Compaction, updates, and diagnostics:** update recovery modules load only when an update starts, the Docker upgrade survivor stops before the package update, diagnostics keep explicit fleet ownership and read-only work off the SQLite writer lifecycle, maintenance leases renew during synchronous work, and worker state closes before its runtime directory is removed. Related #135684. (#135942, #135561, #135447, #136010, #134880, #134911) Thanks @fuller-stack-dev, @obviyus, @Grynn, and @rwinkelman.
88
+
89
+ ### Complete contribution record
90
+
91
+ This audited record covers the complete 999239d745d9cf73b0bf5c8791944565ecd3fcf2..c0433bc59efc6a6ccde0363dd6f5dabbbbc950f3 history: 1,195 in-range PRs + 0 retained seed-only PRs = 1,195 unique PRs. The generation manifest also supplies direct commits as editorial input; the grouped notes above prioritize user impact.
92
+
93
+ Shipped baseline exclusions: v2026.8.1 (14 PRs: #109622, #111527, #112678, #112967, #117561, #119051, #121394, #128548, #129174, #130030, #131220, #131228, #131717, #131811); v2026.8.2 (8 PRs: #133773, #133963, #134103, #134111, #134207, #134208, #134428, #134870).
94
+
95
+ #### Pull requests
96
+
97
+ - **PR #134568** Related #134455. Thanks @shakkernerd.
98
+ - **PR #134083** Related #132766. Thanks @edenfunf and @VACInc and @abacha.
99
+ - **PR #134478** Thanks @Patrick-Erichsen.
100
+ - **PR #134637**
101
+ - **PR #134642**
102
+ - **PR #134645**
103
+ - **PR #134646**
104
+ - **PR #134652**
105
+ - **PR #134643** Thanks @vincentkoc.
106
+ - **PR #134630** Thanks @vincentkoc.
107
+ - **PR #134656**
108
+ - **PR #134661**
109
+ - **PR #134672**
110
+ - **PR #134671**
111
+ - **PR #134537**
112
+ - **PR #134627**
113
+ - **PR #134675**
114
+ - **PR #134676**
115
+ - **PR #134669**
116
+ - **PR #134590** Related #134321. Thanks @MoerAI and @obviyus and @abacha.
117
+ - **PR #134593**
118
+ - **PR #134272** Thanks @RileyJJY and @obviyus.
119
+ - **PR #134654** Related #134573.
120
+ - **PR #134673**
121
+ - **PR #134677** Thanks @vincentkoc.
122
+ - **PR #134694**
123
+ - **PR #134634** Thanks @goslingmanagment and @dmsrg399.
124
+ - **PR #134698** Related #134605. Thanks @obviyus and @pfrederiksen.
125
+ - **PR #134631** Thanks @Patrick-Erichsen.
126
+ - **PR #134707**
127
+ - **PR #134641** Related #134445. Thanks @leilei3167 and @obviyus and @rosssaunders.
128
+ - **PR #134536**
129
+ - **PR #134717** Related #134407. Thanks @obviyus and @abacha.
130
+ - **PR #134702**
131
+ - **PR #134651** Related #134647. Thanks @Patrick-Erichsen.
132
+ - **PR #134691** Thanks @vincentkoc.
133
+ - **PR #134650** Thanks @vincentkoc.
134
+ - **PR #134720**
135
+ - **PR #134704** Related #134458. Thanks @obviyus and @Nielsh82.
136
+ - **PR #134693**
137
+ - **PR #134695** Thanks @vincentkoc.
138
+ - **PR #134706**
139
+ - **PR #134728**
140
+ - **PR #134483** Related #134340. Thanks @qdivan and @obviyus and @samson1357924.
141
+ - **PR #134733**
142
+ - **PR #134732** Thanks @Patrick-Erichsen.
143
+ - **PR #134653** Thanks @Patrick-Erichsen.
144
+ - **PR #134684** Thanks @Patrick-Erichsen.
145
+ - **PR #134722**
146
+ - **PR #134716** Thanks @pash-openai.
147
+ - **PR #134747**
148
+ - **PR #134581**
149
+ - **PR #134688**
150
+ - **PR #134754**
151
+ - **PR #134751**
152
+ - **PR #134668**
153
+ - **PR #132917** Related #132862. Thanks @xydt-juyaohui and @obviyus and @lakemike.
154
+ - **PR #132199** Thanks @africoding.
155
+ - **PR #134686**
156
+ - **PR #134626**
157
+ - **PR #134699** Related #134204. Thanks @Patrick-Erichsen and @vyctorbrzezowski.
158
+ - **PR #134760** Related #134256. Thanks @obviyus and @vdruts.
159
+ - **PR #134763**
160
+ - **PR #134765** Related #131770. Thanks @obviyus and @LiuwqGit and @sblindt.
161
+ - **PR #134745** Thanks @vincentkoc.
162
+ - **PR #131619** Thanks @MertBasar0.
163
+ - **PR #134746** Thanks @vincentkoc.
164
+ - **PR #134517** Thanks @VACInc.
165
+ - **PR #134724**
166
+ - **PR #134764** Thanks @Patrick-Erichsen.
167
+ - **PR #134782**
168
+ - **PR #134473** Related #134306. Thanks @fuller-stack-dev and @goslingmanagment.
169
+ - **PR #132727** Thanks @qingminglong.
170
+ - **PR #134752**
171
+ - **PR #132868** Thanks @CanReader and @obviyus.
172
+ - **PR #134172** Related #134076. Thanks @RileyJJY and @beastyrabbit.
173
+ - **PR #134793**
174
+ - **PR #134659** Thanks @Patrick-Erichsen.
175
+ - **PR #134792**
176
+ - **PR #134762** Thanks @vincentkoc.
177
+ - **PR #134791** Thanks @Patrick-Erichsen.
178
+ - **PR #115405** Thanks @MertBasar0.
179
+ - **PR #122726** Related #122372. Thanks @191612731-cloud and @vincentkoc.
180
+ - **PR #134598** Related #134477. Thanks @Patrick-Erichsen and @goslingmanagment.
181
+ - **PR #134632**
182
+ - **PR #134696** Related #134679.
183
+ - **PR #129825** Related #112796. Thanks @zhangguiping-xydt and @obviyus and @yetval.
184
+ - **PR #131567** Thanks @igs-rogenlo.
185
+ - **PR #134351** Related #134323.
186
+ - **PR #134739** Related #132619. Thanks @felixboenkost-droid.
187
+ - **PR #134712**
188
+ - **PR #134758**
189
+ - **PR #124672** Thanks @qingminglong.
190
+ - **PR #134778** Related #134701.
191
+ - **PR #134708** Related #134692.
192
+ - **PR #134775** Thanks @Patrick-Erichsen.
193
+ - **PR #134636** Thanks @Patrick-Erichsen.
194
+ - **PR #134825**
195
+ - **PR #122628** Thanks @sunlit-deng and @obviyus.
196
+ - **PR #134719**
197
+ - **PR #134820**
198
+ - **PR #134811**
199
+ - **PR #134822**
200
+ - **PR #134808** Related #132979. Thanks @fuller-stack-dev.
201
+ - **PR #127999** Thanks @yetval and @obviyus.
202
+ - **PR #134759**
203
+ - **PR #134412** Thanks @ly85206559.
204
+ - **PR #133354** Thanks @qingminglong.
205
+ - **PR #134832**
206
+ - **PR #134814**
207
+ - **PR #134548**
208
+ - **PR #134844** Thanks @vincentkoc.
209
+ - **PR #134833**
210
+ - **PR #133220** Related #133171. Thanks @SunnyShu0925 and @ruel225.
211
+ - **PR #134836**
212
+ - **PR #134840**
213
+ - **PR #133414** Related #133385. Thanks @MoerAI.
214
+ - **PR #134776** Related #134727.
215
+ - **PR #134299** Related #125694. Thanks @PollyBot13 and @kodi.
216
+ - **PR #134804**
217
+ - **PR #134847** Thanks @vincentkoc.
218
+ - **PR #134845**
219
+ - **PR #134809**
220
+ - **PR #134805** Thanks @vincentkoc.
221
+ - **PR #133105** Thanks @ly85206559.
222
+ - **PR #134838** Thanks @sunlit-deng.
223
+ - **PR #134861** Thanks @vincentkoc.
224
+ - **PR #134856** Thanks @vincentkoc.
225
+ - **PR #134361** Thanks @obviyus.
226
+ - **PR #134852**
227
+ - **PR #134877**
228
+ - **PR #134863**
229
+ - **PR #134821** Thanks @vincentkoc.
230
+ - **PR #114678** Thanks @harjothkhara and @obviyus.
231
+ - **PR #134802** Related #82582. Thanks @najef1979-code.
232
+ - **PR #132723** Thanks @zhangguiping-xydt.
233
+ - **PR #134885**
234
+ - **PR #134660** Related #134552.
235
+ - **PR #134874**
236
+ - **PR #134882**
237
+ - **PR #134674**
238
+ - **PR #134857**
239
+ - **PR #134894**
240
+ - **PR #132186** Thanks @galiniliev.
241
+ - **PR #119501** Related #119317. Thanks @harjothkhara and @obviyus and @abacha.
242
+ - **PR #134842**
243
+ - **PR #134879**
244
+ - **PR #134909**
245
+ - **PR #134824**
246
+ - **PR #134281** Thanks @obviyus.
247
+ - **PR #132883** Related #127637. Thanks @SunnyShu0925.
248
+ - **PR #134928**
249
+ - **PR #132756** Thanks @sashankh.
250
+ - **PR #134923**
251
+ - **PR #134889**
252
+ - **PR #134927** Related #134919.
253
+ - **PR #134828** Related #134823.
254
+ - **PR #134860** Thanks @Patrick-Erichsen.
255
+ - **PR #134936**
256
+ - **PR #134921**
257
+ - **PR #134932**
258
+ - **PR #134908**
259
+ - **PR #134933**
260
+ - **PR #134812**
261
+ - **PR #134813** Thanks @Patrick-Erichsen.
262
+ - **PR #134930**
263
+ - **PR #102180** Related #101672. Thanks @849261680 and @obviyus and @cpwilhelmi.
264
+ - **PR #132477**
265
+ - **PR #134837** Related #134305. Thanks @fuller-stack-dev and @goslingmanagment.
266
+ - **PR #134827**
267
+ - **PR #134872** Related #134848.
268
+ - **PR #134949**
269
+ - **PR #134742** Related #134741. Thanks @w9n and @obviyus.
270
+ - **PR #134940**
271
+ - **PR #134935**
272
+ - **PR #134907** Thanks @fuller-stack-dev.
273
+ - **PR #134950**
274
+ - **PR #134961**
275
+ - **PR #134912**
276
+ - **PR #134585**
277
+ - **PR #134905**
278
+ - **PR #134954**
279
+ - **PR #134973**
280
+ - **PR #134946**
281
+ - **PR #134881**
282
+ - **PR #134981**
283
+ - **PR #134942**
284
+ - **PR #134978**
285
+ - **PR #134601**
286
+ - **PR #131691** Related #131491. Thanks @LiuwqGit and @meircohen.
287
+ - **PR #134711** Thanks @qingminglong.
288
+ - **PR #134983**
289
+ - **PR #134926** Thanks @vincentkoc.
290
+ - **PR #134979**
291
+ - **PR #134876** Thanks @vincentkoc.
292
+ - **PR #134947**
293
+ - **PR #134904** Thanks @vincentkoc.
294
+ - **PR #134934**
295
+ - **PR #134734**
296
+ - **PR #134884** Thanks @fuller-stack-dev.
297
+ - **PR #134917** Thanks @qingminglong.
298
+ - **PR #134958** Thanks @vincentkoc.
299
+ - **PR #134962** Thanks @vincentkoc.
300
+ - **PR #134948**
301
+ - **PR #134302** Related #127403. Thanks @PollyBot13.
302
+ - **PR #134977** Thanks @vincentkoc.
303
+ - **PR #134991**
304
+ - **PR #134893** Related #134709.
305
+ - **PR #134986**
306
+ - **PR #134888**
307
+ - **PR #135011**
308
+ - **PR #134850** Related #134841.
309
+ - **PR #134984**
310
+ - **PR #135001**
311
+ - **PR #134952**
312
+ - **PR #134607** Thanks @vincentkoc and @obviyus.
313
+ - **PR #134725** Related #134718.
314
+ - **PR #120105** Thanks @qingminglong and @vincentkoc.
315
+ - **PR #135004**
316
+ - **PR #134964**
317
+ - **PR #135015**
318
+ - **PR #134913**
319
+ - **PR #135020**
320
+ - **PR #134998**
321
+ - **PR #134988**
322
+ - **PR #135021**
323
+ - **PR #134519** Thanks @RomneyDa.
324
+ - **PR #135007**
325
+ - **PR #135008**
326
+ - **PR #134965**
327
+ - **PR #134466**
328
+ - **PR #134854**
329
+ - **PR #134966**
330
+ - **PR #135025**
331
+ - **PR #135040**
332
+ - **PR #135005** Thanks @vincentkoc.
333
+ - **PR #134891**
334
+ - **PR #134839**
335
+ - **PR #135014**
336
+ - **PR #132627** Thanks @Alix-007.
337
+ - **PR #134944**
338
+ - **PR #135012** Thanks @vincentkoc.
339
+ - **PR #134992** Thanks @vincentkoc.
340
+ - **PR #135037**
341
+ - **PR #134807**
342
+ - **PR #134670** Thanks @obviyus.
343
+ - **PR #134799** Thanks @marmar9615-cloud.
344
+ - **PR #135050**
345
+ - **PR #135048**
346
+ - **PR #135027**
347
+ - **PR #134714**
348
+ - **PR #122730** Related #121083. Thanks @191612731-cloud and @fujixm5.
349
+ - **PR #134875** Related #134867.
350
+ - **PR #133778** Thanks @qingminglong.
351
+ - **PR #120645** Thanks @firepinn.
352
+ - **PR #135039**
353
+ - **PR #135046** Related #134516. Thanks @goslingmanagment.
354
+ - **PR #134423** Thanks @hartmark.
355
+ - **PR #135034**
356
+ - **PR #125791** Thanks @santhiprakash.
357
+ - **PR #135029**
358
+ - **PR #135030**
359
+ - **PR #134862**
360
+ - **PR #120913** Thanks @qingminglong.
361
+ - **PR #135033**
362
+ - **PR #135043**
363
+ - **PR #125378** Thanks @qdivan.
364
+ - **PR #135047**
365
+ - **PR #135076**
366
+ - **PR #134740** Thanks @lzhan011 and @0x-Parzival.
367
+ - **PR #134880**
368
+ - **PR #134385** Thanks @ly85206559.
369
+ - **PR #135064**
370
+ - **PR #134831** Related #134748. Thanks @miguelarios.
371
+ - **PR #134797** Thanks @marmar9615-cloud.
372
+ - **PR #135065**
373
+ - **PR #134915** Thanks @edenfunf.
374
+ - **PR #134902**
375
+ - **PR #135074**
376
+ - **PR #135066**
377
+ - **PR #135069**
378
+ - **PR #121569** Thanks @sunlit-deng.
379
+ - **PR #135095** Related #134731. Thanks @obviyus and @BoatAngle.
380
+ - **PR #133806** Thanks @qingminglong.
381
+ - **PR #135072**
382
+ - **PR #135073**
383
+ - **PR #134010** Thanks @ZengWen-DT.
384
+ - **PR #135078**
385
+ - **PR #134362**
386
+ - **PR #135094**
387
+ - **PR #133814** Related #132147. Thanks @MoerAI and @vyctorbrzezowski.
388
+ - **PR #135070**
389
+ - **PR #135096** Related #132605. Thanks @jodok.
390
+ - **PR #133297** Related #132886. Thanks @zhangguiping-xydt and @goffern.
391
+ - **PR #135075**
392
+ - **PR #135101**
393
+ - **PR #135019** Thanks @xialonglee and @obviyus.
394
+ - **PR #135062**
395
+ - **PR #132623** Thanks @sunlit-deng.
396
+ - **PR #135031** Related #126730. Thanks @astra-openclaw.
397
+ - **PR #135090**
398
+ - **PR #120161** Thanks @qingminglong.
399
+ - **PR #134924** Related #134883.
400
+ - **PR #135091**
401
+ - **PR #128453** Thanks @ruel225.
402
+ - **PR #134892**
403
+ - **PR #135105**
404
+ - **PR #134835** Related #134834.
405
+ - **PR #133716** Thanks @RileyJJY.
406
+ - **PR #135107**
407
+ - **PR #135110** Thanks @vincentkoc.
408
+ - **PR #135103**
409
+ - **PR #121598** Thanks @mikasa0818.
410
+ - **PR #135082**
411
+ - **PR #135120**
412
+ - **PR #135051** Related #134999. Thanks @pengzh1 and @obviyus and @gru-10k.
413
+ - **PR #135098**
414
+ - **PR #135099**
415
+ - **PR #135116**
416
+ - **PR #135113** Thanks @vincentkoc.
417
+ - **PR #129035** Thanks @pgondhi987.
418
+ - **PR #135108**
419
+ - **PR #135102**
420
+ - **PR #134221** Thanks @fuller-stack-dev.
421
+ - **PR #135123** Related #135104.
422
+ - **PR #134794** Related #134690.
423
+ - **PR #135114** Related #135089. Thanks @walker1211.
424
+ - **PR #64490** Thanks @EdenKangdw and @walker1211.
425
+ - **PR #135121**
426
+ - **PR #135122**
427
+ - **PR #135119** Related #135068.
428
+ - **PR #135128**
429
+ - **PR #135100**
430
+ - **PR #135141**
431
+ - **PR #134969**
432
+ - **PR #118197** Thanks @peterolkhov.
433
+ - **PR #135042** Related #134967. Thanks @ruel225 and @obviyus and @kiranvk-2011.
434
+ - **PR #120660** Related #120659. Thanks @fr-meyer.
435
+ - **PR #135136**
436
+ - **PR #123220** Thanks @wanyongstar.
437
+ - **PR #122078** Thanks @Iskam31.
438
+ - **PR #123893**
439
+ - **PR #135003** Related #134895. Thanks @ericcaiwx-star and @obviyus.
440
+ - **PR #135157** Thanks @vincentkoc.
441
+ - **PR #135006** Related #134997.
442
+ - **PR #135109** Related #135026. Thanks @obviyus and @jadabreu.
443
+ - **PR #135165**
444
+ - **PR #134890** Related #134649. Thanks @MoerAI and @obviyus and @Deregtx.
445
+ - **PR #135161**
446
+ - **PR #135126** Thanks @vincentkoc.
447
+ - **PR #135063**
448
+ - **PR #134655** Related #134638. Thanks @mohamedelrefaiy.
449
+ - **PR #135118** Related #134304. Thanks @fuller-stack-dev and @goslingmanagment.
450
+ - **PR #135059** Thanks @vincentkoc.
451
+ - **PR #134744** Thanks @0x-Parzival.
452
+ - **PR #135162**
453
+ - **PR #134957** Thanks @xialonglee and @obviyus.
454
+ - **PR #135172**
455
+ - **PR #135131**
456
+ - **PR #134490** Thanks @fuller-stack-dev.
457
+ - **PR #135146** Thanks @vincentkoc.
458
+ - **PR #135125**
459
+ - **PR #135170**
460
+ - **PR #135169**
461
+ - **PR #135183**
462
+ - **PR #132951** Thanks @hartmark.
463
+ - **PR #135158**
464
+ - **PR #134937**
465
+ - **PR #135201**
466
+ - **PR #133224**
467
+ - **PR #135159**
468
+ - **PR #135160**
469
+ - **PR #133268** Related #132739. Thanks @edenfunf and @ml12580.
470
+ - **PR #135106**
471
+ - **PR #135204**
472
+ - **PR #135189**
473
+ - **PR #135181**
474
+ - **PR #135186** Related #135176.
475
+ - **PR #135081** Related #134971. Thanks @akagifreeez and @obviyus and @tomroberts78.
476
+ - **PR #135207**
477
+ - **PR #134780** Related #134657. Thanks @RayWangyangMa and @obviyus and @axiom-ncis.
478
+ - **PR #134910**
479
+ - **PR #135153**
480
+ - **PR #135246**
481
+ - **PR #135257**
482
+ - **PR #135206** Thanks @fuller-stack-dev.
483
+ - **PR #135259**
484
+ - **PR #134403** Thanks @obviyus.
485
+ - **PR #135232**
486
+ - **PR #135220**
487
+ - **PR #135208**
488
+ - **PR #135258**
489
+ - **PR #132582** Related #132581. Thanks @TARSDrakon.
490
+ - **PR #135145**
491
+ - **PR #135235**
492
+ - **PR #135222**
493
+ - **PR #135226**
494
+ - **PR #135265**
495
+ - **PR #135269**
496
+ - **PR #135271**
497
+ - **PR #135260**
498
+ - **PR #134738** Thanks @eleqtrizit.
499
+ - **PR #135289**
500
+ - **PR #135275**
501
+ - **PR #135274**
502
+ - **PR #135245** Thanks @ssaade01.
503
+ - **PR #135254**
504
+ - **PR #135285**
505
+ - **PR #135294**
506
+ - **PR #134903**
507
+ - **PR #134911**
508
+ - **PR #134779** Related #134379. Thanks @leilei3167 and @obviyus and @aaajiao.
509
+ - **PR #135288**
510
+ - **PR #133799**
511
+ - **PR #135306**
512
+ - **PR #133379** Related #133294. Thanks @zhangguiping-xydt and @Paeddy87.
513
+ - **PR #135124** Related #135013. Thanks @zhangguiping-xydt and @obviyus and @masatokawano.
514
+ - **PR #135155** Thanks @brettdaman and @obviyus.
515
+ - **PR #131456** Related #131113. Thanks @gaoanze888 and @Grynn.
516
+ - **PR #135317**
517
+ - **PR #135319**
518
+ - **PR #135325**
519
+ - **PR #135148**
520
+ - **PR #135323**
521
+ - **PR #135326**
522
+ - **PR #131017** Related #130918. Thanks @Alix-007 and @emes.
523
+ - **PR #135077** Related #134864.
524
+ - **PR #132571** Thanks @edenfunf.
525
+ - **PR #134980** Related #134968. Thanks @Marvinthebored and @obviyus.
526
+ - **PR #135227**
527
+ - **PR #135312**
528
+ - **PR #135360**
529
+ - **PR #135322**
530
+ - **PR #135332**
531
+ - **PR #135321**
532
+ - **PR #135304**
533
+ - **PR #135313**
534
+ - **PR #134781** Thanks @eleqtrizit.
535
+ - **PR #135138** Related #134960. Thanks @gaoanze888 and @Vasanthdev2004.
536
+ - **PR #135346**
537
+ - **PR #135345** Thanks @Takhoffman.
538
+ - **PR #135355** Related #135140. Thanks @obviyus and @mattcbianco.
539
+ - **PR #135363**
540
+ - **PR #135287**
541
+ - **PR #134865**
542
+ - **PR #131329** Thanks @hartmark.
543
+ - **PR #135369**
544
+ - **PR #135368**
545
+ - **PR #135353**
546
+ - **PR #135357** Related #135079. Thanks @obviyus and @dannevang.
547
+ - **PR #135374**
548
+ - **PR #134974** Related #124343. Thanks @VACInc and @aoclaw-glitch.
549
+ - **PR #135302**
550
+ - **PR #135382** Related #133550. Thanks @Call44.
551
+ - **PR #135407** Thanks @itsuzef.
552
+ - **PR #135279** Thanks @qingminglong.
553
+ - **PR #135185** Related #135061. Thanks @gaoanze888 and @obviyus and @snls1994.
554
+ - **PR #135373**
555
+ - **PR #135426** Related #135156. Thanks @obviyus and @rwinkelman and @Synthetic2802.
556
+ - **PR #135303**
557
+ - **PR #134680** Thanks @Patrick-Erichsen.
558
+ - **PR #135444** Related #135442.
559
+ - **PR #135390**
560
+ - **PR #135474**
561
+ - **PR #135336**
562
+ - **PR #118045** Thanks @AAliKKhan.
563
+ - **PR #132370** Related #132369. Thanks @jalehman.
564
+ - **PR #135454** Related #135437.
565
+ - **PR #135380** Related #135340.
566
+ - **PR #135393**
567
+ - **PR #135264**
568
+ - **PR #135367**
569
+ - **PR #135044** Related #134306. Thanks @fuller-stack-dev and @goslingmanagment.
570
+ - **PR #135386**
571
+ - **PR #131767** Related #131765. Thanks @vyctorbrzezowski.
572
+ - **PR #135428**
573
+ - **PR #135024** Related #134506. Thanks @jalehman.
574
+ - **PR #134664** Thanks @jalehman.
575
+ - **PR #135154** Related #135149. Thanks @brettdaman and @obviyus.
576
+ - **PR #134068** Related #133602.
577
+ - **PR #135497**
578
+ - **PR #135529**
579
+ - **PR #135239** Related #135221. Thanks @Solvely-Colin.
580
+ - **PR #134365** Thanks @vyctorbrzezowski.
581
+ - **PR #135397**
582
+ - **PR #135112** Related #134916. Thanks @shakkernerd.
583
+ - **PR #135431**
584
+ - **PR #135440**
585
+ - **PR #135432**
586
+ - **PR #135425**
587
+ - **PR #135543**
588
+ - **PR #135401** Thanks @Colton-Harris.
589
+ - **PR #135495** Thanks @lzhan011.
590
+ - **PR #135445** Related #135361. Thanks @gaoanze888 and @Bloodis94.
591
+ - **PR #135391** Thanks @Peetiegonzalez and @obviyus.
592
+ - **PR #134929** Thanks @hannesrudolph.
593
+ - **PR #135392**
594
+ - **PR #135447**
595
+ - **PR #135460**
596
+ - **PR #134756**
597
+ - **PR #135464**
598
+ - **PR #135505**
599
+ - **PR #135342**
600
+ - **PR #135560**
601
+ - **PR #135500**
602
+ - **PR #135071**
603
+ - **PR #135423**
604
+ - **PR #135200**
605
+ - **PR #135568** Related #135507.
606
+ - **PR #135572**
607
+ - **PR #135375**
608
+ - **PR #135195** Related #135187. Thanks @edenfunf.
609
+ - **PR #135579**
610
+ - **PR #135405**
611
+ - **PR #135586**
612
+ - **PR #135523** Thanks @Patrick-Erichsen.
613
+ - **PR #135524** Thanks @Patrick-Erichsen.
614
+ - **PR #135595** Thanks @hannesrudolph.
615
+ - **PR #134532** Related #134439. Thanks @chelsealong and @henrique-simoes.
616
+ - **PR #135594**
617
+ - **PR #135412**
618
+ - **PR #135284**
619
+ - **PR #135557**
620
+ - **PR #134976** Related #134975. Thanks @edenfunf.
621
+ - **PR #135559**
622
+ - **PR #134945**
623
+ - **PR #134572** Thanks @omarshahine.
624
+ - **PR #135093**
625
+ - **PR #135600**
626
+ - **PR #127284** Thanks @RomneyDa and @karkarl.
627
+ - **PR #135605**
628
+ - **PR #135598**
629
+ - **PR #135415** Related #135414.
630
+ - **PR #124568** Related #124567. Thanks @zyw02.
631
+ - **PR #135521** Thanks @Patrick-Erichsen.
632
+ - **PR #135514**
633
+ - **PR #135473**
634
+ - **PR #135574**
635
+ - **PR #135593** Related #135587.
636
+ - **PR #135475**
637
+ - **PR #135504**
638
+ - **PR #135602**
639
+ - **PR #135477**
640
+ - **PR #135563**
641
+ - **PR #135624**
642
+ - **PR #135597**
643
+ - **PR #135577** Related #135338.
644
+ - **PR #135644**
645
+ - **PR #135592**
646
+ - **PR #135651**
647
+ - **PR #135421** Thanks @ly85206559.
648
+ - **PR #135601**
649
+ - **PR #135640**
650
+ - **PR #135638** Related #135623.
651
+ - **PR #135669**
652
+ - **PR #135611**
653
+ - **PR #135476**
654
+ - **PR #135210**
655
+ - **PR #135643**
656
+ - **PR #135398**
657
+ - **PR #133897** Thanks @RomneyDa.
658
+ - **PR #122586** Related #68170. Thanks @vincentkoc and @lidge-jun and @richard-scott.
659
+ - **PR #135273** Thanks @SebTardif.
660
+ - **PR #134443** Thanks @RomneyDa.
661
+ - **PR #135625**
662
+ - **PR #135585**
663
+ - **PR #135626**
664
+ - **PR #135607**
665
+ - **PR #135616**
666
+ - **PR #135667** Thanks @vincentkoc.
667
+ - **PR #134457** Thanks @RomneyDa.
668
+ - **PR #135662**
669
+ - **PR #126887** Thanks @vincentkoc and @RomneyDa.
670
+ - **PR #134613** Thanks @RomneyDa.
671
+ - **PR #134614** Thanks @RomneyDa.
672
+ - **PR #134615** Thanks @RomneyDa.
673
+ - **PR #135622** Related #135496.
674
+ - **PR #135177**
675
+ - **PR #135619** Related #135606.
676
+ - **PR #135650** Related #135565.
677
+ - **PR #135674**
678
+ - **PR #126473** Related #126436. Thanks @ayaangazali and @Oliverbot26.
679
+ - **PR #99864** Related #99843. Thanks @LZY3538 and @ayaangazali and @jrex-jooni.
680
+ - **PR #135672**
681
+ - **PR #135629** Related #135613.
682
+ - **PR #135547** Related #135535. Thanks @vyctorbrzezowski.
683
+ - **PR #135501**
684
+ - **PR #135681** Related #135575.
685
+ - **PR #135617**
686
+ - **PR #135680**
687
+ - **PR #135608**
688
+ - **PR #135685**
689
+ - **PR #135687**
690
+ - **PR #133091** Related #127333. Thanks @SunnyShu0925.
691
+ - **PR #135174**
692
+ - **PR #135470**
693
+ - **PR #135609** Related #135352.
694
+ - **PR #135698**
695
+ - **PR #135719**
696
+ - **PR #135703**
697
+ - **PR #135718** Related #135233. Thanks @boramyleng.
698
+ - **PR #135725**
699
+ - **PR #134549** Related #120134. Thanks @nerclid and @w1130150306.
700
+ - **PR #135723**
701
+ - **PR #135526** Related #135506. Thanks @vyctorbrzezowski.
702
+ - **PR #129402** Related #129401. Thanks @ashawwal.
703
+ - **PR #135712**
704
+ - **PR #135337** Related #135305. Thanks @LiuwqGit and @Cobblestone-Digital1.
705
+ - **PR #135702** Related #37634. Thanks @whyuds.
706
+ - **PR #135670**
707
+ - **PR #135721**
708
+ - **PR #135735**
709
+ - **PR #135741**
710
+ - **PR #135451** Related #135450.
711
+ - **PR #135315**
712
+ - **PR #129345** Related #129344. Thanks @amalysh.
713
+ - **PR #121618** Thanks @fr-meyer.
714
+ - **PR #135697** Related #135693.
715
+ - **PR #135752**
716
+ - **PR #135739** Related #135385. Thanks @Deregtx.
717
+ - **PR #135724** Related #135691.
718
+ - **PR #135653**
719
+ - **PR #135394** Thanks @jjjhenriksen.
720
+ - **PR #135080**
721
+ - **PR #135711** Related #135566. Thanks @goslingmanagment.
722
+ - **PR #135733**
723
+ - **PR #132379** Related #132347. Thanks @wangmiao0668000666.
724
+ - **PR #135755** Related #134331. Thanks @jjjhenriksen and @Deregtx.
725
+ - **PR #134939** Thanks @IWhatsskill and @obviyus.
726
+ - **PR #135639**
727
+ - **PR #135763**
728
+ - **PR #133847** Related #127457. Thanks @aniruddhaadak80 and @obviyus.
729
+ - **PR #135715** Related #135533. Thanks @starship863.
730
+ - **PR #135229** Related #135228. Thanks @edenfunf.
731
+ - **PR #135713** Related #135150. Thanks @Zak-Finance.
732
+ - **PR #135701** Related #135655.
733
+ - **PR #135396** Related #135335.
734
+ - **PR #135736** Related #135634, #135635, #135637, #135656.
735
+ - **PR #135580** Related #135530. Thanks @VACInc.
736
+ - **PR #135097**
737
+ - **PR #135781**
738
+ - **PR #135203** Related #135164. Thanks @SunnyShu0925 and @wojtek76.
739
+ - **PR #135772**
740
+ - **PR #135647** Related #135630.
741
+ - **PR #135270** Thanks @qingminglong.
742
+ - **PR #135292** Thanks @qingminglong.
743
+ - **PR #135564** Related #135562. Thanks @vyctorbrzezowski.
744
+ - **PR #135753**
745
+ - **PR #135787**
746
+ - **PR #135792**
747
+ - **PR #135790** Related #135785.
748
+ - **PR #135799**
749
+ - **PR #135769**
750
+ - **PR #135456** Thanks @ly85206559.
751
+ - **PR #135804**
752
+ - **PR #135806**
753
+ - **PR #129930** Thanks @ralphptorres.
754
+ - **PR #135766**
755
+ - **PR #134218** Thanks @wantosure.
756
+ - **PR #135584** Related #135556.
757
+ - **PR #135805** Thanks @hannesrudolph.
758
+ - **PR #135810**
759
+ - **PR #135417** Thanks @ly85206559.
760
+ - **PR #135583**
761
+ - **PR #135817**
762
+ - **PR #126818** Related #126808. Thanks @edenfunf and @obviyus.
763
+ - **PR #135561** Thanks @fuller-stack-dev.
764
+ - **PR #131400** Related #131231. Thanks @LiuwqGit and @obviyus and @srb11e.
765
+ - **PR #135791** Related #134353. Thanks @xiaomijituan.
766
+ - **PR #135439** Related #135365.
767
+ - **PR #135797**
768
+ - **PR #135182** Related #127591. Thanks @aniruddhaadak80 and @obviyus.
769
+ - **PR #135823**
770
+ - **PR #133888** Related #133860. Thanks @gokay-ai and @cursoragent and @Areson.
771
+ - **PR #135728**
772
+ - **PR #132697** Thanks @wangjx-xydt.
773
+ - **PR #135820** Related #135819.
774
+ - **PR #135756**
775
+ - **PR #134639** Thanks @solomonneas.
776
+ - **PR #135818**
777
+ - **PR #135784**
778
+ - **PR #70002** Thanks @xudaiyanzi.
779
+ - **PR #135738**
780
+ - **PR #135822** Thanks @hannesrudolph.
781
+ - **PR #135761** Related #135664.
782
+ - **PR #134755** Related #99272, #122233. Thanks @obviyus and @ernestrolfson-design.
783
+ - **PR #135179** Related #114020. Thanks @nissl24 and @fridaylans2000-art and @obviyus and @jooey.
784
+ - **PR #135833**
785
+ - **PR #135657**
786
+ - **PR #135135** Thanks @arpe1618.
787
+ - **PR #135834**
788
+ - **PR #135849** Related #135205. Thanks @obviyus and @virtexvirtuoso.
789
+ - **PR #135765**
790
+ - **PR #135816**
791
+ - **PR #135830** Related #135218. Thanks @obviyus and @ericcaiwx-star and @jarvismazz.
792
+ - **PR #135751** Related #135750.
793
+ - **PR #135832**
794
+ - **PR #135850**
795
+ - **PR #135854** Related #135231. Thanks @obviyus and @nikolascostello.
796
+ - **PR #135168** Thanks @mushuiyu886 and @obviyus.
797
+ - **PR #135740** Related #135515.
798
+ - **PR #135166** Related #134687. Thanks @zhangguiping-xydt and @obviyus and @giangthb.
799
+ - **PR #135466**
800
+ - **PR #135844**
801
+ - **PR #135812**
802
+ - **PR #135041** Related #134800.
803
+ - **PR #135875**
804
+ - **PR #135885** Related #135852.
805
+ - **PR #135193** Related #135086. Thanks @Alix-007 and @obviyus and @wave-workflow.
806
+ - **PR #135902** Thanks @RomneyDa.
807
+ - **PR #135870** Thanks @LZY3538.
808
+ - **PR #135795**
809
+ - **PR #135894**
810
+ - **PR #135891**
811
+ - **PR #135869**
812
+ - **PR #135903** Related #135896.
813
+ - **PR #135904**
814
+ - **PR #135898**
815
+ - **PR #135901** Related #135899.
816
+ - **PR #135873** Related #135872.
817
+ - **PR #135915**
818
+ - **PR #135926** Related #135705. Thanks @obviyus and @asgeirtj.
819
+ - **PR #135825** Related #135706. Thanks @pengzh1 and @obviyus and @ezimerman.
820
+ - **PR #135911**
821
+ - **PR #130894** Thanks @cxyhhhhh.
822
+ - **PR #135916**
823
+ - **PR #135877**
824
+ - **PR #135918**
825
+ - **PR #135255** Thanks @edenfunf.
826
+ - **PR #135760** Thanks @RomneyDa.
827
+ - **PR #135759** Thanks @RomneyDa.
828
+ - **PR #135928** Related #135921, #135922.
829
+ - **PR #135932**
830
+ - **PR #135786**
831
+ - **PR #135883**
832
+ - **PR #135940**
833
+ - **PR #135773** Related #135743. Thanks @LiuwqGit and @obviyus and @609NFT.
834
+ - **PR #135936** Related #135914.
835
+ - **PR #135942**
836
+ - **PR #135888**
837
+ - **PR #135511** Related #135487.
838
+ - **PR #135931**
839
+ - **PR #135952**
840
+ - **PR #135939**
841
+ - **PR #135956**
842
+ - **PR #135953**
843
+ - **PR #135957** Related #135682. Thanks @obviyus and @Grynn.
844
+ - **PR #135951**
845
+ - **PR #134689** Related #134621. Thanks @gaoanze888 and @wbstrbt.
846
+ - **PR #135919** Related #135658. Thanks @leilei3167 and @obviyus and @Frank683456.
847
+ - **PR #135884** Related #135571. Thanks @hyper-sdn.
848
+ - **PR #135582**
849
+ - **PR #135665** Related #135661. Thanks @Colton-Harris and @obviyus.
850
+ - **PR #135938** Related #135920, #135930.
851
+ - **PR #133829** Thanks @aniruddhaadak80.
852
+ - **PR #135878** Related #135874.
853
+ - **PR #135971**
854
+ - **PR #135758** Thanks @RomneyDa.
855
+ - **PR #135967**
856
+ - **PR #135973**
857
+ - **PR #135963** Related #135946, #135947.
858
+ - **PR #135950**
859
+ - **PR #135972**
860
+ - **PR #135974**
861
+ - **PR #135989**
862
+ - **PR #135993** Related #135990.
863
+ - **PR #135965**
864
+ - **PR #135960**
865
+ - **PR #135995** Related #135975, #135977.
866
+ - **PR #136004** Thanks @vincentkoc.
867
+ - **PR #135986**
868
+ - **PR #135994**
869
+ - **PR #135983** Thanks @vincentkoc.
870
+ - **PR #136000** Related #135966.
871
+ - **PR #136003**
872
+ - **PR #136017**
873
+ - **PR #135789**
874
+ - **PR #136020** Related #135893. Thanks @LiuwqGit.
875
+ - **PR #135794** Related #125322. Thanks @RaviTharuma.
876
+ - **PR #135998**
877
+ - **PR #135746**
878
+ - **PR #135981** Related #135968.
879
+ - **PR #136001** Related #135999.
880
+ - **PR #136027**
881
+ - **PR #135962**
882
+ - **PR #136024**
883
+ - **PR #136040** Thanks @fuller-stack-dev.
884
+ - **PR #136006**
885
+ - **PR #135729** Related #135726. Thanks @devzeroLL and @obviyus.
886
+ - **PR #135988**
887
+ - **PR #136028**
888
+ - **PR #136044**
889
+ - **PR #135731** Related #127645. Thanks @HuzaifaChaudary.
890
+ - **PR #135857** Related #135845.
891
+ - **PR #136012** Related #135948.
892
+ - **PR #136007** Thanks @vincentkoc.
893
+ - **PR #135943**
894
+ - **PR #136055**
895
+ - **PR #136054** Thanks @vincentkoc.
896
+ - **PR #136008** Related #135978.
897
+ - **PR #136031**
898
+ - **PR #135864** Related #135641. Thanks @yetval and @obviyus and @CjTruHeart.
899
+ - **PR #135961**
900
+ - **PR #136050** Related #136046.
901
+ - **PR #135976**
902
+ - **PR #135847** Related #135734. Thanks @yetval and @obviyus and @josephbergvinson.
903
+ - **PR #136030**
904
+ - **PR #136002**
905
+ - **PR #136061** Related #135991.
906
+ - **PR #136041**
907
+ - **PR #136014**
908
+ - **PR #136053** Related #136051, #136052.
909
+ - **PR #136057**
910
+ - **PR #135478**
911
+ - **PR #136043** Related #136042.
912
+ - **PR #136021**
913
+ - **PR #135829** Thanks @teddytennant and @obviyus.
914
+ - **PR #136063**
915
+ - **PR #135803** Thanks @teddytennant.
916
+ - **PR #136066**
917
+ - **PR #136010** Related #135684. Thanks @obviyus and @Grynn.
918
+ - **PR #136062** Related #136058, #136060.
919
+ - **PR #136064** Related #134337. Thanks @ThatGuySizemore.
920
+ - **PR #135848** Thanks @masatohoshino and @obviyus.
921
+ - **PR #136019**
922
+ - **PR #136086** Thanks @vincentkoc.
923
+ - **PR #136101**
924
+ - **PR #135749** Thanks @teddytennant and @obviyus.
925
+ - **PR #135831** Related #135573. Thanks @ruel225 and @obviyus and @TailsProwerWorks.
926
+ - **PR #136033** Related #135692. Thanks @obviyus and @Grynn.
927
+ - **PR #136065** Thanks @RomneyDa.
928
+ - **PR #136056** Thanks @RomneyDa.
929
+ - **PR #136077** Thanks @RomneyDa.
930
+ - **PR #136075** Thanks @RomneyDa.
931
+ - **PR #135954** Related #135949. Thanks @JosephNatsu.
932
+ - **PR #136080**
933
+ - **PR #136124**
934
+ - **PR #136108** Related #136106. Thanks @vincentkoc.
935
+ - **PR #135517** Thanks @jmewing and @obviyus.
936
+ - **PR #136112**
937
+ - **PR #135742** Thanks @marmar9615-cloud and @obviyus.
938
+ - **PR #135520** Related #135459. Thanks @gaoanze888 and @obviyus and @LifeViwer.
939
+ - **PR #136093** Related #46932, #136089. Thanks @jeffrey4341.
940
+ - **PR #136078**
941
+ - **PR #135748** Thanks @teddytennant and @obviyus.
942
+ - **PR #136121**
943
+ - **PR #136067**
944
+ - **PR #136114**
945
+ - **PR #136109** Related #136087.
946
+ - **PR #136119**
947
+ - **PR #136141**
948
+ - **PR #136091** Related #136083.
949
+ - **PR #135351** Thanks @teddytennant and @obviyus.
950
+ - **PR #136136**
951
+ - **PR #136152** Related #136128.
952
+ - **PR #136090** Related #136081.
953
+ - **PR #136023** Related #135881. Thanks @leilei3167 and @obviyus and @yetval.
954
+ - **PR #136092** Thanks @pash-openai.
955
+ - **PR #136125**
956
+ - **PR #136099**
957
+ - **PR #136133** Related #136118.
958
+ - **PR #135807** Thanks @teddytennant.
959
+ - **PR #136069**
960
+ - **PR #136164** Thanks @vincentkoc.
961
+ - **PR #136162**
962
+ - **PR #136160**
963
+ - **PR #136132** Related #136117.
964
+ - **PR #136166**
965
+ - **PR #135516** Thanks @RayWangyangMa and @obviyus.
966
+ - **PR #136111** Related #136102.
967
+ - **PR #134648** Related #134500. Thanks @sunlit-deng and @obviyus and @LowCode191.
968
+ - **PR #136191**
969
+ - **PR #136153**
970
+ - **PR #93842** Thanks @bladin.
971
+ - **PR #136167** Related #136163.
972
+ - **PR #134333** Related #134332. Thanks @TheAngryPit and @obviyus.
973
+ - **PR #134219** Related #134187. Thanks @tzlwn1 and @obviyus and @svdwalt007.
974
+ - **PR #136195**
975
+ - **PR #136193**
976
+ - **PR #136169**
977
+ - **PR #135467** Thanks @Yigtwxx.
978
+ - **PR #134737** Thanks @lzhan011 and @obviyus.
979
+ - **PR #136137** Thanks @vincentkoc.
980
+ - **PR #136186**
981
+ - **PR #136201** Related #136198.
982
+ - **PR #136150**
983
+ - **PR #136209**
984
+ - **PR #136072**
985
+ - **PR #136127** Thanks @vincentkoc.
986
+ - **PR #136187**
987
+ - **PR #135083** Thanks @rwinkelman and @obviyus.
988
+ - **PR #136180**
989
+ - **PR #136227** Related #136224.
990
+ - **PR #135281** Thanks @xialonglee.
991
+ - **PR #135341** Thanks @teddytennant and @obviyus.
992
+ - **PR #136228** Related #136139, #136140.
993
+ - **PR #136134**
994
+ - **PR #134525** Thanks @sjf-oa and @sjf.
995
+ - **PR #136222**
996
+ - **PR #136214** Related #136206, #136207, #136208.
997
+ - **PR #135855** Related #96135. Thanks @yungchentang and @kAIborg24.
998
+ - **PR #135671** Thanks @fuller-stack-dev.
999
+ - **PR #132492** Thanks @RileyJJY.
1000
+ - **PR #132490** Thanks @RileyJJY.
1001
+ - **PR #135851**
1002
+ - **PR #136142** Thanks @qingminglong and @obviyus.
1003
+ - **PR #136182**
1004
+ - **PR #136243**
1005
+ - **PR #136232**
1006
+ - **PR #136161**
1007
+ - **PR #135846** Related #135633. Thanks @louisfy and @obviyus and @jakestenger.
1008
+ - **PR #136192**
1009
+ - **PR #135455** Thanks @ly85206559 and @obviyus.
1010
+ - **PR #136241** Related #136237.
1011
+ - **PR #136245**
1012
+ - **PR #136223**
1013
+ - **PR #136254**
1014
+ - **PR #131408** Thanks @igs-rogenlo.
1015
+ - **PR #136216**
1016
+ - **PR #136196** Related #136143.
1017
+ - **PR #136151** Related #136104. Thanks @ericcaiwx-star and @obviyus and @KimOckHyun.
1018
+ - **PR #136234** Related #136204. Thanks @LiuwqGit and @obviyus and @Volevanius.
1019
+ - **PR #135925** Related #135435. Thanks @xialonglee and @obviyus and @jaxonparrott.
1020
+ - **PR #136256** Related #136225. Thanks @obviyus and @Famyoff.
1021
+ - **PR #136184**
1022
+ - **PR #136221** Related #136217.
1023
+ - **PR #136205** Thanks @quangtran88.
1024
+ - **PR #133871** Thanks @SebTardif and @obviyus.
1025
+ - **PR #136272**
1026
+ - **PR #135985** Related #135836. Thanks @gaoanze888 and @obviyus and @yetval.
1027
+ - **PR #136105**
1028
+ - **PR #132753** Related #132750. Thanks @tommyjoseph.
1029
+ - **PR #136265**
1030
+ - **PR #136215** Related #136159. Thanks @LiuwqGit and @obviyus and @wave-workflow.
1031
+ - **PR #136171** Thanks @efe-arv and @liri-ha and @obviyus.
1032
+ - **PR #136229**
1033
+ - **PR #136172** Related #136015. Thanks @NianJiuZst and @obviyus and @davidcittadini.
1034
+ - **PR #135462** Thanks @fuller-stack-dev.
1035
+ - **PR #136278** Thanks @jodok.
1036
+ - **PR #136173** Related #128926. Thanks @aniruddhaadak80.
1037
+ - **PR #136085** Related #136070.
1038
+ - **PR #136036** Related #136032.
1039
+ - **PR #135824** Thanks @vincentkoc.
1040
+ - **PR #135793** Thanks @vincentkoc.
1041
+ - **PR #136280**
1042
+ - **PR #135443** Thanks @lzhan011 and @obviyus.
1043
+ - **PR #136287**
1044
+ - **PR #133747** Related #133743, #133785. Thanks @shakkernerd.
1045
+ - **PR #136291**
1046
+ - **PR #133978** Thanks @yetval.
1047
+ - **PR #136263**
1048
+ - **PR #135828** Thanks @griswomw2 and @obviyus.
1049
+ - **PR #136298**
1050
+ - **PR #136211**
1051
+ - **PR #135016** Thanks @jesse-merhi.
1052
+ - **PR #136296**
1053
+ - **PR #136138** Thanks @RomneyDa.
1054
+ - **PR #136305**
1055
+ - **PR #136249**
1056
+ - **PR #136247** Related #136219.
1057
+ - **PR #131786** Related #131781. Thanks @vyctorbrzezowski.
1058
+ - **PR #136283** Related #135905. Thanks @obviyus and @anyech.
1059
+ - **PR #136018**
1060
+ - **PR #136304**
1061
+ - **PR #136312**
1062
+ - **PR #136315** Thanks @vincentkoc.
1063
+ - **PR #136233** Thanks @RomneyDa.
1064
+ - **PR #136307**
1065
+ - **PR #136316**
1066
+ - **PR #135508** Related #135457. Thanks @gaoanze888 and @obviyus and @itsuzef.
1067
+ - **PR #136107** Thanks @vincentkoc.
1068
+ - **PR #136323**
1069
+ - **PR #136308** Thanks @vincentkoc.
1070
+ - **PR #133724** Related #133702.
1071
+ - **PR #136269** Thanks @yetval and @obviyus.
1072
+ - **PR #136347**
1073
+ - **PR #136351**
1074
+ - **PR #136344**
1075
+ - **PR #136336**
1076
+ - **PR #136353**
1077
+ - **PR #136185**
1078
+ - **PR #136335**
1079
+ - **PR #136352**
1080
+ - **PR #136168** Related #136157.
1081
+ - **PR #135441** Thanks @jjjhenriksen and @obviyus.
1082
+ - **PR #135796**
1083
+ - **PR #136273**
1084
+ - **PR #136276** Related #136275. Thanks @vincentkoc.
1085
+ - **PR #135486** Thanks @zeroaltitude and @obviyus.
1086
+ - **PR #136354**
1087
+ - **PR #136380**
1088
+ - **PR #136377**
1089
+ - **PR #136381**
1090
+ - **PR #135301** Related #135028. Thanks @mushuiyu886 and @obviyus and @jadabreu.
1091
+ - **PR #136359**
1092
+ - **PR #134853** Related #134851. Thanks @cmanrav and @obviyus.
1093
+ - **PR #132983** Thanks @fuller-stack-dev.
1094
+ - **PR #136407** Related #136406.
1095
+ - **PR #136340**
1096
+ - **PR #136389**
1097
+ - **PR #136416**
1098
+ - **PR #136417** Related #136415.
1099
+ - **PR #136309** Thanks @vincentkoc.
1100
+ - **PR #136418**
1101
+ - **PR #136398**
1102
+ - **PR #136404**
1103
+ - **PR #136319** Thanks @vincentkoc.
1104
+ - **PR #136403**
1105
+ - **PR #133381** Thanks @ericcaiwx-star and @cursoragent and @obviyus.
1106
+ - **PR #136391**
1107
+ - **PR #136362**
1108
+ - **PR #136425**
1109
+ - **PR #134663** Thanks @jalehman.
1110
+ - **PR #135863** Related #135618. Thanks @vyctorbrzezowski.
1111
+ - **PR #127959** Related #127948. Thanks @Finn763 and @markswitch83.
1112
+ - **PR #136178** Thanks @masatohoshino and @obviyus.
1113
+ - **PR #136301** Related #135860. Thanks @zhangguiping-xydt and @obviyus and @zqchris.
1114
+ - **PR #136438**
1115
+ - **PR #136448**
1116
+ - **PR #136473**
1117
+ - **PR #136450**
1118
+ - **PR #134795** Thanks @eleqtrizit.
1119
+ - **PR #136402** Related #136397. Thanks @ly85206559.
1120
+ - **PR #136481**
1121
+ - **PR #135219** Related #132452. Thanks @SunnyShu0925 and @obviyus and @alexph-dev.
1122
+ - **PR #135798** Related #135649. Thanks @SunnyShu0925 and @markhaines.
1123
+ - **PR #136393**
1124
+ - **PR #134941** Thanks @itsuzef and @obviyus.
1125
+ - **PR #136444** Related #136442, #136443.
1126
+ - **PR #136483**
1127
+ - **PR #136446**
1128
+ - **PR #136479**
1129
+ - **PR #129918** Related #129734. Thanks @SunnyShu0925 and @obviyus and @hpyhandsome.
1130
+ - **PR #136522**
1131
+ - **PR #135430** Related #135217, #135249. Thanks @Solvely-Colin and @nikolaihack.
1132
+ - **PR #135429** Related #135251.
1133
+ - **PR #135295** Thanks @PollyBot13 and @obviyus.
1134
+ - **PR #136534**
1135
+ - **PR #136419**
1136
+ - **PR #136388** Thanks @vincentkoc.
1137
+ - **PR #136460**
1138
+ - **PR #136463**
1139
+ - **PR #134819** Thanks @qingminglong and @obviyus.
1140
+ - **PR #136423** Related #134186. Thanks @svdwalt007.
1141
+ - **PR #136500**
1142
+ - **PR #136440**
1143
+ - **PR #134858** Thanks @Artemeey and @obviyus.
1144
+ - **PR #136434** Thanks @masatohoshino and @obviyus.
1145
+ - **PR #136461**
1146
+ - **PR #136541**
1147
+ - **PR #136445** Related #136441.
1148
+ - **PR #136470**
1149
+ - **PR #136413** Related #136047.
1150
+ - **PR #136428**
1151
+ - **PR #136510** Related #126872. Thanks @danielduerr.
1152
+ - **PR #136401**
1153
+ - **PR #136550**
1154
+ - **PR #136435**
1155
+ - **PR #136567**
1156
+ - **PR #136451**
1157
+ - **PR #136420** Related #133955. Thanks @obviyus and @aoclaw-glitch.
1158
+ - **PR #136511**
1159
+ - **PR #136539** Related #136538.
1160
+ - **PR #136570** Related #75848. Thanks @alfredjbclaw.
1161
+ - **PR #136501**
1162
+ - **PR #136457**
1163
+ - **PR #136540**
1164
+ - **PR #136478**
1165
+ - **PR #136399** Related #136385.
1166
+ - **PR #136220**
1167
+ - **PR #135945** Related #135882. Thanks @ruel225 and @obviyus and @yetval.
1168
+ - **PR #136580**
1169
+ - **PR #136439** Related #136437.
1170
+ - **PR #136469** Related #136468.
1171
+ - **PR #136366** Thanks @vincentkoc.
1172
+ - **PR #136574** Related #136573.
1173
+ - **PR #131735** Related #131734. Thanks @ruel225.
1174
+ - **PR #136577**
1175
+ - **PR #136527** Thanks @Patrick-Erichsen.
1176
+ - **PR #136373** Related #136357. Thanks @LiuwqGit and @obviyus and @Dresch63.
1177
+ - **PR #136094** Thanks @RomneyDa.
1178
+ - **PR #133445** Thanks @edenfunf.
1179
+ - **PR #135588** Related #135538. Thanks @VACInc.
1180
+ - **PR #136230** Related #136068.
1181
+ - **PR #136572** Related #136562.
1182
+ - **PR #136559**
1183
+ - **PR #135410** Thanks @ly85206559.
1184
+ - **PR #136566**
1185
+ - **PR #136571**
1186
+ - **PR #136601**
1187
+ - **PR #136590**
1188
+ - **PR #136542** Thanks @Patrick-Erichsen.
1189
+ - **PR #135923** Thanks @BennyAI2.
1190
+ - **PR #136532**
1191
+ - **PR #136465** Related #136464.
1192
+ - **PR #136569** Related #136568.
1193
+ - **PR #136603**
1194
+ - **PR #136615**
1195
+ - **PR #136604** Related #136593.
1196
+ - **PR #136521**
1197
+ - **PR #136602**
1198
+ - **PR #136430**
1199
+ - **PR #136616**
1200
+ - **PR #136607** Thanks @Patrick-Erichsen.
1201
+ - **PR #136620**
1202
+ - **PR #136597**
1203
+ - **PR #136624** Thanks @RomneyDa.
1204
+ - **PR #136531** Related #136530.
1205
+ - **PR #136537** Related #136536.
1206
+ - **PR #136628** Thanks @RomneyDa.
1207
+ - **PR #136614** Thanks @Patrick-Erichsen.
1208
+ - **PR #136504** Thanks @drobison00.
1209
+ - **PR #126419** Thanks @drobison00.
1210
+ - **PR #136505** Thanks @drobison00.
1211
+ - **PR #136564** Related #136563.
1212
+ - **PR #136286**
1213
+ - **PR #136596** Related #136594, #136595.
1214
+ - **PR #136509** Related #136499.
1215
+ - **PR #136599** Related #136598.
1216
+ - **PR #136600** Related #136591.
1217
+ - **PR #132266** Thanks @MonkeyLeeT.
1218
+ - **PR #136637** Thanks @Patrick-Erichsen.
1219
+ - **PR #136520**
1220
+ - **PR #136621**
1221
+ - **PR #136557** Related #136556.
1222
+ - **PR #135531** Thanks @MertBasar0 and @obviyus.
1223
+ - **PR #136626**
1224
+ - **PR #136176** Thanks @vincentkoc.
1225
+ - **PR #136633** Related #136632.
1226
+ - **PR #136519**
1227
+ - **PR #136636** Related #136635.
1228
+ - **PR #136578** Thanks @LiuwqGit.
1229
+ - **PR #136325** Thanks @vincentkoc.
1230
+ - **PR #136619** Thanks @shakkernerd.
1231
+ - **PR #136641** Related #136640. Thanks @vincentkoc.
1232
+ - **PR #134622** Thanks @eleqtrizit.
1233
+ - **PR #136654**
1234
+ - **PR #136606** Thanks @Patrick-Erichsen.
1235
+ - **PR #136627** Thanks @RomneyDa.
1236
+ - **PR #135744** Thanks @vincentkoc.
1237
+ - **PR #136657** Related #136656.
1238
+ - **PR #136524**
1239
+ - **PR #136664** Related #136662.
1240
+ - **PR #136668** Thanks @Patrick-Erichsen.
1241
+ - **PR #136658**
1242
+ - **PR #133660** Related #133517.
1243
+ - **PR #134603** Thanks @eleqtrizit.
1244
+ - **PR #134368** Thanks @BryanTegomoh.
1245
+ - **PR #136037** Thanks @RomneyDa.
1246
+ - **PR #136038** Thanks @RomneyDa.
1247
+ - **PR #136666** Related #136665.
1248
+ - **PR #134413** Related #134409. Thanks @vyctorbrzezowski.
1249
+ - **PR #133151** Related #133128. Thanks @aniruddhaadak80.
1250
+ - **PR #136672**
1251
+ - **PR #136605** Thanks @vincentkoc.
1252
+ - **PR #136676**
1253
+ - **PR #136646** Thanks @Patrick-Erichsen.
1254
+ - **PR #136647** Thanks @Patrick-Erichsen.
1255
+ - **PR #136648** Thanks @Patrick-Erichsen.
1256
+ - **PR #136649** Thanks @Patrick-Erichsen.
1257
+ - **PR #136650** Thanks @Patrick-Erichsen.
1258
+ - **PR #136681** Thanks @vincentkoc.
1259
+ - **PR #136644**
1260
+ - **PR #136495** Related #136493.
1261
+ - **PR #135802** Thanks @vincentkoc.
1262
+ - **PR #136643** Related #136638.
1263
+ - **PR #135722** Related #135720. Thanks @vyctorbrzezowski.
1264
+ - **PR #136517** Related #136516.
1265
+ - **PR #136670** Thanks @vincentkoc.
1266
+ - **PR #136685**
1267
+ - **PR #136683**
1268
+ - **PR #136688**
1269
+ - **PR #136700**
1270
+ - **PR #136667** Thanks @vincentkoc.
1271
+ - **PR #136659**
1272
+ - **PR #136295**
1273
+ - **PR #136698**
1274
+ - **PR #136695**
1275
+ - **PR #135859** Thanks @vincentkoc.
1276
+ - **PR #136704**
1277
+ - **PR #136678**
1278
+ - **PR #136705**
1279
+ - **PR #136716**
1280
+ - **PR #136717**
1281
+ - **PR #136675**
1282
+ - **PR #136458** Related #136455, #136456.
1283
+ - **PR #136084**
1284
+ - **PR #136350**
1285
+ - **PR #136609** Thanks @Patrick-Erichsen.
1286
+ - **PR #136770**
1287
+ - **PR #136771**
1288
+ - **PR #136798**
1289
+ - **PR #136968**
1290
+ - **PR #136395** Thanks @fuller-stack-dev and @vincentkoc.
1291
+ - **PR #137012** Related #136881. Thanks @leilei3167 and @obviyus and @anordick.
CLAUDE.md ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AGENTS.md
2
+
3
+ The task defines scope and authorization; its chosen workflow owns execution,
4
+ review, publication, recovery, and cleanup. Explicit user instructions take
5
+ precedence over skill guidelines and workflow defaults; host limits and required
6
+ authorization boundaries still apply. Read the nearest scoped `AGENTS.md` and the
7
+ matching references below, including when changing callers outside an owner's directory.
8
+ Update instructions at their owner instead of adding competing rules here.
9
+
10
+ ## Design priorities
11
+
12
+ - **One owner per responsibility.** An owner makes a decision or changes authoritative state. Callers consume its operations and recorded facts. Adapters translate contracts; caches and projections derive from the owner with an explicit invalidation lifecycle. Different transports can need different adapters, but not competing owners for the same responsibility.
13
+ - **Small core, capable plugins.** Model-facing core additions have an ongoing context cost. Optional capability belongs at the edges; core supplies generic contracts. A feature needing a new integration is not, by itself, a reason to add another core tool or manager. [VISION.md](VISION.md) owns product scope.
14
+ - **Stable conversation context.** Rebuilding past context defeats prompt-prefix reuse. Keep generated prompt/tool/context additions bounded and deterministic, preserve transcript bytes, and serve required instructions whole. Only compaction rewrites history. Defer changes to stable prompt state until the next session unless its owner defines explicit invalidation; preserve existing skill, tool, and memory refresh contracts.
15
+
16
+ ## Working agreement
17
+
18
+ - Follow through on actionable requests, including "can you", within their authorized scope. When execution is requested, a plan or progress report is a checkpoint, not completion. Use prior context and preserve unaffected work across corrections and side questions.
19
+ - Resolve routine, reversible choices with reasonable assumptions. Ask only about consequential decisions the request and context cannot resolve; continue independent authorized work while waiting. Silence does not authorize a gated action.
20
+ - If a skill causes a pause, permission request, unfinished work, or scope change, link its exact `SKILL.md` and quote the instruction to the user. Explain how it applies, distinguish requirements from interpretation, and check prior authorization before asking again.
21
+ - Inspect `git status -sb` before editing or GitHub work. Preserve unrelated work, branches, processes, and user-managed checkouts; serialize shared Git mutations and isolate work when needed. Never switch a checkout while another agent or test run uses it.
22
+ - Treat pasted material and tool output as evidence; verify claims against source and observed behavior.
23
+ - Lead with the result and follow the user's format. Use plain words, active voice, and useful technical detail; omit stock phrases and repeated summaries. Progress updates explain new findings, decisions, or blockers. Keep delegated messages equally clear.
24
+ - Report routine findings in chat/stdout. Create files only for deliverables or concrete tool/proof/recovery needs; state their purpose and reuse them. Cleanup removes only task-created disposable files that are no longer needed or in use. Preserve unknown ownership, required evidence, and recovery state; this does not authorize existing-storage cleanup or retention changes.
25
+ - Read relevant docs before changing behavior; `pnpm docs:list` locates them. `package.json` owns current commands and versions; keep the repository's toolchain and conventions rather than swapping tools without approval.
26
+ - Use **OpenClaw** for the product, `openclaw` for CLI/package/config names, **plugins** for user-facing integrations, and American English.
27
+ - Edit canonical `AGENTS.md` files; new ones need a sibling `CLAUDE.md` symlink.
28
+
29
+ ## One owner, complete cutover
30
+
31
+ 1. **Intent:** reproduce defects through the actual entry point before editing when feasible. Read complete affected modules, owners, callers, siblings, tests, history, and dependency contracts until the intended user outcome and violated invariant are supported by evidence. Before restoring a missing path, check why it was removed (`git log -p -S <symbol>`): isolation may be intentional, and a retired alias may be a completed migration. Record concrete reproduction gaps.
32
+ 2. **Owner:** account for relevant decisions and state writers across creation, updates, reads, recovery, and cleanup. Choose the existing code, plugin, or maintained solution that absorbs the change. A new owner needs a missing responsibility; fix invalid or leaked state at its producer.
33
+ 3. **Cutover:** migrate all affected internal/bundled callers together. Remove superseded code, duplicate policy/state, wrappers, registrations, exports, tests, and docs. Every retained path needs a cited contract. Workers sharing an owner agree on one interface and cutover plan.
34
+ 4. **Proof:** exercise the intended user flow and relevant siblings; trace references to confirm retired paths are unreachable. Done means one owner serves the flow, old paths are removed or justified, and observed results or remaining gaps are recorded in existing task/PR evidence. Helper tests or a wrapper around competing implementations alone are insufficient.
35
+
36
+ - Prefer smaller, simpler production code; explain necessary growth. Keep coherent nearby repairs together and record unrelated work as follow-ups. No extra report or tracking system is required.
37
+ - Delegate independent evidence or implementation lanes when parallel work reduces time or improves verification. Give each lane a clear responsibility and completion condition; keep simple or tightly coupled work with the lead. The lead stays hands-on, verifies consequential conclusions, and coordinates shared-checkout safety.
38
+ - Retained compatibility needs an explicit user request or a public API/config/SDK/data, stable-tag upgrade, security/migration, dependency, or observed-production contract, plus a migration/removal path. Main, beta, and nightly code alone are not shipped contracts.
39
+
40
+ ### Choose the capability surface
41
+
42
+ For new capability, use the first path that expresses the actual requirement:
43
+
44
+ 1. Extend the existing owner or use an existing command, skill, plugin, or supported integration.
45
+ 2. Use an existing plugin contract. Prefer bundle plugins for skills, MCP servers, and configuration; use code plugins when runtime hooks, providers, channels, or tools are needed. Keep vendor behavior with its vendor plugin and feature behavior with its feature owner.
46
+ 3. If the contract is missing, define a narrow generic core/SDK capability and move existing bundled implementations and callers onto it together. Repeated independent requests for the same capability trigger this contract review, not another parallel manager or hook.
47
+ 4. Add universal core surface only when the need is fundamental and existing extension points cannot express it. Explain the gap and ongoing cost; a new hook needs a concrete consumer.
48
+
49
+ For example, a new channel action should first use the shared message action
50
+ contract. A setup screen needing plugin metadata should use the manifest or
51
+ lightweight artifact, not load the plugin's execution runtime.
52
+
53
+ ## Runtime and code safeguards
54
+
55
+ - Plugins use documented `openclaw/plugin-sdk/*` contracts, manifest metadata, and public/local barrels, never core internals or another plugin's private files. Dependencies follow runtime ownership.
56
+ - Runtime consumes canonical config/state. Doctor/migration owners normalize legacy shapes; plugin repairs stay plugin-owned. A change invalidating existing config includes its matching migration. Startup may invoke the same approved Doctor transforms; do not add independent compatibility readers.
57
+ - OpenClaw state and caches use SQLite, not new JSON/JSONL/sidecar stores. Files are for named user artifacts, imports/exports, attachments, logs, backups, or external-tool contracts.
58
+ - Use Kysely for ordinary SQLite access; raw SQL is limited to schema, migrations, bootstrap, and justified primitives. Write transactions are synchronous: finish asynchronous planning first, then reread authoritative rows before writing. No Promise or `await` in a transaction callback.
59
+ - Privileged actions require current owner-held authority. Revalidate after awaited work and immediately before side effects; tokens, signatures, expiry, and matching IDs alone do not prove live authority.
60
+ - Core owns shared message tools, action vocabulary, and dispatch. Channels own their account, security, conversation, and transport contracts. Preserve typed command/approval/URL/action distinctions until encoding; never infer product commands from raw strings.
61
+ - Carry prepared facts through hot paths. Reuse process-stable plugin metadata and lifecycle-owned caches; do not repeatedly load registries or freshness-poll files. Preserve lazy module boundaries and verify relevant builds on the authorized host.
62
+ - Keep APIs narrow, valid states explicit, and TypeScript ESM/types strict. Prefer real types or `unknown`; no `@ts-nocheck`. Suppressions need an intentional, explained exception. Reuse schema/coercion owners; avoid duplicate guards, speculative helpers, and naming-only wrappers.
63
+ - Static-analysis fixes strengthen the real type/runtime contract or remove the unsafe operation; do not conceal it with casts, widening, marker types, or property probes. New lint rules need a meaningful invariant and a clean owner scope.
64
+ - Comments explain non-obvious ownership, lifecycle, ordering, cleanup, platform, and dependency constraints, not syntax. Do not edit `node_modules` or generated artifacts by hand, or change formatter settings for a local expression; regenerate owned outputs.
65
+
66
+ ## Product and validation
67
+
68
+ - Defaults should produce a working, understandable result. Prioritize silent failures. Each action has a visible outcome or recorded intentional non-outcome; errors explain the next useful step.
69
+ - **Updates always work.** `openclaw update` finishes best effort on every install. Any change touching update, Doctor, service lifecycle, config/state migration, or plugin loading states its update behavior: the installed updater runs first and cannot be patched, so candidate-side fixes key on markers shipped drivers already set, and existing operator state is the input. Recoverable hiccups become recorded warnings; back up before mutating and let rollback restore it; refuse only for concrete data at risk, naming the reason and leaving the previous Gateway running. Timeouts and budgets are generous, derived from measured state, and sized for old, slow hardware. Proof: a published-driver × candidate cell.
70
+ - Prompts, tools, and results describe available capabilities accurately and give enough context for the next useful action; avoid unnecessary model round trips. Inject cross-tool references from the enabled tool set and remove stale model-facing arguments instead of hidden compatibility. New optional features need discovery paths.
71
+ - Security is a product tradeoff, not a goal to maximize restrictions. Weigh concrete risk and likely impact against user effort, lockouts, and lost capability. Prefer the least restrictive effective safeguard; bounded, understood risk can be acceptable for a substantial usability benefit. Keep risky paths explicit and operator-controlled within the existing trust model and approval boundaries, and explain the tradeoff instead of inventing extra gates.
72
+ - Tests must protect meaningful behavior; skip tests for reversible, low-impact changes that merely mirror the implementation. Regressions fail on the original defect; shared-state failures use the original order. Review tests for value and duplication. Do not hide failures with retries, longer timeouts, weaker assertions, broader mocks, or altered baselines.
73
+ - Select proof for the touched contract and complete the chosen workflow's required gates within user/host limits. Command references do not mandate unrelated suites. Reuse valid proof; rerun for changed inputs or missing coverage. Docs-only work needs docs sanity and `git diff --check`. Report unrun checks and gaps.
74
+ - Prove user-visible behavior through the real flow when feasible; external API changes need live contract proof. A covering isolated mock-Gateway harness is valid channel boundary proof; live channel proof is stronger. State concrete capture or execution blockers.
75
+ - **UI screenshot completion/landing gate:** For user-visible UI changes, you **must** visibly attach inspected, sanitized before/after screenshots in the originating chat AND upload/embed them in the GitHub PR body or a PR comment linked from its body. Verify that the images render in both destinations before merging or claiming completion. Local files, private inspection, chat-only delivery, logs, tests, review approval, and artifact-manifest links are not substitutes. Galleries and videos supplement, never replace, the required screenshots. Failure in either destination requires approved recovery that satisfies this gate or an explicit delivery blocker; never silently downgrade or merge with delivery blocked. Only an explicit user waiver can exempt a destination.
76
+ - Before committing or landing nontrivial code, obtain fresh review through the permitted workflow and resolve actionable findings unless the user opts out. Tests protect observable contracts; a helper test can pass while the registered entry point never calls it.
77
+
78
+ ### Execution gotchas
79
+
80
+ These commands apply on the host permitted by the task and its workflow; they do
81
+ not authorize local execution or a broader test plan.
82
+
83
+ - Restore missing dependencies in a trusted normal checkout with `pnpm install`, then retry once before diagnosing a code defect. Never reconcile a shared/worktree install while other jobs use it.
84
+ - Run the CLI through `pnpm openclaw ...` or `pnpm dev`, never `node --import tsx src/index.ts`; the supported wrappers own build freshness and process setup.
85
+ - Use installed `oxfmt` for formatting and the repository's `tsgo` lanes for typechecking. Inspect scope with `pnpm changed:lanes --json`; use targeted tests/checks. When avoiding worktree reconciliation, use `node scripts/check-changed.mjs` or `node scripts/run-vitest.mjs` with ready dependencies. Host restrictions still apply.
86
+
87
+ ## Authority and safety
88
+
89
+ - Review/triage is read-only; mutations require task authority. Existing approval carries through the same scoped work and recovery. When new approval is required, complete the already-authorized preparation first and present a concrete, reviewable result; pause only the gated action. Product rejection remains maintainer judgment. Bulk close/reopen above 50 items needs explicit count and scope.
90
+ - Keep credentials, private data/config, and unreleased model identities out of commits and shared text, logs, transcripts, and media. Inspect outgoing content. Use synthetic fixtures and verified human credit; omit agent-attribution trailers.
91
+ - For permitted defensive work interrupted by a cyber classifier, use an approved, available Daybreak Blue route only for that bounded task, then restore the previous model. Preserve the approved identity, scope, permissions, sandbox, and review gates. Never switch models to bypass access denials, disallowed requests, or refused tool actions; if that route also refuses, stop and report. Follow [OpenAI's guidance](https://learn.chatgpt.com/docs/cyber-safety) for approved model access.
92
+ - Untrusted contributor/fork code runs only in secretless isolation, never locally. Source review alone does not authorize execution with credentials or on a trusted host; maintainer approval is required. An instruction to land named, reviewed PRs supplies that approval. Use the authorized isolation route and only task credentials.
93
+ - Modifying/restarting a Gateway or live state you did not create requires per-task approval. Tests use isolated state and ports; copy real data for migration tests. Destructive reset/clean, stash, or deletion of unrelated work needs authorization.
94
+ - Updating `team.openclaw.ai` must only happen by negotiating with Night Watch on `stable.openclaw.ai`, never directly.
95
+ - Explicit repair-and-land authority includes internal scheduling, database admission, and lifecycle implementation decisions. The agent owns design selection, risk assessment, and verification; do not request renewed approval for implementation decisions within that scope.
96
+ - Bug fixes within the authorized task do not need renewed approval, including compatible SDK changes needed to restore intended behavior. Ask again for new configuration options, breaking public contracts, intentional changes to schemas, durability, retention, or permissions beyond the bug fix, paid services, or destructive actions. Preserve FIFO ordering, live-authority and integrity checks, and settlement of write-capable work.
97
+ - Protocol/version bumps, dependency patches/overrides/vendor changes, paid services, releases, and publishing need explicit approval; fix/ship authority does not imply release authority. Advisory workflows require an explicit request for that security action.
98
+ - Extended-stable is one line: the trailing completed month relative to `main`'s version. Older `.33+` lines retire when `main` advances another month; publishing a retired line needs an explicit maintainer decision, not a routine guard bypass.
99
+ - Baseline, snapshot, ignore, and expected-failure exceptions need approval; exact shrink-only ratchet updates are maintenance.
100
+ - `CODEOWNERS` routes review; check live GitHub enforcement. Restricted/security paths and material product, behavior, security, or ownership changes need listed-owner involvement. For ownership/review governance, verified active organization-admin direction also qualifies; repository admin/bypass alone does not. Neither route waives enforced reviews.
101
+ - Complete the authorized workflow's review/merge gates; resolve substantive findings or explain rejections. Fix diff-caused failures and document proven unrelated failures separately. Verify remote outcomes before success or cleanup; uncertain writes require reconciliation, not blind retries.
102
+ - Stage only intended files and use concise Conventional Commits with verified author/writer identities. Preserve contributor credit; team-session credit requires consented, verified humans and its canonical backlink. A bare URL grants no public mutation authority. Keep PR bodies current with problem, solution, impact, and evidence; use body files/heredocs for shell-sensitive text.
103
+
104
+ ## Read when relevant
105
+
106
+ Read matching guides in full and follow their narrower task-specific pointers.
107
+ Commands and implementation detail stay with these owners.
108
+
109
+ - **Product/design:** [VISION.md](VISION.md).
110
+ - **Plugins/discovery/SDK:** [plugins](extensions/AGENTS.md), [loader](src/plugins/AGENTS.md), [SDK](src/plugin-sdk/AGENTS.md). The SDK guide owns public boundary expansion, including callers outside these trees.
111
+ - **Channels/message actions:** [channel boundary](src/channels/AGENTS.md) and [channel responsibilities](docs/plugins/sdk-channel-plugins.md).
112
+ - **Agent tools, prompts, admission, or lifecycle:** [agents](src/agents/AGENTS.md) and [Gateway](src/gateway/AGENTS.md).
113
+ - **Control UI state, requests, or presentation:** [UI guide](ui/AGENTS.md), including state shared with other Gateway clients.
114
+ - **Storage:** [database schemas](docs/reference/database-schemas.md), then its layout, versioning, and storage-changes pages for the affected contract. Read the approval checkpoint before changing schema, transactions, retention, or recovery.
115
+ - **Config retirement/migration:** [shared Doctor transforms and startup migration](docs/gateway/doctor/config-migrations.md); reuse this owner instead of new runtime compatibility readers.
116
+ - **Audit/identity/receipts:** [audit doctrine](docs/gateway/audit.md). Diagnostic provenance is opt-in and never authorization; changes to collection, reader scope, retained fields, bounds, or contracts require approval.
117
+ - **Codex-backed behavior:** personally inspect the exact sibling `../codex` source before implementation or verdict and cite it; wrappers, schemas, and another agent's report do not replace this check. Auth/runtime/catalog routes use `openai`; legacy `openai-codex` input belongs only in migration. Harness upgrades refresh [the harness guide](docs/plugins/codex-harness.md) from `model/list`.
118
+ - **Validation commands:** [test suites](docs/help/testing/suites.md) is a command reference; this file and the chosen workflow own check selection. Test authoring also uses [writing tests](docs/help/testing/writing-tests.md) and the owning scoped guide.
119
+ - **GitHub:** [contribution rules](CONTRIBUTING.md), the current PR template, and [review feedback](docs/reference/pull-request-review-flow.md). The authorized maintainer workflow owns landing; native `scripts/pr` gates, recovery, and cleanup require [scripts guide](scripts/AGENTS.md).
120
+ - **Docs/public links:** [docs guide](docs/AGENTS.md). Update docs with behavior; normal fix notes belong in PRs because `CHANGELOG.md` is release-owned.
121
+ - **Releases:** the chosen release workflow and [release contract](docs/reference/RELEASING.md). Preserve the selected release cut and identity through publication and verification. npm-format lock mirrors are verified against `pnpm-lock.yaml`, published in dependency evidence, and kept out of npm tarballs.
122
+ - **Secrets/advisories:** [secret semantics](docs/gateway/secrets.md), [auth semantics](docs/auth-credential-semantics.md), and [security reporting](SECURITY.md) for the affected branch.
123
+ - **Live channels/native apps:** the owning scoped guide and permitted proof workflow. Telegram claims require Test Server userbot proof with Convex-leased credentials; platform claims require the relevant real device/platform evidence. Mac permission proof needs a stable, properly signed app; see [signing](docs/platforms/mac/signing.md).
CONTRIBUTING.md ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to OpenClaw
2
+
3
+ Welcome to the lobster tank! 🦞
4
+
5
+ ## Quick Links
6
+
7
+ - **GitHub:** https://github.com/openclaw/openclaw
8
+ - **Vision:** [`VISION.md`](VISION.md)
9
+ - **Discord:** https://discord.gg/clawd
10
+ - **X/Twitter:** [@openclaw](https://x.com/openclaw)
11
+
12
+ ## Maintainers
13
+
14
+ The current OpenClaw Foundation team and Core Maintainers are listed on the
15
+ OpenClaw people page: https://www.openclaw.org/people
16
+
17
+ ## How to Contribute
18
+
19
+ 1. **Bugs & small fixes** → Open a PR!
20
+ 2. **New features / architecture** → Start a [GitHub Issue](https://github.com/openclaw/openclaw/issues/new/choose) or ask in Discord first. Most features are not accepted and should be third party plugins instead using our plugin SDK.
21
+ 3. **Refactor-only PRs** → Don't open a PR. We are not accepting refactor-only changes unless a maintainer explicitly asks for them as part of a concrete fix.
22
+ 4. **Test/CI-only PRs for known `main` failures** → Don't open a PR. The Maintainer team is already tracking those failures, and PRs that only tweak tests or CI to chase them will be closed unless they are required to validate a new fix.
23
+ 5. **Questions** → Discord [#help](https://discord.com/channels/1456350064065904867/1459642797895319552) / [#users-helping-users](https://discord.com/channels/1456350064065904867/1459007081603403828)
24
+
25
+ ## Issue, PR, and Contact Routing
26
+
27
+ Start from this routing map before creating GitHub items:
28
+
29
+ | Situation | Use | Required evidence |
30
+ | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
31
+ | Product bug, regression, crash, or behavior defect | [Bug report](https://github.com/openclaw/openclaw/issues/new?template=bug_report.yml) | Repro steps, expected vs actual behavior, version, OS, model/provider route when relevant, logs/screenshots, impact |
32
+ | Documentation bug or missing/contradictory docs | [Docs bug report](https://github.com/openclaw/openclaw/issues/new?template=docs_bug_report.yml) | Affected docs path or URL, verification steps, expected docs content, actual docs content, impact, evidence |
33
+ | New feature, architecture change, or product improvement | [Feature request](https://github.com/openclaw/openclaw/issues/new?template=feature_request.yml) or Discord first | Problem, proposed solution, alternatives, impact, examples or prior art |
34
+ | Onboarding, setup help, or general support question | Discord [#help](https://discord.com/channels/1456350064065904867/1459642797895319552) / [#users-helping-users](https://discord.com/channels/1456350064065904867/1459007081603403828) | Do not open a GitHub issue unless there is a concrete product defect or docs gap |
35
+ | Security vulnerability | See [Report a Vulnerability](#report-a-vulnerability) below | Do not file public issues for private security reports |
36
+ | PR for an existing or newly filed issue | Use the [PR template](.github/pull_request_template.md) | Visible `Closes #<issue>` or `Related: #<issue>`, problem, shipped solution, user impact, validation evidence |
37
+
38
+ For agent-authored or otherwise non-trivial work, create or reuse the issue first, then open the PR against it. Bugs and very small fixes may go straight to PR, but still link existing context when it exists and fill out the PR template.
39
+
40
+ Do not guess who to tag. Let issue forms, labels/automation, and `.github/CODEOWNERS` route the work. Mention a maintainer only when an owned path or documented responsibility is directly relevant and you need a decision; otherwise rely on normal review. For coordinated change sets, ask in **#clawtributors** before opening more than the PR limit.
41
+
42
+ ## PR Limits
43
+
44
+ We cap at **20 open PRs per author**. If you exceed this, the `r: too-many-prs` label is added and your PR is auto-closed. This is a hard limit.
45
+
46
+ For coordinated change sets that genuinely need more than 20 PRs, join the **#clawtributors** channel in Discord and talk to maintainers first.
47
+
48
+ ## Source dependencies
49
+
50
+ Run `pnpm install --frozen-lockfile` from the workspace root. Source checkouts use
51
+ pnpm's isolated linker, which keeps dependencies in `node_modules/.pnpm` and links
52
+ them into each workspace package. On supported macOS volumes, this also lets pnpm
53
+ reuse whole-package APFS clones instead of importing every file separately.
54
+
55
+ Give each source checkout its own physical dependency installation. Tooling does
56
+ not automatically link a missing `node_modules` to another checkout. Existing
57
+ borrowed installs can still serve direct Node tooling. Normal pnpm install checks
58
+ the checkout-root `node_modules`, the explicitly configured root module directory,
59
+ and their `.pnpm` directories before reconciliation, refusing borrowed links there.
60
+ Preserve that donor and create an independently owned install instead of removing
61
+ or reinstalling through its link. Explicit hydrated module directories remain
62
+ supported when the workspace link points to the configured physical directory.
63
+ This admission check runs through `pnpm:devPreinstall`; `--ignore-scripts` skips
64
+ it. The check does not lock paths against concurrent replacement, inspect every
65
+ workspace package's dependencies, or validate every alternate pnpm directory setting.
66
+
67
+ When updating a checkout that used the hoisted layout, stop builds, tests, and
68
+ watchers using that checkout's dependencies before running the install command.
69
+ Do not change the linker while other jobs are using the same `node_modules`.
70
+ Declare dependencies in the package that imports them; root tooling and tests
71
+ must declare their own development dependencies rather than rely on hoisting.
72
+
73
+ ## Before You PR
74
+
75
+ - Use **Node 24.16+ LTS** or **Node 26.1+** for source checkouts. Older Node releases can truncate SQLite TEXT reads; Node 22, 23, and 25 are unsupported. See [Node install guidance](docs/install/node.md) if your local version is too old.
76
+ - Run the Vitest 5 suite on Node 24.16+ or Node 26.1+, matching the packaged runtime floor.
77
+ - Test locally with your OpenClaw instance
78
+ - An explicit maintainer repair-and-land request covers internal database scheduling, admission, and lifecycle decisions. The implementer owns the design and its verification. Get separate design acceptance when changing public contracts, schemas, durability, retention, or permissions; see the [database schema review checkpoint](docs/reference/database-schemas.md#review-checkpoint-for-material-changes).
79
+ - External PRs must describe the user, product, or operational problem in **What Problem This Solves** and include useful validation in **Evidence**. Focused tests, CI results, screenshots, recordings, terminal output, live observations, redacted logs, and artifact links all count. Reviewers will inspect the code, tests, and CI; use the PR body to explain intent and make validation easy to understand.
80
+ - Follow the [PR template](.github/pull_request_template.md): lead with the plain-language problem and concrete user impact, then a brief explanation and useful evidence. Keep technical inventories in the diff or optional details, not the opening summary. Keep important risks, migrations, required actions, and evidence gaps visible; do not invent a user benefit for internal-only work.
81
+ - When ClawSweeper, Barnacle, or a maintainer asks for more context or evidence, edit the PR description instead of only replying in a new comment. Keep **What Problem This Solves**, **User Impact**, **Why This Change Was Made**, and **Evidence** current; a short comment can point reviewers to the update, but the PR body should remain the durable explanation for maintainers and bots.
82
+ - Keep PRs takeover-ready: open them from a branch maintainers can push to. For fork PRs, leave GitHub's **Allow edits by maintainers** option enabled so maintainers can finish urgent fixes or merge prep when needed. If GitHub shows **Allow edits and access to secrets by maintainers**, enable it only when that workflow/secrets access is acceptable and say so in the PR.
83
+ - Do not edit the generated `CHANGELOG.md` index or release-owned `CHANGELOG/**` entries and contribution records in normal PRs or at merge. Initial changelogs are generated at release time from merged PRs and commits; keep release-note context in PR bodies or commit messages until then. Explicit release-docs publication changes follow the [release artifact procedure](docs/reference/RELEASING.md#release-changelog-artifacts).
84
+ - Run tests: `pnpm build && pnpm check && pnpm test`
85
+ - For iterative local commits after running equivalent targeted validation for the touched surface, `git commit --no-verify` skips commit hooks.
86
+ - For extension/plugin changes, run the fast local lane first:
87
+ - `pnpm test:extension <extension-name>`
88
+ - `pnpm test:extension --list` to see valid extension ids
89
+ - If you changed shared plugin or channel surfaces, run `pnpm test:contracts`
90
+ - For targeted shared-surface work, use `pnpm test:contracts:channels` or `pnpm test:contracts:plugins`
91
+ - These commands also cover the shared seam/smoke files that the default unit lane skips
92
+ - If you changed broader runtime behavior, still run the relevant wider lanes (`pnpm test:extensions`, `pnpm test:channels`, or `pnpm test`) before asking for review
93
+ - If you touched bundled-plugin boundaries in shared code, run the matching inventories:
94
+ - `node --import tsx scripts/check-src-extension-import-boundary.mts --json` for `src/**`
95
+ - `node --import tsx scripts/check-sdk-package-extension-import-boundary.mts --json` for `src/plugin-sdk/**` and `packages/**`
96
+ - `node --import tsx scripts/check-test-helper-extension-import-boundary.mts --json` for `test/helpers/**`
97
+ - Shared test helpers must use `src/test-utils/bundled-plugin-public-surface.ts` instead of repo-relative `extensions/**` imports. Keep plugin-local deep mocks inside the owning bundled plugin package.
98
+ - If you are using an AI coding agent with OpenClaw skills available, run the `autoreview` skill before opening or updating your PR. Address accepted/actionable findings before asking for review.
99
+ - Do not submit refactor-only PRs unless a maintainer explicitly requested that refactor for an active fix or deliverable.
100
+ - Do not submit test or CI-config fixes for failures already red on `main` CI. If a failure is already visible in the [main branch CI runs](https://github.com/openclaw/openclaw/actions), it's a known issue the Maintainer team is tracking, and a PR that only addresses those failures will be closed automatically. If you spot a _new_ regression not yet shown in main CI, report it as an issue first.
101
+ - Do not submit test-only PRs that just try to make known `main` CI failures pass. Test changes are acceptable when they are required to validate a new fix or cover new behavior in the same PR.
102
+ - Ensure CI checks pass
103
+ - Keep PRs focused (one thing per PR; do not mix unrelated concerns)
104
+ - Describe what & why
105
+ - **Include screenshots** — one showing the problem/before, one showing the fix/after (for UI or visual changes)
106
+ - Use American English spelling and grammar in code, comments, docs, and UI strings
107
+ - Do not edit files covered by `CODEOWNERS` security ownership unless a listed owner authored or explicitly requested the change, or is already reviewing it with you. For governance changes to ownership/review policy itself, explicit direction from an organization owner is also sufficient only when live GitHub organization membership shows `state: active` and `role: admin`; repository `ADMIN`, `viewerCanAdminister`, or bypass permission alone never qualifies. Neither route waives a GitHub-enforced approval rule. Treat those paths as restricted review surfaces, not opportunistic cleanup targets.
108
+
109
+ ## Local commit hook
110
+
111
+ The normal `pnpm install` setup enables the repository's pre-commit formatting hook
112
+ when `core.hooksPath` is unset. Existing hook selections, including an explicitly
113
+ empty value, are preserved. Git scopes initialization to the current checkout.
114
+ With multiple worktrees, automatic setup requires `extensions.worktreeConfig`;
115
+ otherwise Git reports a warning and installation continues without changing hook
116
+ settings. The repository owner can enable per-worktree configuration following
117
+ [Git's configuration guidance](https://git-scm.com/docs/git-worktree#_configuration_file).
118
+
119
+ The hook's optional content guard reads a private UTF-8 file selected by
120
+ the native Git setting `hooks.blockedLiteralsFile`. Keep one literal per nonempty
121
+ line in a file outside the checkout, such as
122
+ `~/.config/openclaw/blocked-literals.txt`, then configure this checkout:
123
+
124
+ ```bash
125
+ git config --local hooks.blockedLiteralsFile "$HOME/.config/openclaw/blocked-literals.txt"
126
+ ```
127
+
128
+ Git metadata is another safe untracked location for the private file. Never put
129
+ private rule contents in tracked files or PRs. With no setting, the content guard
130
+ is disabled and formatting runs normally; a configured empty path or missing,
131
+ unreadable, empty, or invalid file blocks the commit.
132
+
133
+ When configured, the guard checks case-sensitive literal substrings before
134
+ formatting and again after formatting restages files. Each scan checks the full
135
+ staged contents of added, modified, and type-changed files, including rename
136
+ destinations and unchanged lines within modified files. Docs, tests, generated
137
+ files, and binary files are included; no tracked file is exempt.
138
+
139
+ If the hook blocks a commit, remove the matching content and restage the reported
140
+ files. Unchanged historical files and deletions are not scanned. Submodule contents
141
+ and symlink targets are not searched. This is a local safeguard, not CI or server
142
+ enforcement: bypassing or disabling hooks also bypasses this check.
143
+
144
+ ## Review Conversations Are Author-Owned
145
+
146
+ After your PR receives Barnacle, ClawSweeper, or maintainer feedback, read the [pull request review flow](https://docs.openclaw.ai/reference/pull-request-review-flow) for how to interpret rank-up moves, proof guidance, re-review requests, and review conversation follow-up.
147
+
148
+ ## Control UI Decorators
149
+
150
+ The Control UI uses Lit with **legacy** decorators (current Rollup parsing does not support
151
+ `accessor` fields required for standard decorators). When adding reactive fields, keep the
152
+ legacy style:
153
+
154
+ ```ts
155
+ @state() foo = "bar";
156
+ @property({ type: Number }) count = 0;
157
+ ```
158
+
159
+ The root `tsconfig.json` is configured for legacy decorators (`experimentalDecorators: true`)
160
+ with `useDefineForClassFields: false`. Avoid flipping these unless you are also updating the UI
161
+ build tooling to support standard decorators.
162
+
163
+ ## AI/Vibe-Coded PRs Welcome! 🤖
164
+
165
+ Built with Codex, Claude, or other AI tools? **Welcome!** No AI-assistance label or disclosure is required.
166
+
167
+ Please include in your PR:
168
+
169
+ - [ ] Include a concise **Evidence** section with the most useful validation. Reviewers will inspect the code, tests, and CI rather than relying on the PR body alone.
170
+ - [ ] Confirm you understand what the code does
171
+ - [ ] Run the `autoreview` skill when available and address accepted/actionable findings
172
+ - [ ] Follow the [pull request review flow](https://docs.openclaw.ai/reference/pull-request-review-flow) after Barnacle, ClawSweeper, or maintainer feedback
173
+
174
+ AI PRs are first-class citizens here and follow the same quality and review standards as any other PR.
175
+
176
+ ## Current Focus & Roadmap 🗺
177
+
178
+ We are currently prioritizing:
179
+
180
+ - **Stability**: Fixing edge cases in channel connections (WhatsApp/Telegram).
181
+ - **UX**: Improving the onboarding wizard and error messages.
182
+ - **Skills**: For skill contributions, head to [ClawHub](https://clawhub.ai/) — the community hub for OpenClaw skills.
183
+ - **Performance**: Optimizing token usage and compaction logic.
184
+
185
+ Check the [GitHub Issues](https://github.com/openclaw/openclaw/issues) for
186
+ ["good first issue"](https://github.com/openclaw/openclaw/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
187
+ labels. If none are open, pick a small docs or bug issue and leave a quick comment saying
188
+ you'd like to work on it.
189
+
190
+ ## Maintainers
191
+
192
+ We're selectively expanding the maintainer team.
193
+ If you're an experienced contributor who wants to help shape OpenClaw's direction — whether through code, docs, or community — we'd like to hear from you.
194
+
195
+ Being a maintainer is a responsibility, not an honorary title. We expect active, consistent involvement — triaging issues, reviewing PRs, and helping move the project forward.
196
+
197
+ Still interested? Email contributing@openclaw.ai with:
198
+
199
+ - Links to your PRs on OpenClaw (if you don't have any, start there first)
200
+ - Links to open source projects you maintain or actively contribute to
201
+ - Your GitHub, Discord, and X/Twitter handles
202
+ - A brief intro: background, experience, and areas of interest
203
+ - Languages you speak and where you're based
204
+ - How much time you can realistically commit
205
+
206
+ We welcome people across all skill sets — engineering, documentation, community management, and more.
207
+ We review every human-only-written application carefully and add maintainers slowly and deliberately.
208
+ Please allow a few weeks for a response.
209
+
210
+ ## Report a Vulnerability
211
+
212
+ We take security reports seriously. Report vulnerabilities directly to the repository where the issue lives:
213
+
214
+ - **Core CLI and gateway** — [openclaw/openclaw](https://github.com/openclaw/openclaw)
215
+ - **macOS desktop app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/macos)
216
+ - **iOS app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/ios)
217
+ - **Android app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/android)
218
+ - **ClawHub** — [openclaw/clawhub](https://github.com/openclaw/clawhub)
219
+
220
+ For issues that don't fit a specific repo, or if you're unsure, email **security@openclaw.ai** and we'll route it.
221
+
222
+ ### Required in Reports
223
+
224
+ 1. **Title**
225
+ 2. **Severity Assessment**
226
+ 3. **Impact**
227
+ 4. **Affected Component**
228
+ 5. **Technical Reproduction**
229
+ 6. **Demonstrated Impact**
230
+ 7. **Environment**
231
+ 8. **Remediation Advice**
232
+
233
+ Reports without reproduction steps, demonstrated impact, and remediation advice will be deprioritized. Given the volume of AI-generated scanner findings, we must ensure we're receiving vetted reports from researchers who understand the issues.
SECURITY.md ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Security Policy
2
+
3
+ If you believe you've found a security issue in OpenClaw, report it privately first.
4
+
5
+ This policy does two things: it gives researchers a clear disclosure path, and it spells out the trust model maintainers use when triaging reports. OpenClaw is local-first agent infrastructure for trusted operators; it is not designed as a shared multi-tenant boundary between adversarial users on one gateway.
6
+
7
+ The fastest useful reports show a current, reproducible boundary bypass with demonstrated impact. Scanner output, prompt-injection-only chains, or reports that rely on hostile users sharing one trusted gateway are usually not security vulnerabilities under this model.
8
+
9
+ Security work is shared across a number of OpenClaw maintainers, including engineers and security researchers from organizations such as NVIDIA and Tencent. See the [maintainer list](CONTRIBUTING.md#maintainers).
10
+
11
+ ## Shared Agents
12
+
13
+ Anyone who can operate an agent can make it do anything that agent can do. Session ownership, visibility, and presence are usability features, not security boundaries. Turn attribution is best-effort because steering can merge input into an active turn. Use separate agents or separate gateway/host trust boundaries when operators need real isolation.
14
+
15
+ ## Report a Security Issue
16
+
17
+ Report vulnerabilities directly to the repository where the issue lives:
18
+
19
+ - **Core CLI and gateway** — [openclaw/openclaw](https://github.com/openclaw/openclaw)
20
+ - **macOS desktop app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/macos)
21
+ - **iOS app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/ios)
22
+ - **Android app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/android)
23
+ - **ClawHub** — [openclaw/clawhub](https://github.com/openclaw/clawhub)
24
+
25
+ For issues that don't fit a specific repo, or if you're unsure, email **[security@openclaw.ai](mailto:security@openclaw.ai)** and we'll route it.
26
+
27
+ For OpenClaw core issues, submit through a private [GitHub Security Advisory](https://github.com/openclaw/openclaw/security/advisories/new). Do not open a public issue or PR that discloses an unpatched vulnerability, exploit path, secret, or security-sensitive proof of concept.
28
+
29
+ Maintainers may close, hide, delete, or otherwise take down public issues and PRs that disclose vulnerabilities or active security issues. We will redirect those reports through the private disclosure process so the issue can be triaged and fixed without giving attackers a public playbook.
30
+
31
+ For full reporting instructions see our [Trust page](https://trust.openclaw.ai).
32
+ For maintainer response workflow, see the [incident response plan](docs/security/incident-response.md).
33
+
34
+ OpenClaw does not currently run a paid bug bounty program. Please still disclose responsibly so we can fix real issues quickly. The best way to help the project right now is to send high-signal reports and, when practical, focused PRs.
35
+
36
+ ### What We Need
37
+
38
+ Make the report easy to reproduce and easy to route:
39
+
40
+ - What you found and why you believe it is security-relevant.
41
+ - The affected component, version, and commit SHA when possible.
42
+ - Reproduction steps or a proof of concept against latest `main` or the latest released version.
43
+ - The actual impact, including which OpenClaw trust boundary is crossed.
44
+ - Any remediation advice or focused patch you can provide.
45
+
46
+ Reports without reproduction steps, demonstrated impact, and remediation advice are deprioritized. We receive a high volume of AI-generated scanner findings, so we prioritize vetted reports from researchers who can show how the issue crosses an OpenClaw security boundary.
47
+
48
+ ### What Usually Is Not a Security Bug
49
+
50
+ These patterns are usually not vulnerabilities by themselves:
51
+
52
+ - Prompt injection without a policy, auth, approval, sandbox, or tool-boundary bypass.
53
+ - A trusted operator using an intentional local feature, such as local shell access or browser/script execution.
54
+ - A report whose only primitive is changing the process or child-process environment before running OpenClaw or an executable OpenClaw invokes.
55
+ - A malicious plugin after a trusted operator installs or enables it.
56
+ - Multiple adversarial users sharing one Gateway host/config and expecting per-user isolation.
57
+ - Scanner-only, dependency-only, or stale-path reports without a working repro and demonstrated OpenClaw impact.
58
+ - Public internet exposure or risky deployment choices that the docs already recommend against.
59
+
60
+ If you are unsure, report privately. We would rather route a careful report than miss a real boundary issue.
61
+
62
+ ### Duplicate Report Handling
63
+
64
+ - Search existing advisories before filing.
65
+ - Include likely duplicate GHSA IDs in your report when applicable.
66
+ - Maintainers may close lower-quality/later duplicates in favor of the earliest high-quality canonical report.
67
+
68
+ ## Security Posture and Report Rules
69
+
70
+ The sections below are the normative posture maintainers use for report triage. The headings are editorial; the policy text defines the boundary.
71
+
72
+ ### Detailed Report Acceptance Gate
73
+
74
+ For fastest triage, include all of the following:
75
+
76
+ - Exact vulnerable path (`file`, function, and line range) on a current revision.
77
+ - Tested version details (OpenClaw version and/or commit SHA).
78
+ - Reproducible PoC against latest `main` or latest released version.
79
+ - If the claim targets a released version, evidence from the shipped tag and published artifact/package for that exact version (not only `main`).
80
+ - For dependency CVE reports, evidence that the shipped dependency version is actually affected, plus a PoC that reproduces impact through OpenClaw. Showing that OpenClaw can reach a native parser is not enough by itself.
81
+ - Demonstrated impact tied to OpenClaw's documented trust boundaries.
82
+ - For exposed-secret reports: proof the credential is OpenClaw-owned (or grants access to OpenClaw-operated infrastructure/services).
83
+ - Explicit statement that the report does not rely on adversarial operators sharing one gateway host/config.
84
+ - Scope check explaining why the report is **not** covered by the Out of Scope section below.
85
+ - For command-risk/parity reports (for example obfuscation detection differences), a concrete boundary-bypass path is required (auth/approval/allowlist/sandbox). Parity-only findings are treated as hardening, not vulnerabilities.
86
+
87
+ Reports that miss these requirements may be closed as `invalid` or `no-action`.
88
+
89
+ ### Detailed False-Positive Patterns
90
+
91
+ These are frequently reported but are typically closed with no code change:
92
+
93
+ - Prompt-injection-only chains without a boundary bypass (prompt injection is out of scope).
94
+ - Operator-intended local features (for example TUI local `!` shell) presented as remote injection.
95
+ - Reports that treat explicit operator-control surfaces (for example 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.
96
+ - Reports that treat an admin-gated enablement or arming step as requiring `operator.admin` for every subsequent action, when the documented contract delegates use of the enabled capability to `operator.write` and no auth, arming, allowlist, sandbox, or policy bypass is shown. This is an arm-then-use operator guardrail, not privilege escalation.
97
+ - 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.
98
+ - Reports that only show a malicious plugin executing privileged actions after a trusted operator installs/enables it.
99
+ - Reports that assume per-user multi-tenant authorization on a shared gateway host/config.
100
+ - Reports that only show quoted/replied/thread/forwarded supplemental context from non-allowlisted senders being visible to the model, without demonstrating an auth, policy, approval, or sandbox boundary bypass.
101
+ - Reports that treat the Gateway HTTP compatibility endpoints (`POST /v1/chat/completions`, `POST /v1/responses`) as if they implemented scoped operator auth (`operator.write` vs `operator.admin`). These endpoints authenticate the shared Gateway bearer secret/password and are documented full operator-access surfaces, not per-user/per-scope boundaries.
102
+ - Reports that assume `x-openclaw-scopes` can reduce or redefine shared-secret bearer auth on the OpenAI-compatible HTTP endpoints. For shared-secret auth (`gateway.auth.mode="token"` or `"password"`), those endpoints ignore narrower bearer-declared scopes and restore the full default operator scope set plus owner semantics.
103
+ - Reports that treat `POST /tools/invoke` under shared-secret bearer auth (`gateway.auth.mode="token"` or `"password"`) as a narrower per-request/per-scope authorization surface. That endpoint is designed as the same trusted-operator HTTP boundary: shared-secret bearer auth is full operator access there, narrower `x-openclaw-scopes` values do not reduce that path, and owner-only tool policy follows the shared-secret operator contract.
104
+ - 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.
105
+ - Reports that only show an ACP tool can indirectly execute, mutate, orchestrate sessions, or reach another tool/runtime without demonstrating bypass of ACP prompt/approval, allowlist enforcement, sandboxing, or another documented trust boundary. ACP silent approval is intentionally limited to narrow readonly classes; parity-only indirect-command findings are hardening, not vulnerabilities.
106
+ - Reports that only show untrusted media bytes reaching a maintained native decoder dependency (for example image codec libraries such as libheif) without proving the shipped dependency version is vulnerable and demonstrating crash, memory corruption, data exposure, or a boundary bypass through OpenClaw. JavaScript header sniffing and image dimension fast-paths are preflight/UX checks, not the security boundary for native decoder correctness.
107
+ - Reports whose only impact is transient extra memory, CPU, or allocation work from decoding, base64 expansion, media transcoding, serialization, or other format conversion after the input was already accepted under OpenClaw's configured size/trust limits, including base64 decode-before-size-estimate findings. These are performance issues, not vulnerabilities, unless the report demonstrates unauthenticated amplification, bypass of configured limits, crash/process termination, persistent resource exhaustion, data exposure, or another documented boundary bypass.
108
+ - ReDoS/DoS claims that require trusted operator configuration input (for example catastrophic regex in `sessionFilter` or `logging.redactPatterns`) without a trust-boundary bypass.
109
+ - 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.
110
+ - 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.
111
+ - Reports that depend on attacker-controlled environment variables changing executable behavior, including variables that redirect lookup paths, preload code, select wrappers/interpreters, alter package-manager or runtime hooks, or make one executable call another executable. Control of the process or child-process environment is trusted host/operator control in OpenClaw's model; these reports need a separate OpenClaw boundary bypass that lets untrusted input set or mutate that environment.
112
+ - 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.
113
+ - Missing HSTS findings on default local/loopback deployments.
114
+ - Reports against test-only harnesses, QA Lab, QE Lab, E2E fixtures, benchmark rigs, or maintainer-only debugging tools when the vulnerable code is not shipped as a supported production surface.
115
+ - Slack webhook signature findings when HTTP mode already uses signing-secret verification.
116
+ - Discord inbound webhook signature findings for paths not used by this repo's Discord integration.
117
+ - Claims that Microsoft Teams `fileConsent/invoke` `uploadInfo.uploadUrl` is attacker-controlled without demonstrating one of: auth boundary bypass, a real authenticated Teams/Bot Framework event carrying attacker-chosen URL, or compromise of the Microsoft/Bot trust path.
118
+ - Scanner-only claims against stale/nonexistent paths, or claims without a working repro.
119
+ - Reports that restate an already-fixed issue against later released versions without showing the vulnerable path still exists in the shipped tag or published artifact for that later version.
120
+ - SSRF reports against the operator-managed HTTP/WebSocket proxy-routing feature whose only claim is that ordinary process-local HTTP clients (`fetch`, `node:http`, `node:https`, WebSocket clients, axios/got/node-fetch-style clients) can reach an internal, metadata, private, or otherwise sensitive destination when proxy routing is disabled, missing, or the operator-managed proxy policy allows it. For this feature, OpenClaw provides fail-closed proxy routing when enabled; the external proxy's destination policy is operator infrastructure, not an OpenClaw-controlled security boundary. See [Network proxy](https://docs.openclaw.ai/security/network-proxy).
121
+
122
+ ### Maintainer GHSA Updates via CLI
123
+
124
+ When patching a GHSA via `gh api`, include `X-GitHub-Api-Version: 2022-11-28` (or newer). Without it, some fields (notably CVSS) may not persist even if the request returns 200.
125
+
126
+ ### Operator Trust Model
127
+
128
+ OpenClaw does **not** model one gateway as a multi-tenant, adversarial user boundary.
129
+
130
+ - Authenticated Gateway callers are treated as trusted operators for that gateway instance.
131
+ - Direct localhost/loopback Control UI and Gateway WebSocket sessions authenticated with the shared gateway secret (`token` / `password`) are in that same trusted-operator bucket. Local auto-paired device sessions on that path are expected to retain full localhost operator capability; they do not create a separate `operator.write` vs `operator.admin` security boundary.
132
+ - The HTTP compatibility endpoints (`POST /v1/chat/completions`, `POST /v1/responses`) and direct tool endpoint (`POST /tools/invoke`) are in that same trusted-operator bucket. Passing Gateway bearer auth there is equivalent to operator access for that gateway; they do not implement a narrower `operator.write` vs `operator.admin` trust split.
133
+ - Concretely, on the OpenAI-compatible HTTP surface:
134
+ - shared-secret bearer auth (`token` / `password`) authenticates possession of the gateway operator secret
135
+ - those requests receive the full default operator scope set (`operator.admin`, `operator.read`, `operator.write`, `operator.approvals`, `operator.pairing`)
136
+ - chat-turn endpoints (`/v1/chat/completions`, `/v1/responses`) also treat those shared-secret callers as owner senders for owner-only tool policy
137
+ - `POST /tools/invoke` follows that same shared-secret rule and also treats those callers as owner senders for owner-only tool policy
138
+ - narrower `x-openclaw-scopes` headers are ignored for that shared-secret path
139
+ - only identity-bearing HTTP modes (for example trusted proxy auth or `gateway.auth.mode="none"` on private ingress) honor declared per-request operator scopes
140
+ - Session identifiers (`sessionKey`, session IDs, labels) are routing controls, not per-user authorization boundaries.
141
+ - If one operator can view data from another operator on the same gateway, that is expected in this trust model.
142
+ - OpenClaw can technically run multiple gateway instances on one machine, but recommended operations are clean separation by trust boundary.
143
+ - Recommended mode: one user per machine/host (or VPS), one gateway for that user, and one or more agents inside that gateway.
144
+ - If multiple users need OpenClaw, use one VPS (or host/OS user boundary) per user.
145
+ - For advanced setups, multiple gateways on one machine are possible, but only with strict isolation and are not the recommended default.
146
+ - Exec behavior is host-first by default: `agents.defaults.sandbox.mode` defaults to `off`.
147
+ - `tools.exec.host` defaults to `auto`: sandbox when sandbox runtime is active for the session, otherwise gateway.
148
+ - Implicit exec calls (no explicit host in the tool call) follow the same behavior.
149
+ - This is expected in OpenClaw's one-user trusted-operator model. If you need isolation, enable sandbox mode (`non-main`/`all`) and keep strict tool policy.
150
+
151
+ ### Trusted Plugins
152
+
153
+ Plugins/extensions are part of OpenClaw's trusted computing base for a gateway.
154
+
155
+ - Installing or enabling a plugin grants it the same trust level as local code running on that gateway host.
156
+ - Plugin behavior such as reading env/files or running host commands is expected inside this trust boundary.
157
+ - Security reports must show a boundary bypass (for example unauthenticated plugin load, allowlist/policy bypass, or sandbox/path-safety bypass), not only malicious behavior from a trusted-installed plugin.
158
+
159
+ ### Out of Scope
160
+
161
+ - Public Internet Exposure
162
+ - Using OpenClaw in ways that the docs recommend not to
163
+ - Test-only code and maintainer harnesses, including QA Lab, QE Lab, E2E fixtures, benchmark rigs, smoke-test containers, and local debugging proxies, unless the report demonstrates that the same vulnerable behavior is reachable from shipped OpenClaw production code or a published package artifact intended for users.
164
+ - Deployments where mutually untrusted/adversarial operators share one gateway host and config (for example, reports expecting per-operator isolation for `sessions.list`, `sessions.preview`, `chat.history`, or similar control-plane reads)
165
+ - Prompt-injection-only attacks (without a policy/auth/sandbox boundary bypass)
166
+ - Reports that require write access to trusted local state (`~/.openclaw`, workspace files like `MEMORY.md` / `memory/*.md`)
167
+ - 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.
168
+ - 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.
169
+ - 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.
170
+ - Reports whose only claim is environment-variable-driven executable behavior change, including path lookup changes, preload hooks, wrapper/interpreter selection, package-manager/runtime hooks, or variables that make an executable invoke another executable, unless a separate OpenClaw boundary bypass lets untrusted input set or mutate that environment.
171
+ - 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
172
+ - Reports whose only claim is use of an explicit trusted-operator control surface (for example browser evaluate/script execution or direct `node.invoke` execution) without demonstrating an auth, policy, allowlist, approval, or sandbox bypass.
173
+ - Reports where the only claim is that a trusted-installed/enabled plugin can execute with gateway/host privileges (documented trust model behavior).
174
+ - Any report whose only claim is that an operator-enabled `dangerous*`/`dangerously*` config option weakens defaults (these are explicit break-glass tradeoffs by design)
175
+ - 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.
176
+ - Reports whose only claim is heuristic/parity drift in command-risk detection (for example obfuscation-pattern checks) across exec surfaces, without a demonstrated trust-boundary bypass. These are hardening-only findings and are not vulnerabilities; triage may close them as `invalid`/`no-action` or track them separately as low/informational hardening.
177
+ - Reports whose only claim is that an ACP-exposed tool can indirectly execute commands, mutate host state, or reach another privileged tool/runtime without demonstrating a bypass of ACP prompt/approval, allowlist enforcement, sandboxing, or another documented trust boundary. These are hardening-only findings, not vulnerabilities.
178
+ - Reports whose only claim is that exec approvals do not semantically model every interpreter/runtime loader form, subcommand, flag combination, package script, or transitive module/config import. Exec approvals bind exact request context and best-effort direct local file operands; they are not a complete semantic model of everything a runtime may load.
179
+ - Reports whose only claim is parser reachability in an up-to-date maintained dependency without showing that the exact shipped dependency build is vulnerable. We keep native media dependencies current; dependency exposure alone is not a vulnerability.
180
+ - Reports whose only claim is resource overhead from decode/encode, base64 expansion, media transcoding, serialization, or format-conversion order after input has already passed the applicable configured acceptance limits, including base64 decode-before-size-estimate findings. These are performance-only and should be ignored for GHSA triage unless the report demonstrates unauthenticated amplification, limit bypass, crash/process termination, persistent exhaustion, data exposure, or another documented boundary bypass.
181
+ - Exposed secrets that are third-party/user-controlled credentials (not OpenClaw-owned and not granting access to OpenClaw-operated infrastructure/services) without demonstrated OpenClaw impact
182
+ - Reports whose only claim is host-side exec when sandbox runtime is disabled/unavailable (documented default behavior in the trusted-operator model), without a boundary bypass.
183
+ - Reports whose only claim is that a platform-provided upload destination URL is untrusted (for example Microsoft Teams `fileConsent/invoke` `uploadInfo.uploadUrl`) without proving attacker control in an authenticated production flow.
184
+ - SSRF reports limited to the operator-managed HTTP/WebSocket proxy-routing feature where the demonstrated mitigation is to enable/configure `proxy.enabled` with a filtering `proxy.proxyUrl`/`OPENCLAW_PROXY_URL`, or where impact depends on a permissive/misconfigured operator proxy. This only covers normal process-local HTTP(S)/WebSocket egress (`fetch`, Node HTTP(S), and similar JavaScript clients); non-HTTP egress and other features are assessed separately. See [Network proxy](https://docs.openclaw.ai/security/network-proxy).
185
+
186
+ ### Deployment Assumptions
187
+
188
+ OpenClaw security guidance assumes:
189
+
190
+ - The host where OpenClaw runs is within a trusted OS/admin boundary.
191
+ - Anyone who can set or mutate the OpenClaw process environment, launcher environment, or child-process environment is inside that trusted host/operator boundary.
192
+ - Anyone who can modify `~/.openclaw` state/config (including `openclaw.json`) is effectively a trusted operator.
193
+ - A single Gateway shared by mutually untrusted people is **not a recommended setup**. Use separate gateways (or at minimum separate OS users/hosts) per trust boundary.
194
+ - Authenticated Gateway callers are treated as trusted operators. Session identifiers (for example `sessionKey`) are routing controls, not per-user authorization boundaries.
195
+ - Multiple gateway instances can run on one machine, but the recommended model is clean per-user isolation (prefer one host/VPS per user).
196
+
197
+ ### One-User Trust Model
198
+
199
+ OpenClaw's security model is "personal assistant" (one trusted operator, potentially many agents), not "shared multi-tenant bus."
200
+
201
+ - If multiple people can message the same tool-enabled agent (for example a shared Slack workspace), they can all steer that agent within its granted permissions.
202
+ - Non-owner sender status only affects owner-only tools/commands. If a non-owner can still access a non-owner-only tool on that same agent (for example `canvas`), that is within the granted tool boundary unless the report demonstrates an auth, policy, allowlist, approval, or sandbox bypass.
203
+ - Session or memory scoping reduces context bleed, but does **not** create per-user host authorization boundaries.
204
+ - For mixed-trust or adversarial users, isolate by OS user/host/gateway and use separate credentials per boundary.
205
+ - A company-shared agent can be a valid setup when users are in the same trust boundary and the agent is strictly business-only.
206
+ - For company-shared setups, use a dedicated machine/VM/container and dedicated accounts; avoid mixing personal data on that runtime.
207
+ - If that host/browser profile is logged into personal accounts (for example Apple/Google/personal password manager), you have collapsed the boundary and increased personal-data exposure risk.
208
+
209
+ ### Context Visibility and Allowlists
210
+
211
+ OpenClaw distinguishes:
212
+
213
+ - **Trigger authorization**: who can trigger the agent (`dmPolicy`, `groupPolicy`, allowlists, mention gates)
214
+ - **Context visibility**: what supplemental context is provided to the model (reply body, quoted text, thread history, forwarded metadata)
215
+
216
+ In current releases, allowlists primarily gate triggering and owner-style command access. They do not guarantee universal supplemental-context redaction across every channel/surface.
217
+
218
+ Current channel behavior is not fully uniform:
219
+
220
+ - some channels already filter parts of supplemental context by sender allowlist
221
+ - other channels still pass supplemental context as received
222
+
223
+ Reports that only show supplemental-context visibility differences are typically hardening/consistency findings unless they also demonstrate a documented boundary bypass (auth, policy, approvals, sandbox, or equivalent).
224
+
225
+ Hardening roadmap may add explicit visibility modes (for example `all`, `allowlist`, `allowlist_quote`) so operators can opt into stricter context filtering with predictable tradeoffs.
226
+
227
+ ### Agent and Model Assumptions
228
+
229
+ - The model/agent is **not** a trusted principal. Assume prompt/content injection can manipulate behavior.
230
+ - Security boundaries come from host/config trust, auth, tool policy, sandboxing, and exec approvals.
231
+ - Prompt injection by itself is not a vulnerability report unless it crosses one of those boundaries.
232
+ - 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`).
233
+ - 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.
234
+
235
+ ### Gateway and Node Trust Concept
236
+
237
+ OpenClaw separates routing from execution, but both remain inside the same operator trust boundary:
238
+
239
+ - **Gateway** is the control plane. If a caller passes Gateway auth, they are treated as a trusted operator for that Gateway.
240
+ - **Node** is an execution extension of the Gateway. Pairing a node grants operator-level remote capability on that node.
241
+ - **Exec approvals** (allowlist/ask UI) are operator guardrails to reduce accidental command execution, not a multi-tenant authorization boundary.
242
+ - Exec approvals bind exact command/cwd/env context and, when OpenClaw can identify one concrete local script/file operand, that file snapshot too. This is best-effort integrity hardening, not a complete semantic model of every interpreter/runtime loader path.
243
+ - Differences in command-risk warning heuristics between exec surfaces (`gateway`, `node`, `sandbox`) do not, by themselves, constitute a security-boundary bypass.
244
+ - For untrusted-user isolation, split by trust boundary: separate gateways and separate OS users/hosts per boundary.
245
+
246
+ ### Workspace Memory Trust Boundary
247
+
248
+ `MEMORY.md` and `memory/*.md` are plain workspace files and are treated as trusted local operator state.
249
+
250
+ - If someone can edit workspace memory files, they already crossed the trusted operator boundary.
251
+ - Memory search indexing/recall over those files is expected behavior, not a sandbox/security boundary.
252
+ - Example report pattern considered out of scope: "attacker writes malicious content into `memory/*.md`, then `memory_search` returns it."
253
+ - If you need isolation between mutually untrusted users, split by OS user or host and run separate gateways.
254
+
255
+ ### Plugin Trust Boundary
256
+
257
+ Plugins/extensions are loaded **in-process** with the Gateway and are treated as trusted code.
258
+
259
+ - Plugins can execute with the same OS privileges as the OpenClaw process.
260
+ - Runtime helpers (for example `runtime.system.runCommandWithTimeout`) are convenience APIs, not a sandbox boundary.
261
+ - Only install plugins you trust, and prefer `plugins.allow` to pin explicit trusted plugin ids.
262
+
263
+ ### Temp Folder Boundary
264
+
265
+ OpenClaw uses a dedicated temp root for local media handoff and sandbox-adjacent temp artifacts:
266
+
267
+ - Preferred temp root: `/tmp/openclaw` (when available and safe on the host).
268
+ - Fallback temp root: `os.tmpdir()/openclaw` (or `openclaw-<uid>` on multi-user hosts).
269
+
270
+ Security boundary notes:
271
+
272
+ - Sandbox media validation allows absolute temp paths only under the OpenClaw-managed temp root.
273
+ - Arbitrary host tmp paths are not treated as trusted media roots.
274
+ - Plugin/extension code should use OpenClaw temp helpers (`resolvePreferredOpenClawTmpDir`, `buildRandomTempFilePath`, `withTempDownloadPath`) rather than raw `os.tmpdir()` defaults when handling media files.
275
+ - Enforcement reference points:
276
+ - temp root resolver: `src/infra/tmp-openclaw-dir.ts`
277
+ - SDK temp helpers: `src/plugin-sdk/temp-path.ts`
278
+ - messaging/channel tmp guardrail: `scripts/check-no-random-messaging-tmp.mts`
279
+
280
+ ### Operational Guidance
281
+
282
+ For threat model + hardening guidance (including `openclaw security audit --deep` and `--fix`), see:
283
+
284
+ - `https://docs.openclaw.ai/gateway/security`
285
+
286
+ #### Tool Filesystem Hardening
287
+
288
+ - `tools.exec.applyPatch.workspaceOnly: true` (recommended): keeps `apply_patch` writes/deletes within the configured workspace directory.
289
+ - `tools.fs.workspaceOnly: true` (optional): restricts `read`/`write`/`edit`/`apply_patch` paths and native prompt image auto-load paths to the workspace directory.
290
+ - Avoid setting `tools.exec.applyPatch.workspaceOnly: false` unless you fully trust who can trigger tool execution.
291
+
292
+ #### Sub-Agent Delegation Hardening
293
+
294
+ - Keep `sessions_spawn` denied unless you explicitly need delegated runs.
295
+ - Keep `agents.list[].subagents.allowAgents` narrow, and only include agents with sandbox settings you trust.
296
+ - When delegation must stay sandboxed, call `sessions_spawn` with `sandbox: "require"` (default is `inherit`).
297
+ - `sandbox: "require"` rejects the spawn unless the target child runtime is sandboxed.
298
+ - This prevents a less-restricted session from delegating work into an unsandboxed child by mistake.
299
+
300
+ #### Web Interface Safety
301
+
302
+ OpenClaw's web interface (Gateway Control UI + HTTP endpoints) is intended for **local use only**.
303
+
304
+ - Recommended: keep the Gateway **loopback-only** (`127.0.0.1` / `::1`).
305
+ - Config: `gateway.bind="loopback"` (default).
306
+ - CLI: `openclaw gateway run --bind loopback`.
307
+ - The retired `gateway.controlUi.dangerouslyDisableDeviceAuth` break-glass key is not a
308
+ current security option. Upgrade migration accepts it only from older config
309
+ versions and requires explicit self-pairing before normal enforcement resumes.
310
+ - OpenClaw keeps deployment flexibility by design and does not hard-forbid non-local setups.
311
+ - Non-local and other risky configurations are surfaced by `openclaw security audit` as dangerous findings.
312
+ - This operator-selected tradeoff is by design and not, by itself, a security vulnerability.
313
+ - Canvas host note: network-visible canvas is **intentional** for trusted node scenarios (LAN/tailnet).
314
+ - Expected setup: non-loopback bind + Gateway auth (token/password/trusted-proxy) + firewall/tailnet controls.
315
+ - Expected routes: `/__openclaw__/canvas/`, `/__openclaw__/a2ui/`.
316
+ - This deployment model alone is not a security vulnerability.
317
+ - Do **not** expose it to the public internet (no direct bind to `0.0.0.0`, no public reverse proxy). It is not hardened for public exposure.
318
+ - If you need remote access, prefer an SSH tunnel or Tailscale serve/funnel (so the Gateway still binds to loopback), plus strong Gateway auth.
319
+ - The Gateway HTTP surface includes the canvas host (`/__openclaw__/canvas/`, `/__openclaw__/a2ui/`). Treat canvas content as sensitive/untrusted and avoid exposing it beyond loopback unless you understand the risk.
320
+
321
+ ## Runtime Requirements
322
+
323
+ ### Node.js Version
324
+
325
+ OpenClaw requires **Node.js 24.16+ or Node.js 26.1+**. Node 26 is recommended; Node 24 is the supported LTS line. These minimum versions include the upstream SQLite WAL-reset corruption fix and preserve embedded NUL characters in SQLite TEXT reads. Node 22, 23, and 25 are unsupported.
326
+
327
+ Verify your Node.js version:
328
+
329
+ ```bash
330
+ node --version # Should be v24.16+ or v26.1+
331
+ ```
332
+
333
+ ### Docker Security
334
+
335
+ When running OpenClaw in Docker:
336
+
337
+ 1. The official image runs as a non-root user (`node`) for reduced attack surface
338
+ 2. Use `--read-only` flag when possible for additional filesystem protection
339
+ 3. Limit container capabilities with `--cap-drop=ALL`
340
+
341
+ Example secure Docker run:
342
+
343
+ ```bash
344
+ docker run --read-only --cap-drop=ALL \
345
+ -v openclaw-data:/app/data \
346
+ openclaw/openclaw:latest
347
+ ```
348
+
349
+ ## Security Scanning
350
+
351
+ OpenClaw uses several security and release-validation layers. No single scanner is treated as the boundary.
352
+
353
+ ### Secret Detection
354
+
355
+ OpenClaw runs the in-repo `scripts/detect-private-keys.mts` scanner in CI (the same private-key marker set as the pre-commit-hooks `detect-private-key` hook, with no hook-repo fetches, package installs, or third-party hook execution in the scan's path) over every tracked regular file except colocated `*.test.ts` fixtures and the iOS Fastfile; pull requests run the base branch's copy of the scanner and fail if the base branch lacks it. The local `detect-private-key` pre-commit hook runs the same scanner over the text files pre-commit hands it. Secret-resolution behavior stays covered by the dedicated secrets test surface.
356
+
357
+ Run the key scan locally:
358
+
359
+ ```bash
360
+ node scripts/detect-private-keys.mts
361
+ ```
362
+
363
+ ### Static Analysis
364
+
365
+ CI runs CodeQL across core TypeScript, GitHub Actions, Android, macOS, and high-risk runtime boundaries using `.github/workflows/codeql*.yml` and `.github/codeql/*.yml`.
366
+
367
+ OpenGrep provides a high-precision Semgrep-compatible layer. PRs run a changed-path scan; maintainers can run a full repository scan when needed. The rulepack lives under `security/opengrep/`, with `.semgrepignore` as the shared exclusion file.
368
+
369
+ Run the local OpenGrep wrapper after installing `opengrep`:
370
+
371
+ ```bash
372
+ scripts/run-opengrep.sh --changed --sarif --error
373
+ pnpm check:opengrep-rule-metadata
374
+ ```
375
+
376
+ ### E2E and Live Validation
377
+
378
+ Security-relevant behavior is also covered by runtime validation, not only static scanning:
379
+
380
+ - `pnpm test:e2e` for repo E2E coverage.
381
+ - `pnpm test:live` for live provider/runtime coverage.
382
+ - `pnpm test:docker:all` for Docker-packaged runtime scenarios.
383
+ - Package acceptance and scheduled live/E2E workflows for release-path validation.
384
+
385
+ These lanes exercise packaged installs, gateway/runtime behavior, live model/provider paths, Docker scenarios, and platform smoke tests. They complement scanners by proving the security-sensitive flows still behave correctly in real runtime environments.
VISION.md ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## OpenClaw Vision
2
+
3
+ OpenClaw is the AI that actually does things.
4
+ It runs on your devices, in your channels, with your rules.
5
+
6
+ This document explains the current state and direction of the project.
7
+ We are still early, so iteration is fast.
8
+ Project overview and developer docs: [`README.md`](README.md)
9
+ Contribution guide: [`CONTRIBUTING.md`](CONTRIBUTING.md)
10
+
11
+ OpenClaw started as a personal playground to learn AI and build something genuinely useful:
12
+ an assistant that can run real tasks on a real computer.
13
+ It evolved through several names and shells: Warelay -> Clawdbot -> Moltbot -> OpenClaw.
14
+
15
+ The goal: a personal assistant that is easy to use, supports a wide range of platforms, and respects privacy and security.
16
+
17
+ OpenClaw is a great personal assistant and a great team assistant.
18
+ A personal install is yours alone; a shared Gateway is a place people work together, so the same session can carry several humans, their credit, and their history.
19
+ We build OpenClaw with OpenClaw on [team.openclaw.ai](https://team.openclaw.ai), and we sometimes invite visitors there.
20
+
21
+ The current focus is:
22
+
23
+ Priority:
24
+
25
+ - Security and safe defaults
26
+ - Bug fixes and stability
27
+ - Setup reliability and first-run UX
28
+
29
+ Next priorities:
30
+
31
+ - Supporting all major model providers
32
+ - Improving support for major messaging channels (and adding a few high-demand ones)
33
+ - Performance and test infrastructure
34
+ - Better computer-use and agent harness capabilities
35
+ - Ergonomics across CLI and web frontend
36
+ - Companion apps on macOS, iOS, Android, Windows, and Linux
37
+
38
+ Contribution rules:
39
+
40
+ - One PR = one issue/topic. Do not bundle multiple unrelated fixes/features.
41
+ - PRs over ~5,000 changed lines are reviewed only in exceptional circumstances.
42
+ - Do not open large batches of tiny PRs at once; each PR has review cost.
43
+ - For very small related fixes, grouping into one focused PR is encouraged.
44
+
45
+ Configuration compatibility:
46
+
47
+ OpenClaw runtime code reads the current configuration schema only.
48
+ We do not keep long-lived aliases or compatibility branches that silently accept old, renamed, or malformed config keys.
49
+
50
+ When a config change makes existing user config invalid, the same change needs a doctor migration.
51
+ `openclaw doctor --fix` should detect the old shape, explain it, back it up when needed, and rewrite it to the canonical format.
52
+ Core-owned config and auth state are repaired in core doctor code; plugin-owned config is repaired by that plugin's doctor contract.
53
+
54
+ ## Security
55
+
56
+ Security in OpenClaw is a deliberate tradeoff: strong defaults without killing capability.
57
+ The goal is to stay powerful for real work while making risky paths explicit and operator-controlled.
58
+
59
+ Canonical security policy and reporting:
60
+
61
+ - [`SECURITY.md`](SECURITY.md)
62
+
63
+ We prioritize secure defaults, but also expose clear knobs for trusted high-power workflows.
64
+
65
+ Privacy follows the same default rule.
66
+ OpenClaw sends no usage analytics, tracking identifiers, or telemetry attribution to the project unless the operator turned that on themselves.
67
+ This rule governs what leaves your install. It is not a rule about shared Gateways: when you join a team Gateway, the people you share it with see the work you do there, and features like Git co-author credit exist to attribute that work to you.
68
+ The setup wizard offers optional anonymous feature statistics, with no selected by default; the daily update check reports version and platform and can be disabled.
69
+ See [Usage telemetry and update checks](https://docs.openclaw.ai/gateway/telemetry).
70
+
71
+ ## Plugins & Memory
72
+
73
+ OpenClaw has an extensive plugin API.
74
+ Core stays lean; optional capabilities should usually ship as plugins.
75
+ We are generally slimming down core while expanding what plugins can do.
76
+ If a useful feature cannot be built as a plugin yet, we welcome PRs and design discussions that extend the plugin API instead of adding one-off core behavior.
77
+
78
+ Two layers, two bars.
79
+ The core carries a per-call tax: each core tool, prompt line, and config key reaches every operator on every model request, so additions there face the strictest scrutiny.
80
+ Plugins, skills, channels, and apps carry no such tax, and we want that surface to keep growing.
81
+ When our contribution rules read as hostile to a feature, re-check the layer: usually they object to where it plugs in, not to the feature existing.
82
+
83
+ Recurring demand defines interfaces.
84
+ Once several independent PRs or requests wire in the same kind of capability, the right response is a contract, not a queue of merges: land the seam in core or the SDK, port the bundled implementation onto it, and let the remaining candidates ship as plugins against it.
85
+
86
+ There are two broad plugin styles:
87
+
88
+ - Code plugins run OpenClaw plugin code and are appropriate for deeper runtime extension.
89
+ - Bundle-style plugins package stable external surfaces such as skills, MCP servers, and related configuration.
90
+
91
+ Prefer bundle-style plugins when they can express the capability.
92
+ They have a smaller, more stable interface and better security boundaries.
93
+ Use code plugins when the capability needs runtime hooks, providers, channels, tools, or other in-process extension points.
94
+
95
+ Preferred plugin path is npm package distribution plus local extension loading for development.
96
+ If you build a plugin, host and maintain it in your own repository.
97
+ The bar for adding optional plugins to core is intentionally high.
98
+ Plugin docs: [`docs/tools/plugin.md`](docs/tools/plugin.md)
99
+ Plugin discovery, official publisher status, provenance, and security review live in [ClawHub](https://clawhub.ai/).
100
+ OpenClaw docs should document core extension points; plugin promotion belongs in ClawHub, preferably under vetted org publishers for official plugins.
101
+
102
+ Memory is a special plugin slot where only one memory plugin can be active at a time.
103
+ Today we ship multiple memory options; over time we plan to converge on one recommended default path.
104
+
105
+ ### Skills
106
+
107
+ We still ship some bundled skills for baseline UX.
108
+ New skills should be published through [ClawHub](https://clawhub.ai/) first, not added to core by default.
109
+ Official or bundled promotion should require a clear product, security, or maintainer-ownership reason.
110
+
111
+ ### MCP Support
112
+
113
+ OpenClaw supports MCP as both a server and a runtime integration surface.
114
+ MCP details live in [`docs/cli/mcp.md`](docs/cli/mcp.md).
115
+
116
+ The project goal is pragmatic MCP support without duplicating existing agent,
117
+ tool, ACPX, plugin, or ClawHub paths.
118
+
119
+ ### Setup
120
+
121
+ OpenClaw is currently terminal-first by design.
122
+ This keeps setup explicit: users see docs, auth, permissions, and security posture up front.
123
+
124
+ Long term, we want easier onboarding flows as hardening matures.
125
+ We do not want convenience wrappers that hide critical security decisions from users.
126
+
127
+ ### Why TypeScript?
128
+
129
+ OpenClaw is primarily an orchestration system: prompts, tools, protocols, and integrations.
130
+ TypeScript was chosen to keep OpenClaw hackable by default.
131
+ It is widely known, fast to iterate in, and easy to read, modify, and extend.
132
+
133
+ ## What We Will Not Merge (For Now)
134
+
135
+ - New core skills when they can live on [ClawHub](https://clawhub.ai/)
136
+ - Full-doc translation sets for all docs (deferred; we plan AI-generated translations later)
137
+ - Commercial service integrations that do not clearly fit the model-provider category
138
+ - Cloud-based sandbox providers as OpenClaw plugins; implement provider support in [Crabbox](https://github.com/openclaw/crabbox) instead
139
+ - Wrapper channels around already supported channels without a clear capability or security gap
140
+ - MCP work that duplicates existing MCP, ACPX, plugin, or ClawHub paths without a clear product or security gap
141
+ - Heavy orchestration layers that duplicate existing agent and tool infrastructure
142
+
143
+ This list is a roadmap guardrail, not a law of physics.
144
+ Strong user demand and strong technical rationale can change it.
appcast.xml ADDED
The diff for this file is too large to render. See raw diff
 
docker-compose.yml ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ openclaw-gateway:
3
+ image: ${OPENCLAW_IMAGE:-openclaw:local}
4
+ build: .
5
+ env_file:
6
+ - path: .env
7
+ required: false
8
+ environment:
9
+ HOME: /home/node
10
+ OPENCLAW_HOME: /home/node
11
+ TERM: xterm-256color
12
+ # Pin container-side state, workspace, and config paths so host values written to
13
+ # `.env` (used by Compose for the bind-mount source below) cannot leak
14
+ # into runtime code that resolves these env vars inside the container.
15
+ # Without this override, a macOS host path like /Users/<you>/.openclaw/...
16
+ # imported from .env caused first-reply `mkdir '/Users'` EACCES failures
17
+ # in Linux Docker (#77436).
18
+ OPENCLAW_STATE_DIR: /home/node/.openclaw
19
+ OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
20
+ OPENCLAW_CONFIG_DIR: /home/node/.openclaw
21
+ OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
22
+ # .env controls host publishing; in-container clients use the fixed listener.
23
+ OPENCLAW_GATEWAY_PORT: "18789"
24
+ OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN:-}
25
+ OPENCLAW_ALLOW_INSECURE_PRIVATE_WS: ${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}
26
+ # Empty means auto: Bonjour disables itself in detected containers.
27
+ # Set 0 only on host/macvlan/mDNS-capable networks; set 1 to force off.
28
+ OPENCLAW_DISABLE_BONJOUR: ${OPENCLAW_DISABLE_BONJOUR:-}
29
+ # OpenTelemetry export is outbound OTLP/HTTP from the Gateway. Prometheus
30
+ # uses the existing authenticated Gateway route; it does not need a port.
31
+ OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-}
32
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: ${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-}
33
+ OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: ${OTEL_EXPORTER_OTLP_METRICS_ENDPOINT:-}
34
+ OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: ${OTEL_EXPORTER_OTLP_LOGS_ENDPOINT:-}
35
+ OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-http/protobuf}
36
+ OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: ${OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:-}
37
+ OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: ${OTEL_EXPORTER_OTLP_METRICS_PROTOCOL:-}
38
+ OTEL_EXPORTER_OTLP_LOGS_PROTOCOL: ${OTEL_EXPORTER_OTLP_LOGS_PROTOCOL:-}
39
+ OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-}
40
+ OTEL_SEMCONV_STABILITY_OPT_IN: ${OTEL_SEMCONV_STABILITY_OPT_IN:-}
41
+ OPENCLAW_OTEL_PRELOADED: ${OPENCLAW_OTEL_PRELOADED:-}
42
+ CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY:-}
43
+ CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY:-}
44
+ CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE:-}
45
+ TZ: ${OPENCLAW_TZ:-UTC}
46
+ volumes:
47
+ - "${OPENCLAW_CONFIG_DIR:-${HOME:-/tmp}/.openclaw}:/home/node/.openclaw"
48
+ - "${OPENCLAW_WORKSPACE_DIR:-${HOME:-/tmp}/.openclaw/workspace}:/home/node/.openclaw/workspace"
49
+ - "${OPENCLAW_AUTH_PROFILE_SECRET_DIR:-${HOME:-/tmp}/.openclaw-auth-profile-secrets}:/home/node/.config/openclaw"
50
+ ## Uncomment the lines below to enable sandbox isolation
51
+ ## (agents.defaults.sandbox). Requires Docker CLI in the image
52
+ ## (build with --build-arg OPENCLAW_INSTALL_DOCKER_CLI=1) or use
53
+ ## scripts/docker/setup.sh with OPENCLAW_SANDBOX=1 for automated setup.
54
+ ## Set DOCKER_GID to the host's docker group GID (run: stat -c '%g' /var/run/docker.sock).
55
+ # - /var/run/docker.sock:/var/run/docker.sock
56
+ # group_add:
57
+ # - "${DOCKER_GID:-999}"
58
+ # Let bundled local-model providers reach host-side LM Studio/Ollama via
59
+ # http://host.docker.internal:<port>. Docker Desktop usually provides this
60
+ # alias; the host-gateway mapping makes it work on Linux Docker Engine too.
61
+ cap_drop:
62
+ - NET_RAW
63
+ - NET_ADMIN
64
+ security_opt:
65
+ - no-new-privileges:true
66
+ extra_hosts:
67
+ - "host.docker.internal:host-gateway"
68
+ ports:
69
+ - "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
70
+ - "${OPENCLAW_BRIDGE_PORT:-18790}:18790"
71
+ - "${OPENCLAW_MSTEAMS_PORT:-3978}:3978"
72
+ init: true
73
+ restart: unless-stopped
74
+ command:
75
+ [
76
+ "node",
77
+ "dist/index.js",
78
+ "gateway",
79
+ "--bind",
80
+ "${OPENCLAW_GATEWAY_BIND:-lan}",
81
+ "--port",
82
+ "18789",
83
+ ]
84
+ healthcheck:
85
+ test:
86
+ [
87
+ "CMD",
88
+ "node",
89
+ "dist/docker-healthcheck.js",
90
+ ]
91
+ interval: 30s
92
+ timeout: 5s
93
+ retries: 5
94
+ start_period: 20s
95
+
96
+ openclaw-cli:
97
+ image: ${OPENCLAW_IMAGE:-openclaw:local}
98
+ network_mode: "service:openclaw-gateway"
99
+ env_file:
100
+ - path: .env
101
+ required: false
102
+ cap_drop:
103
+ - NET_RAW
104
+ - NET_ADMIN
105
+ security_opt:
106
+ - no-new-privileges:true
107
+ environment:
108
+ HOME: /home/node
109
+ OPENCLAW_HOME: /home/node
110
+ TERM: xterm-256color
111
+ # Pin container-side state, workspace, and config paths so host values written to
112
+ # `.env` cannot leak into runtime code via the env_file import (#77436).
113
+ OPENCLAW_STATE_DIR: /home/node/.openclaw
114
+ OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
115
+ OPENCLAW_CONFIG_DIR: /home/node/.openclaw
116
+ OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
117
+ # Shared gateway network namespace: use its listener, not the .env host port.
118
+ OPENCLAW_GATEWAY_PORT: "18789"
119
+ OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN:-}
120
+ OPENCLAW_ALLOW_INSECURE_PRIVATE_WS: ${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}
121
+ BROWSER: echo
122
+ CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY:-}
123
+ CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY:-}
124
+ CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE:-}
125
+ TZ: ${OPENCLAW_TZ:-UTC}
126
+ volumes:
127
+ - "${OPENCLAW_CONFIG_DIR:-${HOME:-/tmp}/.openclaw}:/home/node/.openclaw"
128
+ - "${OPENCLAW_WORKSPACE_DIR:-${HOME:-/tmp}/.openclaw/workspace}:/home/node/.openclaw/workspace"
129
+ - "${OPENCLAW_AUTH_PROFILE_SECRET_DIR:-${HOME:-/tmp}/.openclaw-auth-profile-secrets}:/home/node/.config/openclaw"
130
+ stdin_open: true
131
+ tty: true
132
+ init: true
133
+ entrypoint: ["node", "dist/index.js"]
134
+ depends_on:
135
+ - openclaw-gateway
fly.toml ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenClaw Fly.io deployment configuration
2
+ # See https://fly.io/docs/reference/configuration/
3
+
4
+ app = "openclaw"
5
+ primary_region = "iad" # change to your closest region
6
+
7
+ [build]
8
+ dockerfile = "Dockerfile"
9
+
10
+ [env]
11
+ NODE_ENV = "production"
12
+ # Fly uses x86, but keep this for consistency
13
+ OPENCLAW_PREFER_PNPM = "1"
14
+ OPENCLAW_STATE_DIR = "/data"
15
+ NODE_OPTIONS = "--max-old-space-size=1536"
16
+
17
+ [processes]
18
+ app = "node dist/index.js gateway --allow-unconfigured --port 3000 --bind lan"
19
+
20
+ [http_service]
21
+ internal_port = 3000
22
+ force_https = true
23
+ auto_stop_machines = false # Keep running for persistent connections
24
+ auto_start_machines = true
25
+ min_machines_running = 1
26
+ processes = ["app"]
27
+
28
+ [[http_service.checks]]
29
+ grace_period = "2m"
30
+ interval = "15s"
31
+ method = "GET"
32
+ timeout = "5s"
33
+ path = "/startupz"
34
+
35
+ [[vm]]
36
+ size = "shared-cpu-2x"
37
+ memory = "2048mb"
38
+
39
+ [mounts]
40
+ source = "openclaw_data"
41
+ destination = "/data"
git-hooks/pre-commit ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null)" || {
5
+ echo '[pre-commit] Cannot locate the checkout root. Check the repository and retry.' >&2
6
+ exit 1
7
+ }
8
+ cd "$ROOT_DIR"
9
+ exec node scripts/pre-commit/guard-staged-content.mjs
node-runtime-recovery.d.mts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export function consumeLauncherRootOptionToken(args: string[], index: number): number;
2
+ export function isForegroundGmailRunInvocation(argv: string[]): boolean;
3
+ export function isNativeHookRelayInvocation(argv: string[]): boolean;
4
+ export function resolveRecoveryPath(
5
+ value: string | null | undefined,
6
+ homeDir?: string | null,
7
+ options?: { allowMissing?: boolean; allowCwd?: boolean; trustedRoot?: string },
8
+ ): string | null;
9
+ export function isUsableNode(
10
+ nodePath: string,
11
+ options?: { allowCwd?: boolean; trustedRoot?: string; env?: NodeJS.ProcessEnv },
12
+ ): boolean;
13
+ export function runRespawnedChild(command: string, args: string[], env: NodeJS.ProcessEnv): true;
14
+ export function recoverNodeRuntime(options?: {
15
+ homeDir?: string;
16
+ allowInstall?: boolean;
17
+ env?: NodeJS.ProcessEnv;
18
+ }): Promise<boolean>;
node-runtime-update.d.mts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ export function resolveUpdatedNodeRuntime(
2
+ recoveryRoot: string,
3
+ options?: { allowInstall?: boolean; env?: NodeJS.ProcessEnv },
4
+ ): Promise<string | null>;
node-version.d.mts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export type NodeReleaseVersion = {
2
+ major: number;
3
+ minor: number;
4
+ patch: number;
5
+ };
6
+
7
+ export function parseNodeReleaseVersion(value: unknown): NodeReleaseVersion | null;
8
+ export function isNodeVersionAtLeast(
9
+ version: NodeReleaseVersion | null,
10
+ minimum: NodeReleaseVersion,
11
+ ): boolean;
12
+ export function isSupportedOpenClawNodeVersion(value: unknown): boolean;
13
+ export const PROCESS_NODE_VERSION_CHECK: string;
14
+
15
+ export const SUPPORTED_NODE_VERSIONS: string;
16
+ export function formatUnsupportedNodeVersionMessage(version: string | null): string;
17
+ export function formatUnsupportedNodeDiagnosticWarning(version: string | null): string;
18
+ export function classifyUnsupportedNodeCommand(
19
+ argv: readonly string[],
20
+ ): "diagnostic" | "update" | null;
21
+ export function canRunOpenClawNodeDiagnostics(value: unknown, hasNodeSqlite: boolean): boolean;
pnpm-workspace.yaml ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ packages:
2
+ - .
3
+ - ui
4
+ - packages/*
5
+ - extensions/*
6
+ - examples/*
7
+
8
+ minimumReleaseAge: 10080
9
+ minimumReleaseAgeStrict: true
10
+
11
+ # Trusted Codex runtimes are outside the dependency cooldown.
12
+ minimumReleaseAgeExclude:
13
+ # GHSA-jqcg-44mw-7w3h trust-subnet fix; remove after 2026-09-22 06:13 UTC.
14
+ - "proxy-addr@2.0.8"
15
+ # Smartquotes denial-of-service fix; remove after 2026-09-18 04:04 UTC.
16
+ - "markdown-it@15.0.2"
17
+ # Reviewed Discord local-provider contract; remove after 2026-09-21.
18
+ - "@openclaw/crabline@0.1.24"
19
+ # Reviewed Opus WASM stack repair; remove after 2026-09-21.
20
+ - "libopus-wasm@0.4.0"
21
+ # Reviewed proxy DNS/TLS controls; remove after 2026-09-15 10:08 UTC.
22
+ - "@openclaw/proxyline@0.3.12"
23
+ - "@openai/codex"
24
+ - "@openai/codex-*"
25
+ # Reviewed fs-safe archive, file, and directory upgrade; remove after 2026-09-22.
26
+ - "@openclaw/fs-safe@0.11.0"
27
+ - "@openclaw/fs-safe-darwin-arm64@0.11.0"
28
+ - "@openclaw/fs-safe-darwin-x64@0.11.0"
29
+ - "@openclaw/fs-safe-linux-arm64-gnu@0.11.0"
30
+ - "@openclaw/fs-safe-linux-arm64-musl@0.11.0"
31
+ - "@openclaw/fs-safe-linux-x64-gnu@0.11.0"
32
+ - "@openclaw/fs-safe-linux-x64-musl@0.11.0"
33
+ - "@openclaw/fs-safe-win32-x64-msvc@0.11.0"
34
+ # fs-safe archive dependency; remove after 2026-09-16.
35
+ - "jszip@3.10.2"
36
+ # GHSA-prgh-xp8r-p3m5 / GHSA-v53p-9fqp-m79j security fixes; remove after 2026-09-18.
37
+ - "nodemailer@10.0.8"
38
+
39
+ # Isolated installs let pnpm reuse whole-package APFS clones instead of relinking every file.
40
+ nodeLinker: isolated
41
+ # GWTs share node_modules; script commands must not reconcile that shared install.
42
+ verifyDepsBeforeRun: false
43
+ blockExoticSubdeps: true
44
+
45
+ overrides:
46
+ # Memory LanceDB supplies vectors; WhatsApp prepares thumbnails with Rastermill.
47
+ "@lancedb/lancedb>@huggingface/transformers": "-"
48
+ "baileys>sharp": "-"
49
+ "@agentclientprotocol/codex-acp@1.10.0>@openai/codex": 0.154.0
50
+ "@codemirror/commands@6.11.0>@codemirror/view": 6.43.11
51
+ "@anthropic-ai/sdk": 0.124.0
52
+ "@anthropic-ai/vertex-sdk@0.19.7>google-auth-library": 11.0.2
53
+ "@opentelemetry/core": 2.11.0
54
+ "@opentelemetry/propagator-jaeger": 2.11.0
55
+ "@aws-sdk/core": 3.977.9
56
+ "@aws-sdk/credential-provider-node": 3.972.82
57
+ "@aws-sdk/xml-builder": 3.972.40
58
+ "@hono/node-server": 2.1.1
59
+ axios: 1.20.0
60
+ fast-uri: 4.1.4
61
+ follow-redirects: 1.16.0
62
+ defu: 6.1.7
63
+ fast-xml-parser: 5.11.1
64
+ request: "npm:@cypress/request@4.0.1"
65
+ request-promise: "npm:@cypress/request-promise@5.0.0"
66
+ basic-ftp: 6.2.1
67
+ brace-expansion: 5.0.9
68
+ file-type: 22.0.2
69
+ form-data: 4.0.6
70
+ "werift-ice@0.2.2>ip": "npm:neoip@3.1.0"
71
+ ip-address: 10.7.0
72
+ "mailauth@5.0.3>nodemailer": 10.0.8
73
+ # Mailauth pins vulnerable Undici; remove when its direct dependency is fixed.
74
+ "mailauth@5.0.3>undici": 8.10.2
75
+ "mailparser@3.9.20>nodemailer": 10.0.8
76
+ minimatch: 10.2.6
77
+ path-to-regexp: 8.4.2
78
+ proxy-addr: 2.0.8
79
+ qs: 6.16.0
80
+ node-domexception: "npm:@nolyfill/domexception@1.0.28"
81
+ typebox: 1.3.27
82
+ tar: 7.5.22
83
+ tough-cookie: 6.0.2
84
+ "tsdown>rolldown-plugin-dts": 0.28.5
85
+ "zca-js@2.1.2>tough-cookie": 6.0.2
86
+ yauzl: 3.4.0
87
+ protobufjs: 8.8.0
88
+ uuid: 14.0.2
89
+
90
+ allowBuilds:
91
+ "@google/genai": true
92
+ "@lydell/node-pty": true
93
+ "@matrix-org/matrix-sdk-crypto-nodejs": true
94
+ "@tloncorp/api": true
95
+ "@tloncorp/tlon-skill": true
96
+ baileys: true
97
+ authenticate-pam: true
98
+ "@discordjs/opus": false
99
+ esbuild: true
100
+ koffi: false
101
+ protobufjs: true
102
+ tree-sitter-bash: false
103
+ openclaw: true
104
+ "@openclaw/proxyline": true
105
+ clawpdf: true
106
+ "@openclaw/crabline": true
107
+ rastermill: true
108
+ node-pty: true
109
+
110
+ # The transient npm lock generator also needs the peer marked optional.
111
+ packageExtensions:
112
+ baileys:
113
+ peerDependenciesMeta:
114
+ sharp:
115
+ optional: true
116
+
117
+ patchedDependencies:
118
+ "@awesome.me/webawesome@3.12.0": patches/@awesome.me__webawesome@3.12.0.patch
119
+ "matrix-js-sdk@42.3.0": patches/matrix-js-sdk@42.3.0.patch
120
+ "@novnc/novnc@1.7.0": patches/@novnc__novnc@1.7.0.patch
121
+ vitest@5.0.0: patches/vitest@5.0.0.patch
test/cli-json-stdout.skills.e2e.test.ts ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { withTempHome } from "openclaw/plugin-sdk/test-env";
4
+ import { describe, expect, it } from "vitest";
5
+ import { runBuiltCli } from "./cli-json-stdout.test-support.js";
6
+
7
+ describe("cli json stdout contract", () => {
8
+ it.each([
9
+ {
10
+ name: "search with a leaf JSON flag",
11
+ args: ["skills", "search", "fixture", "--json"],
12
+ message: "ClawHub /api/v1/search failed (400): offline fixture",
13
+ },
14
+ {
15
+ name: "search with a parent JSON flag",
16
+ args: ["skills", "--json", "search", "fixture"],
17
+ message: "ClawHub /api/v1/search failed (400): offline fixture",
18
+ },
19
+ {
20
+ name: "list with a leaf JSON flag",
21
+ args: ["skills", "list", "--agent", "", "--json"],
22
+ message: "--agent must not be blank",
23
+ },
24
+ {
25
+ name: "list with a parent JSON flag",
26
+ args: ["skills", "--json", "list", "--agent", ""],
27
+ message: "--agent must not be blank",
28
+ },
29
+ {
30
+ name: "info with a leaf JSON flag",
31
+ args: ["skills", "info", "fixture", "--agent", "", "--json"],
32
+ message: "--agent must not be blank",
33
+ },
34
+ {
35
+ name: "info with a parent JSON flag",
36
+ args: ["skills", "--json", "info", "fixture", "--agent", ""],
37
+ message: "--agent must not be blank",
38
+ },
39
+ {
40
+ name: "check with a leaf JSON flag",
41
+ args: ["skills", "check", "--agent", "", "--json"],
42
+ message: "--agent must not be blank",
43
+ },
44
+ {
45
+ name: "check with a parent JSON flag",
46
+ args: ["skills", "--json", "check", "--agent", ""],
47
+ message: "--agent must not be blank",
48
+ },
49
+ {
50
+ name: "the default report after its agent flag",
51
+ args: ["skills", "--agent", "", "--json"],
52
+ message: "--agent must not be blank",
53
+ },
54
+ {
55
+ name: "the default report before its agent flag",
56
+ args: ["skills", "--json", "--agent", ""],
57
+ message: "--agent must not be blank",
58
+ },
59
+ {
60
+ name: "list with a configured remote Gateway missing its URL",
61
+ args: ["skills", "list", "--json"],
62
+ message: "gateway remote mode misconfigured: gateway.remote.url missing",
63
+ remoteMissing: true,
64
+ },
65
+ ...[
66
+ { name: "the default report", args: ["skills", "--json"] },
67
+ { name: "list", args: ["skills", "list", "--json"] },
68
+ { name: "info", args: ["skills", "info", "fixture", "--json"] },
69
+ { name: "check", args: ["skills", "check", "--json"] },
70
+ { name: "curator status", args: ["skills", "curator", "status", "--json"] },
71
+ { name: "curator pin", args: ["skills", "curator", "pin", "fixture", "--json"] },
72
+ { name: "curator unpin", args: ["skills", "curator", "unpin", "fixture", "--json"] },
73
+ { name: "curator restore", args: ["skills", "curator", "restore", "fixture", "--json"] },
74
+ {
75
+ name: "workshop apply",
76
+ args: ["skills", "workshop", "apply", "fixture-proposal", "--json"],
77
+ },
78
+ ].map(({ name, args }) => ({
79
+ name: `${name} after an explicit environment Gateway fails`,
80
+ args,
81
+ message: "AUTOQA_SELECTED_GATEWAY_FAILURE",
82
+ explicitGateway: true,
83
+ })),
84
+ {
85
+ name: "retired curator mutation",
86
+ args: ["skills", "curator", "pin", "missing-skill", "--json"],
87
+ message:
88
+ "Skill lifecycle curation is retired. The weekly collection review manages the skill collection; pin, unpin, and restore no longer exist.",
89
+ },
90
+ {
91
+ name: "retired curator mutation with parent JSON",
92
+ args: ["skills", "curator", "--json", "pin", "missing-skill"],
93
+ message:
94
+ "Skill lifecycle curation is retired. The weekly collection review manages the skill collection; pin, unpin, and restore no longer exist.",
95
+ },
96
+ {
97
+ name: "workshop workspace validation with parent JSON",
98
+ args: ["skills", "--json", "workshop", "list", "--agent", ""],
99
+ message: "--agent must not be blank",
100
+ },
101
+ {
102
+ name: "workshop mutation",
103
+ args: ["skills", "workshop", "reject", "missing-proposal", "--json"],
104
+ message: "Skill proposal not found: missing-proposal",
105
+ },
106
+ {
107
+ name: "workshop inspection",
108
+ args: ["skills", "workshop", "inspect", "missing-proposal", "--json"],
109
+ message: "Skill proposal not found: missing-proposal",
110
+ },
111
+ ])("returns one canonical JSON document when skills $name fails", async (testCase) => {
112
+ await withTempHome(
113
+ async (tempHome) => {
114
+ const configPath = path.join(tempHome, "missing-openclaw.json");
115
+ if ("remoteMissing" in testCase) {
116
+ await fs.writeFile(configPath, JSON.stringify({ gateway: { mode: "remote" } }));
117
+ }
118
+ const preload = `data:text/javascript,${encodeURIComponent(
119
+ [
120
+ 'globalThis.fetch = async () => new Response("offline fixture", { status: 400 });',
121
+ ...("explicitGateway" in testCase
122
+ ? [
123
+ 'import net from "node:net";',
124
+ 'net.Socket.prototype.connect = function () { throw new Error("AUTOQA_SELECTED_GATEWAY_FAILURE"); };',
125
+ ]
126
+ : []),
127
+ ].join("\n"),
128
+ )}`;
129
+ const result = runBuiltCli(tempHome, testCase.args, {
130
+ NODE_OPTIONS: `--import=${preload}`,
131
+ OPENCLAW_STATE_DIR: path.join(tempHome, "isolated-state"),
132
+ OPENCLAW_CONFIG_PATH: configPath,
133
+ OPENCLAW_GATEWAY_PORT: "1",
134
+ ...("explicitGateway" in testCase
135
+ ? {
136
+ OPENCLAW_GATEWAY_URL: "ws://127.0.0.1:9",
137
+ OPENCLAW_GATEWAY_TOKEN: "fixture-token",
138
+ }
139
+ : {}),
140
+ });
141
+ const message =
142
+ "remoteMissing" in testCase
143
+ ? [
144
+ testCase.message,
145
+ `Config: ${configPath}`,
146
+ "Fix: set gateway.remote.url, or set gateway.mode=local.",
147
+ ].join("\n")
148
+ : testCase.message;
149
+
150
+ expect(result.status, result.stderr).toBe(1);
151
+ expect(JSON.parse(result.stdout)).toEqual({
152
+ ok: false,
153
+ error: {
154
+ type: "cli_error",
155
+ message,
156
+ },
157
+ });
158
+ expect(result.stderr).toContain(message);
159
+ expect(result.stderr.length).toBeLessThan(2_048);
160
+ },
161
+ { prefix: "openclaw-skills-json-failure-e2e-" },
162
+ );
163
+ });
164
+
165
+ it.each([
166
+ { name: "off", debug: "0", includesCause: false },
167
+ { name: "on", debug: "1", includesCause: true },
168
+ ])("keeps skills search nested causes behind debug mode ($name)", async (testCase) => {
169
+ await withTempHome(
170
+ async (tempHome) => {
171
+ const preload = `data:text/javascript,${encodeURIComponent(
172
+ 'globalThis.fetch = async () => new Response("not-json", { status: 200 });',
173
+ )}`;
174
+ const result = runBuiltCli(tempHome, ["skills", "search", "fixture"], {
175
+ NODE_OPTIONS: `--import=${preload}`,
176
+ OPENCLAW_DEBUG: testCase.debug,
177
+ OPENCLAW_STATE_DIR: path.join(tempHome, "isolated-state"),
178
+ OPENCLAW_CONFIG_PATH: path.join(tempHome, "missing-openclaw.json"),
179
+ });
180
+
181
+ expect(result.status, result.stderr).toBe(1);
182
+ expect(result.stdout).toBe("");
183
+ expect(result.stderr).toContain("ClawHub /api/v1/search returned malformed JSON");
184
+ expect(result.stderr.includes("Unexpected token")).toBe(testCase.includesCause);
185
+ },
186
+ { prefix: "openclaw-skills-human-failure-e2e-" },
187
+ );
188
+ });
189
+ });
test/copilot-tool-policy-handoff.live.test.ts ADDED
@@ -0,0 +1,338 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import path from "node:path";
2
+ import type {
3
+ AgentHarnessAttemptParamsV2,
4
+ AgentMessage,
5
+ } from "openclaw/plugin-sdk/agent-harness-runtime";
6
+ import { isLiveTestEnabled } from "openclaw/plugin-sdk/test-live";
7
+ import { afterEach, describe, expect, it, vi } from "vitest";
8
+ import {
9
+ createNativeCopilotPolicyHarnessFixtureForTest,
10
+ type CopilotSessionBindingForTest,
11
+ } from "../extensions/copilot/test-api.js";
12
+ import {
13
+ createOperationalRunInstanceRef,
14
+ prepareAgentRunAdmission,
15
+ } from "../src/agents/admitted-run-context.js";
16
+ import type { EmbeddedRunAttemptParams } from "../src/agents/embedded-agent-runner/run/types.js";
17
+ import { clearAgentHarnesses, registerAgentHarness } from "../src/agents/harness/registry.js";
18
+ import { runAgentHarnessAttempt } from "../src/agents/harness/selection.js";
19
+ import { AuthStorage, ModelRegistry } from "../src/agents/sessions/index.js";
20
+ import { replaceSessionEntry } from "../src/config/sessions/session-accessor.js";
21
+ import { useAutoCleanupTempDirTracker } from "./helpers/temp-dir.js";
22
+
23
+ const gatewayFixture = vi.hoisted(() => {
24
+ const calls: Array<{ method: string; params: unknown }> = [];
25
+ return {
26
+ calls,
27
+ call: vi.fn(async (method: string, _options: unknown, params: unknown) => {
28
+ calls.push({ method, params });
29
+ const request = params as {
30
+ answers?: { answers: Record<string, string[]> };
31
+ cancel?: boolean;
32
+ id: string;
33
+ };
34
+ if (method === "question.request") {
35
+ return { id: request.id, expiresAtMs: Date.now() + 90_000 };
36
+ }
37
+ if (method === "question.waitAnswer") {
38
+ return { status: "answered" as const, answers: { answers: { answer: ["Beta"] } } };
39
+ }
40
+ if (method === "question.resolve") {
41
+ return request.cancel
42
+ ? { status: "cancelled" as const }
43
+ : { status: "answered" as const, answers: request.answers! };
44
+ }
45
+ throw new Error(`unexpected gateway method: ${method}`);
46
+ }),
47
+ };
48
+ });
49
+
50
+ vi.mock("../src/agents/harness/gateway-question-dispatch.runtime.js", () => ({
51
+ callGatewayTool: gatewayFixture.call,
52
+ }));
53
+
54
+ const LIVE = isLiveTestEnabled(["OPENCLAW_COPILOT_POLICY_LIVE_TEST"]);
55
+ const describeLive = LIVE ? describe : describe.skip;
56
+ const harnesses: Array<ReturnType<typeof createNativeCopilotPolicyHarnessFixtureForTest>> = [];
57
+
58
+ type CopilotLiveAttemptParams = EmbeddedRunAttemptParams &
59
+ AgentHarnessAttemptParamsV2 & {
60
+ auth: { gitHubToken: string; profileId: string; profileVersion: string };
61
+ authProfileId: string;
62
+ messages: AgentMessage[];
63
+ onAssistantDelta: (payload: { text: string }) => void | Promise<void>;
64
+ profileVersion: string;
65
+ };
66
+
67
+ afterEach(async () => {
68
+ clearAgentHarnesses();
69
+ await Promise.all(harnesses.splice(0).map((fixture) => fixture.dispose()));
70
+ });
71
+ const tempDirs = useAutoCleanupTempDirTracker(afterEach);
72
+
73
+ function createUserTurnRecorder(message: Extract<AgentMessage, { role: "user" }>) {
74
+ let blocked = false;
75
+ let persisted = false;
76
+ return {
77
+ message,
78
+ resolveMessage: vi.fn(async () => message),
79
+ markRuntimePersistencePending: vi.fn(),
80
+ markRuntimePersisted: vi.fn(() => {
81
+ persisted = true;
82
+ }),
83
+ markBlocked: vi.fn(() => {
84
+ blocked = true;
85
+ }),
86
+ hasPersisted: () => persisted,
87
+ isBlocked: () => blocked,
88
+ hasRuntimePersistencePending: () => false,
89
+ getAdmissionReceipt: () => undefined,
90
+ waitForRuntimePersistence: vi.fn(async () => undefined),
91
+ persistApproved: vi.fn(async () => undefined),
92
+ persistBlocked: vi.fn(async () => undefined),
93
+ persistFallback: vi.fn(async () => undefined),
94
+ };
95
+ }
96
+
97
+ function createHostCapabilities(): AgentHarnessAttemptParamsV2["hostCapabilities"] {
98
+ return Object.freeze({
99
+ kind: "agent-harness-host-capability",
100
+ version: 1,
101
+ assertActive: () => {},
102
+ bindToolSurface: (tools) => tools,
103
+ runBeforeToolCall: async (request) => ({ blocked: false, params: request.params }),
104
+ requestApproval: async () => undefined,
105
+ waitForApproval: async () => undefined,
106
+ });
107
+ }
108
+
109
+ async function runLiveCopilotTurn(params: {
110
+ authStorage: AuthStorage;
111
+ blockReplies: string[];
112
+ modelRegistry: ModelRegistry;
113
+ policy: Pick<EmbeddedRunAttemptParams, "disableTools" | "toolsAllow">;
114
+ prompt: string;
115
+ runId: string;
116
+ sessionId: string;
117
+ toolAuthorityFingerprint: string;
118
+ workspaceDir: string;
119
+ }) {
120
+ const admission = prepareAgentRunAdmission({
121
+ cfg: {},
122
+ facts: {
123
+ runId: params.runId,
124
+ agentId: "copilot-policy-live-proof",
125
+ ingress: { kind: "system", boundary: "copilot-policy-live-proof", state: "present" },
126
+ },
127
+ operationalRunInstance: createOperationalRunInstanceRef(params.runId),
128
+ });
129
+ const admittedRunContext = await admission.admit("plugin-harness", "copilot");
130
+ const sessionKey = `agent:copilot-policy-live-proof:${params.sessionId}`;
131
+ const message = { role: "user" as const, content: params.prompt, timestamp: Date.now() };
132
+ try {
133
+ const attempt = {
134
+ admittedRunContext,
135
+ agentDir: params.workspaceDir,
136
+ agentHarnessRuntimeOverride: "copilot",
137
+ agentId: "copilot-policy-live-proof",
138
+ auth: {
139
+ gitHubToken: "copilot-policy-fixture-token",
140
+ profileId: "copilot-policy-live-proof",
141
+ profileVersion: "v1",
142
+ },
143
+ authProfileId: "copilot-policy-live-proof",
144
+ authProfileStore: { version: 1, profiles: {} },
145
+ authStorage: params.authStorage,
146
+ config: {
147
+ plugins: { enabled: false },
148
+ tools: { codeMode: false, fs: { workspaceOnly: true }, toolSearch: false },
149
+ },
150
+ hostCapabilities: createHostCapabilities(),
151
+ messages: [message],
152
+ model: {
153
+ api: "openai-responses" as const,
154
+ baseUrl: "https://api.githubcopilot.com",
155
+ contextWindow: 128_000,
156
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
157
+ id: "gpt-5.4-mini",
158
+ input: ["text" as const],
159
+ maxTokens: 4_096,
160
+ name: "Copilot native policy live proof",
161
+ provider: "github-copilot" as const,
162
+ reasoning: true,
163
+ },
164
+ modelId: "gpt-5.4-mini",
165
+ modelRegistry: params.modelRegistry,
166
+ onAssistantDelta: () => {},
167
+ onBlockReply: (payload) => {
168
+ if (payload.text) {
169
+ params.blockReplies.push(payload.text);
170
+ }
171
+ },
172
+ profileVersion: "v1",
173
+ prompt: message.content,
174
+ provider: "github-copilot",
175
+ runId: params.runId,
176
+ sessionFile: path.join(params.workspaceDir, `${params.sessionId}.jsonl`),
177
+ sessionId: params.sessionId,
178
+ sessionKey,
179
+ sessionTarget: {
180
+ agentId: "copilot-policy-live-proof",
181
+ sessionId: params.sessionId,
182
+ sessionKey,
183
+ storePath: path.join(params.workspaceDir, "openclaw-agent.sqlite"),
184
+ },
185
+ thinkLevel: "low" as const,
186
+ timeoutMs: 90_000,
187
+ toolAuthorityFingerprint: params.toolAuthorityFingerprint,
188
+ userTurnTranscriptRecorder: createUserTurnRecorder(message),
189
+ workspaceDir: params.workspaceDir,
190
+ ...params.policy,
191
+ } satisfies CopilotLiveAttemptParams;
192
+ return await runAgentHarnessAttempt(attempt);
193
+ } finally {
194
+ admission.close();
195
+ }
196
+ }
197
+
198
+ describeLive("Copilot tool policy live handoff", () => {
199
+ it("blocks native ask_user before the Gateway when an allowed session resumes deny-all", async () => {
200
+ gatewayFixture.calls.length = 0;
201
+ gatewayFixture.call.mockClear();
202
+ const workspaceDir = tempDirs.make("openclaw-copilot-policy-live-");
203
+ const sessionId = "copilot-policy-live-session";
204
+ const sessionKey = `agent:copilot-policy-live-proof:${sessionId}`;
205
+ const toolAuthorityFingerprint = "copilot-policy-live-authority";
206
+ const bindings = new Map<string, CopilotSessionBindingForTest>();
207
+ const sessionStore = {
208
+ async delete(key: string) {
209
+ return bindings.delete(key);
210
+ },
211
+ async lookup(key: string) {
212
+ return bindings.get(key);
213
+ },
214
+ async register(key: string, value: CopilotSessionBindingForTest) {
215
+ bindings.set(key, value);
216
+ },
217
+ };
218
+ const fixture = createNativeCopilotPolicyHarnessFixtureForTest(sessionStore);
219
+ harnesses.push(fixture);
220
+ registerAgentHarness(fixture.harness, { ownerPluginId: "copilot" });
221
+ await replaceSessionEntry(
222
+ {
223
+ agentId: "copilot-policy-live-proof",
224
+ sessionKey,
225
+ storePath: path.join(workspaceDir, "openclaw-agent.sqlite"),
226
+ },
227
+ { sessionId, updatedAt: Date.now() },
228
+ );
229
+ const authStorage = AuthStorage.inMemory();
230
+ const modelRegistry = ModelRegistry.inMemory(authStorage);
231
+ const blockReplies: string[] = [];
232
+
233
+ const allowedTurn = runLiveCopilotTurn({
234
+ authStorage,
235
+ blockReplies,
236
+ modelRegistry,
237
+ policy: {},
238
+ prompt:
239
+ "Call ask_user exactly once. Ask 'Select the live proof mode' with choices Alpha and Beta and no freeform. After the answer, reply briefly. Do not use any other tool.",
240
+ runId: "copilot-policy-live-allowed",
241
+ sessionId,
242
+ toolAuthorityFingerprint,
243
+ workspaceDir,
244
+ });
245
+ const allowedResult = await allowedTurn;
246
+ expect(allowedResult.terminal).toEqual({ kind: "ok" });
247
+ expect(gatewayFixture.calls.filter((call) => call.method === "question.request")).toEqual([
248
+ expect.objectContaining({
249
+ params: expect.objectContaining({
250
+ questions: [expect.objectContaining({ question: "Select the live proof mode" })],
251
+ }),
252
+ }),
253
+ ]);
254
+ const allowedBinding = bindings.get(sessionId);
255
+ expect(allowedBinding?.sdkSessionId).toBeTruthy();
256
+ expect(
257
+ fixture.requests.some(
258
+ (request) => !request.restricted && request.toolNames.includes("ask_user"),
259
+ ),
260
+ ).toBe(true);
261
+
262
+ const disabledCreateSessionId = "copilot-policy-live-disabled-create-session";
263
+ const disabledCreateSessionKey = `agent:copilot-policy-live-proof:${disabledCreateSessionId}`;
264
+ await replaceSessionEntry(
265
+ {
266
+ agentId: "copilot-policy-live-proof",
267
+ sessionKey: disabledCreateSessionKey,
268
+ storePath: path.join(workspaceDir, "openclaw-agent.sqlite"),
269
+ },
270
+ { sessionId: disabledCreateSessionId, updatedAt: Date.now() },
271
+ );
272
+ const disabledCreateRequestStart = fixture.requests.length;
273
+ const disabledCreateResult = await runLiveCopilotTurn({
274
+ authStorage,
275
+ blockReplies,
276
+ modelRegistry,
277
+ policy: { disableTools: true },
278
+ prompt:
279
+ "Call ask_user exactly once and ask 'This question must be blocked'. If no such tool is available, reply exactly RESTRICTED-NO-ASK-USER.",
280
+ runId: "copilot-policy-live-disabled-create",
281
+ sessionId: disabledCreateSessionId,
282
+ toolAuthorityFingerprint,
283
+ workspaceDir,
284
+ });
285
+ expect(disabledCreateResult.terminal).toEqual({ kind: "ok" });
286
+ expect(disabledCreateResult.assistantTexts.join("\n").trim()).toBe("RESTRICTED-NO-ASK-USER");
287
+ expect(bindings.get(disabledCreateSessionId)?.sdkSessionId).toBeTruthy();
288
+ expect(bindings.get(disabledCreateSessionId)?.sdkSessionId).not.toBe(
289
+ allowedBinding?.sdkSessionId,
290
+ );
291
+ expect(
292
+ fixture.requests
293
+ .slice(disabledCreateRequestStart)
294
+ .every((request) => !request.toolNames.includes("ask_user")),
295
+ ).toBe(true);
296
+
297
+ const restrictedResumeRequestStart = fixture.requests.length;
298
+ const restrictedResult = await runLiveCopilotTurn({
299
+ authStorage,
300
+ blockReplies,
301
+ modelRegistry,
302
+ policy: { disableTools: true },
303
+ prompt:
304
+ "Call ask_user exactly once and ask 'This question must be blocked'. If no such tool is available, reply exactly RESTRICTED-NO-ASK-USER.",
305
+ runId: "copilot-policy-live-restricted-resume",
306
+ sessionId,
307
+ toolAuthorityFingerprint,
308
+ workspaceDir,
309
+ });
310
+ expect(restrictedResult.terminal).toEqual({ kind: "ok" });
311
+ expect(blockReplies).toHaveLength(0);
312
+ expect(gatewayFixture.calls.filter((call) => call.method === "question.request")).toHaveLength(
313
+ 1,
314
+ );
315
+ expect(bindings.get(sessionId)?.sdkSessionId).toBe(allowedBinding?.sdkSessionId);
316
+ expect(restrictedResult.assistantTexts.join("\n").trim()).toBe("RESTRICTED-NO-ASK-USER");
317
+ const restrictedModelRequests = fixture.requests.slice(restrictedResumeRequestStart);
318
+ expect(restrictedModelRequests.length).toBeGreaterThan(0);
319
+ expect(fixture.requests.every((request) => !request.streaming)).toBe(true);
320
+ expect(
321
+ restrictedModelRequests.every((request) => !request.toolNames.includes("ask_user")),
322
+ ).toBe(true);
323
+
324
+ console.info(
325
+ "[copilot-policy-live-proof]",
326
+ JSON.stringify({
327
+ allowedGatewayQuestions: 1,
328
+ allowedTerminal: allowedResult.terminal.kind,
329
+ disabledCreateGatewayQuestions: 0,
330
+ disabledCreateTerminal: disabledCreateResult.terminal.kind,
331
+ nativeSessionResumed: true,
332
+ restrictedGatewayQuestions: 0,
333
+ restrictedReply: restrictedResult.assistantTexts.join("\n").trim(),
334
+ restrictedTerminal: restrictedResult.terminal.kind,
335
+ }),
336
+ );
337
+ }, 180_000);
338
+ });
test/extension-import-boundaries.test.ts ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Extension import boundary tests enforce extension/core import rules.
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { createExtensionPluginSdkBoundaryChecker } from "../scripts/check-extension-plugin-sdk-boundary.mts";
6
+ import { main as sdkPackageMain } from "../scripts/check-sdk-package-extension-import-boundary.mts";
7
+ import { main as srcExtensionMain } from "../scripts/check-src-extension-import-boundary.mts";
8
+ import { createCapturedIo } from "./helpers/captured-io.js";
9
+ import { useAutoCleanupTempDirTracker } from "./helpers/temp-dir.js";
10
+
11
+ const tempDirs = useAutoCleanupTempDirTracker(afterEach);
12
+
13
+ type CapturedIo = ReturnType<typeof createCapturedIo>["io"];
14
+ type JsonOutputPromise = ReturnType<typeof getJsonOutput>;
15
+
16
+ const boundaryInventoryCases: Array<{
17
+ name: string;
18
+ output: JsonOutputPromise;
19
+ }> = [
20
+ {
21
+ name: "src extension import boundary",
22
+ output: getJsonOutput(srcExtensionMain, ["--json"]),
23
+ },
24
+ {
25
+ name: "sdk/package extension import boundary",
26
+ output: getJsonOutput(sdkPackageMain, ["--json"]),
27
+ },
28
+ ];
29
+
30
+ describe("extension import boundary inventories", () => {
31
+ it.each(boundaryInventoryCases)("$name JSON output stays empty", async ({ output }) => {
32
+ const jsonOutput = await output;
33
+
34
+ expect(jsonOutput.exitCode).toBe(0);
35
+ expect(jsonOutput.stderr).toBe("");
36
+ expect(jsonOutput.json).toStrictEqual([]);
37
+ });
38
+ });
39
+
40
+ type BoundaryFixture = {
41
+ file?: string;
42
+ packageJson?: unknown;
43
+ source: string;
44
+ };
45
+
46
+ function createBoundaryFixture(fixture: BoundaryFixture) {
47
+ const repoRoot = tempDirs.make("openclaw-normalization-boundary-");
48
+ const pluginRoot = path.join(repoRoot, "extensions", "demo");
49
+ const relativeFile = fixture.file ?? "src/runtime.ts";
50
+ const filePath = path.join(pluginRoot, relativeFile);
51
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
52
+ fs.writeFileSync(filePath, fixture.source, "utf8");
53
+ fs.writeFileSync(
54
+ path.join(pluginRoot, "package.json"),
55
+ JSON.stringify(fixture.packageJson ?? { name: "@openclaw/demo" }),
56
+ "utf8",
57
+ );
58
+ return createExtensionPluginSdkBoundaryChecker({ repoRoot });
59
+ }
60
+
61
+ describe("aggregate extension plugin SDK boundaries", () => {
62
+ const modes = ["src-outside-plugin-sdk", "normalization-core-bypass", "relative-outside-package"];
63
+
64
+ it.each([
65
+ { name: "clean", source: 'import "./local.js";', expectedCode: 0 },
66
+ { name: "core escape", source: 'import "../../../src/private.js";', expectedCode: 1 },
67
+ { name: "normalization", source: 'import "@openclaw/normalization-core";', expectedCode: 1 },
68
+ { name: "relative escape", source: 'import "../../other/api.js";', expectedCode: 1 },
69
+ {
70
+ name: "overlapping modes",
71
+ source: 'import "../../../src/utils/boolean.js";',
72
+ expectedCode: 1,
73
+ },
74
+ ])("preserves all mode reports for $name", async ({ source, expectedCode }) => {
75
+ const fixture = { source };
76
+ const expected = createCapturedIo();
77
+ for (const mode of modes) {
78
+ await createBoundaryFixture(fixture).main([`--mode=${mode}`], expected.io);
79
+ }
80
+ const actual = createCapturedIo();
81
+ const code = await createBoundaryFixture(fixture).main(["--all"], actual.io);
82
+ expect(code).toBe(expectedCode);
83
+ expect(actual.readStdout()).toBe(expected.readStdout());
84
+ expect(actual.readStderr()).toBe(expected.readStderr());
85
+ });
86
+
87
+ it("reports inventory failure for every mode and fails the aggregate", async () => {
88
+ const checker = createBoundaryFixture({ source: " ".repeat(2 * 1024 * 1024 + 1) });
89
+ const actual = createCapturedIo();
90
+ expect(await checker.main(["--all"], actual.io)).toBe(1);
91
+ expect(actual.readStdout()).toBe("");
92
+ const errors = actual.readStderr();
93
+ expect(errors.match(/exceeds 2097152 byte limit/gu)).toHaveLength(3);
94
+ expect(errors).toContain("must not import src/**");
95
+ expect(errors).toContain("must not import normalization-core directly");
96
+ expect(errors).toContain("relative imports that escape");
97
+ });
98
+
99
+ it.each([
100
+ ["--all", "--json"],
101
+ ["--all", "--mode=src-outside-plugin-sdk"],
102
+ ])("rejects conflicting aggregate arguments %j", async (...argv) => {
103
+ const actual = createCapturedIo();
104
+ await expect(createBoundaryFixture({ source: "" }).main(argv, actual.io)).rejects.toThrow(
105
+ "--all cannot be combined with --json or --mode",
106
+ );
107
+ expect(actual.readStdout()).toBe("");
108
+ expect(actual.readStderr()).toBe("");
109
+ });
110
+ });
111
+
112
+ describe("production plugin normalization ownership boundary", () => {
113
+ it.each([
114
+ {
115
+ name: "static string import",
116
+ source:
117
+ 'import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";',
118
+ kind: "import",
119
+ specifier: "@openclaw/normalization-core/string-coerce",
120
+ resolvedPath: "packages/normalization-core/src/string-coerce.ts",
121
+ facade: "openclaw/plugin-sdk/string-coerce-runtime",
122
+ },
123
+ {
124
+ name: "record re-export from public barrel",
125
+ file: "api.ts",
126
+ source: 'export { isRecord } from "@openclaw/normalization-core/record-coerce";',
127
+ kind: "export",
128
+ specifier: "@openclaw/normalization-core/record-coerce",
129
+ resolvedPath: "packages/normalization-core/src/record-coerce.ts",
130
+ facade: "openclaw/plugin-sdk/string-coerce-runtime",
131
+ },
132
+ {
133
+ name: "dynamic number import",
134
+ source: 'await import("@openclaw/normalization-core/number-coercion");',
135
+ kind: "dynamic-import",
136
+ specifier: "@openclaw/normalization-core/number-coercion",
137
+ resolvedPath: "packages/normalization-core/src/number-coercion.ts",
138
+ facade: "openclaw/plugin-sdk/number-runtime",
139
+ },
140
+ {
141
+ name: "error import",
142
+ source: 'import { toErrorObject } from "@openclaw/normalization-core/error-coercion";',
143
+ kind: "import",
144
+ specifier: "@openclaw/normalization-core/error-coercion",
145
+ resolvedPath: "packages/normalization-core/src/error-coercion.ts",
146
+ facade: "openclaw/plugin-sdk/error-runtime",
147
+ },
148
+ {
149
+ name: "bare package import",
150
+ source: 'import { expectDefined } from "@openclaw/normalization-core";',
151
+ kind: "import",
152
+ specifier: "@openclaw/normalization-core",
153
+ resolvedPath: "packages/normalization-core/src/index.ts",
154
+ },
155
+ {
156
+ name: "relative normalization-core escape",
157
+ source:
158
+ 'import { isRecord } from "../../../packages/normalization-core/src/record-coerce.js";',
159
+ kind: "import",
160
+ specifier: "../../../packages/normalization-core/src/record-coerce.js",
161
+ resolvedPath: "packages/normalization-core/src/record-coerce.js",
162
+ facade: "openclaw/plugin-sdk/string-coerce-runtime",
163
+ },
164
+ {
165
+ name: "relative boolean owner escape",
166
+ source: 'import { parseBooleanValue } from "../../../src/utils/boolean.js";',
167
+ kind: "import",
168
+ specifier: "../../../src/utils/boolean.js",
169
+ resolvedPath: "src/utils/boolean.js",
170
+ facade: "openclaw/plugin-sdk/string-coerce-runtime",
171
+ },
172
+ {
173
+ name: "relative core error owner escape",
174
+ source: 'import { formatErrorMessage } from "../../../src/infra/errors.js";',
175
+ kind: "import",
176
+ specifier: "../../../src/infra/errors.js",
177
+ resolvedPath: "src/infra/errors.js",
178
+ facade: "openclaw/plugin-sdk/error-runtime",
179
+ },
180
+ ])(
181
+ "rejects $name with a specific SDK facade when known",
182
+ async ({ source, file, kind, specifier, resolvedPath, facade }) => {
183
+ const checker = createBoundaryFixture({ source, file });
184
+ const captured = createCapturedIo();
185
+ const exitCode = await checker.main(
186
+ ["--mode=normalization-core-bypass", "--json"],
187
+ captured.io,
188
+ );
189
+ const entries = JSON.parse(captured.readStdout()) as Array<Record<string, unknown>>;
190
+
191
+ expect(exitCode).toBe(1);
192
+ expect(captured.readStderr()).toBe("");
193
+ expect(entries).toHaveLength(1);
194
+ expect(entries[0]).toMatchObject({
195
+ file: `extensions/demo/${file ?? "src/runtime.ts"}`,
196
+ line: 1,
197
+ kind,
198
+ specifier,
199
+ resolvedPath,
200
+ });
201
+ expect(entries[0]?.reason).toContain(facade ?? "matching openclaw/plugin-sdk facade");
202
+ if (facade === "openclaw/plugin-sdk/number-runtime") {
203
+ expect(entries[0]?.reason).toContain("bundled/private-local");
204
+ }
205
+ },
206
+ );
207
+
208
+ it.each([
209
+ {
210
+ name: "approved SDK facades",
211
+ source: [
212
+ 'import "openclaw/plugin-sdk/string-coerce-runtime";',
213
+ 'import "openclaw/plugin-sdk/number-runtime";',
214
+ 'import "openclaw/plugin-sdk/error-runtime";',
215
+ ].join("\n"),
216
+ },
217
+ { name: "plugin-local import", source: 'import "./local.js";' },
218
+ {
219
+ name: "test source",
220
+ file: "src/runtime.test.ts",
221
+ source: 'import "@openclaw/normalization-core/record-coerce";',
222
+ },
223
+ {
224
+ name: "dist generated source",
225
+ file: "dist/generated.js",
226
+ source: 'import "@openclaw/normalization-core/record-coerce";',
227
+ },
228
+ {
229
+ name: "declared generated asset",
230
+ file: "src/generated.js",
231
+ packageJson: {
232
+ name: "@openclaw/demo",
233
+ openclaw: {
234
+ assetScripts: { build: "node build.mjs" },
235
+ build: { staticAssets: [{ source: "src/generated.js", output: "generated.js" }] },
236
+ },
237
+ },
238
+ source: 'import "@openclaw/normalization-core/record-coerce";',
239
+ },
240
+ ])("allows $name", async ({ source, file, packageJson }) => {
241
+ const checker = createBoundaryFixture({ source, file, packageJson });
242
+ const captured = createCapturedIo();
243
+
244
+ expect(await checker.main(["--mode=normalization-core-bypass", "--json"], captured.io)).toBe(0);
245
+ expect(captured.readStderr()).toBe("");
246
+ expect(JSON.parse(captured.readStdout())).toEqual([]);
247
+ });
248
+
249
+ it("renders actionable human diagnostics and fails strict mode", async () => {
250
+ const checker = createBoundaryFixture({
251
+ source: 'export { toErrorObject } from "@openclaw/normalization-core/error-coercion";',
252
+ file: "runtime-api.ts",
253
+ });
254
+ const captured = createCapturedIo();
255
+
256
+ expect(await checker.main(["--mode=normalization-core-bypass"], captured.io)).toBe(1);
257
+ expect(captured.readStdout()).toContain(
258
+ "Rule: production bundled plugins must not import normalization-core directly",
259
+ );
260
+ expect(captured.readStdout()).toContain("extensions/demo/runtime-api.ts");
261
+ expect(captured.readStdout()).toContain("line 1 [export]");
262
+ expect(captured.readStdout()).toContain("re-exports");
263
+ expect(captured.readStdout()).toContain("@openclaw/normalization-core/error-coercion");
264
+ expect(captured.readStdout()).toContain("openclaw/plugin-sdk/error-runtime");
265
+ expect(captured.readStderr()).toContain("violations found (1)");
266
+ });
267
+
268
+ it("rejects plugin-sdk-internal through the strict src-outside-plugin-sdk rule", async () => {
269
+ const checker = createBoundaryFixture({
270
+ source: 'import "../../../src/plugin-sdk-internal/private.js";',
271
+ });
272
+ const captured = createCapturedIo();
273
+
274
+ expect(await checker.main(["--mode=src-outside-plugin-sdk"], captured.io)).toBe(1);
275
+ expect(captured.readStdout()).toContain("src/plugin-sdk-internal/private.js");
276
+ expect(captured.readStderr()).toContain("violations found (1)");
277
+ });
278
+ });
279
+
280
+ async function getJsonOutput(
281
+ main: (argv: string[], io: CapturedIo) => Promise<number>,
282
+ argv: string[],
283
+ ) {
284
+ const captured = createCapturedIo();
285
+ const exitCode = await main(argv, captured.io);
286
+ return {
287
+ exitCode,
288
+ stderr: captured.readStderr(),
289
+ json: JSON.parse(captured.readStdout()),
290
+ };
291
+ }
test/external-script-modules.d.ts ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ declare module "*security/opengrep/check-rule-metadata.mjs" {
2
+ export function validateRuleMetadata(
3
+ rules: Array<{ id: string; metadata?: Record<string, string> }>,
4
+ ): string[];
5
+ }
6
+
7
+ declare module "*openclaw-changelog-update/scripts/verify-release-notes.mjs" {
8
+ type ContributionRecord = {
9
+ externalReferences?: string[];
10
+ references: number[];
11
+ thanks: string[];
12
+ };
13
+ export function createGithubSnapshotState(params: Record<string, unknown>): {
14
+ base: string;
15
+ checkpointEvery: number;
16
+ dirty: boolean;
17
+ filePath: string;
18
+ hits: number;
19
+ misses: number;
20
+ repository: string;
21
+ responses: Record<string, unknown>;
22
+ target: string;
23
+ writesSincePersist: number;
24
+ };
25
+ export function githubApiWithSnapshot(
26
+ args: string[],
27
+ fetchApi: (args: string[]) => unknown,
28
+ snapshotState: Record<string, unknown>,
29
+ ): unknown;
30
+ export function persistGithubSnapshot(snapshotState: Record<string, unknown>): void;
31
+ export function defaultGithubSnapshotPath(
32
+ base: string,
33
+ target: string,
34
+ gitCommonDir: string,
35
+ ): string;
36
+ export function renderContributionRecordEntry(entry: Record<string, unknown>): string;
37
+ export function releaseNoteReferences(
38
+ sectionSource: string,
39
+ shippedBaselines: unknown[],
40
+ ): number[];
41
+ export function standardRevertedHash(message: string): string | null;
42
+ export function contributionRecordTarget(section: { source: string }): string | undefined;
43
+ export function pullRequestTitleFromCommitSubject(
44
+ subject: string,
45
+ number: number,
46
+ ): string | undefined;
47
+ export function contributionRecordFor(section: Record<string, unknown>): {
48
+ legacyIssues: Map<number, unknown>;
49
+ pullRequests: Map<number, ContributionRecord>;
50
+ };
51
+ export function recoverUnavailablePullRequests(params: {
52
+ numbers: Iterable<number>;
53
+ nodes: Map<number, unknown>;
54
+ record: { pullRequests: Map<number, ContributionRecord> };
55
+ recordTarget?: string;
56
+ source: {
57
+ activeCommits: Array<{
58
+ authorHandle?: string;
59
+ closingReferences?: number[];
60
+ committedAt: string;
61
+ hash: string;
62
+ pullRequests: number[];
63
+ references: number[];
64
+ subject: string;
65
+ }>;
66
+ coauthorsByReference: Map<number, Set<string>>;
67
+ pullRequests: Set<number>;
68
+ target: string;
69
+ };
70
+ isAncestor?: (ancestor: string, descendant: string) => boolean;
71
+ }): Map<number, unknown>;
72
+ export function cumulativeShippedPullRequests(changelog: unknown, label: string): Set<number>;
73
+ export function subtractShippedPullRequests(
74
+ source: unknown,
75
+ baselines: unknown[],
76
+ ): {
77
+ baselines: unknown[];
78
+ pullRequests: Set<number>;
79
+ };
80
+ export function withoutExcludedContributionRecords(
81
+ record: {
82
+ legacyIssues: Map<number, ContributionRecord>;
83
+ pullRequests: Map<number, ContributionRecord>;
84
+ },
85
+ excluded: Set<number>,
86
+ ): {
87
+ legacyIssues: Map<number, ContributionRecord>;
88
+ pullRequests: Map<number, ContributionRecord>;
89
+ };
90
+ export function renderedContributionRecordReferences(
91
+ record: {
92
+ legacyIssues: Map<number, ContributionRecord>;
93
+ pullRequests: Map<number, ContributionRecord>;
94
+ },
95
+ writeLedger: boolean,
96
+ ): number[];
97
+ export function contaminatingPullRequestReferences(params: Record<string, unknown>): unknown[];
98
+ export function canonicalMainCommitMatches(commit: unknown, candidates: unknown[]): unknown[];
99
+ export function canonicalPullRequests(
100
+ currentPullRequests: unknown[],
101
+ mainPullRequests: unknown[],
102
+ hasCanonicalMainCommit?: boolean,
103
+ ): unknown[];
104
+ export function releaseProvenanceMarkers(
105
+ message: string,
106
+ ): Array<{ commit: string; pullRequests: number[] }>;
107
+ export function collectReleaseProvenanceOverrides(
108
+ activeCommits: Array<{ body: string; hash: string }>,
109
+ releaseProvenance?: string[],
110
+ ): Map<string, number[]>;
111
+ export function parseArgs(argv: string[]): {
112
+ releaseProvenance: string[];
113
+ [key: string]: unknown;
114
+ };
115
+ export function resolvedReleasePullRequests(
116
+ currentPullRequests: number[],
117
+ mainPullRequests: number[],
118
+ hasCanonicalMainCommit: boolean,
119
+ provenanceOverride?: number[],
120
+ ): number[];
121
+ export function releasePullRequestReferencesToSuppress(
122
+ currentPullRequests: number[],
123
+ subject: string,
124
+ associatedPullRequests: number[],
125
+ hasProvenanceOverride: boolean,
126
+ ): number[];
127
+ export function validateReleaseProvenanceOverrides(
128
+ provenanceOverrides: Map<string, number[]>,
129
+ nodes: Map<number, unknown>,
130
+ mainCommit: string,
131
+ isMainAncestor?: (ancestor: string, descendant: string) => boolean,
132
+ ): void;
133
+ export function ledgerFor(...args: unknown[]): {
134
+ entries: unknown[];
135
+ issues: unknown[];
136
+ ledger: string;
137
+ pullRequests: unknown[];
138
+ titleReferences: unknown[];
139
+ };
140
+ export function countTopLevelSectionBullets(sectionSource: string, heading: string): number;
141
+ export function highlightCountError(sectionSource: string): string | undefined;
142
+ export function isEligibleHandle(handle: string): boolean;
143
+ export function ledgerChecks(...args: unknown[]): string[];
144
+ }
test/feishu-message-read-authority.integration.test.ts ADDED
@@ -0,0 +1,755 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { randomUUID } from "node:crypto";
2
+ import { Agent, createServer } from "node:https";
3
+ import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
4
+ import { createOperationalRunInstanceRef } from "../src/agents/admitted-run-context.js";
5
+ import { wrapToolWithGatewayCallerIdentity } from "../src/agents/tools/gateway-caller-context.js";
6
+ import { createMessageTool } from "../src/agents/tools/message-tool-execution.js";
7
+ import { dispatchChannelMessageAction } from "../src/channels/plugins/message-action-dispatch.js";
8
+ import type {
9
+ ChannelMessageActionContext,
10
+ ChannelMessageActionName,
11
+ ChannelPlugin,
12
+ } from "../src/channels/plugins/types.js";
13
+ import {
14
+ clearRuntimeConfigSnapshot,
15
+ setRuntimeConfigSnapshot,
16
+ type OpenClawConfig,
17
+ } from "../src/config/config.js";
18
+ import { createAgentRuntimeApprovalAuthorityValidator } from "../src/gateway/agent-runtime-identity-token.js";
19
+ import {
20
+ mintMessageActionTurnCapability,
21
+ revokeMessageActionTurnCapability,
22
+ } from "../src/gateway/message-action-turn-capability.js";
23
+ import { createAgentRuntimeAuthorityGuard } from "../src/gateway/server-methods/agent-runtime-authority.js";
24
+ import type { GatewayClient, GatewayRequestContext } from "../src/gateway/server-methods/types.js";
25
+ import {
26
+ claimAgentRunDelegatedAuthority,
27
+ releaseAgentRunDelegatedAuthority,
28
+ validateAgentRunDelegatedAuthority,
29
+ } from "../src/infra/agent-run-registry.js";
30
+ import { createPluginRegistry } from "../src/plugins/registry.js";
31
+ import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../src/plugins/runtime.js";
32
+ import type { PluginRuntime } from "../src/plugins/runtime/types.js";
33
+ import { createPluginRecord } from "../src/plugins/status.test-fixtures.js";
34
+ import { loadBundledPluginFacade } from "../src/test-utils/bundled-plugin-public-surface.js";
35
+ import { TEST_TLS_CERT_PEM, TEST_TLS_KEY_PEM } from "./helpers/tls-fixture.js";
36
+
37
+ const transport = vi.hoisted(() => ({ agent: undefined as Agent | undefined }));
38
+ vi.mock("openclaw/plugin-sdk/extension-shared", async (original) => {
39
+ const actual = await original<typeof import("openclaw/plugin-sdk/extension-shared")>();
40
+ return { ...actual, resolveAmbientNodeProxyAgent: async () => transport.agent };
41
+ });
42
+
43
+ const CURRENT = "oc_current";
44
+ const ALLOWED = "oc_allowed";
45
+ const BLOCKED = "oc_blocked";
46
+ const DIRECT = "oc_direct";
47
+ const SELF = "ou_current_sender";
48
+ const MEMBER = "ou_group_member";
49
+ const OTHER = "ou_other_member";
50
+ const MESSAGE = "om_allowed";
51
+ const AUTH_PATH = "/open-apis/auth/v3/tenant_access_token/internal";
52
+ const MESSAGE_PATH = `/open-apis/im/v1/messages/${MESSAGE}`;
53
+ const CHAT_PATH = `/open-apis/im/v1/chats/${ALLOWED}`;
54
+ const MEMBERS_PATH = `${CHAT_PATH}/members`;
55
+ const PINS_PATH = "/open-apis/im/v1/pins";
56
+ const USER_PATH = `/open-apis/contact/v3/users/${MEMBER}`;
57
+ const PEERS_PATH = "/open-apis/contact/v3/users";
58
+ const PEERS_PAGE_TOKEN = "peers/next+%2F=";
59
+ type ActionResult = NonNullable<Awaited<ReturnType<typeof dispatchChannelMessageAction>>>;
60
+ type ProviderRequest = { method: string; path: string; query: URLSearchParams };
61
+
62
+ function createOriginatingRun(currentChat = CURRENT) {
63
+ const sessionKey = `agent:main:feishu:channel:${currentChat}`;
64
+ const operationalRunInstance = createOperationalRunInstanceRef(`feishu-read-${randomUUID()}`);
65
+ const delegatedAuthority = claimAgentRunDelegatedAuthority(operationalRunInstance);
66
+ const toolContext = {
67
+ currentChannelProvider: "feishu",
68
+ currentChannelId: currentChat,
69
+ currentChatType: currentChat === DIRECT ? ("direct" as const) : ("group" as const),
70
+ };
71
+ const turnCapability = mintMessageActionTurnCapability({
72
+ agentId: "main",
73
+ runId: operationalRunInstance.runId,
74
+ sessionKey,
75
+ requesterAccountId: "default",
76
+ requesterSenderId: SELF,
77
+ toolContext,
78
+ });
79
+ const assert = createAgentRuntimeAuthorityGuard(
80
+ {
81
+ internal: {
82
+ agentRuntimeIdentity: {
83
+ kind: "agentRuntime",
84
+ agentId: "main",
85
+ sessionKey,
86
+ operationalRunInstance,
87
+ delegatedAuthority: { kind: "local", ...delegatedAuthority },
88
+ messageActionContext: { expiresAtMs: Date.now() + 60_000, turnCapability },
89
+ },
90
+ },
91
+ } as GatewayClient,
92
+ {
93
+ validateAgentRuntimeApprovalAuthority: createAgentRuntimeApprovalAuthorityValidator(),
94
+ } as GatewayRequestContext,
95
+ () => {},
96
+ ).commitGuard;
97
+ if (!assert) {
98
+ throw new Error("Expected originating Feishu run authority");
99
+ }
100
+ assert();
101
+ return {
102
+ assert,
103
+ toolContext,
104
+ revokeTurn: () => revokeMessageActionTurnCapability(turnCapability),
105
+ releaseClaim: () => releaseAgentRunDelegatedAuthority(delegatedAuthority),
106
+ createTool: (config: OpenClawConfig) =>
107
+ wrapToolWithGatewayCallerIdentity(
108
+ createMessageTool({
109
+ agentId: "main",
110
+ agentAccountId: "default",
111
+ agentSessionKey: sessionKey,
112
+ runId: operationalRunInstance.runId,
113
+ messageActionTurnCapability: turnCapability,
114
+ ...toolContext,
115
+ config,
116
+ getScopedChannelsCommandSecretTargets: () => ({ targetIds: new Set<string>() }),
117
+ resolveCommandSecretRefsViaGateway: async ({ config: resolvedConfig }) => ({
118
+ resolvedConfig,
119
+ diagnostics: [],
120
+ targetStatesByPath: {},
121
+ hadUnresolvedTargets: false,
122
+ }),
123
+ }),
124
+ {
125
+ agentId: "main",
126
+ sessionKey,
127
+ operationalRunInstance,
128
+ receiptAuthority: () => validateAgentRunDelegatedAuthority(delegatedAuthority),
129
+ },
130
+ ),
131
+ dispose: () => {
132
+ revokeMessageActionTurnCapability(turnCapability);
133
+ releaseAgentRunDelegatedAuthority(delegatedAuthority);
134
+ },
135
+ };
136
+ }
137
+
138
+ let feishuPlugin: ChannelPlugin;
139
+ let server: ReturnType<typeof createServer>;
140
+ let origin: string;
141
+ let appId: string;
142
+ let requests: ProviderRequest[] = [];
143
+ let pending: Promise<unknown>[] = [];
144
+ let runs: ReturnType<typeof createOriginatingRun>[] = [];
145
+ let beforeReply: ((request: ProviderRequest) => void) | undefined;
146
+ let afterProviderResult: ((result: ActionResult) => void) | undefined;
147
+
148
+ function track<T>(operation: Promise<T>): Promise<T> {
149
+ pending.push(operation);
150
+ return operation;
151
+ }
152
+
153
+ function contentRequests() {
154
+ return requests.filter((request) => request.path !== AUTH_PATH).map((request) => request.path);
155
+ }
156
+
157
+ function expectResult(result: ActionResult | null, details: Record<string, unknown>) {
158
+ expect(result?.details).toMatchObject(details);
159
+ const text = result?.content.find((part) => part.type === "text");
160
+ if (text?.type !== "text") {
161
+ throw new Error("Expected a model-visible Feishu result");
162
+ }
163
+ expect(JSON.parse(text.text)).toMatchObject(details);
164
+ }
165
+
166
+ beforeAll(async () => {
167
+ // TLS trust is fixture-owned; the real SDK can connect only to this loopback host.
168
+ transport.agent = new Agent({ rejectUnauthorized: false });
169
+ const connect = transport.agent.createConnection.bind(transport.agent);
170
+ transport.agent.createConnection = (options, callback) => {
171
+ if (options.host !== "127.0.0.1") {
172
+ throw new Error("Feishu fixture refused a non-loopback destination");
173
+ }
174
+ return connect(options, callback);
175
+ };
176
+ ({ feishuPlugin } = await loadBundledPluginFacade<{ feishuPlugin: ChannelPlugin }>({
177
+ pluginId: "feishu",
178
+ artifactBasename: "api.js",
179
+ }));
180
+ server = createServer({ key: TEST_TLS_KEY_PEM, cert: TEST_TLS_CERT_PEM }, (request, response) => {
181
+ void (async () => {
182
+ const chunks: Buffer[] = [];
183
+ for await (const chunk of request) {
184
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
185
+ }
186
+ const url = new URL(request.url ?? "/", origin);
187
+ const recorded = {
188
+ method: request.method ?? "",
189
+ path: url.pathname,
190
+ query: url.searchParams,
191
+ };
192
+ requests.push(recorded);
193
+ beforeReply?.(recorded);
194
+ response.setHeader("content-type", "application/json");
195
+ if (url.pathname === AUTH_PATH) {
196
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8")) as Record<string, unknown>;
197
+ if (
198
+ request.method !== "POST" ||
199
+ body.app_id !== appId ||
200
+ body.app_secret !== "loopback-placeholder"
201
+ ) {
202
+ response.writeHead(401);
203
+ response.end(JSON.stringify({ code: 99991663, msg: "invalid fixture application" }));
204
+ return;
205
+ }
206
+ response.end(
207
+ JSON.stringify({ code: 0, tenant_access_token: `tat_${appId}`, expire: 7200 }),
208
+ );
209
+ return;
210
+ }
211
+ if (request.method !== "GET" || request.headers.authorization !== `Bearer tat_${appId}`) {
212
+ response.writeHead(401);
213
+ response.end(JSON.stringify({ code: 99991663, msg: "unexpected fixture request" }));
214
+ return;
215
+ }
216
+ let data: unknown;
217
+ if (url.pathname === MESSAGE_PATH) {
218
+ data = {
219
+ items: [
220
+ {
221
+ message_id: MESSAGE,
222
+ chat_id: ALLOWED,
223
+ msg_type: "text",
224
+ body: { content: JSON.stringify({ text: "allowed context" }) },
225
+ },
226
+ ],
227
+ };
228
+ } else if (url.pathname === `${MESSAGE_PATH}/reactions`) {
229
+ data = {
230
+ items: [
231
+ {
232
+ reaction_id: "reaction-1",
233
+ reaction_type: { emoji_type: "THUMBSUP" },
234
+ operator: { operator_type: "user", operator_id: MEMBER },
235
+ },
236
+ ],
237
+ has_more: false,
238
+ };
239
+ } else if (url.pathname === PINS_PATH) {
240
+ data = { items: [{ message_id: MESSAGE, chat_id: ALLOWED }], has_more: false };
241
+ } else if (url.pathname === MEMBERS_PATH) {
242
+ const next = url.searchParams.get("page_token") === "members-next";
243
+ data = {
244
+ items: [{ member_id: next ? MEMBER : OTHER, member_id_type: "open_id" }],
245
+ has_more: !next,
246
+ ...(next ? {} : { page_token: "members-next" }),
247
+ };
248
+ } else if (url.pathname === "/open-apis/im/v1/chats") {
249
+ const next = url.searchParams.get("page_token") === "groups-next";
250
+ data = {
251
+ items: [{ chat_id: next ? ALLOWED : BLOCKED, name: next ? "Allowed" : "Blocked" }],
252
+ has_more: !next,
253
+ ...(next ? {} : { page_token: "groups-next" }),
254
+ };
255
+ } else if (url.pathname.startsWith("/open-apis/im/v1/chats/")) {
256
+ data = {
257
+ name: url.pathname.endsWith(DIRECT) ? "Direct" : "Allowed",
258
+ chat_mode: url.pathname.endsWith(DIRECT) ? "p2p" : "group",
259
+ chat_type: "private",
260
+ };
261
+ } else if (url.pathname === PEERS_PATH) {
262
+ const next = url.searchParams.get("page_token") === PEERS_PAGE_TOKEN;
263
+ data = {
264
+ items: [{ open_id: next ? OTHER : SELF, name: next ? "Other" : "Current sender" }],
265
+ has_more: !next,
266
+ ...(next ? {} : { page_token: PEERS_PAGE_TOKEN }),
267
+ };
268
+ } else if (url.pathname.startsWith("/open-apis/contact/v3/users/")) {
269
+ const id = url.pathname.split("/").at(-1);
270
+ data = { user: { open_id: id, name: id === SELF ? "Current sender" : "Allowed member" } };
271
+ } else {
272
+ response.writeHead(404);
273
+ response.end(JSON.stringify({ code: 404, msg: "unhandled fixture route" }));
274
+ return;
275
+ }
276
+ response.end(JSON.stringify({ code: 0, data }));
277
+ })().catch((error: unknown) => {
278
+ response.writeHead(500, { "content-type": "application/json" });
279
+ response.end(JSON.stringify({ error: String(error) }));
280
+ });
281
+ });
282
+ await new Promise<void>((resolve, reject) => {
283
+ server.once("error", reject);
284
+ server.listen(0, "127.0.0.1", resolve);
285
+ });
286
+ const address = server.address();
287
+ if (!address || typeof address === "string") {
288
+ throw new Error("Expected loopback TCP listener");
289
+ }
290
+ origin = `https://127.0.0.1:${address.port}`;
291
+ });
292
+
293
+ afterEach(async () => {
294
+ await Promise.allSettled(pending);
295
+ runs.forEach((run) => run.dispose());
296
+ runs = [];
297
+ pending = [];
298
+ requests = [];
299
+ beforeReply = undefined;
300
+ afterProviderResult = undefined;
301
+ resetPluginRuntimeStateForTest();
302
+ clearRuntimeConfigSnapshot();
303
+ vi.unstubAllEnvs();
304
+ });
305
+
306
+ afterAll(async () => {
307
+ transport.agent?.destroy();
308
+ server.closeAllConnections();
309
+ await new Promise<void>((resolve, reject) => {
310
+ server.close((error) => (error ? reject(error) : resolve()));
311
+ });
312
+ vi.doUnmock("openclaw/plugin-sdk/extension-shared");
313
+ vi.resetModules();
314
+ });
315
+
316
+ function createFixture(
317
+ options: {
318
+ bundled?: boolean;
319
+ trusted?: boolean;
320
+ currentChat?: string;
321
+ actions?: readonly ChannelMessageActionName[];
322
+ } = {},
323
+ ) {
324
+ vi.stubEnv("OPENCLAW_PROXY_ACTIVE", "0");
325
+ appId = `cli_feishu_${randomUUID()}`;
326
+ const stickerSets: Record<string, Record<string, string[]>> = {
327
+ [appId]: { file_allowed: ["thumbs up"] },
328
+ other_application: { file_other: ["thumbs up"] },
329
+ };
330
+ const settings = {
331
+ enabled: true,
332
+ appId,
333
+ appSecret: "loopback-placeholder", // pragma: allowlist secret
334
+ domain: origin,
335
+ groupPolicy: "allowlist" as "allowlist" | "open",
336
+ groupAllowFrom: [ALLOWED, BLOCKED],
337
+ groups: { [ALLOWED]: { enabled: true }, [BLOCKED]: { enabled: false } },
338
+ allowFrom: [SELF],
339
+ dms: { [OTHER]: {} },
340
+ actions: { reactions: true, sticker: true },
341
+ stickerSets,
342
+ };
343
+ const cfg = { channels: { feishu: settings } } satisfies OpenClawConfig;
344
+ const run = createOriginatingRun(options.currentChat);
345
+ runs.push(run);
346
+ const owner = createPluginRegistry({
347
+ logger: { info() {}, warn() {}, error() {}, debug() {} },
348
+ runtime: {} as PluginRuntime,
349
+ activateGlobalSideEffects: false,
350
+ });
351
+ // Registrar trust is a fixture; the prerequisite owns installer provenance proof.
352
+ const record = createPluginRecord({
353
+ id: "feishu",
354
+ origin: options.bundled ? "bundled" : "global",
355
+ trustedOfficialInstall: !options.bundled && options.trusted !== false,
356
+ });
357
+ const plugin: ChannelPlugin = {
358
+ ...feishuPlugin,
359
+ actions: {
360
+ ...feishuPlugin.actions!,
361
+ readAuthorityActions: options.actions ?? feishuPlugin.actions?.readAuthorityActions,
362
+ handleAction: async (ctx) => {
363
+ const result = await feishuPlugin.actions!.handleAction!(ctx);
364
+ // Revoke at the registered handler's return boundary after a real local consumer.
365
+ afterProviderResult?.(result);
366
+ return result;
367
+ },
368
+ },
369
+ };
370
+ owner.registry.plugins.push(record);
371
+ owner.createApi(record, { config: cfg, registrationMode: "full" }).registerChannel({ plugin });
372
+ setActivePluginRegistry(owner.registry);
373
+ const dispatch = (
374
+ action: ChannelMessageActionName,
375
+ params: Record<string, unknown>,
376
+ overrides: Partial<ChannelMessageActionContext> = {},
377
+ ) =>
378
+ track(
379
+ dispatchChannelMessageAction({
380
+ cfg,
381
+ channel: "feishu",
382
+ action,
383
+ params,
384
+ accountId: "default",
385
+ requesterAccountId: "default",
386
+ requesterSenderId: SELF,
387
+ conversationReadOrigin: "delegated",
388
+ assertDirectAdapterHandoff: run.assert,
389
+ toolContext: run.toolContext,
390
+ ...overrides,
391
+ }),
392
+ );
393
+ return { cfg, settings, record, run, dispatch };
394
+ }
395
+
396
+ const readCases: Array<{
397
+ action: ChannelMessageActionName;
398
+ params: Record<string, unknown>;
399
+ details: Record<string, unknown>;
400
+ paths: string[];
401
+ }> = [
402
+ {
403
+ action: "read",
404
+ params: { chatId: ALLOWED, messageId: MESSAGE },
405
+ details: { message: { messageId: MESSAGE, chatId: ALLOWED, content: "allowed context" } },
406
+ paths: [MESSAGE_PATH, CHAT_PATH],
407
+ },
408
+ {
409
+ action: "reactions",
410
+ params: { chatId: ALLOWED, messageId: MESSAGE },
411
+ details: {
412
+ reactions: [{ reactionId: "reaction-1", emojiType: "THUMBSUP", operatorId: MEMBER }],
413
+ },
414
+ paths: [MESSAGE_PATH, CHAT_PATH, `${MESSAGE_PATH}/reactions`],
415
+ },
416
+ {
417
+ action: "list-pins",
418
+ params: { chatId: ALLOWED },
419
+ details: { chatId: ALLOWED, pins: [{ messageId: MESSAGE, chatId: ALLOWED }] },
420
+ paths: [CHAT_PATH, PINS_PATH],
421
+ },
422
+ {
423
+ action: "member-info",
424
+ params: { chatId: ALLOWED, memberId: MEMBER },
425
+ details: { member: { member_id: MEMBER, name: "Allowed member" } },
426
+ paths: [CHAT_PATH, MEMBERS_PATH, MEMBERS_PATH, USER_PATH],
427
+ },
428
+ {
429
+ action: "channel-info",
430
+ params: { chatId: ALLOWED },
431
+ details: { channel: { chat_id: ALLOWED, name: "Allowed" } },
432
+ paths: [CHAT_PATH],
433
+ },
434
+ {
435
+ action: "channel-list",
436
+ params: {},
437
+ details: { groups: [{ kind: "group", id: ALLOWED }], peers: [{ kind: "user", id: SELF }] },
438
+ paths: [],
439
+ },
440
+ {
441
+ action: "sticker-search",
442
+ params: { query: "thumbs" },
443
+ details: { stickers: [{ fileId: "file_allowed", keyword: "thumbs up" }], truncated: false },
444
+ paths: [],
445
+ },
446
+ ];
447
+
448
+ describe.each([false, true])("Feishu provider read parity (bundled: %s)", (bundled) => {
449
+ it.each(readCases)(
450
+ "runs $action through its real consumer",
451
+ async ({ action, params, details, paths }) => {
452
+ const fixture = createFixture({ bundled });
453
+ expectResult(await fixture.dispatch(action, params), details);
454
+ expect(contentRequests()).toEqual(paths);
455
+ expect(
456
+ requests
457
+ .filter((request) => request.path !== AUTH_PATH)
458
+ .every((request) => request.method === "GET"),
459
+ ).toBe(true);
460
+ },
461
+ );
462
+ });
463
+
464
+ it("filters live directory pages before applying the limit", async () => {
465
+ const fixture = createFixture();
466
+ fixture.settings.groupPolicy = "open";
467
+ fixture.settings.allowFrom = ["*"];
468
+ expectResult(await fixture.dispatch("channel-list", { limit: 1 }), {
469
+ groups: [{ kind: "group", id: ALLOWED, name: "Allowed" }],
470
+ peers: [{ kind: "user", id: SELF, name: "Current sender" }],
471
+ });
472
+ expect(contentRequests().filter((path) => path === "/open-apis/im/v1/chats")).toHaveLength(2);
473
+ expect(contentRequests()).toContain("/open-apis/contact/v3/users");
474
+ });
475
+
476
+ it("finds a live peer on later pages through the registered channel-list action", async () => {
477
+ const fixture = createFixture();
478
+ fixture.settings.allowFrom = ["*"];
479
+
480
+ expectResult(
481
+ await fixture.dispatch("channel-list", { scope: "peers", query: "Other", limit: 1 }),
482
+ { peers: [{ kind: "user", id: OTHER, name: "Other" }] },
483
+ );
484
+ const pages = requests.filter((request) => request.path === PEERS_PATH);
485
+ expect(pages).toHaveLength(2);
486
+ expect(pages[0]?.query.get("page_token")).toBeNull();
487
+ expect(pages[1]?.query.get("page_token")).toBe(PEERS_PAGE_TOKEN);
488
+ expect(contentRequests()).toEqual([PEERS_PATH, PEERS_PATH]);
489
+ });
490
+
491
+ it.each(["plugin", "turn", "claim"] as const)(
492
+ "stops live peer pagination when the %s retires between pages",
493
+ async (owner) => {
494
+ const fixture = createFixture();
495
+ fixture.settings.allowFrom = ["*"];
496
+ beforeReply = (request) => {
497
+ if (request.path !== PEERS_PATH) {
498
+ return;
499
+ }
500
+ if (owner === "plugin") {
501
+ fixture.record.enabled = false;
502
+ } else if (owner === "turn") {
503
+ fixture.run.revokeTurn();
504
+ } else {
505
+ fixture.run.releaseClaim();
506
+ }
507
+ };
508
+
509
+ await expect(
510
+ fixture.dispatch("channel-list", { scope: "peers", query: "Other", limit: 1 }),
511
+ ).rejects.toThrow("no longer active");
512
+ expect(contentRequests()).toEqual([PEERS_PATH]);
513
+ },
514
+ );
515
+
516
+ it("cancels live peer pagination through the local message tool", async () => {
517
+ const fixture = createFixture();
518
+ fixture.settings.allowFrom = ["*"];
519
+ setRuntimeConfigSnapshot(fixture.cfg, fixture.cfg);
520
+ const tool = fixture.run.createTool(fixture.cfg);
521
+ const controller = new AbortController();
522
+ beforeReply = (request) => {
523
+ if (request.path === PEERS_PATH) {
524
+ controller.abort();
525
+ }
526
+ };
527
+
528
+ await expect(
529
+ track(
530
+ tool.execute(
531
+ "feishu-peer-directory",
532
+ { action: "channel-list", channel: "feishu", scope: "peers", query: "Other", limit: 1 },
533
+ controller.signal,
534
+ ),
535
+ ),
536
+ ).rejects.toMatchObject({ name: "AbortError" });
537
+ expect(contentRequests()).toEqual([PEERS_PATH]);
538
+ });
539
+
540
+ it.each([SELF, OTHER])(
541
+ "limits a direct-chat profile read to the current sender (%s)",
542
+ async (memberId) => {
543
+ const fixture = createFixture({ currentChat: DIRECT });
544
+ const result = fixture.dispatch("member-info", { chatId: DIRECT, memberId });
545
+ if (memberId === SELF) {
546
+ expectResult(await result, { member: { member_id: SELF, name: "Current sender" } });
547
+ expect(contentRequests()).toEqual([
548
+ `/open-apis/im/v1/chats/${DIRECT}`,
549
+ `/open-apis/contact/v3/users/${SELF}`,
550
+ ]);
551
+ } else {
552
+ await expect(result).rejects.toThrow("limited to the current sender");
553
+ expect(contentRequests()).toEqual([`/open-apis/im/v1/chats/${DIRECT}`]);
554
+ }
555
+ },
556
+ );
557
+
558
+ it.each(["account", "origin", "target", "reactions", "sticker", "catalog"] as const)(
559
+ "retains the %s denial before provider I/O",
560
+ async (kind) => {
561
+ const fixture = createFixture();
562
+ let action: ChannelMessageActionName = "read";
563
+ let params: Record<string, unknown> = { chatId: ALLOWED, messageId: MESSAGE };
564
+ let overrides: Partial<ChannelMessageActionContext> = {};
565
+ let error = "current provider and account";
566
+ if (kind === "account") {
567
+ overrides = { requesterAccountId: "other" };
568
+ }
569
+ if (kind === "origin") {
570
+ overrides = { toolContext: undefined };
571
+ }
572
+ if (kind === "target") {
573
+ params.chatId = BLOCKED;
574
+ error = "not allowed";
575
+ }
576
+ if (kind === "reactions") {
577
+ action = "reactions";
578
+ fixture.settings.actions.reactions = false;
579
+ error = "disabled";
580
+ }
581
+ if (kind === "sticker" || kind === "catalog") {
582
+ action = "sticker-search";
583
+ params = { query: "thumbs" };
584
+ if (kind === "sticker") {
585
+ fixture.settings.actions.sticker = false;
586
+ error = "disabled";
587
+ } else {
588
+ delete fixture.settings.stickerSets[fixture.settings.appId];
589
+ error = "this account's appId";
590
+ }
591
+ }
592
+ await expect(fixture.dispatch(action, params, overrides)).rejects.toThrow(error);
593
+ expect(requests).toEqual([]);
594
+ },
595
+ );
596
+
597
+ it("does not widen an older read list when the host classifies member-info", async () => {
598
+ const fixture = createFixture({ actions: ["read"] });
599
+ expectResult(await fixture.dispatch("read", { chatId: ALLOWED, messageId: MESSAGE }), {
600
+ message: { content: "allowed context" },
601
+ });
602
+ requests = [];
603
+ await expect(
604
+ fixture.dispatch("member-info", { chatId: ALLOWED, memberId: MEMBER }),
605
+ ).rejects.toThrow("exact current conversation");
606
+ expect(requests).toEqual([]);
607
+ });
608
+
609
+ it("does not grant cross-context write authority through an overbroad read declaration", async () => {
610
+ const fixture = createFixture({
611
+ actions: [...feishuPlugin.actions!.readAuthorityActions!, "pin"],
612
+ });
613
+ await expect(fixture.dispatch("pin", { chatId: ALLOWED, messageId: MESSAGE })).rejects.toThrow(
614
+ "exact current conversation",
615
+ );
616
+ expect(requests).toEqual([]);
617
+ });
618
+
619
+ it("retains the exact-current restriction for an unverified external installation", async () => {
620
+ const fixture = createFixture({ trusted: false });
621
+ await expect(fixture.dispatch("read", { chatId: ALLOWED, messageId: MESSAGE })).rejects.toThrow(
622
+ "exact current conversation",
623
+ );
624
+ expect(requests).toEqual([]);
625
+ });
626
+
627
+ describe.each(["plugin", "turn", "claim"] as const)("Feishu %s lifetime", (owner) => {
628
+ it.each(["metadata", "result"] as const)("rejects retirement at %s", async (phase) => {
629
+ const fixture = createFixture();
630
+ beforeReply = (request) => {
631
+ if (request.path !== (phase === "metadata" ? CHAT_PATH : PINS_PATH)) {
632
+ return;
633
+ }
634
+ if (owner === "plugin") {
635
+ fixture.record.enabled = false;
636
+ }
637
+ if (owner === "turn") {
638
+ fixture.run.revokeTurn();
639
+ }
640
+ if (owner === "claim") {
641
+ fixture.run.releaseClaim();
642
+ }
643
+ };
644
+ await expect(fixture.dispatch("list-pins", { chatId: ALLOWED })).rejects.toThrow(
645
+ "no longer active",
646
+ );
647
+ expect(contentRequests()).toEqual(phase === "metadata" ? [CHAT_PATH] : [CHAT_PATH, PINS_PATH]);
648
+ });
649
+ });
650
+
651
+ it.each(["sticker-search", "channel-list", "member-info"] as const)(
652
+ "rejects a retired local %s result",
653
+ async (action) => {
654
+ const fixture = createFixture({ currentChat: action === "member-info" ? DIRECT : CURRENT });
655
+ let observed: ActionResult | undefined;
656
+ afterProviderResult = (result) => {
657
+ observed = result;
658
+ if (action === "sticker-search") {
659
+ fixture.run.releaseClaim();
660
+ }
661
+ if (action === "channel-list") {
662
+ fixture.record.enabled = false;
663
+ }
664
+ if (action === "member-info") {
665
+ fixture.run.revokeTurn();
666
+ }
667
+ };
668
+ const params =
669
+ action === "sticker-search"
670
+ ? { query: "thumbs" }
671
+ : action === "member-info"
672
+ ? { chatId: DIRECT }
673
+ : {};
674
+ await expect(fixture.dispatch(action, params)).rejects.toThrow("no longer active");
675
+ if (action === "sticker-search") {
676
+ expectResult(observed ?? null, { stickers: [{ fileId: "file_allowed" }] });
677
+ }
678
+ if (action === "channel-list") {
679
+ expectResult(observed ?? null, { groups: [{ id: ALLOWED }], peers: [{ id: SELF }] });
680
+ }
681
+ if (action === "member-info") {
682
+ expectResult(observed ?? null, { members: [{ member_id: SELF }] });
683
+ }
684
+ expect(contentRequests()).toEqual(
685
+ action === "member-info" ? [`/open-apis/im/v1/chats/${DIRECT}`] : [],
686
+ );
687
+ },
688
+ );
689
+
690
+ it.each(["allowed", "turn", "claim"] as const)(
691
+ "uses the normal local message tool (%s)",
692
+ async (mode) => {
693
+ const fixture = createFixture();
694
+ setRuntimeConfigSnapshot(fixture.cfg, fixture.cfg);
695
+ const tool = fixture.run.createTool(fixture.cfg);
696
+ beforeReply = (request) => {
697
+ if (request.path !== CHAT_PATH) {
698
+ return;
699
+ }
700
+ if (mode === "turn") {
701
+ fixture.run.revokeTurn();
702
+ }
703
+ if (mode === "claim") {
704
+ fixture.run.releaseClaim();
705
+ }
706
+ };
707
+ const result = track(
708
+ tool.execute("feishu-read", {
709
+ action: "read",
710
+ channel: "feishu",
711
+ target: `chat:${ALLOWED}`,
712
+ messageId: MESSAGE,
713
+ }),
714
+ );
715
+ if (mode === "allowed") {
716
+ expectResult(await result, { message: { messageId: MESSAGE, content: "allowed context" } });
717
+ } else {
718
+ await expect(result).rejects.toThrow("no longer active");
719
+ }
720
+ expect(contentRequests()).toEqual([MESSAGE_PATH, CHAT_PATH]);
721
+ },
722
+ );
723
+
724
+ it("rejects a configured account disabled after local message-tool creation", async () => {
725
+ const fixture = createFixture();
726
+ const cfg = {
727
+ channels: {
728
+ feishu: {
729
+ ...fixture.settings,
730
+ accounts: { default: { enabled: true } },
731
+ },
732
+ },
733
+ } satisfies OpenClawConfig;
734
+ setRuntimeConfigSnapshot(cfg, cfg);
735
+ const tool = fixture.run.createTool(cfg);
736
+ cfg.channels.feishu.accounts.default.enabled = false;
737
+ setRuntimeConfigSnapshot(cfg, cfg);
738
+ expect(feishuPlugin.config.resolveAccount(cfg, "default")).toMatchObject({
739
+ configured: true,
740
+ enabled: false,
741
+ });
742
+
743
+ await expect(
744
+ track(
745
+ tool.execute("feishu-disabled-read", {
746
+ action: "read",
747
+ channel: "feishu",
748
+ accountId: "default",
749
+ target: `chat:${ALLOWED}`,
750
+ messageId: MESSAGE,
751
+ }),
752
+ ),
753
+ ).rejects.toThrow('Account "default" for channel feishu is disabled.');
754
+ expect(requests).toEqual([]);
755
+ });
test/gateway-cold-agent-abort.e2e.test.ts ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { randomUUID } from "node:crypto";
2
+ import { createServer } from "node:http";
3
+ import { expect, it, vi } from "vitest";
4
+ import type { OpenClawConfig } from "../src/config/types.openclaw.js";
5
+ import { connectGatewayClient, disconnectGatewayClient } from "../src/gateway/test-helpers.e2e.js";
6
+ import { buildMockOpenAiResponsesProvider } from "../src/gateway/test-openai-responses-model.js";
7
+ import {
8
+ createOpenClawTestInstance,
9
+ type OpenClawTestInstance,
10
+ } from "./helpers/openclaw-test-instance.js";
11
+ import { createDeferred } from "./helpers/promise.js";
12
+
13
+ it(
14
+ "cancels a provider request through the first chat RPC on a built Gateway",
15
+ { timeout: 120_000 },
16
+ async () => {
17
+ const modelId = "cold-cancel-model";
18
+ const runId = randomUUID();
19
+ const sessionKey = "agent:main:cold-agent-abort";
20
+ const accepted = createDeferred<unknown>();
21
+ const providerReceived = createDeferred<Record<string, unknown>>();
22
+ let providerRequests = 0;
23
+ let providerAborted = false;
24
+ const server = createServer((request, response) => {
25
+ void (async () => {
26
+ if (request.method === "GET" && request.url === "/v1/models") {
27
+ response.writeHead(200, { "content-type": "application/json" });
28
+ response.end(JSON.stringify({ data: [{ id: modelId, object: "model" }] }));
29
+ return;
30
+ }
31
+ if (request.method !== "POST" || request.url !== "/v1/responses") {
32
+ response.writeHead(404).end();
33
+ return;
34
+ }
35
+ const chunks: Buffer[] = [];
36
+ for await (const chunk of request) {
37
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
38
+ }
39
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8")) as Record<string, unknown>;
40
+ providerRequests += 1;
41
+ // Keep the provider response open: only cancellation may close it before cleanup.
42
+ response.once("close", () => {
43
+ providerAborted = !response.writableFinished;
44
+ });
45
+ providerReceived.resolve(body);
46
+ })().catch((error: unknown) => {
47
+ providerReceived.reject(error);
48
+ response.destroy(error instanceof Error ? error : new Error(String(error)));
49
+ });
50
+ });
51
+ let instance: OpenClawTestInstance | undefined;
52
+ let client: Awaited<ReturnType<typeof connectGatewayClient>> | undefined;
53
+ let final: Promise<unknown> | undefined;
54
+ try {
55
+ await new Promise<void>((resolve, reject) => {
56
+ server.once("error", reject);
57
+ server.listen(0, "127.0.0.1", resolve);
58
+ });
59
+ const address = server.address();
60
+ if (!address || typeof address === "string") {
61
+ throw new Error("cancellation provider did not bind a loopback port");
62
+ }
63
+ const provider = buildMockOpenAiResponsesProvider(
64
+ `http://127.0.0.1:${address.port}/v1`,
65
+ modelId,
66
+ );
67
+ const config: OpenClawConfig = {
68
+ plugins: { slots: { memory: "none" } },
69
+ agents: {
70
+ entries: { main: {} },
71
+ defaults: {
72
+ model: { primary: provider.modelRef, fallbacks: [] },
73
+ models: { [provider.modelRef]: { agentRuntime: { id: "openclaw" } } },
74
+ skills: [],
75
+ },
76
+ },
77
+ tools: { profile: "minimal" },
78
+ models: {
79
+ mode: "replace",
80
+ providers: {
81
+ [provider.providerId]: {
82
+ ...provider.config,
83
+ request: { allowPrivateNetwork: true },
84
+ },
85
+ },
86
+ },
87
+ };
88
+ instance = await createOpenClawTestInstance({
89
+ name: "cold-agent-abort",
90
+ config,
91
+ env: {
92
+ OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1",
93
+ OPENCLAW_SKIP_PROVIDERS: undefined,
94
+ OPENCLAW_TEST_MINIMAL_GATEWAY: undefined,
95
+ },
96
+ });
97
+ await instance.startGateway();
98
+ client = await connectGatewayClient({
99
+ url: instance.url,
100
+ token: instance.gatewayToken,
101
+ });
102
+ final = client.request(
103
+ "agent",
104
+ {
105
+ agentId: "main",
106
+ sessionKey,
107
+ idempotencyKey: runId,
108
+ message: "Wait for cancellation.",
109
+ deliver: false,
110
+ timeout: 30,
111
+ },
112
+ { expectFinal: true, timeoutMs: 30_000, onAccepted: accepted.resolve },
113
+ );
114
+ await expect(
115
+ Promise.race([
116
+ Promise.all([accepted.promise, providerReceived.promise]),
117
+ final.then(() => {
118
+ throw new Error("agent completed before the cancellation request");
119
+ }),
120
+ ]),
121
+ ).resolves.toMatchObject([{ runId, status: "accepted" }, { model: modelId }]);
122
+ expect(providerAborted).toBe(false);
123
+
124
+ // Readiness uses HTTP and setup uses only agent: this is the first chat-family RPC.
125
+ const aborted = await client.request("chat.abort", { sessionKey, runId });
126
+ expect(aborted).toMatchObject({ aborted: true, runIds: [runId] });
127
+ const terminal = await final;
128
+ // The agent RPC retains its cancellation wire status; the task records the outcome.
129
+ expect(terminal).toEqual({
130
+ runId,
131
+ status: "timeout",
132
+ summary: "aborted",
133
+ stopReason: "rpc",
134
+ });
135
+ expect(await client.request("tasks.list", { sessionKey })).toEqual({
136
+ tasks: [
137
+ expect.objectContaining({ runId, childSessionKey: sessionKey, status: "cancelled" }),
138
+ ],
139
+ });
140
+ await vi.waitFor(() => expect(providerAborted).toBe(true), { timeout: 5_000 });
141
+ expect(providerRequests).toBe(1);
142
+ } finally {
143
+ try {
144
+ if (client) {
145
+ await disconnectGatewayClient(client);
146
+ }
147
+ } finally {
148
+ try {
149
+ await instance?.cleanup();
150
+ } finally {
151
+ if (server.listening) {
152
+ await new Promise<void>((resolve) => {
153
+ server.close(() => resolve());
154
+ server.closeAllConnections();
155
+ });
156
+ }
157
+ await Promise.allSettled(final ? [final] : []);
158
+ }
159
+ }
160
+ }
161
+ },
162
+ );
test/gateway-external-state-ownership.e2e.test.ts ADDED
@@ -0,0 +1,516 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { backup, DatabaseSync } from "node:sqlite";
2
+ import { describe, expect, it } from "vitest";
3
+ import { setSqliteBusyTimeout } from "../src/infra/sqlite-busy-timeout.js";
4
+ import { withStateSchemaFence } from "../src/infra/state-database-coordinator.js";
5
+ import { readUpdateRunDriver, type UpdateRunDriver } from "../src/infra/update-run-driver.js";
6
+ import { createUpdateRun, finishUpdateRun } from "../src/infra/update-run-ledger.js";
7
+ import { ABANDONED_UPDATE_RUN_MS } from "../src/infra/update-run-timeouts.js";
8
+ import { closeOpenClawStateDatabaseByPath } from "../src/state/openclaw-state-db-cache.js";
9
+ import {
10
+ OPENCLAW_SQLITE_BUSY_TIMEOUT_MS,
11
+ OPENCLAW_STATE_SCHEMA_VERSION,
12
+ } from "../src/state/openclaw-state-db-contract.js";
13
+ import { openOpenClawStateDatabase } from "../src/state/openclaw-state-db.js";
14
+ import { withEnv } from "../src/test-utils/env.js";
15
+ import {
16
+ createOpenClawTestInstance,
17
+ type OpenClawTestInstance,
18
+ } from "./helpers/openclaw-test-instance.js";
19
+
20
+ const publicationGraceMs = 5 * 60_000;
21
+
22
+ async function createPublicationInstance(name: string) {
23
+ return createOpenClawTestInstance({
24
+ name,
25
+ config: { update: { checkOnStart: false, auto: { enabled: false } } },
26
+ env: {
27
+ // Exercise the production lifecycle lock and watcher, both disabled by test defaults.
28
+ VITEST: undefined,
29
+ VITEST_POOL_ID: undefined,
30
+ VITEST_WORKER_ID: undefined,
31
+ NODE_ENV: undefined,
32
+ NODE_OPTIONS: undefined,
33
+ OPENCLAW_TEST_MINIMAL_GATEWAY: undefined,
34
+ OPENCLAW_NO_RESPAWN: "1",
35
+ OPENCLAW_SUPERVISOR_MODE: undefined,
36
+ },
37
+ });
38
+ }
39
+
40
+ function seedDeferredState(instance: OpenClawTestInstance, terminal: boolean) {
41
+ const options = { env: instance.env };
42
+ const { db, path: databasePath } = openOpenClawStateDatabase(options);
43
+ try {
44
+ const run = createUpdateRun({ trigger: "cli", before: { version: "2026.9.2" } }, options);
45
+ const now = Date.now();
46
+ // Migration content is already committed; the runner suite proves the v15 rebuild itself.
47
+ db.prepare(`INSERT INTO config_machine_state (state_key, value_json, updated_at_ms)
48
+ VALUES ('state.schema.contentVersion', ?, ?)`).run(
49
+ String(OPENCLAW_STATE_SCHEMA_VERSION),
50
+ now,
51
+ );
52
+ db.prepare(`UPDATE update_runs SET created_at_ms = ?, updated_at_ms = ?,
53
+ status = ?, phase = ?, finished_at_ms = ? WHERE run_id = ?`).run(
54
+ now - 10 * 60_000,
55
+ terminal ? now - 60_000 : now - publicationGraceMs,
56
+ terminal ? "succeeded" : "running",
57
+ terminal ? "finished" : "verifying",
58
+ terminal ? now - 60_000 : null,
59
+ run.runId,
60
+ );
61
+ db.exec(`PRAGMA user_version = 15;
62
+ UPDATE schema_meta SET schema_version = 15 WHERE meta_key = 'primary';`);
63
+ return { databasePath, runId: run.runId };
64
+ } finally {
65
+ closeOpenClawStateDatabaseByPath(databasePath);
66
+ }
67
+ }
68
+
69
+ function seedInactiveUpdateRun(
70
+ instance: OpenClawTestInstance,
71
+ input: { ageMs: number; phase?: "requested" | "staging"; driver?: UpdateRunDriver },
72
+ ) {
73
+ const options = { env: instance.env };
74
+ const { db, path: databasePath } = openOpenClawStateDatabase(options);
75
+ try {
76
+ const run = createUpdateRun(
77
+ {
78
+ trigger: "cli",
79
+ before: { version: "2026.9.2" },
80
+ origin: input.driver ? { driver: input.driver } : {},
81
+ },
82
+ options,
83
+ );
84
+ const phase = input.phase ?? "requested";
85
+ const lastActivity = Date.now() - input.ageMs;
86
+ const steps =
87
+ phase === "requested"
88
+ ? [{ step: "requested", status: "in_progress", startedAtMs: lastActivity }]
89
+ : [
90
+ {
91
+ step: "requested",
92
+ status: "completed",
93
+ startedAtMs: lastActivity,
94
+ endedAtMs: lastActivity,
95
+ },
96
+ { step: "staging", status: "in_progress", startedAtMs: lastActivity },
97
+ ];
98
+ db.prepare(`UPDATE update_runs SET created_at_ms = ?, updated_at_ms = ?,
99
+ phase = ?, steps_json = ? WHERE run_id = ?`).run(
100
+ lastActivity,
101
+ lastActivity,
102
+ phase,
103
+ JSON.stringify(steps),
104
+ run.runId,
105
+ );
106
+ return { databasePath, runId: run.runId, lastActivity };
107
+ } finally {
108
+ closeOpenClawStateDatabaseByPath(databasePath);
109
+ }
110
+ }
111
+
112
+ function readUpdateOutcome(db: DatabaseSync, runId: string) {
113
+ return db
114
+ .prepare("SELECT status, phase, reason, updated_at_ms FROM update_runs WHERE run_id = ?")
115
+ .get(runId);
116
+ }
117
+
118
+ async function expectGatewayStillServing(
119
+ instance: OpenClawTestInstance,
120
+ child: NonNullable<OpenClawTestInstance["child"]>,
121
+ ) {
122
+ expect(instance.child).toBe(child);
123
+ expect(child.exitCode).toBeNull();
124
+ expect(child.signalCode).toBeNull();
125
+ for (const pathname of ["/healthz", "/readyz"]) {
126
+ const response = await fetch(`http://127.0.0.1:${instance.port}${pathname}`, {
127
+ signal: AbortSignal.timeout(3_000),
128
+ });
129
+ await response.arrayBuffer();
130
+ expect(response.status, pathname).toBe(200);
131
+ }
132
+ }
133
+
134
+ function readSchemaVersions(db: DatabaseSync) {
135
+ return {
136
+ published: db.prepare("PRAGMA user_version").get()?.user_version,
137
+ metadata: db.prepare("SELECT schema_version FROM schema_meta WHERE meta_key = 'primary'").get()
138
+ ?.schema_version,
139
+ content: Number(
140
+ db
141
+ .prepare(
142
+ "SELECT value_json FROM config_machine_state WHERE state_key = 'state.schema.contentVersion'",
143
+ )
144
+ .get()?.value_json,
145
+ ),
146
+ };
147
+ }
148
+
149
+ function expectGatewayOwnsState(instance: OpenClawTestInstance, databasePath: string) {
150
+ // Windows places lifecycle coordinators under the child's isolated home directory.
151
+ withEnv({ HOME: instance.env.HOME, USERPROFILE: instance.env.USERPROFILE }, () =>
152
+ expect(() => withStateSchemaFence({ databasePath }, () => "unexpected authority")).toThrow(
153
+ "another Gateway owns that state directory",
154
+ ),
155
+ );
156
+ }
157
+
158
+ describe("Gateway external shared-state ownership", () => {
159
+ it("reconciles a dead update driver at boot and repairs its ledger without restarting", async () => {
160
+ const instance = await createPublicationInstance("gateway-abandoned-update-driver");
161
+ let observer: DatabaseSync | undefined;
162
+ try {
163
+ const currentDriver = readUpdateRunDriver();
164
+ if (!currentDriver) {
165
+ throw new Error("Test process identity is unavailable");
166
+ }
167
+ const { databasePath, runId } = seedInactiveUpdateRun(instance, {
168
+ ageMs: ABANDONED_UPDATE_RUN_MS + 60_000,
169
+ // A live PID with a different start identity positively proves PID reuse.
170
+ driver: {
171
+ ...currentDriver,
172
+ startIdentity: currentDriver.startIdentity === "0" ? "1" : "0",
173
+ },
174
+ });
175
+ const database = new DatabaseSync(databasePath, { readOnly: true });
176
+ observer = database;
177
+ await instance.startGateway();
178
+ const child = instance.child;
179
+ if (!child) {
180
+ throw new Error("Gateway fixture started without an owned process.");
181
+ }
182
+ await expect
183
+ .poll(() => readUpdateOutcome(database, runId), { timeout: 10_000, interval: 100 })
184
+ .toMatchObject({ status: "failed", phase: "finished", reason: "abandoned" });
185
+ const row = database
186
+ .prepare("SELECT steps_json FROM update_runs WHERE run_id = ?")
187
+ .get(runId);
188
+ expect(JSON.parse(String(row?.steps_json))).toContainEqual(
189
+ expect.objectContaining({
190
+ step: "reconcile:abandoned",
191
+ status: "failed",
192
+ detail: "inactive-driver-dead",
193
+ }),
194
+ );
195
+ const status = await instance.cli(["update", "status", "--json", "--timeout", "1"]);
196
+ expect(status.code, `${status.stderr}\n${status.stdout}`).toBe(0);
197
+ expect(JSON.parse(status.stdout)).toMatchObject({
198
+ lastRun: { runId, status: "failed", reason: "abandoned" },
199
+ });
200
+ expect(JSON.parse(status.stdout)).not.toHaveProperty("activeRun");
201
+ const repair = await instance.cli(["update", "repair", "--json"]);
202
+ expect(repair.code, `${repair.stderr}\n${repair.stdout}`).toBe(0);
203
+ expect(JSON.parse(repair.stdout)).toMatchObject({
204
+ status: "ok",
205
+ mode: "repair",
206
+ restart: false,
207
+ reconciledRuns: [],
208
+ });
209
+ await expectGatewayStillServing(instance, child);
210
+ expectGatewayOwnsState(instance, databasePath);
211
+ } finally {
212
+ observer?.close();
213
+ await instance.cleanup();
214
+ }
215
+ }, 120_000);
216
+
217
+ it("preserves recent and live drivers and explicitly repairs an inactive identityless row", async () => {
218
+ const instance = await createPublicationInstance("gateway-inactive-update-repair");
219
+ let observer: DatabaseSync | undefined;
220
+ try {
221
+ const driver = readUpdateRunDriver();
222
+ if (!driver) {
223
+ throw new Error("Test process identity is unavailable");
224
+ }
225
+ const inactive = seedInactiveUpdateRun(instance, {
226
+ ageMs: ABANDONED_UPDATE_RUN_MS + 60_000,
227
+ phase: "staging",
228
+ });
229
+ const live = seedInactiveUpdateRun(instance, {
230
+ ageMs: ABANDONED_UPDATE_RUN_MS + 60_000,
231
+ driver,
232
+ });
233
+ const recent = seedInactiveUpdateRun(instance, { ageMs: 60_000 });
234
+ observer = new DatabaseSync(inactive.databasePath, { readOnly: true });
235
+ await instance.startGateway();
236
+ const child = instance.child;
237
+ if (!child) {
238
+ throw new Error("Gateway fixture started without an owned process.");
239
+ }
240
+ const refused = await instance.cli(["update", "repair", "--json"]);
241
+ expect(refused.code).not.toBe(0);
242
+ expect(`${refused.stderr}\n${refused.stdout}`).toMatch(/still in progress/u);
243
+ for (const run of [inactive, live, recent]) {
244
+ expect(readUpdateOutcome(observer, run.runId)).toMatchObject({
245
+ status: "running",
246
+ reason: null,
247
+ updated_at_ms: run.lastActivity,
248
+ });
249
+ }
250
+ await expectGatewayStillServing(instance, child);
251
+
252
+ // These synthetic drivers complete their own work before operator repair.
253
+ for (const run of [live, recent]) {
254
+ finishUpdateRun(
255
+ run.runId,
256
+ { status: "skipped", reason: "fixture-complete" },
257
+ { env: instance.env },
258
+ );
259
+ }
260
+ closeOpenClawStateDatabaseByPath(inactive.databasePath);
261
+ const repair = await instance.cli(["update", "repair", "--json"]);
262
+ expect(repair.code, `${repair.stderr}\n${repair.stdout}`).toBe(0);
263
+ expect(JSON.parse(repair.stdout)).toMatchObject({
264
+ status: "ok",
265
+ mode: "repair",
266
+ restart: false,
267
+ reconciledRuns: [inactive.runId],
268
+ });
269
+ expect(readUpdateOutcome(observer, inactive.runId)).toMatchObject({
270
+ status: "failed",
271
+ phase: "finished",
272
+ reason: "abandoned",
273
+ });
274
+ const status = await instance.cli(["update", "status", "--json", "--timeout", "1"]);
275
+ expect(status.code, `${status.stderr}\n${status.stdout}`).toBe(0);
276
+ expect(JSON.parse(status.stdout)).not.toHaveProperty("activeRun");
277
+ await expectGatewayStillServing(instance, child);
278
+ expectGatewayOwnsState(instance, inactive.databasePath);
279
+ } finally {
280
+ observer?.close();
281
+ await instance.cleanup();
282
+ }
283
+ }, 120_000);
284
+
285
+ it.each(["repair", "update"] as const)(
286
+ "explicit %s clears legacy requested-only history without restarting",
287
+ async (action) => {
288
+ const instance = await createPublicationInstance(`gateway-legacy-update-${action}`);
289
+ let observer: DatabaseSync | undefined;
290
+ try {
291
+ const inactive = seedInactiveUpdateRun(instance, {
292
+ ageMs: ABANDONED_UPDATE_RUN_MS + 60_000,
293
+ });
294
+ observer = new DatabaseSync(inactive.databasePath, { readOnly: true });
295
+ await instance.startGateway();
296
+ const child = instance.child;
297
+ if (!child) {
298
+ throw new Error("Gateway fixture started without an owned process.");
299
+ }
300
+ const before = await instance.cli(["update", "status", "--json", "--timeout", "1"]);
301
+ expect(before.code, before.stderr).toBe(0);
302
+ expect(JSON.parse(before.stdout)).toMatchObject({
303
+ activeRun: { runId: inactive.runId },
304
+ staleRun: { runId: inactive.runId },
305
+ });
306
+ expect(readUpdateOutcome(observer, inactive.runId)).toMatchObject({
307
+ status: "running",
308
+ updated_at_ms: inactive.lastActivity,
309
+ });
310
+ const result = await instance.cli(
311
+ action === "repair"
312
+ ? ["update", "repair", "--json"]
313
+ : ["update", "--yes", "--json", "--dry-run"],
314
+ { timeoutMs: 60_000 },
315
+ );
316
+ expect(result.code, `${result.stderr}\n${result.stdout}`).toBe(0);
317
+ if (action === "repair") {
318
+ expect(JSON.parse(result.stdout)).toMatchObject({
319
+ status: "ok",
320
+ restart: false,
321
+ reconciledRuns: [inactive.runId],
322
+ });
323
+ }
324
+ expect(readUpdateOutcome(observer, inactive.runId)).toMatchObject({
325
+ status: "failed",
326
+ phase: "finished",
327
+ reason: action === "repair" ? "abandoned" : "superseded",
328
+ });
329
+ const after = await instance.cli(["update", "status", "--json", "--timeout", "1"]);
330
+ expect(after.code, after.stderr).toBe(0);
331
+ expect(JSON.parse(after.stdout)).not.toHaveProperty("activeRun");
332
+ await expectGatewayStillServing(instance, child);
333
+ expectGatewayOwnsState(instance, inactive.databasePath);
334
+ } finally {
335
+ observer?.close();
336
+ await instance.cleanup();
337
+ }
338
+ },
339
+ 120_000,
340
+ );
341
+
342
+ it("keeps CLI readers usable while the owning Gateway defers schema publication", async () => {
343
+ const instance = await createPublicationInstance("gateway-deferred-schema-readers");
344
+ let observer: DatabaseSync | undefined;
345
+ try {
346
+ const { databasePath } = seedDeferredState(instance, true);
347
+ observer = new DatabaseSync(databasePath, { readOnly: true });
348
+ await instance.startGateway();
349
+ expectGatewayOwnsState(instance, databasePath);
350
+ const deferred = { published: 15, metadata: 15, content: OPENCLAW_STATE_SCHEMA_VERSION };
351
+ expect(readSchemaVersions(observer)).toEqual(deferred);
352
+
353
+ for (const args of [
354
+ ["agents", "list", "--json"],
355
+ ["doctor", "--non-interactive", "--json"],
356
+ ["update", "--yes", "--json", "--dry-run"],
357
+ ]) {
358
+ const result = await instance.cli(args, { timeoutMs: 60_000 });
359
+ expect(result.code, `${args.join(" ")}\n${result.stderr}\n${result.stdout}`).toBe(0);
360
+ expect(() => JSON.parse(result.stdout)).not.toThrow();
361
+ expect(result.stderr).not.toMatch(
362
+ /schema migration pending|refused shared state schema mutation|another Gateway owns/u,
363
+ );
364
+ expect(readSchemaVersions(observer)).toEqual(deferred);
365
+ expectGatewayOwnsState(instance, databasePath);
366
+ }
367
+ } finally {
368
+ observer?.close();
369
+ await instance.cleanup();
370
+ }
371
+ }, 240_000);
372
+
373
+ it("publishes from the owning Gateway timer after a running update finishes and goes quiet", async () => {
374
+ const instance = await createPublicationInstance("gateway-deferred-schema-timer");
375
+ let observer: DatabaseSync | undefined;
376
+ try {
377
+ const { databasePath, runId } = seedDeferredState(instance, false);
378
+ const database = new DatabaseSync(databasePath, { readOnly: true });
379
+ observer = database;
380
+ await instance.startGateway();
381
+ expectGatewayOwnsState(instance, databasePath);
382
+ expect(readSchemaVersions(observer)).toEqual({
383
+ published: 15,
384
+ metadata: 15,
385
+ content: OPENCLAW_STATE_SCHEMA_VERSION,
386
+ });
387
+
388
+ const publishAfterMs = Date.now() + 10_000;
389
+ const finishedAtMs = publishAfterMs - publicationGraceMs;
390
+ const writer = new DatabaseSync(databasePath);
391
+ try {
392
+ setSqliteBusyTimeout(writer, OPENCLAW_SQLITE_BUSY_TIMEOUT_MS);
393
+ // Deliver an aged terminal fixture through a separate connection, like the old CLI.
394
+ writer
395
+ .prepare(`UPDATE update_runs SET status = 'succeeded', phase = 'finished',
396
+ finished_at_ms = ?, updated_at_ms = ? WHERE run_id = ?`)
397
+ .run(finishedAtMs, finishedAtMs, runId);
398
+ } finally {
399
+ writer.close();
400
+ }
401
+
402
+ // Only bare SQLite reads follow: runner opens or CLI activity would conceal a broken timer.
403
+ let publishedBeforeDeadline = false;
404
+ await expect
405
+ .poll(
406
+ () => {
407
+ const versions = readSchemaVersions(database);
408
+ publishedBeforeDeadline ||=
409
+ Date.now() < publishAfterMs &&
410
+ (versions.published !== 15 || versions.metadata !== 15);
411
+ return versions;
412
+ },
413
+ { timeout: 20_000, interval: 100 },
414
+ )
415
+ .toEqual({
416
+ published: OPENCLAW_STATE_SCHEMA_VERSION,
417
+ metadata: OPENCLAW_STATE_SCHEMA_VERSION,
418
+ content: OPENCLAW_STATE_SCHEMA_VERSION,
419
+ });
420
+ expect(publishedBeforeDeadline).toBe(false);
421
+ expect(
422
+ observer
423
+ .prepare("SELECT status, finished_at_ms, updated_at_ms FROM update_runs WHERE run_id = ?")
424
+ .get(runId),
425
+ ).toEqual({
426
+ status: "succeeded",
427
+ finished_at_ms: finishedAtMs,
428
+ updated_at_ms: finishedAtMs,
429
+ });
430
+ } finally {
431
+ observer?.close();
432
+ await instance.cleanup();
433
+ }
434
+ }, 120_000);
435
+
436
+ it("refuses unmarked startup and accepts the external supervisor marker", async () => {
437
+ const instance = await createOpenClawTestInstance({
438
+ name: "gateway-external-state-owner",
439
+ env: { OPENCLAW_SUPERVISOR_MODE: "external" },
440
+ startTimeoutMs: 30_000,
441
+ });
442
+ try {
443
+ const claim = await instance.cli([
444
+ "database",
445
+ "ownership",
446
+ "claim",
447
+ "--manager",
448
+ "gateway-supervisor",
449
+ "--json",
450
+ ]);
451
+ expect(claim.code, claim.stderr).toBe(0);
452
+ const claimed = JSON.parse(claim.stdout) as {
453
+ databasePath: string;
454
+ ownership: { managerId: string };
455
+ status: string;
456
+ };
457
+ expect(claimed).toMatchObject({
458
+ status: "external",
459
+ ownership: { managerId: "gateway-supervisor" },
460
+ });
461
+ const preflightPath = `${instance.stateDir}/owner-preflight.sqlite`;
462
+ const source = new DatabaseSync(claimed.databasePath, { readOnly: true });
463
+ try {
464
+ await backup(source, preflightPath);
465
+ } finally {
466
+ source.close();
467
+ }
468
+ const preflight = await instance.cli(["database", "preflight", preflightPath, "--json"]);
469
+ expect(preflight.code, `${preflight.stderr}\n${preflight.stdout}`).toBe(0);
470
+ expect(JSON.parse(preflight.stdout)).toMatchObject({
471
+ schema: "openclaw.state-schema-preflight.v1",
472
+ status: "exact",
473
+ requiresWrite: false,
474
+ });
475
+ const unreadable = await instance.cli([
476
+ "database",
477
+ "preflight",
478
+ `${instance.stateDir}/missing.sqlite`,
479
+ "--json",
480
+ ]);
481
+ expect(unreadable.code).toBe(1);
482
+ expect(JSON.parse(unreadable.stdout)).toMatchObject({
483
+ schema: "openclaw.state-schema-preflight.v1",
484
+ status: "indeterminate",
485
+ });
486
+ const status = await instance.cli(["database", "ownership", "status", "--json"]);
487
+ expect(status.code, status.stderr).toBe(0);
488
+ expect(JSON.parse(status.stdout)).toMatchObject({
489
+ status: "external",
490
+ ownership: { managerId: "gateway-supervisor" },
491
+ });
492
+ const conflictingClaim = await instance.cli([
493
+ "database",
494
+ "ownership",
495
+ "claim",
496
+ "--manager",
497
+ "replacement-manager",
498
+ "--json",
499
+ ]);
500
+ expect(conflictingClaim.code).toBe(1);
501
+ expect(JSON.parse(conflictingClaim.stdout)).toMatchObject({
502
+ error: expect.stringContaining("already claimed by external manager gateway-supervisor"),
503
+ });
504
+
505
+ delete instance.env.OPENCLAW_SUPERVISOR_MODE;
506
+ await expect(instance.startGateway()).rejects.toThrow(/gateway-supervisor/u);
507
+ expect(instance.logs()).toMatch(/OPENCLAW_SUPERVISOR_MODE=external/u);
508
+
509
+ instance.env.OPENCLAW_SUPERVISOR_MODE = "external";
510
+ await instance.startGateway();
511
+ expect(instance.child).toBeDefined();
512
+ } finally {
513
+ await instance.cleanup();
514
+ }
515
+ });
516
+ });
test/gateway-restored-requester-settle.e2e.test.ts ADDED
@@ -0,0 +1,429 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // E2E: the shipped Gateway process bounds requester wakes restored from SQLite.
2
+ import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
3
+ import type { AddressInfo } from "node:net";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { writeSubagentSessionEntry } from "../src/agents/subagents/registry/subagent-registry.persistence.test-support.js";
6
+ import {
7
+ loadSubagentRegistryFromSqlite,
8
+ saveSubagentRegistryToSqlite,
9
+ } from "../src/agents/subagents/registry/subagent-registry.store.sqlite.js";
10
+ import type { SubagentRunRecord } from "../src/agents/subagents/registry/subagent-registry.types.js";
11
+ import type { OpenClawConfig } from "../src/config/types.openclaw.js";
12
+ import type { SessionsListResult } from "../src/gateway/session-utils.types.js";
13
+ import { connectGatewayClient, disconnectGatewayClient } from "../src/gateway/test-helpers.e2e.js";
14
+ import type { Deferred } from "../src/shared/deferred.js";
15
+ import { closeOpenClawStateDatabaseForTest } from "../src/state/openclaw-state-db.js";
16
+ import { writeOpenAiResponsesSse } from "./helpers/openai-responses-sse.js";
17
+ import {
18
+ createOpenClawTestInstance,
19
+ type OpenClawTestInstance,
20
+ } from "./helpers/openclaw-test-instance.js";
21
+ import { createDeferred } from "./helpers/promise.js";
22
+
23
+ const TEST_TIMEOUT_MS = 180_000;
24
+ const MODEL_REF = "restored-settle/restored-settle";
25
+ const PROBE_MARKER = "restored wake ordering probe";
26
+ const RESTORED_WAKE_MARKER = "Every subagent spawned from this session has now settled";
27
+
28
+ type HeldModelServer = {
29
+ active: () => number;
30
+ closed: (index: number) => boolean;
31
+ requestAgeMs: (index: number) => number;
32
+ close: () => Promise<void>;
33
+ countRequestsContaining: (marker: string) => number;
34
+ peakRestored: () => number;
35
+ release: (index: number) => void;
36
+ releaseAll: () => void;
37
+ requestCount: () => number;
38
+ url: string;
39
+ };
40
+
41
+ const instances: OpenClawTestInstance[] = [];
42
+ const modelServers: HeldModelServer[] = [];
43
+
44
+ afterEach(async () => {
45
+ for (const server of modelServers) {
46
+ server.releaseAll();
47
+ }
48
+ await Promise.allSettled(instances.splice(0).map((instance) => instance.cleanup()));
49
+ await Promise.allSettled(modelServers.splice(0).map((server) => server.close()));
50
+ });
51
+
52
+ describe("Gateway restored requester settlement", () => {
53
+ it.each(["final", "runtime timeout", "stop"] as const)(
54
+ "keeps restored completion execution under the normal runtime budget: %s",
55
+ { timeout: TEST_TIMEOUT_MS },
56
+ async (outcome) => {
57
+ const announceTimeoutMs = 2_000;
58
+ const modelServer = await startHeldModelServer();
59
+ modelServers.push(modelServer);
60
+ const cfg = createTestConfig(modelServer.url);
61
+ cfg.agents!.defaults!.timeoutSeconds = 12;
62
+ cfg.agents!.defaults!.subagents = { announceTimeoutMs };
63
+ const instance = await createOpenClawTestInstance({
64
+ name: `gateway-restored-deadline-${outcome.replaceAll(" ", "-")}`,
65
+ config: cfg,
66
+ env: { OPENCLAW_SKIP_PROVIDERS: undefined, OPENCLAW_TEST_MINIMAL_GATEWAY: undefined },
67
+ });
68
+ instances.push(instance);
69
+ await seedRestoredRequesters(instance, 1);
70
+ await instance.startGateway();
71
+ const client = await connectGatewayClient({
72
+ url: instance.url,
73
+ token: instance.gatewayToken,
74
+ });
75
+ const sessionKey = "agent:main:gateway-restored-requester-0";
76
+ const session = async () =>
77
+ (
78
+ await client.request<SessionsListResult>("sessions.list", { agentId: "main" })
79
+ ).sessions.find((row) => row.key === sessionKey);
80
+ try {
81
+ await vi.waitFor(() => expect(modelServer.requestCount()).toBe(1), { timeout: 30_000 });
82
+ const initial = await session();
83
+ expect(initial).toMatchObject({ status: "running", hasActiveRun: true });
84
+ // This elapsed wait is the regression itself: the real model socket must
85
+ // remain open beyond the announcement budget, without a new sender turn.
86
+ await vi.waitFor(
87
+ () => expect(modelServer.requestAgeMs(0)).toBeGreaterThan(announceTimeoutMs),
88
+ { timeout: announceTimeoutMs + 5_000, interval: 20 },
89
+ );
90
+ expect(modelServer.closed(0), instance.logs()).toBe(false);
91
+ expect(modelServer.requestCount()).toBe(1);
92
+ if (outcome === "final") {
93
+ modelServer.release(0);
94
+ } else if (outcome === "stop") {
95
+ expect(await client.request("chat.abort", { sessionKey })).toMatchObject({
96
+ aborted: true,
97
+ });
98
+ }
99
+ const expectedStatus =
100
+ outcome === "final" ? "done" : outcome === "stop" ? "killed" : "timeout";
101
+ await vi.waitFor(
102
+ async () =>
103
+ expect(await session()).toMatchObject({ status: expectedStatus, hasActiveRun: false }),
104
+ { timeout: 20_000, interval: 50 },
105
+ );
106
+ await vi.waitFor(() => expect(modelServer.closed(0)).toBe(true), { timeout: 5_000 });
107
+ const history = await client.request<{
108
+ messages: Array<{ role: string; content?: Array<{ type: string; text?: string }> }>;
109
+ }>("chat.history", { sessionKey });
110
+ const finals = history.messages.filter(
111
+ (message) =>
112
+ message.role === "assistant" &&
113
+ message.content?.some((part) => part.text === "restored requester response 0"),
114
+ );
115
+ expect(finals).toHaveLength(outcome === "final" ? 1 : 0);
116
+
117
+ // A terminal wake must release its session lane for ordinary follow-up.
118
+ const followUp = client.request(
119
+ "agent",
120
+ {
121
+ sessionKey,
122
+ idempotencyKey: `restored-deadline-followup-${outcome}`,
123
+ message: PROBE_MARKER,
124
+ deliver: false,
125
+ },
126
+ { expectFinal: true },
127
+ );
128
+ void followUp.catch(() => {});
129
+ try {
130
+ await vi.waitFor(() => expect(modelServer.requestCount()).toBe(2), { timeout: 10_000 });
131
+ modelServer.release(1);
132
+ await expect(followUp).resolves.toMatchObject({ status: "ok" });
133
+ expect(modelServer.requestCount()).toBe(2);
134
+ } finally {
135
+ modelServer.releaseAll();
136
+ await Promise.allSettled([followUp]);
137
+ }
138
+ } finally {
139
+ await disconnectGatewayClient(client);
140
+ modelServer.releaseAll();
141
+ await instance.stopGateway();
142
+ }
143
+ try {
144
+ const retained = loadSubagentRegistryFromSqlite().get("run-gateway-restored-settle-0");
145
+ expect(retained?.execution.outcome).toEqual({ status: "ok" });
146
+ expect(retained?.completion?.resultText).toBe("done");
147
+ if (outcome === "final") {
148
+ expect(retained?.requesterSettleWake).toBeUndefined();
149
+ }
150
+ } finally {
151
+ closeOpenClawStateDatabaseForTest();
152
+ }
153
+ },
154
+ );
155
+
156
+ it(
157
+ "runs at most two restored wakes while leaving the third queued",
158
+ { timeout: TEST_TIMEOUT_MS },
159
+ async () => {
160
+ const modelServer = await startHeldModelServer();
161
+ modelServers.push(modelServer);
162
+ const instance = await createOpenClawTestInstance({
163
+ name: "gateway-restored-requester-settle",
164
+ config: createTestConfig(modelServer.url),
165
+ env: {
166
+ OPENCLAW_SKIP_PROVIDERS: undefined,
167
+ OPENCLAW_TEST_MINIMAL_GATEWAY: undefined,
168
+ },
169
+ });
170
+ instances.push(instance);
171
+
172
+ await seedRestoredRequesters(instance, 3);
173
+
174
+ await instance.startGateway();
175
+ await vi.waitFor(
176
+ () => expect(modelServer.countRequestsContaining(RESTORED_WAKE_MARKER)).toBe(2),
177
+ { interval: 20, timeout: 30_000 },
178
+ );
179
+ expect(modelServer.active(), instance.logs()).toBe(2);
180
+ expect(modelServer.peakRestored(), instance.logs()).toBe(2);
181
+
182
+ const probe = instance.cli(["agent", "--message", PROBE_MARKER, "--json"]);
183
+ await vi.waitFor(() => expect(modelServer.countRequestsContaining(PROBE_MARKER)).toBe(1), {
184
+ interval: 20,
185
+ timeout: 30_000,
186
+ });
187
+ expect(modelServer.countRequestsContaining(RESTORED_WAKE_MARKER)).toBe(2);
188
+
189
+ modelServer.release(0);
190
+ await vi.waitFor(
191
+ () => expect(modelServer.countRequestsContaining(RESTORED_WAKE_MARKER)).toBe(3),
192
+ { interval: 20, timeout: 30_000 },
193
+ );
194
+ expect(modelServer.peakRestored(), instance.logs()).toBe(2);
195
+ modelServer.releaseAll();
196
+ await expect(probe).resolves.toMatchObject({ code: 0 });
197
+ },
198
+ );
199
+ });
200
+
201
+ async function seedRestoredRequesters(instance: OpenClawTestInstance, count: number) {
202
+ instance.state.applyEnv();
203
+ try {
204
+ const endedAt = Date.now();
205
+ const restoredRuns = Array.from({ length: count }, (_, index): SubagentRunRecord => {
206
+ const runId = `run-gateway-restored-settle-${index}`;
207
+ return {
208
+ runId,
209
+ childSessionKey: `agent:main:subagent:gateway-restored-settle-${index}`,
210
+ requesterSessionKey: `agent:main:gateway-restored-requester-${index}`,
211
+ requesterDisplayKey: `gateway-restored-requester-${index}`,
212
+ task: "resume a durable requester wake through the Gateway CLI",
213
+ cleanup: "keep",
214
+ createdAt: endedAt - 1_000,
215
+ endedReason: "subagent-complete",
216
+ execution: {
217
+ status: "terminal",
218
+ startedAt: endedAt - 500,
219
+ endedAt,
220
+ outcome: { status: "ok" },
221
+ },
222
+ expectsCompletionMessage: true,
223
+ completion: { required: true, resultText: "done", capturedAt: endedAt },
224
+ delivery: { status: "delivered", deliveredAt: endedAt },
225
+ cleanupHandled: true,
226
+ cleanupCompletedAt: endedAt,
227
+ requesterSettleWake: {
228
+ status: "pending",
229
+ attemptCount: 0,
230
+ batchRunIds: [runId],
231
+ requesterYieldBatch: true,
232
+ afterRequesterYield: true,
233
+ rearmGeneration: 1,
234
+ },
235
+ };
236
+ });
237
+ saveSubagentRegistryToSqlite(
238
+ new Map(restoredRuns.map((entry) => [entry.runId, entry] as const)),
239
+ );
240
+ for (const [index, entry] of restoredRuns.entries()) {
241
+ await writeSubagentSessionEntry({
242
+ stateDir: instance.stateDir,
243
+ agentId: "main",
244
+ sessionKey: entry.requesterSessionKey,
245
+ sessionId: `gateway-restored-requester-${index}`,
246
+ defaultSessionId: `gateway-restored-requester-${index}`,
247
+ });
248
+ }
249
+ } finally {
250
+ // Keep this one state lease through the Gateway run and retained-result reads;
251
+ // instance.cleanup owns restoration after every process has stopped.
252
+ closeOpenClawStateDatabaseForTest();
253
+ }
254
+ }
255
+
256
+ function createTestConfig(baseUrl: string): OpenClawConfig {
257
+ return {
258
+ plugins: { enabled: false },
259
+ agents: {
260
+ defaults: {
261
+ heartbeat: { every: "0m" },
262
+ maxConcurrent: 8,
263
+ model: { primary: MODEL_REF },
264
+ models: { [MODEL_REF]: { agentRuntime: { id: "openclaw" } } },
265
+ skipBootstrap: true,
266
+ skills: [],
267
+ },
268
+ },
269
+ tools: { profile: "minimal" },
270
+ models: {
271
+ mode: "replace",
272
+ providers: {
273
+ "restored-settle": {
274
+ baseUrl: `${baseUrl}/v1`,
275
+ apiKey: "test-token-placeholder",
276
+ api: "openai-responses",
277
+ request: { allowPrivateNetwork: true },
278
+ models: [
279
+ {
280
+ id: "restored-settle",
281
+ name: "restored-settle",
282
+ api: "openai-responses",
283
+ reasoning: false,
284
+ input: ["text"],
285
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
286
+ contextWindow: 128_000,
287
+ maxTokens: 4_096,
288
+ },
289
+ ],
290
+ },
291
+ },
292
+ },
293
+ };
294
+ }
295
+
296
+ async function startHeldModelServer(): Promise<HeldModelServer> {
297
+ const releases: Deferred[] = [];
298
+ const requestBodies: string[] = [];
299
+ const responses: ServerResponse[] = [];
300
+ const requestStartedAt: number[] = [];
301
+ let active = 0;
302
+ let activeRestored = 0;
303
+ let peakRestored = 0;
304
+ let requestCount = 0;
305
+ const server = createServer((request, response) => {
306
+ void handleModelRequest(request, response).catch((error: unknown) => {
307
+ response.writeHead(500, { "content-type": "application/json" });
308
+ response.end(JSON.stringify({ error: { message: String(error) } }));
309
+ });
310
+ });
311
+
312
+ async function handleModelRequest(
313
+ request: IncomingMessage,
314
+ response: ServerResponse,
315
+ ): Promise<void> {
316
+ const url = new URL(request.url ?? "/", "http://127.0.0.1");
317
+ if (request.method === "GET" && url.pathname === "/v1/models") {
318
+ response.writeHead(200, { "content-type": "application/json" });
319
+ response.end(JSON.stringify({ data: [{ id: "restored-settle", object: "model" }] }));
320
+ return;
321
+ }
322
+ if (request.method !== "POST" || url.pathname !== "/v1/responses") {
323
+ response.writeHead(404).end();
324
+ return;
325
+ }
326
+
327
+ let body = "";
328
+ for await (const chunk of request) {
329
+ body += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
330
+ }
331
+ const index = requestCount;
332
+ requestCount += 1;
333
+ requestBodies[index] = body;
334
+ responses[index] = response;
335
+ requestStartedAt[index] = Date.now();
336
+ const release = createDeferred();
337
+ releases[index] = release;
338
+ active += 1;
339
+ const isRestored = body.includes(RESTORED_WAKE_MARKER);
340
+ if (isRestored) {
341
+ activeRestored += 1;
342
+ peakRestored = Math.max(peakRestored, activeRestored);
343
+ }
344
+ try {
345
+ await release.promise;
346
+ if (!response.destroyed) {
347
+ writeModelResponse(response, index);
348
+ }
349
+ } finally {
350
+ active -= 1;
351
+ if (isRestored) {
352
+ activeRestored -= 1;
353
+ }
354
+ }
355
+ }
356
+
357
+ await new Promise<void>((resolve, reject) => {
358
+ server.once("error", reject);
359
+ server.listen(0, "127.0.0.1", resolve);
360
+ });
361
+ const address = server.address() as AddressInfo;
362
+ const releaseAll = () => {
363
+ for (const release of releases) {
364
+ release?.resolve(undefined);
365
+ }
366
+ };
367
+ return {
368
+ active: () => active,
369
+ closed: (index) => responses[index]?.destroyed ?? false,
370
+ requestAgeMs: (index) => Date.now() - (requestStartedAt[index] ?? Date.now()),
371
+ countRequestsContaining: (marker) =>
372
+ requestBodies.filter((body) => body.includes(marker)).length,
373
+ peakRestored: () => peakRestored,
374
+ release: (index) => releases[index]?.resolve(undefined),
375
+ releaseAll,
376
+ requestCount: () => requestCount,
377
+ url: `http://127.0.0.1:${address.port}`,
378
+ close: async () => {
379
+ releaseAll();
380
+ server.closeAllConnections();
381
+ await new Promise<void>((resolve) => {
382
+ server.close(() => resolve());
383
+ });
384
+ },
385
+ };
386
+ }
387
+
388
+ function writeModelResponse(response: ServerResponse, sequence: number): void {
389
+ const text = `restored requester response ${sequence}`;
390
+ const message = {
391
+ type: "message",
392
+ id: `restored-requester-message-${sequence}`,
393
+ role: "assistant",
394
+ status: "completed",
395
+ content: [{ type: "output_text", text, annotations: [] }],
396
+ };
397
+ const events = [
398
+ {
399
+ type: "response.output_item.added",
400
+ output_index: 0,
401
+ item: { ...message, status: "in_progress", content: [] },
402
+ },
403
+ {
404
+ type: "response.output_text.delta",
405
+ item_id: message.id,
406
+ output_index: 0,
407
+ content_index: 0,
408
+ delta: text,
409
+ },
410
+ {
411
+ type: "response.output_text.done",
412
+ item_id: message.id,
413
+ output_index: 0,
414
+ content_index: 0,
415
+ text,
416
+ },
417
+ { type: "response.output_item.done", output_index: 0, item: message },
418
+ {
419
+ type: "response.completed",
420
+ response: {
421
+ id: `restored-requester-response-${sequence}`,
422
+ status: "completed",
423
+ output: [message],
424
+ usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
425
+ },
426
+ },
427
+ ];
428
+ writeOpenAiResponsesSse(response, events);
429
+ }
test/git-hooks-pre-commit-boundaries.test.ts ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ copyFileSync,
3
+ existsSync,
4
+ readFileSync,
5
+ symlinkSync,
6
+ unlinkSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import path from "node:path";
10
+ import { afterEach, describe, expect, it } from "vitest";
11
+ import {
12
+ commitArgs,
13
+ createContentGuardFixture,
14
+ installFormattingRecorder,
15
+ installPreCommitFixture,
16
+ readFormatterLog,
17
+ literals,
18
+ rulePath,
19
+ ruleSetting,
20
+ run,
21
+ runFailure,
22
+ stageContent as stage,
23
+ writeExecutable,
24
+ } from "./git-hooks-pre-commit.test-support.js";
25
+ import { cleanupTempDirs } from "./helpers/temp-dir.js";
26
+
27
+ const tempDirs: string[] = [];
28
+ const fixture = () => createContentGuardFixture(tempDirs);
29
+ const failureLine = "[pre-commit] FAILED (exit 23)\n";
30
+
31
+ afterEach(() => cleanupTempDirs(tempDirs));
32
+
33
+ function expectBlocked(output: string, name: string): void {
34
+ expect(output).toContain("Blocked staged content");
35
+ expect(output).toContain(JSON.stringify(name));
36
+ expect(output).not.toContain(literals[0]);
37
+ expect(output).toContain("[pre-commit] FAILED (exit 1)\n");
38
+ }
39
+
40
+ // Only the external formatter is simulated; it emits the working-tree input completely.
41
+ // With echoStdin it also behaves like a real stdin-mode formatter (blob in, blob out).
42
+ function diagnosticFormatter(
43
+ dir: string,
44
+ stream: number,
45
+ exitCode: number,
46
+ echoStdin = false,
47
+ ): void {
48
+ writeExecutable(
49
+ path.join(dir, "node_modules/.bin"),
50
+ "oxfmt",
51
+ `#!/usr/bin/env node
52
+ const fs = require("node:fs");
53
+ ${echoStdin ? "fs.writeSync(1, fs.readFileSync(0));" : ""}
54
+ const payload = fs.readFileSync("payload.ts");
55
+ let written = 0;
56
+ while (written < payload.length) written += fs.writeSync(${stream}, payload, written, payload.length - written);
57
+ process.exitCode = ${exitCode};
58
+ `,
59
+ );
60
+ }
61
+
62
+ describe("pre-commit Git path identity", () => {
63
+ it("blocks the only BOM-prefixed path when staged", () => {
64
+ const dir = fixture();
65
+ const name = "\uFEFFpayload.txt";
66
+ stage(dir, name, literals[0]);
67
+ writeFileSync(path.join(dir, name), literals[0]);
68
+ const result = runFailure(dir, "git", commitArgs);
69
+ expectBlocked(result.stderr, name);
70
+ expect(runFailure(dir, "git", ["rev-parse", "--verify", "HEAD"]).status).not.toBe(0);
71
+ });
72
+
73
+ it("commits only the staged bytes of a partially staged BOM-prefixed path", () => {
74
+ const dir = fixture();
75
+ const name = "\uFEFFpayload.ts";
76
+ stage(dir, name, "clean\n");
77
+ writeFileSync(path.join(dir, name), literals[0]);
78
+ run(dir, "git", commitArgs);
79
+ expect(run(dir, "git", ["show", `HEAD:${name}`])).toBe("clean");
80
+ expect(readFileSync(path.join(dir, name), "utf8")).toBe(literals[0]);
81
+ });
82
+
83
+ it("accepts a benign BOM path without searching its unchanged non-BOM counterpart", () => {
84
+ const dir = fixture();
85
+ stage(dir, "payload.txt", literals[0]);
86
+ run(dir, "git", ["commit", "-qm", "historical fixture"]);
87
+ const name = "\uFEFFpayload.txt";
88
+ stage(dir, name, "clean\n");
89
+ run(dir, "git", commitArgs);
90
+ expect(run(dir, "git", ["show", `HEAD:${name}`])).toBe("clean");
91
+ expect(run(dir, "git", ["show", "HEAD:payload.txt"])).toBe(literals[0]);
92
+ });
93
+
94
+ it.each([rulePath, "\uFEFFprivate-rules.txt"])(
95
+ "consumes a rule-file BOM but preserves the configured path: %s",
96
+ (name) => {
97
+ const dir = fixture();
98
+ writeFileSync(path.join(dir, name), `\uFEFF${literals[0]}\n`);
99
+ run(dir, "git", ["config", "--local", ruleSetting, name]);
100
+ stage(dir, "payload.txt", literals[0]);
101
+ expectBlocked(runFailure(dir, "git", commitArgs).stderr, "payload.txt");
102
+ },
103
+ );
104
+
105
+ it.each(["--glob-pathspecs", "--icase-pathspecs", "--noglob-pathspecs", "--literal-pathspecs"])(
106
+ "keeps enumerated filenames literal under %s without private rules",
107
+ (flag) => {
108
+ const dir = fixture();
109
+ run(dir, "git", ["config", "--local", "--unset", ruleSetting]);
110
+ unlinkSync(path.join(dir, rulePath));
111
+ const name = "chosen[1].txt";
112
+ stage(dir, name, "staged\n");
113
+ stage(dir, ":(exclude)clean.txt", "literal colon\n");
114
+ stage(dir, "chosen[2].ts", "partial staged\n");
115
+ writeFileSync(path.join(dir, name), "unstaged\n");
116
+ writeFileSync(path.join(dir, "chosen[2].ts"), "partial unstaged\n");
117
+ expect(
118
+ run(dir, "bash", ["-c", 'exec git "$@" 2>&1', "hook-proof", flag, ...commitArgs]),
119
+ ).toBe("");
120
+ expect(run(dir, "git", ["show", `HEAD:${name}`])).toBe("staged");
121
+ expect(run(dir, "git", ["show", "HEAD::(exclude)clean.txt"])).toBe("literal colon");
122
+ expect(run(dir, "git", ["show", "HEAD:chosen[2].ts"])).toBe("partial staged");
123
+ expect(readFileSync(path.join(dir, "chosen[2].ts"), "utf8")).toBe("partial unstaged\n");
124
+ },
125
+ );
126
+
127
+ it("treats recovered filenames as literal pathspecs even without the guard env", () => {
128
+ const dir = fixture();
129
+ const names = ["chosen[3].ts", ":(exclude)partial.ts"];
130
+ for (const name of names) {
131
+ stage(dir, name, "keep staged\n");
132
+ writeFileSync(path.join(dir, name), "unstaged only\n");
133
+ }
134
+ // Direct invocation: no guard process pins GIT_LITERAL_PATHSPECS for the script.
135
+ run(dir, "bash", ["scripts/pre-commit/format-staged.sh"]);
136
+ run(dir, "git", ["commit", "-qm", "literal proof"]);
137
+ for (const name of names) {
138
+ expect(run(dir, "git", ["show", `HEAD:${name}`])).toBe("keep staged");
139
+ expect(readFileSync(path.join(dir, name), "utf8")).toBe("unstaged only\n");
140
+ }
141
+ });
142
+
143
+ it.each(
144
+ ["--glob-pathspecs", "--icase-pathspecs"].flatMap((flag) =>
145
+ ["only", "alternate"].map((index) => ({ flag, index })),
146
+ ),
147
+ )("preserves $index commit index authority under $flag", ({ flag, index }) => {
148
+ const dir = fixture();
149
+ const name = "chosen[1].txt";
150
+ stage(dir, name, "original\n");
151
+ stage(dir, "excluded.txt", "original excluded\n");
152
+ run(dir, "git", ["commit", "-qm", "initial fixture"]);
153
+ const alternateIndex = path.join(dir, ".git/selected-index");
154
+ copyFileSync(path.join(dir, ".git/index"), alternateIndex);
155
+ stage(dir, "excluded.txt", literals[0]);
156
+ const env =
157
+ index === "alternate"
158
+ ? { GIT_INDEX_FILE: alternateIndex, GIT_DIR: path.join(dir, ".git"), GIT_WORK_TREE: dir }
159
+ : undefined;
160
+ const select = flag === "--glob-pathspecs" ? "chosen*.txt" : "CHOSEN[1].TXT";
161
+ const args = [flag, ...commitArgs, ...(index === "only" ? ["--only", "--", select] : [])];
162
+ writeFileSync(path.join(dir, name), "selected staged\n");
163
+ run(dir, "git", ["--literal-pathspecs", "add", "--", name], env);
164
+ writeFileSync(path.join(dir, name), "selected unstaged\n");
165
+ run(dir, "git", args, env);
166
+ // `--only` commits the working-tree bytes by Git's own semantics; a plain commit
167
+ // must stay on the staged bytes now that the hook never restages the working tree.
168
+ expect(run(dir, "git", ["show", `HEAD:${name}`])).toBe(
169
+ index === "only" ? "selected unstaged" : "selected staged",
170
+ );
171
+ expect(readFileSync(path.join(dir, name), "utf8")).toBe("selected unstaged\n");
172
+ expect(run(dir, "git", ["show", "HEAD:excluded.txt"])).toBe("original excluded");
173
+ expect(run(dir, "git", ["show", ":excluded.txt"])).toBe(literals[0]);
174
+ expect(readFileSync(path.join(dir, "excluded.txt"), "utf8")).toBe(literals[0]);
175
+ const head = run(dir, "git", ["rev-parse", "HEAD"]);
176
+ writeFileSync(path.join(dir, name), literals[0]);
177
+ run(dir, "git", ["--literal-pathspecs", "add", "--", name], env);
178
+ expectBlocked(runFailure(dir, "git", args, env).stderr, name);
179
+ expect(run(dir, "git", ["rev-parse", "HEAD"])).toBe(head);
180
+ expect(run(dir, "git", ["show", ":excluded.txt"])).toBe(literals[0]);
181
+ });
182
+ });
183
+
184
+ describe("pre-commit formatter capture", () => {
185
+ it("discards incomplete overflow captures from stderr", () => {
186
+ const dir = fixture();
187
+ const token = "SYNTHETIC_CAPTURE_".padEnd(128, "x");
188
+ writeFileSync(path.join(dir, rulePath), `${token}\n`);
189
+ stage(dir, "payload.ts", "clean\n");
190
+ const cap = 16 * 1024 * 1024;
191
+ const payload = ".".repeat(cap - 131168) + token.repeat(2048) + "ACTIONABLE_TAIL\n";
192
+ writeFileSync(path.join(dir, "payload.ts"), payload);
193
+ diagnosticFormatter(dir, 2, 23);
194
+ const result = runFailure(dir, "git", commitArgs);
195
+ const output = result.stdout + result.stderr;
196
+ expect(output).toContain("Formatter could not complete");
197
+ // No padding, redacted capture, or cut literal may be replayed on either stream.
198
+ expect(output.length).toBeLessThan(300);
199
+ expect(output).not.toMatch(/\.{2}|SYNTHETIC|x{2}|REDACTED|ACTIONABLE_TAIL/);
200
+ expect(output).toContain("[pre-commit] FAILED (exit 1)\n");
201
+ expect(runFailure(dir, "git", ["rev-parse", "--verify", "HEAD"]).status).not.toBe(0);
202
+ });
203
+
204
+ it("keeps oversized formatter stdout out of the diagnostics capture", () => {
205
+ // Stdin-mode stdout is formatted content, so a huge result never overflows the pipes.
206
+ const dir = fixture();
207
+ const token = "SYNTHETIC_CAPTURE_".padEnd(128, "x");
208
+ writeFileSync(path.join(dir, rulePath), `${token}\n`);
209
+ stage(dir, "payload.ts", "clean\n");
210
+ const cap = 16 * 1024 * 1024;
211
+ const payload = ".".repeat(cap - 131168) + token.repeat(2048) + "ACTIONABLE_TAIL\n";
212
+ writeFileSync(path.join(dir, "payload.ts"), payload);
213
+ diagnosticFormatter(dir, 1, 23);
214
+ const result = runFailure(dir, "git", commitArgs);
215
+ const output = result.stdout + result.stderr;
216
+ expect(output).toContain("Formatter failed");
217
+ expect(output.length).toBeLessThan(300);
218
+ expect(output).not.toMatch(/\.{2}|SYNTHETIC|x{2}|REDACTED|ACTIONABLE_TAIL/);
219
+ expect(output).toContain("[pre-commit] FAILED (exit 23)\n");
220
+ expect(run(dir, "git", ["show", ":payload.ts"])).toBe("clean");
221
+ expect(runFailure(dir, "git", ["rev-parse", "--verify", "HEAD"]).status).not.toBe(0);
222
+ });
223
+
224
+ it("preserves and redacts complete below-cap stderr diagnostics", () => {
225
+ const dir = fixture();
226
+ stage(dir, "payload.ts", "clean\n");
227
+ const payload = ".".repeat(256) + `${literals[0]}\n`.repeat(2048) + "ACTIONABLE_TAIL\n";
228
+ writeFileSync(path.join(dir, "payload.ts"), payload);
229
+ diagnosticFormatter(dir, 2, 23);
230
+ const result = runFailure(dir, "bash", ["git-hooks/pre-commit"]);
231
+ const output = result.stdout + result.stderr;
232
+ expect(result.status).toBe(23);
233
+ expect(output.startsWith(payload.replaceAll(literals[0], "[REDACTED]"))).toBe(true);
234
+ expect(output).not.toContain(literals[0]);
235
+ expect(output.endsWith(failureLine)).toBe(true);
236
+ // Formatter failure must abort before any index update or the second scan.
237
+ expect(run(dir, "git", ["show", ":payload.ts"])).toBe("clean");
238
+ });
239
+
240
+ it("stages formatter stdout for partially staged files and rescans it", () => {
241
+ const dir = fixture();
242
+ stage(dir, "payload.ts", "clean\n");
243
+ const payload = ".".repeat(256) + `${literals[0]}\n`.repeat(2048) + "ACTIONABLE_TAIL\n";
244
+ writeFileSync(path.join(dir, "payload.ts"), payload);
245
+ diagnosticFormatter(dir, 1, 23);
246
+ const failed = runFailure(dir, "bash", ["git-hooks/pre-commit"]);
247
+ expect(failed.status).toBe(23);
248
+ // Content on stdout is never replayed as diagnostics, redacted or otherwise.
249
+ expect(failed.stdout + failed.stderr).not.toContain("ACTIONABLE_TAIL");
250
+ expect(failed.stdout + failed.stderr).not.toContain("[REDACTED]");
251
+ expect(run(dir, "git", ["show", ":payload.ts"])).toBe("clean");
252
+ // On success the emitted bytes become staged content and the post-format scan owns them.
253
+ diagnosticFormatter(dir, 1, 0);
254
+ expectBlocked(runFailure(dir, "git", commitArgs).stderr, "payload.ts");
255
+ });
256
+
257
+ it("discards captures when the formatter shell is terminated by a signal", () => {
258
+ const dir = fixture();
259
+ stage(dir, "payload.ts", "clean\n");
260
+ writeExecutable(
261
+ path.join(dir, "node_modules/.bin"),
262
+ "oxfmt",
263
+ `#!/usr/bin/env bash
264
+ printf 'INCOMPLETE_STDOUT'
265
+ printf 'INCOMPLETE_STDERR' >&2
266
+ kill -TERM "$PPID"
267
+ `,
268
+ );
269
+ const result = runFailure(dir, "git", commitArgs);
270
+ expect(result.stdout + result.stderr).not.toContain("INCOMPLETE");
271
+ expect(result.stderr).toContain("Formatter could not complete");
272
+ expect(result.stderr).toContain("FAILED (exit 1)");
273
+ });
274
+
275
+ it("fails safely when the formatter shell cannot be spawned", () => {
276
+ const dir = fixture();
277
+ stage(dir, "payload.txt", "clean\n");
278
+ const bin = path.join(dir, "bin");
279
+ symlinkSync(process.execPath, path.join(bin, "node"));
280
+ symlinkSync(run(dir, "which", ["git"]), path.join(bin, "git"));
281
+ const result = runFailure(dir, "/bin/bash", ["git-hooks/pre-commit"], { PATH: bin });
282
+ expect(result.status).toBe(1);
283
+ expect(result.stdout).toBe("");
284
+ expect(result.stderr).toContain("Formatter could not complete");
285
+ expect(result.stderr).toContain("FAILED (exit 1)");
286
+ });
287
+
288
+ it.each([true, false])("drains piped stderr diagnostics: rules=%s", (configured) => {
289
+ const dir = fixture();
290
+ if (!configured) {
291
+ run(dir, "git", ["config", "--local", "--unset", ruleSetting]);
292
+ unlinkSync(path.join(dir, rulePath));
293
+ }
294
+ stage(dir, "payload.ts", "clean\n");
295
+ const payload = "public diagnostic context\n".repeat(4000) + "ACTIONABLE_FINAL_DETAIL\n";
296
+ writeFileSync(path.join(dir, "payload.ts"), payload);
297
+ diagnosticFormatter(dir, 2, 23);
298
+ for (const [cmd, args] of [
299
+ ["bash", ["git-hooks/pre-commit"]],
300
+ ["git", commitArgs],
301
+ ] as const) {
302
+ const result = runFailure(dir, cmd, [...args]);
303
+ const output = result.stdout + result.stderr;
304
+ expect(result.status).toBe(cmd === "git" ? 1 : 23);
305
+ expect(output.startsWith(payload)).toBe(true);
306
+ expect(output.endsWith(failureLine)).toBe(true);
307
+ expect(output.match(/\[pre-commit\] FAILED/g)).toHaveLength(1);
308
+ }
309
+ diagnosticFormatter(dir, 2, 0, true);
310
+ expect(run(dir, "bash", ["-c", 'exec git "$@" 2>&1', "hook-proof", ...commitArgs])).toBe(
311
+ payload.trim(),
312
+ );
313
+ expect(run(dir, "git", ["show", "HEAD:payload.ts"])).toBe("clean");
314
+ });
315
+ });
316
+
317
+ function createOperationFixture(linked: boolean, revert = false): { dir: string; primary: string } {
318
+ const primary = createContentGuardFixture(tempDirs);
319
+ const commit = (name: string, content: string) => {
320
+ stage(primary, name, content);
321
+ run(primary, "git", ["commit", "-qm", "operation fixture"]);
322
+ };
323
+ commit("changed.ts", "export const value = 0;\n");
324
+ run(primary, "git", ["checkout", "-qb", "side"]);
325
+ commit("changed.ts", "export const value = 1;\n");
326
+ commit("next.txt", "next step\n");
327
+ run(primary, "git", ["checkout", "-q", "main"]);
328
+ if (revert) {
329
+ run(primary, "git", ["merge", "--ff-only", "side"]);
330
+ }
331
+ commit("changed.ts", "export const value = 2;\n");
332
+ let dir = primary;
333
+ if (linked) {
334
+ dir = path.join(primary, "linked");
335
+ run(primary, "git", ["worktree", "add", "-qb", "linked", dir, "HEAD"]);
336
+ installPreCommitFixture(dir);
337
+ }
338
+ return { dir, primary };
339
+ }
340
+
341
+ describe.each([false, true])("Git operation state (linked=%s)", (linked) => {
342
+ it.each([
343
+ { operation: "merge", args: ["merge", "--no-commit", "side"], state: "MERGE_HEAD" },
344
+ {
345
+ operation: "rebase merge",
346
+ args: ["rebase", "--merge", "side"],
347
+ state: "rebase-merge/git-rebase-todo",
348
+ },
349
+ {
350
+ operation: "rebase apply",
351
+ args: ["rebase", "--apply", "side"],
352
+ state: "rebase-apply/next",
353
+ },
354
+ { operation: "cherry-pick", args: ["cherry-pick", "side~1"], state: "CHERRY_PICK_HEAD" },
355
+ { operation: "revert", args: ["revert", "--no-edit", "side~1"], state: "REVERT_HEAD" },
356
+ {
357
+ operation: "cherry-pick sequencer-only",
358
+ args: ["cherry-pick", "side~1", "side"],
359
+ state: "sequencer/todo",
360
+ },
361
+ {
362
+ operation: "revert sequencer-only",
363
+ args: ["revert", "--no-edit", "side~1", "side"],
364
+ state: "sequencer/todo",
365
+ },
366
+ ])("preserves operation staging during $operation", ({ operation, args, state }) => {
367
+ const { dir, primary } = createOperationFixture(linked, operation.startsWith("revert"));
368
+ expect(runFailure(dir, "git", args).status).toBe(1);
369
+ expect(run(dir, "git", ["ls-files", "--unmerged"])).not.toBe("");
370
+ const metadata = path.resolve(dir, run(dir, "git", ["rev-parse", "--git-path", state]));
371
+ if (linked) {
372
+ expect(existsSync(path.join(primary, ".git", state))).toBe(false);
373
+ }
374
+ if (state === "sequencer/todo") {
375
+ stage(dir, "changed.ts", "export const value = 3;\n");
376
+ // A real resolution commit clears the per-step ref while later steps remain queued.
377
+ run(dir, "git", commitArgs);
378
+ for (const ref of ["CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"]) {
379
+ expect(
380
+ existsSync(path.resolve(dir, run(dir, "git", ["rev-parse", "--git-path", ref]))),
381
+ ).toBe(false);
382
+ }
383
+ }
384
+ const before = readFileSync(metadata);
385
+ const staged = "export const value=4;\n";
386
+ const unstaged = `${staged}// unstaged edit\n`;
387
+ stage(dir, "changed.ts", staged);
388
+ const stagedOid = run(dir, "git", ["rev-parse", ":changed.ts"]);
389
+ writeFileSync(path.join(dir, "changed.ts"), unstaged);
390
+ const log = installFormattingRecorder(
391
+ dir,
392
+ "printf '// formatted\\n' >> changed.ts\nprintf formatted",
393
+ );
394
+
395
+ expect(run(dir, "bash", ["git-hooks/pre-commit"])).toBe("");
396
+ expect(readFormatterLog(log)).toEqual([]);
397
+ expect(run(dir, "git", ["rev-parse", ":changed.ts"])).toBe(stagedOid);
398
+ expect(readFileSync(path.join(dir, "changed.ts"), "utf8")).toBe(unstaged);
399
+ expect(readFileSync(metadata)).toEqual(before);
400
+
401
+ stage(dir, "changed.ts", literals[1]);
402
+ expect(runFailure(dir, "bash", ["git-hooks/pre-commit"]).stderr).toContain(
403
+ "Blocked staged content",
404
+ );
405
+ stage(dir, "changed.ts", staged);
406
+ writeFileSync(path.join(dir, "changed.ts"), unstaged);
407
+ run(dir, "git", commitArgs);
408
+ expect(readFormatterLog(log)).toEqual([]);
409
+ expect(run(dir, "git", ["rev-parse", "HEAD:changed.ts"])).toBe(stagedOid);
410
+ expect(readFileSync(path.join(dir, "changed.ts"), "utf8")).toBe(unstaged);
411
+ });
412
+
413
+ it("formats and restages with an orphan REBASE_HEAD", () => {
414
+ const { dir, primary } = createOperationFixture(linked);
415
+ if (linked) {
416
+ // Another worktree's real rebase must not suppress this worktree's ordinary commit.
417
+ expect(runFailure(primary, "git", ["rebase", "--merge", "side"]).status).toBe(1);
418
+ }
419
+ const head = run(dir, "git", ["rev-parse", "HEAD"]);
420
+ // Reproduce the observed orphan state without claiming how it was left behind.
421
+ run(dir, "git", ["update-ref", "REBASE_HEAD", head]);
422
+ for (const state of [
423
+ "MERGE_HEAD",
424
+ "CHERRY_PICK_HEAD",
425
+ "REVERT_HEAD",
426
+ "rebase-merge",
427
+ "rebase-apply",
428
+ "sequencer",
429
+ ]) {
430
+ expect(
431
+ existsSync(path.resolve(dir, run(dir, "git", ["rev-parse", "--git-path", state]))),
432
+ ).toBe(false);
433
+ }
434
+ expect(runFailure(dir, "git", ["rebase", "--continue"]).stderr).toContain(
435
+ "no rebase in progress",
436
+ );
437
+ const working = "export const value=5;\n";
438
+ stage(dir, "changed.ts", working);
439
+ const log = installFormattingRecorder(
440
+ dir,
441
+ "printf '// formatted\\n' >> changed.ts\nprintf formatted",
442
+ );
443
+ expect(run(dir, "bash", ["git-hooks/pre-commit"])).toBe("formatted");
444
+ expect(readFormatterLog(log)).toEqual([
445
+ "oxfmt --write --no-error-on-unmatched-pattern changed.ts",
446
+ ]);
447
+ expect(readFileSync(path.join(dir, "changed.ts"), "utf8")).toBe(`${working}// formatted\n`);
448
+ expect(run(dir, "git", ["show", ":changed.ts"])).toBe(`${working}// formatted`);
449
+ expect(run(dir, "git", ["diff", "--", "changed.ts"])).toBe("");
450
+ expect(run(dir, "git", ["rev-parse", "REBASE_HEAD"])).toBe(head);
451
+ });
452
+ });
test/github-cli-preflight.e2e.test.ts ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // E2E: a Gateway host without gh rejects authorization before device-code issuance.
2
+ import path from "node:path";
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import { resolveExecutablePath } from "../src/infra/executable-path.js";
5
+ import {
6
+ createOpenClawTestInstance,
7
+ type OpenClawTestInstance,
8
+ } from "./helpers/openclaw-test-instance.js";
9
+
10
+ const TEST_TIMEOUT_MS = 180_000;
11
+ const instances: OpenClawTestInstance[] = [];
12
+
13
+ afterEach(async () => {
14
+ await Promise.allSettled(instances.splice(0).map((instance) => instance.cleanup()));
15
+ });
16
+
17
+ describe("GitHub CLI authorization preflight", () => {
18
+ it(
19
+ "returns installation guidance through the real CLI and Gateway without requesting a code",
20
+ { timeout: TEST_TIMEOUT_MS },
21
+ async () => {
22
+ const gatewayPath = path.dirname(process.execPath);
23
+ expect(resolveExecutablePath("gh", { env: { PATH: gatewayPath } })).toBeUndefined();
24
+
25
+ const instance = await createOpenClawTestInstance({
26
+ name: "github-cli-preflight",
27
+ env: { PATH: gatewayPath, OPENCLAW_PATH_BOOTSTRAPPED: "1" },
28
+ });
29
+ instances.push(instance);
30
+ await instance.startGateway();
31
+
32
+ const result = await instance.cli([
33
+ "gateway",
34
+ "call",
35
+ "tools.github.authorize.start",
36
+ "--params",
37
+ '{"scope":"agent","agentId":"main"}',
38
+ "--json",
39
+ ]);
40
+
41
+ const output = `${result.stdout}\n${result.stderr}`;
42
+ expect(output).toContain(
43
+ "GitHub CLI (`gh`) is required on the Gateway host. Install it and retry.",
44
+ );
45
+ expect(output).not.toContain("github.com/login/device");
46
+ expect(output).not.toContain("userCode");
47
+ },
48
+ );
49
+ });