Compare commits

..

933 Commits

Author SHA1 Message Date
Josh Lehman
53ac244ec6 Merge branch 'main' into codex/plugin-command-scope-auth 2026-03-26 16:15:50 -07:00
Peter Steinberger
d1d0887932 refactor: remove legacy browser bridge entrypoints 2026-03-26 23:11:17 +00:00
Josh Lehman
c94f10b915 Plugins: harden dynamic scope resolution
Catch dynamic gateway-scope resolver failures in the dispatcher, narrow
forwarded gateway scope strings with an explicit operator-scope guard, add
regression coverage for admin bypass and resolver-throw behavior, and
refresh bundled plugin metadata after main-branch drift.

Regeneration-Prompt: |
  Follow up on review feedback for the centralized plugin command auth
  change. Keep the scope tightly limited to the three review items:
  catch exceptions from `resolveRequiredGatewayScopes`, replace the raw
  `GatewayClientScopes` cast with explicit operator-scope narrowing, and
  add dispatcher-level tests for the `operator.admin` bypass plus the safe
  failure path when dynamic scope resolution throws.

  While landing that patch, the repo hook may report stale bundled plugin
  metadata generated files because main advanced. Regenerate those standard
  outputs with the repo generator so the branch is consistent enough to
  rebase, but do not chase unrelated CI or Discord test failures here.
2026-03-26 16:11:09 -07:00
Josh Lehman
70b43319ff Plugin SDK: refresh API baselines for auth context change
Update the generated Plugin SDK API baseline files after extending plugin
command types for centralized owner and gateway-scope authorization.

Regeneration-Prompt: |
  The prior commit intentionally changed exported plugin SDK types in
  `src/plugins/types.ts` by adding richer plugin command auth context and
  declarative command requirement fields. CI reported plugin SDK API drift,
  which means the generated baseline files under `docs/.generated/` no
  longer matched the exported surface.

  Regenerate only the plugin SDK API baseline artifacts with the repo's
  standard generator, verify `pnpm plugin-sdk:api:check` passes, and keep
  this follow-up scoped to those generated files. Do not fold in unrelated
  failing tests from untouched surfaces.
2026-03-26 16:11:09 -07:00
Peter Steinberger
4b40d4dfa8 perf: optimize cold import paths 2026-03-26 23:11:00 +00:00
Josh Lehman
487f752754 Plugins: centralize plugin command auth requirements
Move plugin command authorization toward the GHSA's long-term model by
preserving richer auth context, supporting declarative owner and gateway
scope requirements, and enforcing them in the shared dispatcher. Convert
`/pair approve` to use the centralized requirement path and add regression
coverage for dispatcher-level auth behavior.

Regeneration-Prompt: |
  This follow-up hardening is for the plugin command auth gap described in
  GHSA-9gwp-pxfh-w6r5. The immediate exploit path was already fixed by
  plumbing gateway scopes into the device-pair plugin and checking `/pair
  approve` inline, but the longer-term goal is to stop relying on lossy,
  plugin-specific auth checks.

  Preserve the existing plugin command flow and keep the change additive.
  Carry richer authorization context into plugin execution, including owner
  status and command surface, and let commands declare owner or internal
  gateway-scope requirements that the central dispatcher enforces. Internal
  callers should fail closed when required scopes are missing, with admin
  scope still satisfying narrower operator requirements, while non-internal
  chat surfaces should keep their current auth behavior.

  Because `/pair` mixes low-risk actions like `qr` and `status` with the
  privileged `approve` action, use a context-sensitive requirement instead
  of making the whole command require pairing scope. Add focused regression
  tests around dispatcher enforcement and update any command-context test
  helpers that now need the richer fields.
2026-03-26 16:10:38 -07:00
Peter Steinberger
15181b3a77 docs(anthropic): dedupe config heading 2026-03-26 23:08:26 +00:00
Peter Steinberger
5f2876911a fix: harden discord gateway cleanup (#55373) (thanks @Takhoffman) 2026-03-26 16:07:13 -07:00
Tak Hoffman
a79c9d50f7 fix(discord): guard gateway cleanup races 2026-03-26 16:07:13 -07:00
Peter Steinberger
f406b20e50 chore(docs): refresh generated baselines 2026-03-26 23:05:59 +00:00
Peter Steinberger
eef27001de docs: explain anthropic claude cli migration 2026-03-26 23:04:47 +00:00
Peter Steinberger
ebf5bd75f4 feat: add anthropic claude cli migration 2026-03-26 23:04:47 +00:00
Peter Steinberger
b96fccadb9 refactor: clean memory plugin host boundary 2026-03-26 23:02:24 +00:00
Peter Steinberger
556ce5cdda test(browser): fix CI after compat re-exports 2026-03-26 22:59:50 +00:00
Peter Steinberger
09c186d5f9 refactor: remove browser compat shadow tree 2026-03-26 22:53:37 +00:00
Peter Steinberger
d72115c9df refactor: genericize speech provider config surface 2026-03-26 22:48:57 +00:00
Peter Steinberger
83ca6fbfc6 refactor: finish browser compat untangle 2026-03-26 22:42:41 +00:00
Peter Steinberger
8ee809f3cc refactor: share plugin entry exports 2026-03-26 22:38:13 +00:00
Peter Steinberger
8df6134a1b refactor: share usage metrics timeslice walker 2026-03-26 22:38:13 +00:00
Peter Steinberger
ff47ad58fc refactor: share config path traversal helper 2026-03-26 22:38:13 +00:00
Peter Steinberger
5445bc68b9 refactor: share tts auto mode normalization 2026-03-26 22:38:13 +00:00
Seungwoo hong
138a92373b fix(talk): prevent double TTS playback when system voice times out (#53511)
Merged via squash.

Prepared head SHA: 864d556fa6
Co-authored-by: hongsw <1100974+hongsw@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06
2026-03-26 15:37:40 -07:00
Peter Steinberger
0f5a77d058 refactor: route memory runtime through memory plugin 2026-03-26 22:30:47 +00:00
Peter Steinberger
2c6d099b01 refactor: plugin-own speech provider config 2026-03-26 22:28:24 +00:00
Peter Steinberger
8eeb7f0829 refactor: switch browser ownership to bundled plugin 2026-03-26 22:20:40 +00:00
Peter Steinberger
197510f693 refactor: add browser plugin runtime package 2026-03-26 22:20:39 +00:00
Peter Steinberger
1619090693 refactor: move memory plugin state into plugin host 2026-03-26 22:15:49 +00:00
Peter Steinberger
00aedb3414 refactor: split claude cli history import pipeline 2026-03-26 22:12:16 +00:00
Peter Steinberger
d0ce2d1044 refactor: split memory-core plugin helpers 2026-03-26 22:06:06 +00:00
Peter Steinberger
9dea807b28 test: share planner and sandbox test helpers 2026-03-26 22:03:18 +00:00
Peter Steinberger
672a24cbde fix: unify claude cli imported tool messages 2026-03-26 22:02:26 +00:00
Peter Steinberger
3d0050c306 refactor: add memory-core extension sources 2026-03-26 22:00:13 +00:00
Peter Steinberger
e955d574b2 refactor: move memory tooling into memory-core extension 2026-03-26 22:00:13 +00:00
Peter Steinberger
e0dfc776bb refactor: move memory flush ownership into memory plugin 2026-03-26 22:00:13 +00:00
Peter Steinberger
48a65f7749 refactor: simplify bundled plugin contracts 2026-03-26 21:55:41 +00:00
Peter Steinberger
8b42ad08e5 perf: speed up shared extension test batches 2026-03-26 21:51:25 +00:00
Peter Steinberger
29069bd250 refactor: share speech normalization helpers 2026-03-26 21:49:20 +00:00
Peter Steinberger
ce9dff1458 refactor: clean plugin capability boundaries 2026-03-26 21:41:49 +00:00
Tak Hoffman
d00dc5f46b fix(ci): repair discord and telegram follow-ups 2026-03-26 16:33:05 -05:00
Peter Steinberger
53f90af990 test: dedupe telegram polling session harness 2026-03-26 21:30:28 +00:00
Peter Steinberger
2a04053854 fix: skip cli backends in models auth warnings 2026-03-26 21:28:28 +00:00
Peter Steinberger
98ea8e244f fix: backfill claude cli chat history 2026-03-26 21:25:35 +00:00
Peter Steinberger
6aa9bec8d7 fix: export shared channel action enum helpers 2026-03-26 21:21:44 +00:00
Peter Steinberger
0c0f1e34cb refactor: split telegram polling and sdk surfaces 2026-03-26 21:13:16 +00:00
Tak Hoffman
0805078118 fix(ci): format discord provider follow-up 2026-03-26 16:05:25 -05:00
Tak Hoffman
aeee72426d fix(ci): restore discord provider test seams 2026-03-26 15:59:51 -05:00
Peter Steinberger
37894d0f1a test: dedupe discord provider proxy overrides 2026-03-26 20:43:02 +00:00
Lyle Hopkins
eb328a85e3 fix(agents): classify "Failed to extract accountId from token" as auth error for failover (#27055) (#55206)
Co-authored-by: Lyle Hopkins <55105+cosmicnet@users.noreply.github.com>
2026-03-26 23:42:38 +03:00
Tak Hoffman
53d3b8e92d fix(ci): clean up discord harness types 2026-03-26 15:39:20 -05:00
Tak Hoffman
a39e57a1bd fix(ci): repair discord harness regressions 2026-03-26 15:39:20 -05:00
Peter Steinberger
3f54076d37 refactor: dedupe cli runner session reuse 2026-03-26 20:35:19 +00:00
Peter Steinberger
61d29efc04 test: eliminate remaining clone seams 2026-03-26 20:28:36 +00:00
Peter Steinberger
5841e5fdf8 test: split cli agent command coverage 2026-03-26 20:25:20 +00:00
Peter Steinberger
12100719b8 fix: preserve cli sessions across model changes 2026-03-26 20:25:20 +00:00
Peter Steinberger
236e041ef9 test: share discord monitor fixtures 2026-03-26 20:12:21 +00:00
pkuGeo
e035a0d98c telegram: rebuild transport after stalled polling cycles 2026-03-26 13:11:15 -07:00
Peter Steinberger
663ba5a3cd perf: speed up test parallelism 2026-03-26 20:09:40 +00:00
Peter Steinberger
2fc017788c test: reduce remaining clone seams 2026-03-26 20:01:01 +00:00
Tak Hoffman
b20ae13c6b fix(ci): repair discord message handler tests 2026-03-26 14:49:06 -05:00
Peter Steinberger
be328e6cd1 test: dedupe extension channel fixtures 2026-03-26 19:47:27 +00:00
Peter Steinberger
e8f9d68bec test: share cli command and discord test helpers 2026-03-26 19:37:14 +00:00
Peter Steinberger
b48df79c0a test(gateway): strip MiniMax live scaffolding 2026-03-26 19:35:03 +00:00
Tak Hoffman
53f15afade fix(ci): repair discord regression tests 2026-03-26 14:23:57 -05:00
Peter Steinberger
ef381743d8 test: share cli and doctor test helpers 2026-03-26 19:16:43 +00:00
Peter Steinberger
ab4de18982 fix: auto-load bundled plugin capabilities from config refs 2026-03-26 19:15:56 +00:00
Peter Steinberger
8f1716ae5a refactor: share slack and telegram action helpers 2026-03-26 19:07:35 +00:00
Peter Steinberger
a1a9819be8 refactor: dedupe gateway session resolve visibility 2026-03-26 18:56:55 +00:00
Peter Steinberger
4069844795 refactor: share discord outbound session routing 2026-03-26 18:51:02 +00:00
Peter Steinberger
e774fe1286 refactor: share browser and sandbox helpers 2026-03-26 18:43:57 +00:00
Tak Hoffman
2b6375faf9 fix: keep spawned session owners in live events 2026-03-26 13:41:46 -05:00
Tak Hoffman
1062a048eb fix: expose spawned session owners in sessions list 2026-03-26 13:41:46 -05:00
Tak Hoffman
c041fcc04d fix: expose parent session keys in sessions list 2026-03-26 13:41:46 -05:00
Tak Hoffman
cb46b08efc fix: include dashboard children in owner filters 2026-03-26 13:41:46 -05:00
Tak Hoffman
c48a3e4fc9 ci: optimize windows test shard fanout (#55261)
* ci: reduce windows test shard fanout

* ci: tighten windows shard target

* ci: back off windows shard target

* ci: restore windows shard cap
2026-03-26 13:40:28 -05:00
Peter Steinberger
cca577a0cc refactor: share plugin setup helpers 2026-03-26 18:34:51 +00:00
Peter Steinberger
c98addeadd test: share auto-reply typing helpers 2026-03-26 18:27:13 +00:00
Peter Steinberger
1f740ff099 test: share cli and channel setup fixtures 2026-03-26 18:14:44 +00:00
Jacob Tomlinson
02cf12371f Gateway: require requester ownership for HTTP session kills (#55308) 2026-03-26 18:13:36 +00:00
Peter Steinberger
f29c1206cd test: dedupe extension channel fixtures 2026-03-26 17:59:05 +00:00
Peter Steinberger
48167a69b9 refactor: dedupe gateway and binding helpers 2026-03-26 17:49:19 +00:00
Jacob Tomlinson
1c45123231 Gateway: align HTTP session history scopes (#55285)
* Gateway: require scopes for HTTP session history

* Gateway: cover missing HTTP history scope header
2026-03-26 17:43:57 +00:00
Jacob Tomlinson
f8c9863078 bluebubbles: honor reaction mention gating (#55283) 2026-03-26 17:42:19 +00:00
Peter Steinberger
e7e4fbcab9 test: dedupe secrets and guardrail fixtures 2026-03-26 17:39:58 +00:00
Jacob Tomlinson
d3d8e316bd gateway: require pairing for backend scope upgrades (#55286) 2026-03-26 17:36:44 +00:00
Jacob Tomlinson
b5d785f1a5 Gateway: require caller scope for subagent session deletion (#55281) 2026-03-26 17:34:09 +00:00
Jacob Tomlinson
ec2dbcff9a fix: keep plugin HTTP runtime scopes least-privileged (#55284) 2026-03-26 17:28:30 +00:00
Tak Hoffman
21a679e567 fix(ci): refresh plugin sdk api baseline 2026-03-26 12:18:26 -05:00
Peter Steinberger
07c41301e3 style: normalize ui slash executor formatting 2026-03-26 17:09:21 +00:00
Peter Steinberger
d6f7de392c test: dedupe ui chat seams 2026-03-26 17:07:27 +00:00
Peter Steinberger
7bb95354c4 test: dedupe matrix setup seams 2026-03-26 17:04:23 +00:00
Peter Steinberger
c12623a857 test: share plugin auth and ui storage fixtures 2026-03-26 16:55:20 +00:00
Peter Steinberger
d748ea9361 docs: note guest openclaw shim in parallels skill 2026-03-26 16:49:52 +00:00
Peter Steinberger
f0991aab57 test: add docker cli-backend smoke 2026-03-26 16:49:52 +00:00
Peter Steinberger
e1f0a85128 refactor: share auto-reply reply helpers 2026-03-26 16:48:34 +00:00
Tak Hoffman
615fe4a06b fix: preserve reset cli session linkage 2026-03-26 11:46:47 -05:00
Tak Hoffman
22f9c19a39 fix: preserve reset acp session metadata 2026-03-26 11:46:47 -05:00
Tak Hoffman
74b0a948e3 fix: preserve reset channel identity 2026-03-26 11:46:47 -05:00
Tak Hoffman
cb0a752156 fix: preserve reset session behavior config 2026-03-26 11:46:47 -05:00
Peter Steinberger
99d052a203 perf: overlap isolated channel runs with shared lane 2026-03-26 16:45:08 +00:00
Peter Steinberger
d5acd7dee5 test: share ui reconnect and storage helpers 2026-03-26 16:41:51 +00:00
Peter Steinberger
03ea6953e0 test: share gateway authz and watchdog fixtures 2026-03-26 16:36:03 +00:00
Peter Steinberger
d9a7dcec4b test: share matrix migration fixtures 2026-03-26 16:25:23 +00:00
Jacob Tomlinson
c2c136ae95 telegram: throttle repeated webhook auth guesses (#55142)
* telegram: throttle repeated webhook auth guesses

* telegram: use per-listener webhook rate limits

* config: stabilize doc baseline ordering
2026-03-26 16:19:31 +00:00
Peter Steinberger
a92fbf7d40 test: dedupe remaining agent test seams 2026-03-26 16:14:45 +00:00
Peter Steinberger
880b2fb7fd perf: enable local channel planner parallelism on node 25 2026-03-26 16:06:09 +00:00
Peter Steinberger
bac603a63e test: share subagent and policy test fixtures 2026-03-26 16:04:34 +00:00
Tak Hoffman
22520a2058 fix: preserve reset spawn context 2026-03-26 10:57:42 -05:00
Tak Hoffman
8c6be29454 fix: preserve reset elevated level 2026-03-26 10:51:01 -05:00
Tak Hoffman
b04ec4bada ci: make docker release tag-driven 2026-03-26 10:47:01 -05:00
Peter Steinberger
4ed5895637 test: dedupe config compatibility fixtures 2026-03-26 15:45:14 +00:00
Tak Hoffman
6bdf5e5634 fix: preserve reset spawn depth 2026-03-26 10:42:12 -05:00
Peter Steinberger
c4048aea41 test: share msteams monitor and pi runner fixtures 2026-03-26 15:40:51 +00:00
Peter Steinberger
339cc33cf8 perf: speed up channel test runs 2026-03-26 15:40:01 +00:00
Tak Hoffman
06b4a0a1f2 test: improve test runner help text (#55227)
* test: improve test runner help text

* test: print extension help to stdout

* test: leave extension help passthrough alone

* test: parse timing update flags in one pass
2026-03-26 10:34:14 -05:00
Tak Hoffman
471da49c59 fix: preserve reset ownership metadata 2026-03-26 10:32:09 -05:00
Jacob Tomlinson
0b4d073374 synology-chat: throttle webhook token guesses (#55141)
* synology-chat: throttle webhook token guesses

* synology-chat: keep valid webhook traffic within configured limits

* docs: refresh generated config baseline

* synology-chat: enforce lockout after repeated token failures
2026-03-26 15:30:06 +00:00
Peter Steinberger
9bc3d33b53 test: dedupe web search provider fixtures 2026-03-26 15:26:11 +00:00
Tak Hoffman
df04ca7da3 fix: preserve metadata on voice session touches 2026-03-26 10:25:18 -05:00
Peter Steinberger
65a1afb9df test: share redact and approval fixtures 2026-03-26 15:23:12 +00:00
Peter Steinberger
5e78232bc5 test: share pi compaction fixtures 2026-03-26 15:19:32 +00:00
Tak Hoffman
d69ff3c022 fix(whatsapp): unwrap quoted wrapper messages 2026-03-26 10:16:33 -05:00
Peter Steinberger
f56a25a596 test: dedupe foundry auth fixtures 2026-03-26 15:14:03 +00:00
Peter Steinberger
a4a00aa1da feat: pluginize cli inference backends 2026-03-26 15:11:15 +00:00
Tak Hoffman
24dd7aec90 fix: prefer freshest duplicate store matches 2026-03-26 10:10:05 -05:00
Peter Steinberger
5f9f08394a refactor: share matrix and telegram dedupe helpers 2026-03-26 15:08:45 +00:00
Ayaan Zaidi
4b1c37a152 fix: avoid duplicate ACP Telegram finals (#55173)
* fix: avoid duplicate final ACP text on telegram

* fix: keep ACP final fallback for non-telegram blocks

* fix: count telegram ACP block replies as success

* fix: recover ACP final fallback after block failures

* fix: settle telegram ACP block delivery before fallback

* test: isolate ACP dispatch mocks under shared workers

* fix: prefer telegram provider for ACP visibility
2026-03-26 20:37:21 +05:30
Peter Steinberger
2ed11a375a refactor: share web media loader 2026-03-26 14:55:32 +00:00
Jacob Tomlinson
5e08ce36d5 fix(bluebubbles): throttle webhook auth guesses (#55133)
* fix(bluebubbles): throttle webhook auth guesses

* test(bluebubbles): isolate attachment ssrf config

* test(bluebubbles): hoist attachment mocks

* docs: refresh bluebubbles config baseline

* fix(bluebubbles): trust proxied webhook client IPs

* fix(bluebubbles): honor trusted proxy webhook IPs

* fix(bluebubbles): honor real-ip fallback for webhooks
2026-03-26 14:54:03 +00:00
Peter Steinberger
5c3e018492 refactor: dedupe msteams graph actions 2026-03-26 14:45:53 +00:00
Tak Hoffman
a4e5b23dc3 docs: update PR template review guidance 2026-03-26 09:36:36 -05:00
Tak Hoffman
9f0305420a docs: add beta blocker contributor guidance (#55199)
* docs: add beta blocker contributor guidance

* fix: tighten beta blocker labeling and flaky config test
2026-03-26 09:31:59 -05:00
Tak Hoffman
e403899cc1 test: fix portable stderr capture and env leakage (#55184) 2026-03-26 09:31:08 -05:00
Tak Hoffman
dd46c3d75b test(memory): initialize providers in lazy manager tests 2026-03-26 09:29:07 -05:00
Tyler Yust
2513a8d852 fix(bluebubbles): refactor sendMessageBlueBubbles to use resolveBlueBubblesServerAccount and enhance private network handling in tests 2026-03-26 07:21:48 -07:00
Jacob Tomlinson
81c45976db Feishu: reject legacy raw card command payloads (#55130)
* Feishu: reject legacy raw card callbacks

* Feishu: cover legacy text card payloads

* Docs: refresh config baseline

* CI: refresh PR checks

* Feishu: limit legacy card guard scope
2026-03-26 14:17:45 +00:00
Jacob Tomlinson
11ea1f6786 Google Chat: require stable group ids (#55131)
* Google Chat: require stable group ids

* Google Chat: fail closed on deprecated room keys
2026-03-26 14:15:51 +00:00
Jacob Tomlinson
464e2c10a5 ACP: sanitize terminal tool titles (#55137)
* ACP: sanitize terminal tool titles

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>

* Config: refresh config baseline and stabilize restart pid test

---------

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>
2026-03-26 14:12:24 +00:00
Peter Steinberger
883239a560 build: prepare 2026.3.25 unreleased 2026-03-26 13:57:45 +00:00
Shakker
e3660f265c docs: sync config baseline 2026-03-26 13:35:48 +00:00
Tak Hoffman
cc7f18d6c2 fix: replace stale canonical duplicate rows 2026-03-26 08:03:24 -05:00
Tak Hoffman
fde3871ee7 fix: prefer freshest duplicate row promotion 2026-03-26 07:54:43 -05:00
Tyler Yust
cc077ef1ef fix(bluebubbles): enable group participant enrichment by default, add fallback fetch and handle field aliases 2026-03-26 05:45:41 -07:00
Tak Hoffman
68c6abe32b docs: add beta release testing guidance 2026-03-26 07:34:08 -05:00
Tak Hoffman
b529d13477 test: fix bluebubbles attachment ssrf expectations 2026-03-26 07:25:14 -05:00
Saurabh Mishra
6fbe9dd935 fix: surface provider-specific rate limit error message (#54433) (#54512)
Merged via squash.

Prepared head SHA: 755cff833c
Co-authored-by: bugkill3r <2924124+bugkill3r@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-26 15:16:06 +03:00
Jacob Tomlinson
2383daf5c4 Matrix: gate verification notices on DM access (#55122) 2026-03-26 11:59:20 +00:00
Tyler Yust
e43600c9e5 fix(bluebubbles): auto-allow private network for local serverUrl and add allowPrivateNetwork to channel schema 2026-03-26 04:55:46 -07:00
Jacob Tomlinson
c5415a474b fix(msteams): align feedback invoke authorization (#55108)
* msteams: align feedback invoke authorization

* msteams: fix feedback allowlist regressions

* msteams: tighten feedback group authorization
2026-03-26 11:51:43 +00:00
Jacob Tomlinson
269282ac69 Telegram: enforce DM auth for callbacks (#55112) 2026-03-26 11:42:27 +00:00
Jacob Tomlinson
d9810811b6 fix(agents): enforce session_status guard after sessionId resolution (#55105)
* fix(agents): enforce visibility guard after sessionId resolution in session_status

When a sessionId (rather than an explicit agent key) is passed to the
session_status tool, the sessionId resolution block rewrites
requestedKeyRaw to an explicit "agent:..." key.  The subsequent
visibility guard check at line 375 tested
`!requestedKeyRaw.startsWith("agent:")`, which was now always false
after resolution — skipping the visibility check entirely.

This meant a sandboxed agent could bypass visibility restrictions by
providing a sessionId instead of an explicit session key.

Fix: use the original `isExplicitAgentKey` flag (captured before
resolution) instead of re-checking the dynamic requestedKeyRaw.
This ensures the visibility guard runs for sessionId inputs while
still skipping the redundant check for inputs that were already
validated at the earlier explicit-key check (lines 281-286).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: cover session status sessionId guard

* test: align parent sessionId guard coverage

---------

Co-authored-by: Kevin Sheng <shenghuikevin@github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:34:22 +00:00
Jacob Tomlinson
5e8cb22176 Feishu: validate webhook signatures before parsing (#55083)
* Feishu: validate webhook signatures before parsing

* Scripts: allow Feishu raw body guard callsite
2026-03-26 10:29:22 +00:00
Nimrod Gutman
a3b85e1583 fix(discord): force fresh gateway reconnects (#54697)
* fix(discord): force fresh gateway reconnects

* fix(discord): harden forced reconnect teardown

* fix(discord): retry after socket drain timeouts

* fix(discord): guard forced socket teardown

* fix(discord): stop cleanly during reconnect drain
2026-03-26 12:05:00 +02:00
Altay
8564480f3e chore: add lockfile entry for extensions/microsoft-foundry 2026-03-26 12:50:33 +03:00
Tyler Yust
4c85fd8569 BlueBubbles: enrich group participants with local Contacts names (#54984)
* BlueBubbles: enrich group participants with Contacts names

* BlueBubbles: gate contact enrichment behind opt in config
2026-03-26 18:38:37 +09:00
Jacob Tomlinson
f92c92515b fix(extensions): route fetch calls through fetchWithSsrFGuard (#53929)
* fix(extensions): route fetch calls through fetchWithSsrFGuard

Replace raw fetch() with fetchWithSsrFGuard in BlueBubbles, Mattermost,
Nextcloud Talk, and Thread Ownership extensions so outbound requests go
through the shared DNS-pinning and network-policy layer.

BlueBubbles: thread allowPrivateNetwork from account config through all
fetch call sites (send, chat, reactions, history, probe, attachments,
multipart). Add _setFetchGuardForTesting hook for test overrides.

Mattermost: add guardedFetchImpl wrapper in createMattermostClient that
buffers the response body before releasing the dispatcher. Handle
null-body status codes (204/304).

Nextcloud Talk: wrap both sendMessage and sendReaction with
fetchWithSsrFGuard and try/finally release.

Thread Ownership: add fetchWithSsrFGuard and ssrfPolicyFromAllowPrivateNetwork
to the plugin SDK surface; use allowPrivateNetwork:true for the
Docker-internal forwarder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extensions): improve null-body handling and test harness cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(bluebubbles): default to strict SSRF policy when allowPrivateNetwork is unset

Callers that omit allowPrivateNetwork previously got undefined policy,
which caused blueBubblesFetchWithTimeout to fall through to raw fetch
and bypass the SSRF guard entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(bluebubbles): thread allowPrivateNetwork through action and monitor call sites

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mattermost,nextcloud-talk): add allowPrivateNetwork config for self-hosted/LAN deployments

* fix: regenerate config docs baseline for new allowPrivateNetwork fields

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 02:04:54 -07:00
pomelo
dad68d319b Remove Qwen OAuth integration (qwen-portal-auth) (#52709)
* Remove Qwen OAuth integration (qwen-portal-auth)

Qwen OAuth via portal.qwen.ai is being deprecated by the Qwen team due
to traffic impact on their primary Qwen Code user base. Users should
migrate to the officially supported Model Studio (Alibaba Cloud Coding
Plan) provider instead.

Ref: https://github.com/openclaw/openclaw/issues/49557

- Delete extensions/qwen-portal-auth/ plugin entirely
- Remove qwen-portal from onboarding auth choices, provider aliases,
  auto-enable list, bundled plugin defaults, and pricing cache
- Remove Qwen CLI credential sync (external-cli-sync, cli-credentials)
- Remove QWEN_OAUTH_MARKER from model auth markers
- Update docs/providers/qwen.md to redirect to Model Studio
- Update model-providers docs (EN + zh-CN) to remove Qwen OAuth section
- Regenerate config and plugin-sdk baselines
- Update all affected tests

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* Clean up residual qwen-portal references after OAuth removal

* Add migration hint for deprecated qwen-portal OAuth provider

* fix: finish qwen oauth removal follow-up

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Frank Yang <frank.ekn@gmail.com>
2026-03-26 16:32:34 +08:00
Ayaan Zaidi
83e6c12f15 build: update plugin sdk api baseline 2026-03-26 13:52:52 +05:30
kevinlin-openai
432d5f863c fix: add slack upload-file action (#54987) (thanks @kevinlin-openai)
* feat(slack): add upload-file action

Co-authored-by: Codex <noreply@openai.com>

* fix(slack): guard upload-file routing

Co-authored-by: Codex <noreply@openai.com>

* fix(slack): tighten upload-file validation

---------

Co-authored-by: kevinlin-openai <kevin@dendron.so>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 13:37:58 +05:30
Ayaan Zaidi
78584413ec docs: refresh config baseline for microsoft foundry 2026-03-26 12:45:47 +05:30
Ayaan Zaidi
7ea17963b0 fix: wire microsoft foundry into contract registry 2026-03-26 12:43:03 +05:30
wenmeng zhou
143275687a Docs: rename modelstudio.md to qwen_modelstudio.md, add Standard API endpoints (#54407)
* Docs: rename modelstudio.md to qwen_modelstudio.md, add Standard API endpoints

* refine docs

* Docs: fix broken link in providers/index.md after modelstudio rename

* Docs: add redirect from /providers/modelstudio to /providers/qwen_modelstudio

* Docs: adjust the order in index.md

* docs: rename modelstudio to qwen_modelstudio, add Standard API endpoints (#54407) (thanks @wenmengzhou)

---------

Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-26 00:11:28 -07:00
Nyanako
d72cc7a380 fix: route codex responses over websocket and preserve tool warnings (#53702) (thanks @Nanako0129)
* fix: route codex responses over websocket and suppress gated core tool warnings

* fix: rebase codex websocket patch onto main

* fix: preserve explicit alsoAllow warnings (#53702) (thanks @Nanako0129)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 12:28:17 +05:30
Tyler Yust
00e932a83c fix: restore inbound image embedding for CLI routed BlueBubbles turns (#51373)
* fix(cli): hydrate prompt image refs for inbound media

* Agents: harden CLI prompt image hydration (#51373)

* test: fix CLI prompt image hydration helper mocks
2026-03-26 15:47:44 +09:00
MetaX e|acc
a16dd967da feat: Add Microsoft Foundry provider with Entra ID authentication (#51973)
* Microsoft Foundry: add native provider

* Microsoft Foundry: tighten review fixes

* Microsoft Foundry: enable by default

* Microsoft Foundry: stabilize API routing
2026-03-26 01:33:14 -05:00
Ayaan Zaidi
06de515b6c fix(plugins): skip allowlist warning for config paths 2026-03-26 11:44:23 +05:30
sudie-codes
6329edfb8d msteams: add search message action (#54832)
* msteams: add pin/unpin, list-pins, and read message actions

Wire up Graph API endpoints for message read, pin, unpin, and list-pins
in the MS Teams extension, following the same patterns as edit/delete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* msteams: address PR review comments for pin/unpin/read actions

- Handle 204 No Content in postGraphJson (Graph mutations may return empty body)
- Strip conversation:/user: prefixes in resolveConversationPath to avoid Graph 404s
- Remove dead variable in channel pin branch
- Rename unpin param from messageId to pinnedMessageId for semantic clarity
- Accept both pinnedMessageId and messageId in unpin action handler for compat

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* msteams: resolve user targets + add User-Agent to Graph helpers

- Resolve user:<aadId> targets to actual conversation IDs via conversation
  store before Graph API calls (fixes 404 for DM-context actions)
- Add User-Agent header to postGraphJson/deleteGraphRequest for consistency
  with fetchGraphJson after rebase onto main

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* msteams: resolve DM targets to Graph chat IDs + expose pin IDs

- Prefer cached graphChatId over Bot Framework conversation IDs for user
  targets; throw descriptive error when no Graph-compatible ID is available
- Add `id` field to list-pins rows so default formatters surface the pinned
  resource ID needed for the unpin flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* msteams: add react and reactions (list) message actions

* msteams: add search message action via Graph API

* msteams: fix search query injection, add ConsistencyLevel header, use manual query string

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 01:09:53 -05:00
sudie-codes
8c852d86f7 msteams: fetch thread history via Graph API for channel replies (#51643)
* msteams: fetch thread history via Graph API for channel replies

* msteams: address PR #51643 review feedback

- Wrap resolveTeamGroupId Graph call in try/catch, fall back to raw
  conversationTeamId when Team.ReadBasic.All permission is missing
- Remove dead fetchChatMessages function (exported but never called)
- Add JSDoc documenting oldest-50-replies Graph API limitation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* msteams: address thread history PR review comments

* msteams: only cache team group IDs on successful Graph lookup

Avoid caching raw conversationTeamId as a Graph team GUID when the
/teams/{id} lookup fails — the raw ID may be a Bot Framework conversation
key, not a valid GUID, causing silent thread-history failures for the
entire cache TTL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 01:09:33 -05:00
George Zhang
6cbd2d36f8 Revert "feat: add video generation core infrastructure and extend image generation parameters (#53681)" (#54943)
This reverts commit 4cb8dde894.
2026-03-25 23:00:14 -07:00
OfflynAI
e45533d568 fix(whatsapp): drop fromMe echoes in self-chat DMs using outbound ID tracking (#54570)
Merged via squash.

Prepared head SHA: dad53caf39
Co-authored-by: joelnishanth <140015627+joelnishanth@users.noreply.github.com>
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Reviewed-by: @mcaxtr
2026-03-26 02:24:24 -03:00
Neerav Makwana
6fd9d2ff38 fix: support OpenAI Codex media understanding (#54829) (thanks @neeravmakwana)
* OpenAI: register Codex media understanding provider

* fix: route codex image prompts through system instructions

* fix: add changelog for codex image tool fix (#54829) (thanks @neeravmakwana)

* fix: remove any from provider registration tests (#54829) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 10:10:11 +05:30
Ted Li
76ff0d9298 fix: restore image-tool generic provider fallback (#54858) (thanks @MonkeyLeeT)
* Image tool: restore generic provider fallback

* Image tool: cover multi-image generic fallback

* test: tighten minimax-portal image fallback coverage

* fix: restore image-tool generic provider fallback (#54858) (thanks @MonkeyLeeT)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 10:07:43 +05:30
Neerav Makwana
8efc6e001e fix: auto-enable configured channel plugins in routed CLI commands (#54809) (thanks @neeravmakwana)
* CLI: auto-enable configured channel plugins in routed commands

* fix: auto-enable configured channel plugins in routed CLI commands (#54809) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 10:06:16 +05:30
sparkyrider
1bc30b7fb9 fix: restore Kimi Code under Moonshot setup (#54619) (thanks @sparkyrider)
* Onboarding: restore Kimi Code under Moonshot setup

* Update extensions/kimi-coding/index.ts

Fix naming convention in metadata

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-26 09:46:40 +05:30
Kevin Boyle
99deba798c fix: restore CLI message transcript mirroring (#54187) (thanks @KevInTheCloud5617)
* fix: pass agentId in CLI message command to enable session transcript writes

The CLI `openclaw message send` command was not passing `agentId` to
`runMessageAction()`, causing the outbound session route resolution to
be skipped (it's gated on `agentId && !dryRun`). Without a route, the
`mirror` object is never constructed, and `appendAssistantMessageToSessionTranscript()`
is never called.

This fix resolves the agent ID from the config (defaulting to "main")
and passes it through, enabling transcript mirroring for all channels
when using the CLI.

Closes #54186

* fix: format message.ts with oxfmt

* fix: use resolveDefaultAgentId instead of cfg.agent

* fix: restore CLI message transcript mirroring (#54187) (thanks @KevInTheCloud5617)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 09:32:43 +05:30
Neerav Makwana
68d854cb9c fix: use provider-aware context window lookup (#54796) (thanks @neeravmakwana)
* fix(status): use provider-aware context window lookup

* test(status): cover provider-aware context lookup

* fix: use provider-aware context window lookup (#54796) (thanks @neeravmakwana)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 09:28:20 +05:30
Greg Retkowski
14430ade57 fix: tighten systemd duplicate gateway detection (#45328) (thanks @gregretkowski)
* daemon: tighten systemd duplicate gateway detection (#15849)

* fix three issues from PR review

* fix windows unit tests due to posix/windows path differences
* ensure line continuations are handled in systemd units
* fix misleading test name

* attempt fix windows test due to fs path separator

* fix system_dir separator, fix platform side-effect

* change approach for mocking systemd filesystem test

* normalize systemd paths to linux style

* revert to vers that didnt impact win32 tests

* back out all systemd inspect tests

* change test approach to avoid other tests issues

* fix: tighten systemd duplicate gateway detection (#45328) (thanks @gregretkowski)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 09:20:10 +05:30
wangchunyue
ebad7490b4 fix: resolve telegram token fallback for binding-created accounts (#54362) (thanks @openperf)
* fix(telegram): resolve channel-level token fallthrough for binding-created accountIds

Fixes #53876

* fix(telegram): align isConfigured with resolveTelegramToken multi-bot guard

* fix(telegram): use normalized account lookup and require available token
2026-03-26 09:16:15 +05:30
Marcus Castro
bc1c308383 fix(whatsapp): clarify allowFrom policy error (#54850) 2026-03-26 00:44:10 -03:00
Tak Hoffman
5b68e52894 ci: collapse preflight manifest routing (#54773)
* ci: collapse preflight manifest routing

* ci: fix preflight workflow outputs

* ci: restore compat workflow tasks

* ci: match macos shards to windows

* ci: collapse macos swift jobs

* ci: skip empty submodule setup

* ci: drop submodule setup from node env
2026-03-25 22:38:30 -05:00
Ted Li
4f297a094a docs: add WeChat channel via official Tencent iLink Bot plugin (#52131) (thanks @MonkeyLeeT)
* docs: add WeChat channel via official Tencent iLink Bot plugin

Add WeChat to the README channel lists and setup section.

Uses the official Tencent-published plugin @tencent-weixin/openclaw-weixin
which connects via the iLink Bot API (QR code login, long-poll).
Requires WeChat 8.0.70+ with the ClawBot plugin enabled; the plugin
is being rolled out gradually by Tencent.

Covers: setup steps, capabilities (DM-only, media up to 100 MB,
multi-account, pairing authorization, typing indicators, config path),
and the context token restart caveat.

* docs: update WeChat plugin install for v2.0 compatibility

- Add version compatibility note (v2.x requires OpenClaw >= 2026.3.22,
  @legacy tag for older hosts)
- Add plugins.allow step (required since plugins.allow was introduced)

* docs: drop manual plugins.allow/enable steps (handled by plugins install)

* docs: fix multi-account instruction to require explicit --account id

* docs: trim WeChat section to match neighboring channels, fix pairing link

* docs: sync WeChat channel docs

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 09:07:01 +05:30
Frank the Builder
74ed75f2e7 fix: deliver verbose tool summaries in Telegram forum topics (#43236) (thanks @frankbuild)
* fix(auto-reply): deliver verbose tool summaries in Telegram forum topics

Forum topics have ChatType 'group' but are threaded conversations where
verbose tool output should be delivered (same as DMs). The
shouldSendToolSummaries gate now checks IsForum to allow tool summaries
in forum topic sessions.

Fixes #43206

* test: add sendToolResult count assertion per review feedback

* fix: add changelog for forum topic verbose tool summaries (#43236) (thanks @frankbuild)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-26 09:04:55 +05:30
xieyongliang
4cb8dde894 feat: add video generation core infrastructure and extend image generation parameters (#53681)
* feat: add video generation core infrastructure and extend image generation parameters

Add full video generation capability to OpenClaw core:

- New `video_generate` agent tool with support for prompt, duration, aspect ratio,
  resolution, seed, watermark, I2V (first/last frame), camerafixed, and draft mode
- New `VideoGenerationProvider` plugin SDK type and `registerVideoGenerationProvider` API
- New `src/video-generation/` module (types, runtime with fallback, provider registry)
- New `openclaw/plugin-sdk/video-generation` export for external plugins
- 200MB max file size for generated videos (vs default 5MB for images)

Extend image generation with additional parameters:
- `seed`, `watermark`, `guidanceScale`, `optimizePrompt`, `providerOptions`
- New `readBooleanParam()` helper in tool common utilities

Update plugin registry, contracts, and all test mocks to include
`videoGenerationProviders` and `videoGenerationProviderIds`.

Made-with: Cursor

* fix: validate aspect ratio against target provider when model override is set

* cleanup: remove redundant ?? undefined from video/image generate tools

* chore: regenerate plugin SDK API baseline after video generation additions

---------

Co-authored-by: yongliang.xie <yongliang.xie@bytedance.com>
2026-03-25 18:45:06 -07:00
Mathias Nagler
39fbfd9b28 fix(mattermost): thread resolved cfg through reply delivery send calls (#48347)
Merged via squash.

Prepared head SHA: 7ca468e365
Co-authored-by: mathiasnagler <9951231+mathiasnagler@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm
2026-03-26 01:31:12 +00:00
gumclaw
208ff68298 fix: allow msteams feedback and welcome config keys (#54679)
Merged via squash.

Prepared head SHA: f56a15ddea
Co-authored-by: gumclaw <265388744+gumclaw@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-26 03:00:52 +03:00
Devin Robison
81ebc7e034 fix(gateway): block silent reconnect scope-upgrade escalation (#54694)
* fix(gateway): block silent reconnect scope-upgrade escalation

* formatting updateas

* Resolve feedback

* formatting fixes

* Update src/gateway/server.silent-scope-upgrade-reconnect.poc.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Feedback updates

* fix unit test

* Feedback update

* Review feedback update

* More Greptile nit fixes

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-25 17:54:14 -06:00
adzendo
19d91aaa8f fix: make buttons schema optional in message tool (#54418)
Merged via squash.

Prepared head SHA: 0805c095e9
Co-authored-by: adzendo <246828680+adzendo@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-26 02:43:15 +03:00
Erhhung Yuan
b6f631e045 fix(schema): tools.web.fetch.maxResponseBytes #53397 (#53401)
Merged via squash.

Prepared head SHA: 5d10a98bdb
Co-authored-by: erhhung <5808864+erhhung@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-26 02:40:00 +03:00
Mikhail Beliakov
fd934a566b feat(cli): add json schema to cli tool (#54523)
Merged via squash.

Prepared head SHA: 39c15ee70d
Co-authored-by: kvokka <15954013+kvokka@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-26 02:30:32 +03:00
Tak Hoffman
ab37d8810d test: introduce planner-backed test runner, stabilize local builds (#54650)
* test: stabilize ci and local vitest workers

* test: introduce planner-backed test runner

* test: address planner review follow-ups

* test: derive planner budgets from host capabilities

* test: restore planner filter helper import

* test: align planner explain output with execution

* test: keep low profile as serial alias

* test: restrict explicit planner file targets

* test: clean planner exits and pnpm launch

* test: tighten wrapper flag validation

* ci: gate heavy fanout on check

* test: key shard assignments by unit identity

* ci(bun): shard vitest lanes further

* test: restore ci overlap and stabilize planner tests

* test: relax planner output worker assertions

* test: reset plugin runtime state in optional tools suite

* ci: split macos node and swift jobs

* test: honor no-isolate top-level concurrency budgets

* ci: fix macos swift format lint

* test: cap max-profile top-level concurrency

* ci: shard macos node checks

* ci: use four macos node shards

* test: normalize explain targets before classification
2026-03-25 18:11:58 -05:00
Devin Robison
764394c78b fix: enforce localRoots sandbox on Feishu docx upload file reads (#54693)
* fix: enforce localRoots sandbox on Feishu docx upload file reads

* Formatting fixes

* Update tests

* Feedback updates
2026-03-25 16:09:00 -06:00
Devin Robison
6a79324802 Filter untrusted CWD .env entries before OpenClaw startup (#54631)
* Filter untrusted CWD .env entries before OpenClaw startup

* Add missing test file

* Fix missing and updated files

* Address feedback

* Feedback updates

* Feedback update

* Add test coverage

* Unit test fix
2026-03-25 15:49:26 -06:00
Tak Hoffman
79fbcfc03b fix(ci): restore main green 2026-03-25 16:17:42 -05:00
Nimrod Gutman
501190d2e8 refactor(sandbox): remove tool policy facade (#54684)
* refactor(sandbox): remove tool policy facade

* fix(sandbox): harden blocked-tool guidance

* fix(sandbox): avoid control-char guidance leaks

* fix: harden sandbox blocked-tool guidance (#54684) (thanks @ngutman)
2026-03-25 23:03:24 +02:00
Jared
c6d8318d07 Trigger preflight compaction from transcript estimates when usage is stale (#49479)
Merged via squash.

Prepared head SHA: 8d214b708b
Co-authored-by: jared596 <37019497+jared596@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 13:22:16 -07:00
Jacob Tomlinson
c02ee8a3a4 OpenShell: exclude hooks/ from mirror sync (#54657)
* OpenShell: exclude hooks/ from mirror sync

* OpenShell: make excludeDirs case-insensitive for cross-platform safety
2026-03-25 19:59:07 +00:00
Jacob Tomlinson
d1bfe08424 fix: apply host-env blocklist to auth-profile env refs in daemon install (#54627)
* fix: apply host-env blocklist to auth-profile env refs in daemon install

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: retrigger checks

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 19:57:22 +00:00
Jacob Tomlinson
e34694733f fix(talk-voice): enforce operator.admin scope on /voice set config writes (#54461)
* fix(talk-voice): enforce operator.admin scope on /voice set config writes

* fix(talk-voice): align scope guard with phone-control pattern

Use optional chaining (?.) instead of Array.isArray so webchat callers
with undefined scopes are rejected, matching the established pattern in
phone-control. Add test for webchat-with-no-scopes case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 19:55:26 +00:00
Joseph Krug
d81593c6e2 fix: trigger compaction on LLM timeout with high context usage (#46417)
Merged via squash.

Prepared head SHA: 619bc4c1fa
Co-authored-by: joeykrug <5925937+joeykrug@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 12:51:36 -07:00
Devin Robison
1b3a1246d0 Block reset-profile on lower-privilege browser request surfaces (#54618)
* Block reset-profile on lower-privilege browser request surfaces

* add missing tests

* Fix tests

* Test fix
2026-03-25 13:36:59 -06:00
Devin Robison
4797bbc5b9 fix: reject path traversal and home-dir patterns in media parse layer (#54642)
* fix: reject path traversal and home-dir patterns in media parse layer

* Update parse tests
2026-03-25 13:35:16 -06:00
kiranvk2011
84401223c7 fix: per-model cooldown scope, stepped backoff, and user-facing rate-limit message (#49834)
Merged via squash.

Prepared head SHA: 7c488c070c
Co-authored-by: kiranvk-2011 <91108465+kiranvk-2011@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-25 22:03:49 +03:00
Tak Hoffman
6efc4e8ef2 test: fix windows tmp root assertions 2026-03-25 13:44:54 -05:00
Devin Robison
b7d70ade3b Fix/telegram writeback admin scope gate (#54561)
* fix(telegram): require operator.admin for legacy target writeback persistence

* Address claude feedback

* Update extensions/telegram/src/target-writeback.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Remove stray brace

* Add updated docs

* Add missing test file, address codex concerns

* Fix test formatting error

* Address comments, fix tests

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-25 12:12:09 -06:00
Andrii Furmanets
89c4c674d1 fix(compaction): surface safeguard cancel reasons and clarify /compact skips (#51072)
Merged via squash.

Prepared head SHA: f1dbef0443
Co-authored-by: afurm <6375192+afurm@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 11:03:22 -07:00
M1a0
7847e67f8a plugin-runtime: expose runHeartbeatOnce in system API (#40299)
* plugin-runtime: expose runHeartbeatOnce in system API

Plugins that enqueue system events and need the agent to deliver
responses to the originating channel currently have no way to
override the default `heartbeat.target: "none"` behaviour.

Expose `runHeartbeatOnce` in the plugin runtime `system` namespace
so plugins can trigger a single heartbeat cycle with an explicit
`heartbeat: { target: "last" }` override — the same pattern the
cron service already uses (see #28508).

Changes:
- Add `RunHeartbeatOnceOptions` type and `runHeartbeatOnce` to
  `PluginRuntimeCore.system` (types-core.ts)
- Wire the function through a thin wrapper in runtime-system.ts
- Update the test-utils plugin-runtime mock

Made-with: Cursor

* feat(plugins): expose runHeartbeatOnce in system API (#40299) (thanks @loveyana)

---------

Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 10:47:01 -07:00
chenxingzhen
4ae4d1fabe fix: mid-turn 429 rate limit silent no-reply and context engine registration failure (#50930)
Merged via squash.

Prepared head SHA: eea7800df3
Co-authored-by: infichen <13826604+infichen@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 10:43:08 -07:00
Matt Van Horn
e0972db7a2 fix: stop leaking reply tags in iMessage outbound text (#39512) (thanks @mvanhorn)
* fix: stop leaking reply tags in iMessage outbound text (#39512) (thanks @mvanhorn)

* fix: preserve iMessage outbound whitespace without directive tags (#39512) (thanks @mvanhorn)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 23:00:16 +05:30
Tak Hoffman
f63c4b0856 test: keep vitest on forks only 2026-03-25 12:22:22 -05:00
Harold Hunt
055ad65896 Telegram: ignore self-authored DM message updates (#54530)
Merged via squash.

Prepared head SHA: c1c8a85168
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
2026-03-25 13:16:35 -04:00
Peter Steinberger
685f17460d build: update appcast for 2026.3.24 2026-03-25 10:10:34 -07:00
Jackal Xin
2de32fbf14 fix: reconcile session compaction count after late compaction success (#45493)
Merged via squash.

Prepared head SHA: d0715a5555
Co-authored-by: jackal092927 <3854860+jackal092927@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 10:00:41 -07:00
Peter Steinberger
cff6dc94e3 docs: format changelog for release 2026-03-25 09:34:30 -07:00
Peter Steinberger
97a7e93db4 build: prepare 2026.3.24 release 2026-03-25 09:31:05 -07:00
liyuan97
e2e9f979ca feat(minimax): add image generation provider and trim model catalog to M2.7 (#54487)
* feat(minimax): add image generation and TTS providers, trim TUI model list

Register MiniMax image-01 and speech-2.8 models as plugin providers for
the image_generate and TTS tools. Both resolve CN/global base URLs from
the configured model endpoint origin.

- Image generation: base64 response, aspect-ratio support, image-to-image
  via subject_reference, registered for minimax and minimax-portal
- TTS: speech-2.8-turbo (default) and speech-2.8-hd, hex-encoded audio,
  voice listing via get_voice API, telephony PCM support
- Add MiniMax to TTS auto-detection cascade (after ElevenLabs, before
  Microsoft) and TTS config section
- Remove MiniMax-VL-01, M2, M2.1, M2.5 and variants from TUI picker;
  keep M2.7 and M2.7-highspeed only (backend routing unchanged)

* feat(minimax): trim legacy model catalog to M2.7 only

Cherry-picked from temp/feat/minimax-trim-legacy-models (949ed28).
Removes MiniMax-VL-01, M2, M2.1, M2.5 and variants from the model
catalog, model order, modern model matchers, OAuth config, docs, and
tests. Keeps only M2.7 and M2.7-highspeed.

Conflicts resolved:
- provider-catalog.ts: removed MINIMAX_TUI_MODELS filter (no longer
  needed since source array is now M2.7-only)
- index.ts: kept image generation + speech provider registrations
  (added by this branch), moved media understanding registrations
  earlier (as intended by the cherry-picked commit)

* fix(minimax): update discovery contract test to reflect M2.7-only catalog

Cherry-picked from temp/feat/minimax-trim-legacy-models (2c750cb).

* feat(minimax): add web search provider and register in plugin entry

* fix(minimax): resolve OAuth credentials for TTS speech provider

* MiniMax: remove web search and TTS providers

* fix(minimax): throw on empty images array after generation failure

* feat(minimax): add image generation provider and trim catalog to M2.7 (#54487) (thanks @liyuan97)

---------

Co-authored-by: tars90percent <tars@minimaxi.com>
Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 09:29:35 -07:00
xieyongliang
7cc86e9685 fix(release): add plugin-sdk:check-exports to release:check (#54283)
* fix(plugins): resolve sdk alias from import.meta.url for external plugins

When a plugin is installed outside the openclaw package (e.g.
~/.openclaw/extensions/), resolveLoaderPluginSdkPackageRoot() fails to
locate the openclaw root via cwd or argv1 hints, resulting in an empty
alias map. Jiti then cannot resolve openclaw/plugin-sdk/* imports and
the plugin fails to load with "Cannot find module".

Since sdk-alias.ts is always compiled into the openclaw package itself,
import.meta.url reliably points inside the installation directory. Add it
as an unconditional fallback in resolveLoaderPluginSdkPackageRoot() so
external plugins can always resolve the plugin SDK.

Fixes: Error: Cannot find module 'openclaw/plugin-sdk/plugin-entry'

* fix(plugins): pass loader moduleUrl to resolve sdk alias for external plugins

The previous approach of adding import.meta.url as an unconditional
fallback inside resolveLoaderPluginSdkPackageRoot() broke test isolation:
tests that expected null from untrusted fixtures started finding the real
openclaw root. Revert that and instead thread an optional moduleUrl through
buildPluginLoaderAliasMap → resolvePluginSdkScopedAliasMap →
listPluginSdkExportedSubpaths → resolveLoaderPluginSdkPackageRoot.

loader.ts passes its own import.meta.url as the hint, which is always
inside the openclaw installation. This guarantees the sdk alias map is
built correctly even when argv1 does not resolve to the openclaw root
(e.g. single-binary distributions, custom launchers, or Docker images
where the binary wrapper is not a standard npm symlink).

Tests that call sdk-alias helpers directly without moduleUrl are
unaffected and continue to enforce the existing isolation semantics.
A new test covers the moduleUrl resolution path explicitly.

* fix(plugins): use existing fixture file for moduleUrl hint in test

The previous test pointed loaderModuleUrl to dist/plugins/loader.js
which is not created by createPluginSdkAliasFixture, causing resolution
to fall back to the real openclaw root instead of the fixture root.
Use fixture.root/openclaw.mjs (created by the bin+marker fixture) so
the moduleUrl hint reliably resolves to the fixture package root.

* fix(test): use fixture.root as cwd in external plugin alias test

When process.cwd() is mocked to the external plugin dir, the
findNearestPluginSdkPackageRoot(process.cwd()) fallback resolves to
the real openclaw repo root in the CI test runner, making the test
resolve the wrong aliases. Using fixture.root as cwd ensures all
resolution paths consistently point to the fixture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(release): add plugin-sdk:check-exports to release:check

plugin-sdk subpath exports (e.g. openclaw/plugin-sdk/plugin-entry,
openclaw/plugin-sdk/provider-auth) were missing from the published
package.json, causing external plugins to fail at load time with
'Cannot find module openclaw/plugin-sdk/plugin-entry'.

Root cause: sync-plugin-sdk-exports.mjs syncs plugin-sdk-entrypoints.json
into package.json exports, but this sync was never validated in the
release:check pipeline. As a result, any drift between
plugin-sdk-entrypoints.json and the published package.json goes
undetected until users hit the runtime error.

Fix: add plugin-sdk:check-exports to release:check so the CI gate
fails loudly if the exports are out of sync before publishing.

* fix(test): isolate moduleUrl hint test from process.cwd() fallback

Use externalPluginRoot as cwd instead of fixture.root, so only the
moduleUrl hint can resolve the openclaw package root. Previously,
withCwd(fixture.root) allowed the process.cwd() fallback to also
resolve the fixture root, making the moduleUrl path untested.

Spotted by greptile-apps review on #54283.

* fix(test): use empty string to disable argv1 in moduleUrl hint test

Passing undefined for argv1 in buildPluginLoaderAliasMap triggers the
STARTUP_ARGV1 default (process.argv[1], the vitest runner binary inside
the openclaw repo). resolveTrustedOpenClawRootFromArgvHint then resolves
to the real openclaw root before the moduleUrl hint is checked, making
the test resolve wrong aliases.

Pass "" instead: falsy so the hint is skipped, but does not trigger the
default parameter value. Only the moduleUrl can bridge the gap.

Made-with: Cursor

* fix(plugins): thread moduleUrl through SDK alias resolution for external plugins (#54283) Thanks @xieyongliang

---------

Co-authored-by: bojsun <bojie.sun@bytedance.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jerry <jerry@JerrydeMacBook-Air-2.local>
Co-authored-by: yongliang.xie <yongliang.xie@bytedance.com>
Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 09:11:17 -07:00
Devin Robison
c2a2edb329 Fix local copied package installs honoring staged project .npmrc (#54543) 2026-03-25 09:59:33 -06:00
Lin Z
a0b9dc0078 fix(feishu): use message create_time for inbound timestamps (#52809)
* fix(feishu): use message create_time instead of Date.now() for Timestamp field

When a message is sent offline and later retried by the Feishu client
upon reconnection, Date.now() captures the *delivery* time rather than
the *authoring* time.  This causes downstream consumers to see a
timestamp that can be minutes or hours after the user actually composed
the message, leading to incorrect temporal semantics — for example, a
"delete this" command may target the wrong resource because the agent
believes the instruction was issued much later than it actually was.

Replace every Date.now() used for message timestamps with the original
create_time from the Feishu event payload (millisecond-epoch string),
falling back to Date.now() only when the field is absent.  The
definition is also hoisted to the top of handleFeishuMessage so that
both the pending-history path and the main inbound-payload path share
the same authoritative value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(feishu): verify Timestamp uses message create_time

Add two test cases:
1. When create_time is present, Timestamp must equal the parsed value
2. When create_time is absent, Timestamp falls back to Date.now()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: revert unrelated formatting change to lifecycle.test.ts

This file was inadvertently formatted in a prior commit. Reverting to
match main and keep the PR scoped to the Feishu timestamp fix only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(feishu): use message create_time for inbound timestamps (#52809) (thanks @schumilin)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 08:36:12 -07:00
Lin Z
bd4237c16c fix(feishu): close WebSocket connections on monitor stop (#52844)
* fix(feishu): close WebSocket connections on monitor stop/abort

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(feishu): add WebSocket cleanup tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(feishu): close WebSocket connections on monitor stop (#52844) (thanks @schumilin)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 08:32:21 -07:00
Nimrod Gutman
edb5123f26 fix(sandbox): honor sandbox alsoAllow and explicit re-allows (#54492)
* fix(sandbox): honor effective sandbox alsoAllow policy

* fix(sandbox): prefer resolved sandbox context policy

* fix: honor sandbox alsoAllow policy (#54492) (thanks @ngutman)
2026-03-25 16:51:13 +02:00
Peter Steinberger
e9ac2860c1 docs: prepare 2026.3.24-beta.2 release 2026-03-25 06:58:39 -07:00
Harold Hunt
da60aff17a Tests: isolate security audit home skill resolution (#54473)
Merged via squash.

Prepared head SHA: 82181e15fb
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
2026-03-25 09:43:19 -04:00
Peter Steinberger
ee714f5a42 test(media): make local roots fixture windows-safe 2026-03-25 06:24:39 -07:00
Peter Steinberger
ea08f2eb8c fix(runtime): support Node 22.14 installs 2026-03-25 06:22:18 -07:00
Harold Hunt
3c3fd8c386 Discord: log rejected native command deploy failures (#54118)
Merged via squash.

Prepared head SHA: be250f9620
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
2026-03-25 09:19:46 -04:00
Peter Steinberger
436aa838fe test(release): sync llama peer fixture 2026-03-25 06:06:47 -07:00
Peter Steinberger
284084672a fix(ci): restore e2e docker cache boundary 2026-03-25 06:06:47 -07:00
Peter Steinberger
66c88b4c77 fix(update): preflight npm target node engine 2026-03-25 06:01:20 -07:00
Peter Steinberger
c92002e1de fix(media): align outbound media access with fs policy 2026-03-25 05:50:21 -07:00
Peter Steinberger
39ad51426c test: add Open WebUI docker smoke 2026-03-25 05:28:51 -07:00
Peter Steinberger
9e95125f06 fix(config): ignore same-base correction publish warnings 2026-03-25 04:58:44 -07:00
Peter Steinberger
b19cc399b6 test: fix clobbered config snapshot expectation 2026-03-25 04:54:37 -07:00
Peter Steinberger
3b6d980c52 refactor: unify whatsapp identity handling 2026-03-25 04:46:24 -07:00
Peter Steinberger
cdba1e6771 fix: copy openclaw bin before docker install 2026-03-25 04:45:31 -07:00
Peter Steinberger
d874f3970a build: prepare 2026.3.24-beta.1 2026-03-25 04:41:26 -07:00
Peter Steinberger
7c2790cec4 test: isolate voice-call temp stores 2026-03-25 11:39:47 +00:00
Peter Steinberger
c3d1dbc696 refactor(openai): extract codex auth identity helper 2026-03-25 04:24:46 -07:00
Peter Steinberger
d363af8c13 refactor(auth): separate profile ids from email metadata 2026-03-25 04:24:46 -07:00
khhjoe
f3fe019e3d fix(whatsapp): use async fs.promises.readFile for selfLid creds read 2026-03-25 04:24:31 -07:00
khhjoe
770a5ee5b1 fix(whatsapp): read selfLid from creds.json for reply-to-bot detection 2026-03-25 04:24:31 -07:00
khhjoe
93594a1440 fix(whatsapp): compare selfLid for reply-to-bot implicit mention in groups 2026-03-25 04:24:31 -07:00
khhjoe
ff25407861 fix(whatsapp): unwrap FutureProofMessage (botInvokeMessage) to restore reply-to-bot detection 2026-03-25 04:24:31 -07:00
Peter Steinberger
52bec1612c test: collapse telegram transport and status suites 2026-03-25 11:23:18 +00:00
Peter Steinberger
12082f47bd test: collapse telegram button and access suites 2026-03-25 11:23:18 +00:00
Peter Steinberger
b7f2b0d7b9 refactor: align pairing replies, daemon hints, and feishu mention policy 2026-03-25 04:22:53 -07:00
Peter Steinberger
524004ff32 docs: add missing changelog items 2026-03-25 04:22:23 -07:00
Peter Steinberger
3de04bdd6d test: collapse telegram context and transport suites 2026-03-25 11:17:58 +00:00
Peter Steinberger
fc49258c12 test: collapse telegram helper suites 2026-03-25 11:17:58 +00:00
Peter Steinberger
9873ef0e39 docs: sort changelog by user impact 2026-03-25 04:14:52 -07:00
Peter Steinberger
94041f06b4 test: harden parallels npm update runner 2026-03-25 11:13:09 +00:00
Ayaan Zaidi
b497f3cda0 fix: normalize before_dispatch conversation id 2026-03-25 16:28:31 +05:30
Ayaan Zaidi
15776091a8 fix(whatsapp): avoid eager login tool runtime access 2026-03-25 16:25:00 +05:30
ZhangXuan
a10d587b41 fix: preserve before_dispatch delivery semantics (#50444) (thanks @gfzhx)
* Plugins: add before_dispatch hook

* Tests: fix before_dispatch hook mock typing

* Rebase: adapt before_dispatch hook to routeReplyRuntime refactor

* fix: preserve before_dispatch delivery semantics (#50444) (thanks @gfzhx)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 16:16:08 +05:30
Ayaan Zaidi
765182dcc6 fix: skip session:patch hook clone without listeners 2026-03-25 16:12:39 +05:30
Ayaan Zaidi
ee0dcaa7b0 fix: unify log timestamp offsets (#38904) (thanks @sahilsatralkar) 2026-03-25 16:06:33 +05:30
Gracie Gould
3e2e9bc238 fix: isolate session:patch hook payload (#53880) (thanks @graciegould)
* gateway: make session:patch hook typed and non-blocking

* gateway(test): add session:patch hook coverage

* docs(gateway): clarify session:patch security note

* fix: address review feedback on session:patch hook

Remove unused createInternalHookEvent import and fix doc example
to use inline event.type check matching existing hook examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: isolate hook payload to prevent mutation leaking into response

Shallow-copy sessionEntry and patch in the session:patch hook event
so fire-and-forget handlers cannot mutate objects used by the
response path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: isolate session:patch hook payload (#53880) (thanks @graciegould)

---------

Co-authored-by: “graciegould” <“graciegould5@gmail.com”>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 15:59:38 +05:30
Liu Yuan
419824729a fix: fail loud when PTY cursor mode is unknown (#51490) (thanks @liuy)
* fix(process): auto-detect PTY cursor key mode for send-keys

When a PTY session sends smkx (\x1b[?1h) or rmkx (\x1b[?1l) to switch
cursor key mode, send-keys now detects this and encodes cursor keys
accordingly.

- smkx/rmkx detection in handleStdout before sanitizeBinaryOutput
- cursorKeyMode stored in ProcessSession
- encodeKeySequence accepts cursorKeyMode parameter
- DECCKM_SS3_KEYS for application mode (arrows + home/end)
- CSI sequences for normal mode
- Modified keys (including alt) always use xterm modifier scheme
- Extract detectCursorKeyMode for unit testing
- Use lastIndexOf to find last toggle in chunk (later one wins)

Fixes #51488

* fix: fail loud when PTY cursor mode is unknown (#51490) (thanks @liuy)

* style: format process send-keys guard (#51490) (thanks @liuy)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 15:51:27 +05:30
Ayaan Zaidi
717ff0d667 fix: cover macOS Edge osascript fallback path (#48561) (thanks @zoherghadyali) 2026-03-25 15:47:04 +05:30
Ayaan Zaidi
0295271f97 fix: add changelog for macOS Edge default browser detection (#48561) (thanks @zoherghadyali) 2026-03-25 15:47:04 +05:30
Zoher Ghadyali
2fe38b0201 fix(browser): add Edge LaunchServices bundle IDs for macOS default browser detection
macOS registers Edge as 'com.microsoft.edgemac' in LaunchServices, which
differs from the CFBundleIdentifier 'com.microsoft.Edge' in the app's own
Info.plist. Without recognising the LaunchServices IDs, Edge users who set
Edge as their default browser are not detected as having a Chromium browser.

Add the four com.microsoft.edgemac* variants to CHROMIUM_BUNDLE_IDS and a
corresponding test that mocks the LaunchServices → osascript resolution
path for Edge.
2026-03-25 15:47:04 +05:30
Sparkyrider
55dc6a8bb2 cron: queue isolated delivery awareness 2026-03-25 15:21:14 +05:30
Ayaan Zaidi
2a40612058 fix: make telegram thread create use topic payload (#54336) (thanks @andyliu) 2026-03-25 13:43:09 +05:30
Andy
e1cd90db6e fix(cli): route telegram thread create to topic-create 2026-03-25 13:43:09 +05:30
ToToKr
4140100807 fix: clarify cron best-effort partial delivery status (#42535) (thanks @MoerAI)
* fix(cron): track and log bestEffort delivery failures, mark not delivered on partial failure

* fix(cron): cache successful results on partial failure to preserve replay idempotency

When a best-effort send partially fails, we now still cache the successful delivery results via rememberCompletedDirectCronDelivery. This prevents duplicate sends on same-process replay while still correctly marking the job as not fully delivered.

* fix(cron): preserve partial-failure state on replay (#27069)

* fix(cron): restore test infrastructure and fix formatting

* fix: clarify cron best-effort partial delivery status (#42535) (thanks @MoerAI)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 12:49:32 +05:30
hnshah
c7f021f70f fix: preflight invalid telegram photos (#52545) (thanks @hnshah)
* fix(telegram): validate photo dimensions before sendPhoto

Prevents PHOTO_INVALID_DIMENSIONS errors by checking image dimensions
against Telegram Bot API requirements before calling sendPhoto.

If dimensions exceed limits (width + height > 10,000px), automatically
falls back to sending as document instead of crashing with 400 error.

Tested in production (openclaw 2026.3.13) where this error occurred:
  [telegram] tool reply failed: GrammyError: Call to 'sendPhoto' failed!
  (400: Bad Request: PHOTO_INVALID_DIMENSIONS)

Uses existing sharp dependency to read image metadata. Gracefully
degrades if sharp fails (lets Telegram handle validation, backward
compatible behavior).

Closes: #XXXXX (will reference OpenClaw issue if one exists)

* fix(telegram): validate photo aspect ratio

* refactor: use shared telegram image metadata

* fix: fail closed on telegram image metadata

* fix: preflight invalid telegram photos (#52545) (thanks @hnshah)

---------

Co-authored-by: Bob Shah <bobshah@Macs-Mac-Studio.local>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 12:00:20 +05:30
Peter Steinberger
b9857a2b79 test: allow daemon start hints to grow on linux (#54058) (thanks @byungsker) 2026-03-24 23:09:04 -07:00
Peter Steinberger
8b80690a1a test: accept fenced discord pairing codes (#54058) (thanks @byungsker) 2026-03-24 23:09:04 -07:00
Peter Steinberger
fac0a172e5 test: refresh pairing reply assertions for fenced codes (#54058) (thanks @byungsker) 2026-03-24 23:09:04 -07:00
Peter Steinberger
2566d6b300 fix: finish feishu open-group docs and baselines (#54058) (thanks @byungsker) 2026-03-24 23:09:04 -07:00
lbo728
a322059efa test(feishu): update config-schema test for removed requireMention default 2026-03-24 23:09:04 -07:00
lbo728
69195f7e9d fix(feishu): default requireMention to false for groupPolicy open
Groups configured with groupPolicy: open are expected to respond to all
messages. Previously, requireMention defaulted to true regardless of
groupPolicy, causing image (and other non-text) messages to be silently
dropped because they cannot carry @-mentions.

Fix: when groupPolicy is 'open' and requireMention is not explicitly
configured, resolve it to false instead of true. Users who want
mention-required behaviour in open groups can still set requireMention: true
explicitly.

Adds three regression tests covering the new default, explicit override, and
the unchanged allowlist-policy behaviour.

Closes #52553
2026-03-24 23:09:04 -07:00
VACInc
89b7fee352 fix: preserve Telegram forum topic last-route delivery (#53052) (thanks @VACInc)
* fix(telegram): preserve forum topic thread in last-route delivery

* style(telegram): format last-route update

* test(telegram): cover General topic last-route thread

* test(telegram): align topic route helper

* fix(telegram): skip bound-topic last-route writes

---------

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 11:31:01 +05:30
Peter Steinberger
1c82b06645 test: collapse msteams state and monitor suites 2026-03-25 05:57:02 +00:00
Peter Steinberger
e53809035e test: collapse msteams graph suites 2026-03-25 05:57:02 +00:00
Peter Steinberger
b99b521a92 test: collapse msteams helper suites 2026-03-25 05:57:02 +00:00
Peter Steinberger
f5408d82d2 refactor: unify gateway handshake timeout wiring 2026-03-24 22:53:55 -07:00
Peter Steinberger
258a214bcb refactor: centralize daemon service start state flow 2026-03-24 22:49:34 -07:00
Liren Pan
5dec3dddc4 style(auth): wrap codex fallback formatting 2026-03-24 22:49:06 -07:00
Liren Pan
773427470a test(auth): cover codex jwt fallback branches 2026-03-24 22:49:06 -07:00
Liren Pan
b6e70a5cdd auth: derive codex oauth profile ids from jwt claims 2026-03-24 22:49:06 -07:00
nimbleenigma
abec3ed645 fix: keep Telegram native commands on runtime snapshot (#53179) (thanks @nimbleenigma)
* fix(telegram): use runtime snapshot for native commands

* fix: keep Telegram native commands on runtime snapshot (#53179) (thanks @nimbleenigma)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 11:18:54 +05:30
Peter Steinberger
57e2223eec test: align pairing reply assertions 2026-03-25 05:48:31 +00:00
Peter Steinberger
6c3e767289 refactor: centralize Discord gateway supervision 2026-03-24 22:47:12 -07:00
Peter Steinberger
efafbece17 test: collapse nextcloud-talk send and helper suites 2026-03-25 05:39:11 +00:00
dobbylorenzbot
717ee2fa59 fix(gateway): raise default connect challenge timeout 2026-03-24 22:38:17 -07:00
HCL
db35f30005 fix: validate config before restart + derive loaded from real state
Address Codex P1 + Greptile P2:
- Move config validation before the restart attempt so invalid config
  is caught in the stop→start path (not just the already-loaded path)
- Derive service.loaded from actual isLoaded() after restart instead
  of hardcoded true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
2026-03-24 22:35:09 -07:00
HCL
d2248534d8 fix(daemon): bootstrap stopped service on gateway start
After `gateway stop` (which runs `launchctl bootout`), `gateway start`
checks `isLoaded` → false → prints "not loaded" hints and exits.
The service is never re-bootstrapped, so `start` cannot recover from
`stop` — only `gateway install` works.

Root cause: src/cli/daemon-cli/lifecycle-core.ts:208-217 — runServiceStart
calls handleServiceNotLoaded which only prints hints, never attempts
service.restart() (which already handles bootstrap via
bootstrapLaunchAgentOrThrow at launchd.ts:598).

Fix: when service is not loaded, attempt service.restart() first (which
handles re-bootstrapping on all platforms). If restart fails (e.g. plist
was deleted, not just booted out), fall back to the existing hints.

The restart path is already proven: restartLaunchAgent (launchd.ts:556)
handles "not loaded" via bootstrapLaunchAgentOrThrow. This fix routes
the start command through the same recovery path.

Closes #53878

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
2026-03-24 22:35:09 -07:00
Peter Steinberger
7467f304a7 test: collapse nextcloud-talk helper suites 2026-03-25 05:33:57 +00:00
Peter Steinberger
e8e45a4936 test: collapse synology-chat helper suites 2026-03-25 05:33:57 +00:00
Peter Steinberger
c22f3c514b test: collapse googlechat helper suites 2026-03-25 05:33:57 +00:00
SUMUKH
149c4683a3 fix: scope Telegram pairing code blocks (#52784) (thanks @sumukhj1219)
* Telegram: format pairing challenge for easier copy

* test: restore Telegram pairing chatId assertion

* fix: scope Telegram pairing code blocks (#52784) (thanks @sumukhj1219)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 11:03:33 +05:30
sfwn
9ab226d275 fix(discord): remove safety error listener after teardown to prevent leak 2026-03-24 22:31:03 -07:00
sfwn
731016472c fix(discord): prevent uncaught gateway errors from crashing the process
Move cleanup() after disconnect() in waitForDiscordGatewayStop so the
error listener is still active during disconnect. Add a safety error
listener in the lifecycle finally block to suppress late errors emitted
by Carbon during teardown.

Fixes the "Max reconnect attempts (0) reached after code 1006" uncaught
exception that kills the entire gateway process when a Discord WebSocket
drops and reconnection fails.
2026-03-24 22:31:03 -07:00
w-sss
247f82119c fix: improve Telegram 403 membership delivery errors (#53635) (thanks @w-sss)
* fix(telegram): improve error messages for 403 bot not member errors

- Detect 403 'bot is not a member' errors specifically
- Provide actionable guidance for users to fix the issue
- Fixes #48273 where outbound sendMessage fails with 403

Root cause:
When a Telegram bot tries to send a message to a channel/group it's not
a member of, the API returns 403 'bot is not a member of the channel chat'.
The error message was not clear about how to fix this.

Fix:
1. Detect 403 errors in wrapTelegramChatNotFoundError
2. Provide clear error message explaining the issue
3. Suggest adding the bot to the channel/group

* fix(telegram): fix regex precedence for 403 error detection

- Group alternatives correctly: /403.*(bot.*not.*member|bot was blocked)/i
- Require 403 for both alternatives (previously bot.*blocked matched any error)
- Update error message to cover both scenarios
- Fixes Greptile review feedback

* fix(telegram): correct regex alternation precedence for 403 errors

- Fix: /403.*(bot.*not.*member|bot was blocked)/ → /403.*(bot.*not.*member|bot.*blocked)/
- Ensures 403 requirement applies to both alternatives
- Fixes Greptile review comment on PR #48650

* fix(telegram): add 'bot was kicked' to 403 error regex and message

* fix(telegram): preserve membership delivery errors

* fix: improve Telegram 403 membership delivery errors (#53635) (thanks @w-sss)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 10:59:29 +05:30
w-sss
0bdb8ac7ad fix(ui): add width/height to context-notice SVG icon
- Fixes #47924
- Prevents SVG icon from expanding and covering entire chat window
- Adds explicit 24x24px dimensions to context-notice__icon SVG

Root cause:
The SVG element lacked explicit width and height attributes,
causing it to expand to fill the parent container when the context
usage warning appears (at ~85% token limit).
2026-03-25 00:25:55 -05:00
Peter Steinberger
33d31e2b0d test: collapse imessage test suites 2026-03-25 05:21:16 +00:00
Peter Steinberger
bc8622c659 test: collapse helper extension test suites 2026-03-25 05:21:16 +00:00
Peter Steinberger
6f137fff76 test: collapse telegram and whatsapp target suites 2026-03-25 05:21:16 +00:00
Ayaan Zaidi
793b36c5d2 fix: bootstrap proxy for LanceDB embeddings (#54119) (thanks @neeravmakwana) 2026-03-25 10:50:00 +05:30
Neerav Makwana
1a815e323c test(memory): unmock infra runtime cleanup 2026-03-25 10:50:00 +05:30
Neerav Makwana
09a4453026 fix(memory): bootstrap proxy for LanceDB embeddings 2026-03-25 10:50:00 +05:30
Jealous
2c3cf4f387 chore(tts): rename VOICE_BUBBLE identifiers to OPUS and update docs 2026-03-25 10:49:21 +05:30
Peter Steinberger
46d3617d25 refactor: split gateway plugin bootstrap and registry surfaces 2026-03-24 22:16:26 -07:00
Josh Avant
10161c2d79 Plugins: enforce terminal hook decision semantics for tool/message guards (#54241)
* Plugins: enforce terminal hook decision policies

* Tests: assert terminal hook behavior in integration paths

* Docs: clarify terminal hook decision semantics

* Docs: add hook guard semantics to plugin guides

* Tests: isolate outbound format label expectations

* changelog

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>

---------

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-03-25 00:11:13 -05:00
dongdong
5a5c5d4cde fix: refresh DeepSeek pricing to current V3.2 rates (#54143) (thanks @arkyu2077)
* fix: add actual DeepSeek API pricing to model catalog

Replace zero-cost placeholder with real pricing from DeepSeek docs:
- deepseek-chat (V3): /bin/bash.27/1M input, .10/1M output, /bin/bash.07 cache read
- deepseek-reasoner (R1): /bin/bash.55/1M input, .19/1M output, /bin/bash.14 cache read

Fixes #54134

* fix: refresh DeepSeek pricing to current V3.2 rates

* fix: refresh DeepSeek pricing to current V3.2 rates (#54143) (thanks @arkyu2077)

---------

Co-authored-by: Jasmine Zhang <jasminezhang@192.168.1.75>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 10:34:03 +05:30
Quinn H.
d43dda465d fix: note marketplace streaming and ClawHub URL (#54160) (thanks @QuinnH496)
* fix: correct ClawHub URL in system prompt and use streaming download in marketplace

- Fix #54154: Change clawhub.com to clawhub.ai in system prompt
- Fix #54156: Replace arrayBuffer() with streaming pipeline for marketplace
  plugin downloads to avoid OOM on memory-constrained devices

* fix: guard marketplace archive stream body

* fix: note marketplace streaming and ClawHub URL (#54160) (thanks @QuinnH496)

---------

Co-authored-by: Li Enying <li.enying@openclaw.ai>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 10:29:21 +05:30
Peter Steinberger
61dd61e917 refactor: tighten split-runtime live-state guardrails 2026-03-24 21:58:50 -07:00
Peter Steinberger
94425764a8 refactor: centralize systemd unavailable classification 2026-03-24 21:57:48 -07:00
Jonathan Jing
30e80fb947 fix: isolate channel startup failures (#54215) (thanks @JonathanJing)
* fix(gateway): isolate channel startup failures to prevent cascade

When one channel (e.g., WhatsApp) fails to start due to missing runtime
modules, it should not block other channels (e.g., Discord) from starting.

Changes:
- Use Promise.allSettled to start channels concurrently
- Catch individual channel startup errors without affecting others
- Add startup summary logging for observability

Before: Sequential await startChannel() - if one throws, subsequent
channels never start.

After: Concurrent startup with per-channel error handling - all channels
attempt to start, failures are logged but don't cascade.

Fixes: P0 - WhatsApp runtime exception no longer blocks Discord startup

* fix(gateway): keep channel startup isolation sequential

* fix: isolate channel startup failures (#54215) (thanks @JonathanJing)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 10:22:42 +05:30
Peter Steinberger
8a463e7aa9 test: collapse helper plugin test suites 2026-03-25 04:52:36 +00:00
Peter Steinberger
fe84148724 test: collapse messaging target test suites 2026-03-25 04:52:36 +00:00
Peter Steinberger
6e050808ef test: collapse channel setup test suites 2026-03-25 04:52:36 +00:00
Sally O'Malley
e5d0d810e1 fixes for cli-containerized (#54223)
Signed-off-by: sallyom <somalley@redhat.com>
2026-03-25 00:51:55 -04:00
VACInc
1c9f62fad3 fix(gateway): restart sentinel wakes session after restart and preserves thread routing (#53940) thanks @VACInc
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
2026-03-24 23:47:21 -05:00
Peter Steinberger
23a4932997 refactor: share channel card selectors and layout 2026-03-24 21:44:28 -07:00
kevinten10
c00372e559 fix(agents): correct ClawHub URL in system prompt
Change clawhub.com to clawhub.ai in agent system prompt.
The .com domain is incorrect and doesn't point to the real ClawHub.

Fixes #54154
2026-03-25 10:10:37 +05:30
Peter Steinberger
039e87c942 fix: restore WhatsApp active listener singleton (#54232) 2026-03-24 21:36:20 -07:00
chocobo9
762fed1f90 fix(daemon): add headless server hints to systemd unavailable error
Add loginctl enable-linger and XDG_RUNTIME_DIR recovery hints to the
generic (non-WSL) systemd unavailable error path, helping users on
SSH/headless servers diagnose and fix the issue without a desktop
session.

Fixes #11805

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 21:33:18 -07:00
Ayaan Zaidi
2aaea9f99e chore: drop unrelated gitignore change (#53944) (thanks @affsantos) 2026-03-24 21:33:04 -07:00
Ayaan Zaidi
03dc287a29 fix: keep minimal gateway channel registry live (#53944) (thanks @affsantos) 2026-03-24 21:33:04 -07:00
Ayaan Zaidi
5eb6fdca6f fix(gateway): close runtime state on startup abort 2026-03-24 21:33:04 -07:00
Ayaan Zaidi
ef5e554def fix(gateway): invalidate channel caches on re-pin 2026-03-24 21:33:04 -07:00
affsantos
fae4492d92 fix: re-pin channel registry after deferred plugin reload
When preferSetupRuntimeForChannelPlugins is active, gateway boot performs
two plugin loads: a setup-runtime pass and a full reload after listen.
The initial pin captured the setup-entry snapshot. The deferred reload now
re-pins so getChannelPlugin() resolves against the full implementations.
2026-03-24 21:33:04 -07:00
affsantos
61d866838f fix: add inline comment clarifying dual-release scope
Address Greptile review: releasePluginRouteRegistry now releases both
HTTP-route and channel registry pins. Added comment for clarity.
2026-03-24 21:33:04 -07:00
affsantos
3a4c860798 fix(gateway): pin channel registry at startup to survive registry swaps
Channel plugin resolution fails with 'Channel is unavailable: <channel>'
after the active plugin registry is replaced at runtime. The root cause is
that getChannelPlugin() resolves against the live registry snapshot, which
is replaced when non-primary registry loads (e.g., config-schema reads)
call loadOpenClawPlugins(). If the replacement registry does not carry the
same channel entries, outbound message delivery and subagent announce
silently break.

This mirrors the existing pinActivePluginHttpRouteRegistry pattern: the
channel registry is pinned at gateway startup and released on shutdown.
Subsequent setActivePluginRegistry calls no longer evict the channel
snapshot, so getChannelPlugin() always resolves against the registry that
was active when the gateway booted.
2026-03-24 21:33:04 -07:00
ted
4d41b8664c fix(ui): return null when default account ID is stale instead of falling back to first account 2026-03-24 21:28:53 -07:00
ted
dc85235bf0 UI: derive channel configured state from default account 2026-03-24 21:28:53 -07:00
Peter Steinberger
43058c021e test: collapse setup and monitor channel suites 2026-03-25 04:25:02 +00:00
Peter Steinberger
cb76ba2406 test: collapse line channel suites 2026-03-25 04:25:02 +00:00
Peter Steinberger
ed9646516d test: collapse utility plugin suites 2026-03-25 04:25:02 +00:00
Peter Steinberger
410c2dba65 test: collapse provider plugin suites 2026-03-25 04:25:02 +00:00
fishking
6c04ce3092 fix(reasoning): guard model default reasoning when thinking active
- Add hasAgentReasoningDefault to reasoningExplicitlySet check
  This prevents model default from overriding agent's explicit "off"
- Restore !thinkingActive guard for model default fallback
  Prevents redundant Reasoning: output alongside internal thinking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 09:54:12 +05:30
fishking
b91374eb0d fix(reasoning): apply reasoningDefault independently of thinking level
The reasoningDefault was incorrectly skipped when thinking was active.
Thinking controls reasoning depth while reasoning controls visibility -
they should be independent settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 09:54:12 +05:30
Tak Hoffman
f48571bec6 fix: prefer freshest duplicate rows in session loads 2026-03-24 22:36:50 -05:00
Tak Hoffman
40f820ff7f fix: prefer freshest duplicate session rows in reads 2026-03-24 22:28:50 -05:00
Tak Hoffman
93656da672 test: make vitest config tests platform-aware 2026-03-24 22:23:23 -05:00
Tak Hoffman
f3eb620824 fix: refresh available tools when the session model changes (#54184) 2026-03-24 22:07:06 -05:00
Tak Hoffman
0c35ac4423 fix: prefer freshest transcript session owners 2026-03-24 21:58:53 -05:00
Tak Hoffman
64432f8e46 test: disable Vitest fs cache on Windows 2026-03-24 21:51:55 -05:00
Tak Hoffman
3c46e0307a fix: prefer deterministic transcript session keys 2026-03-24 21:30:54 -05:00
Tak Hoffman
7a7e4cd4c4 fix: prefer deterministic session usage targets 2026-03-24 21:21:57 -05:00
Tak Hoffman
df58b4f5fb fix: prefer deterministic session id resume targets 2026-03-24 21:18:40 -05:00
Tak Hoffman
9c7823350b feat: add /tools runtime availability view (#54088)
* test(memory): lock qmd status counts regression

* feat: make /tools show what the agent can use right now

* fix: sync web ui slash commands with the shared registry

* feat: add profile and unavailable counts to /tools

* refine: keep /tools focused on available tools

* fix: resolve /tools review regressions

* fix: honor model compat in /tools inventory

* fix: sync generated protocol models for /tools

* fix: restore canonical slash command names

* fix: avoid ci lint drift in google helper exports

* perf: stop computing unused /tools unavailable counts

* docs: clarify /tools runtime behavior
2026-03-24 21:09:51 -05:00
Tak Hoffman
fb04801ed7 fix: enforce sandbox visibility for session_status ids 2026-03-24 21:05:25 -05:00
Tak Hoffman
2c1d16e261 fix: drop spawned visibility list caps 2026-03-24 20:52:05 -05:00
Tak Hoffman
6651511e90 fix: verify exact spawned session visibility 2026-03-24 20:39:00 -05:00
Tak Hoffman
57fd0a9b23 fix: enforce spawned session visibility in key resolve 2026-03-24 20:26:29 -05:00
Tak Hoffman
154e14f18f fix: resolve exact session ids without fuzzy limits 2026-03-24 20:26:29 -05:00
Ted Li
5799322d9e Discord: resolve /think autocomplete from session model (#49176)
* Discord: use session model for /think autocomplete

* Discord: use cached session store in think autocomplete

* Discord: align think autocomplete with effective bound route

* Discord: fix think autocomplete route-resolution test mocks

* Discord: stabilize think autocomplete CI coverage

* Discord: gate think autocomplete context behind auth

* Discord: share slash auth for think autocomplete

* Discord: localize think autocomplete auth gate

* Discord: drop think autocomplete auth gating

* Discord: align native autocomplete auth and route readiness

* Discord: use effective route for model picker

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-24 20:13:11 -05:00
Vincent Koc
2069e124a9 chore(agents): normalize pi embedded runner imports 2026-03-24 18:06:24 -07:00
Vincent Koc
d10669629d feat(gateway): make openai compatibility agent-first 2026-03-24 18:06:24 -07:00
Vincent Koc
e1d16ba42e test(parallel): force unit-fast batch planning 2026-03-24 18:04:20 -07:00
Vincent Koc
8d87e85705 test(browser): stabilize default browser detection mocks 2026-03-24 18:02:47 -07:00
Tak Hoffman
1b5b23d2b1 fix: prefer current parents in session rows 2026-03-24 20:00:17 -05:00
Tak Hoffman
475983a364 fix: prefer current subagent owners in session rows 2026-03-24 19:54:07 -05:00
Tak Hoffman
ad818bda84 fix: ignore moved child rows in spawnedBy session filters 2026-03-24 19:47:36 -05:00
Tak Hoffman
6eaff70b55 fix: ignore moved child rows in subagent announces 2026-03-24 19:47:36 -05:00
Tak Hoffman
16d2e68610 fix: ignore stale store ownership in session child lists 2026-03-24 19:47:36 -05:00
Peter Steinberger
f7de5c3b83 test: collapse search helper suites 2026-03-25 00:42:09 +00:00
Peter Steinberger
83591fabfb test: consolidate plugin provider suites 2026-03-25 00:42:09 +00:00
Peter Steinberger
3a1b517581 fix: repair CI regression checks 2026-03-25 00:20:24 +00:00
Tak Hoffman
e6db1dde45 fix: hide moved subagents from stale command targets 2026-03-24 19:15:47 -05:00
Peter Steinberger
f6205de73a refactor: split feishu helpers and tests 2026-03-24 17:12:25 -07:00
Peter Steinberger
5cdb50abe6 refactor: unify Google Generative AI normalization 2026-03-24 17:09:11 -07:00
Devin Robison
56eeec4099 fix: require operator.admin for mutating internal /allowlist commands (#54097) 2026-03-24 18:05:59 -06:00
Peter Steinberger
561acd1675 test: tighten shared card schema coverage 2026-03-24 17:04:07 -07:00
Tak Hoffman
639706f298 fix: ignore moved child rows in subagent status 2026-03-24 18:57:42 -05:00
Peter Steinberger
3664c2ce46 fix: polish feishu retry helper (#43788) (thanks @lefarcen) 2026-03-24 16:55:37 -07:00
Elian
b9f48707dc fix(feishu): prevent silent group message drops when bot-info probe times out
When OpenClaw restarts under load, the Feishu bot-info probe
(`/open-apis/bot/v3/info`) can exceed the 10-second timeout due to
event-loop contention during channel initialization. This leaves
`botOpenId` empty, causing `checkBotMentioned()` to return `false`
for every group message — silently dropping them all while DMs
continue to work fine.

Two fixes:

1. **Increase startup probe timeout from 10s to 30s** and make it
   configurable via `OPENCLAW_FEISHU_STARTUP_PROBE_TIMEOUT_MS` env var.
   The previous 10s budget was too tight when multiple channels
   (Slack, Discord, Feishu) initialize concurrently.

2. **Graceful degradation in `checkBotMentioned()`**: when `botOpenId`
   is unknown, return `true` (assume mentioned) instead of `false`.
   This prevents group messages from being silently discarded when the
   probe fails for any reason. The trade-off is that the bot may
   respond to non-@-mentioned messages temporarily until the next
   successful probe, which is far preferable to total silence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:55:37 -07:00
Peter Steinberger
d4fda79ff7 fix: add merged message tool schema guardrail (#53715) (thanks @lndyzwdxhs) 2026-03-24 16:53:56 -07:00
grassylcao
ca578a9183 fix: mark card field as optional in message tool schema
The `createMessageToolCardSchema()` helper returned a bare `Type.Object()`
which TypeBox treats as required when merged into the parent tool schema via
`Type.Object({ card: ... })`. This caused schema validation to reject
media-only sends on Feishu and MSTeams with "must have required property
card", even though the implementation correctly treats card as optional.

Wrap the return value in `Type.Optional()` so the card field is excluded
from the JSON Schema `required` array. Fixes the catch-22 where omitting
card fails validation and including an empty card triggers the runtime
"does not support card with media" guard.

Closes #53697

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:53:56 -07:00
Vincent Koc
eaad4ad1be feat(gateway): add missing OpenAI-compatible endpoints (models and embeddings) (#53992)
* feat(gateway): add OpenAI-compatible models and embeddings

* docs(gateway): clarify model list and agent routing

* Update index.md

* fix(gateway): harden embeddings HTTP provider selection

* fix(gateway): validate compat model overrides

* fix(gateway): harden embeddings and response continuity

* fix(gateway): restore compat model id handling
2026-03-24 16:53:51 -07:00
Peter Steinberger
0709224ce3 fix: tighten gateway compose port parsing (#44083) (thanks @bebule) 2026-03-24 16:51:36 -07:00
Kwanghee Park (hugh.k)
ac7ca52090 Gateway: harden Compose-style gateway port parsing 2026-03-24 16:51:36 -07:00
Kwanghee Park (hugh.k)
b665749e9f Gateway: parse Compose-style gateway port env values 2026-03-24 16:51:36 -07:00
Tak Hoffman
e48a0b80a8 fix: ignore moved subagent children on stale parents 2026-03-24 18:46:37 -05:00
Peter Steinberger
33e9e485b8 refactor: clarify docker setup cli phases 2026-03-24 16:46:12 -07:00
Peter Steinberger
1ba436b372 test: speed up media and image-generation suites 2026-03-24 23:45:33 +00:00
Peter Steinberger
1a7914521b test: speed up infra and shared suites 2026-03-24 23:45:33 +00:00
Peter Steinberger
c9f4dd3c1b test: speed up browser control suites 2026-03-24 23:45:33 +00:00
Aria
63b0036248 fix: normalize baseUrl for custom Google Generative AI providers
Custom providers using `api: "google-generative-ai"` (e.g. a paid
Google tier) resolved in the model picker but failed at runtime with
HTTP 404 because the base URL lacked the required `/v1beta` path
segment and provider normalization was gated on the provider key
being exactly `"google"`.

Two targeted fixes, both keyed on the semantic `api` field rather
than provider name strings:

1. `models-config.providers.ts` — change the normalization gate from
   `normalizedKey === "google"` to
   `normalizedProvider?.api === "google-generative-ai"` and add
   `normalizeGoogleBaseUrl()` to ensure the canonical `/v1beta` suffix.

2. `pi-embedded-runner/model.ts` — apply
   `normalizeGoogleGenerativeAiBaseUrl()` in three resolution paths
   (`applyConfiguredProviderOverrides`, `buildInlineProviderModels`,
   fallback model construction) so the base URL is corrected at
   runtime regardless of how the model was discovered.

No changes to name-only call sites (`model-selection`,
`live-model-filter`, `model-forward-compat`); those paths are not
required for custom provider resolution and broadening their provider
checks would incorrectly capture unrelated providers like
`google-antigravity`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 16:42:56 -07:00
Peter Steinberger
e10ea53ea1 fix: add changelog for docker setup namespace loop (#53385) (thanks @amsminn) 2026-03-24 16:35:42 -07:00
김채완
d21ecd7642 Tests: match any pre-start openclaw-cli run 2026-03-24 16:35:42 -07:00
김채완
3ce09bd071 Tests: reset docker setup log before isolated assert 2026-03-24 16:35:42 -07:00
김채완
81be4b45a6 Docker: seed localhost control UI origin 2026-03-24 16:35:42 -07:00
김채완
dbb806d257 Docker: avoid setup CLI namespace loop 2026-03-24 16:35:42 -07:00
Peter Steinberger
6f6468027a refactor: dedupe test and runtime seams 2026-03-24 23:33:30 +00:00
Tak Hoffman
369119b6b5 fix: ignore stale parent rows in session child lists 2026-03-24 18:29:03 -05:00
Devin Robison
1d7cb6fc03 fix: close sandbox media root bypass for mediaUrl/fileUrl aliases (#54034)
* fix: close sandbox media root bypass for mediaUrl/fileUrl aliases

* Address Greptile feedback

* Fix windows test case failure
2026-03-24 17:28:53 -06:00
Tak Hoffman
907b5254f6 fix: ignore stale rows in subagent kill cascade 2026-03-24 18:12:48 -05:00
Tak Hoffman
1fd684329d fix: ignore stale rows in fast abort 2026-03-24 17:52:28 -05:00
Tak Hoffman
a03bbca4df fix: cascade bulk subagent kills past stale rows 2026-03-24 17:43:21 -05:00
Tak Hoffman
b6031a98e7 fix: ignore stale rows in subagent steer 2026-03-24 17:38:38 -05:00
Tak Hoffman
fee9d4cf37 fix: dedupe stale child completion announces 2026-03-24 17:25:14 -05:00
Tak Hoffman
2c5c5acb1b fix: ignore stale rows in subagent admin kill 2026-03-24 17:25:14 -05:00
Tak Hoffman
c90ae1ee7f fix: prefer latest subagent rows for session control 2026-03-24 17:25:14 -05:00
Tak Hoffman
b8a0258618 fix: ignore stale rows in subagent activity checks 2026-03-24 17:25:14 -05:00
Peter Steinberger
40ab7aca3d test: speed up slack monitor suites 2026-03-24 22:17:12 +00:00
Peter Steinberger
d282667321 test: speed up cli and command suites 2026-03-24 22:17:12 +00:00
Peter Steinberger
3dc139b0c0 test: speed up discord monitor suites 2026-03-24 22:17:12 +00:00
Vincent Koc
e28b516fb5 fix(slack): trim DM reply overhead and restore Codex auto transport (#53957)
* perf(slack): instrument runtime and trim DM overhead

* perf(slack): lazy-init draft previews

* perf(slack): add turn summary diagnostics

* perf(core): trim repeated runtime setup noise

* perf(core): preselect default web search providers

* perf(agent): restore OpenAI auto transport defaults

* refactor(slack): drop temporary perf wiring

* fix(slack): address follow-up review notes

* fix(security): tighten slack and runtime defaults

* style(web-search): fix import ordering

* style(agent): remove useless spread fallback

* docs(changelog): note slack runtime hardening
2026-03-24 15:03:40 -07:00
Devin Robison
47dc7fe816 fix: blcok non-owner authorized senders from chaning /send policy (#53994) 2026-03-24 15:58:39 -06:00
Tak Hoffman
c541cde0f6 fix: dedupe restarted descendant session counts 2026-03-24 16:52:50 -05:00
Tak Hoffman
e24704d5eb fix: dedupe active child session counts 2026-03-24 16:52:50 -05:00
Tak Hoffman
eb40f0b961 fix: clean up matrix /agents binding labels 2026-03-24 16:52:49 -05:00
Vincent Koc
ac8a5a614b ci: increase test shard fanout 2026-03-24 14:50:31 -07:00
Peter Steinberger
a18e156316 test: speed up telegram and whatsapp suites 2026-03-24 21:48:07 +00:00
Peter Steinberger
14e3c2de5f test: speed up discord channel suites 2026-03-24 21:48:07 +00:00
Peter Steinberger
e5173af77e test: speed up slack monitor suites 2026-03-24 21:48:07 +00:00
Peter Steinberger
3622569853 test: speed up memory provider suites 2026-03-24 21:48:07 +00:00
Vincent Koc
d648aebf4d perf(memory): builtin sqlite hot-path follow-ups (#53939)
* chore(perf): start builtin sqlite hotpath workstream

* perf(memory): reuse sqlite statements during sync

* perf(memory): snapshot file state during sync

* perf(memory): consolidate status sqlite reads

* docs(changelog): note builtin sqlite perf work

* perf(memory): avoid session table scans on targeted sync
2026-03-24 14:47:40 -07:00
Peter Steinberger
23a4ae4759 refactor: dedupe test helpers and harnesses 2026-03-24 21:41:46 +00:00
Tak Hoffman
9f4f997472 fix: align /agents ids with subagent targets 2026-03-24 16:29:49 -05:00
Tak Hoffman
a4ccd75ff3 fix: dedupe verbose subagent status counts 2026-03-24 16:18:03 -05:00
Tak Hoffman
51e59983a1 fix: report deduped subagent totals 2026-03-24 16:13:25 -05:00
Peter Steinberger
cf96fa67af ci: batch shared extensions test lane 2026-03-24 21:07:40 +00:00
Tak Hoffman
69d6e95c2a fix: dedupe stale subagent rows in reply views 2026-03-24 16:07:19 -05:00
Devin Robison
3031f061fc Adjust Feishu webhook request body limits (#53933) 2026-03-24 15:02:05 -06:00
Peter Steinberger
68b36cd9de test: fix rebase gate regressions 2026-03-24 21:01:04 +00:00
Peter Steinberger
bcd61f0a38 refactor: dedupe helpers and source seams 2026-03-24 21:00:36 +00:00
Tak Hoffman
ebe18c0379 fix: keep active-descendant subagents visible in reply status 2026-03-24 15:55:57 -05:00
Vincent Koc
0d2315ed15 fix(test): isolate github copilot token imports 2026-03-24 13:54:07 -07:00
Vincent Koc
6bf90a1d68 fix(test): stabilize memory vector dedupe assertion 2026-03-24 13:45:18 -07:00
Vincent Koc
eda1ef7b1a fix(ci): align lazy memory provider tests 2026-03-24 13:40:03 -07:00
Peter Steinberger
ddf65a995a test: speed up memory and secrets suites 2026-03-24 20:39:13 +00:00
Peter Steinberger
e2acfcf527 test: speed up browser pw-tools-core suites 2026-03-24 20:39:13 +00:00
Tak Hoffman
caa718a554 fix: steer ended subagent orchestrators with live descendants 2026-03-24 15:27:19 -05:00
Tak Hoffman
e99c270684 fix: allow follow-up sends to finished subagents 2026-03-24 15:20:39 -05:00
Vincent Koc
7d6d112656 perf(sqlite): use existence probes for empty memory search 2026-03-24 13:15:41 -07:00
Tak Hoffman
f6a0cdc25a fix: let subagent kill cascade through ended parents 2026-03-24 15:15:01 -05:00
Vincent Koc
aaf2d6359e fix(test): satisfy cli backend config typing 2026-03-24 13:06:20 -07:00
Vincent Koc
7330e2ce23 perf(memory): avoid eager provider init on empty search 2026-03-24 13:03:02 -07:00
Tak Hoffman
db0f957aba fix: surface finished subagent send targets 2026-03-24 15:01:34 -05:00
Devin Robison
c2fb7f1948 Adjust CLI backend environment handling before spawn (#53921)
security(agents): sanitize CLI backend env overrides before spawn
2026-03-24 12:58:10 -07:00
Vincent Koc
1beda4aff1 fix(ci): use target-platform npm path semantics 2026-03-24 12:47:34 -07:00
Tak Hoffman
231d62582f fix: prefer current subagent targets over stale rows 2026-03-24 14:38:34 -05:00
Peter Steinberger
4029ce738c test: speed up targeted unit suites 2026-03-24 19:36:08 +00:00
Vincent Koc
698c02e775 test(gateway): align safe open error code 2026-03-24 12:33:15 -07:00
Vincent Koc
87919dec2c fix(test): stabilize npm runner path assertion 2026-03-24 12:32:01 -07:00
Vincent Koc
805bff6e7e fix(cli): precompute bare root help startup path 2026-03-24 12:24:52 -07:00
Tak Hoffman
91b1e41132 fix: ignore stale bulk subagent kill targets 2026-03-24 14:17:28 -05:00
Tak Hoffman
ec23552b58 test: fix manifest registry fixture typing 2026-03-24 14:17:28 -05:00
Peter Steinberger
a4327ad544 refactor: dedupe tests and harden suite isolation 2026-03-24 19:16:19 +00:00
Devin Robison
d60112287f fix: validate agent workspace paths before writing identity files (#53882)
* fix: validate agent workspace paths before writing identity files

* Feedback updates and formatting fixes
2026-03-24 19:15:11 +00:00
Tak Hoffman
870c52aac7 fix: ignore stale subagent send targets 2026-03-24 14:05:00 -05:00
Vincent Koc
40315556d0 perf(plugins): scope web search plugin loads 2026-03-24 12:01:16 -07:00
Tak Hoffman
627ab895e2 fix: ignore stale subagent kill targets 2026-03-24 13:57:03 -05:00
Peter Steinberger
7101ddc5d3 chore: refresh plugin sdk api baseline 2026-03-24 18:49:51 +00:00
Vincent Koc
783cbd1e9d fix(ci): refresh plugin sdk baseline and formatting 2026-03-24 11:45:37 -07:00
Vincent Koc
a9da52da50 refactor(core): make event and queue state lazy 2026-03-24 11:45:27 -07:00
Peter Steinberger
f6b3377af2 test: stabilize low-profile parallel gate 2026-03-24 18:40:46 +00:00
Peter Steinberger
2383107711 fix: unblock supervisor and memory gate failures 2026-03-24 18:40:46 +00:00
Vincent Koc
a97188ceb3 ci: start required checks earlier (#53844)
* ci: start required checks earlier

* ci: restore pnpm in security-fast

* ci: skip docs-only payloads in early check jobs

* ci: harden untrusted pull request execution

* ci: pin gradle setup action

* ci: normalize pull request concurrency cancellation

* ci: remove duplicate early-lane setup

* ci: keep install-smoke push runs unique
2026-03-24 11:37:58 -07:00
Vincent Koc
e4ce1d9a0e fix(runtime): stabilize dist runtime artifacts (#53855)
* fix(build): stabilize lazy runtime entry paths

* fix(runtime): harden bundled plugin npm staging

* docs(changelog): note runtime artifact fixes

* fix(runtime): stop trusting npm_execpath

* fix(runtime): harden Windows npm staging

* fix(runtime): add safe Windows npm fallback
2026-03-24 11:37:39 -07:00
Vincent Koc
0cdd4db6e9 fix(memory): align status manager concurrency test 2026-03-24 11:31:35 -07:00
Vincent Koc
0caafa587f refactor(plugins): make interactive state lazy 2026-03-24 11:29:20 -07:00
Vincent Koc
d0002c5e1e refactor(gateway): make plugin fallback state lazy 2026-03-24 11:26:21 -07:00
scoootscooob
3a4cc89c53 fix: allow compact retry after failed session compaction (#53875) 2026-03-24 11:23:42 -07:00
Vincent Koc
6bef8deda9 refactor(plugins): make command registry lazy 2026-03-24 11:09:34 -07:00
Vincent Koc
f41bdf3c54 refactor(plugins): make hook runner global lazy 2026-03-24 11:07:37 -07:00
Vincent Koc
6451beddb2 refactor(plugins): make runtime registry lazy 2026-03-24 11:04:03 -07:00
Vincent Koc
e16f0cf908 refactor(channels): route registry lookups through runtime 2026-03-24 11:01:48 -07:00
Bob
7fab2c2897 fix(discord): notify user on discord when inbound worker times out (#53823)
* fix(discord): notify user on discord when inbound worker times out.

* fix(discord): notify user on discord when inbound worker times out.

* Discord: await timeout fallback reply

* Discord: add changelog for timeout reply fix (#53823) (thanks @Kimbo7870)

---------

Co-authored-by: VioGarden <111024100+VioGarden@users.noreply.github.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
2026-03-24 19:01:12 +01:00
Tak Hoffman
03ed0bccf1 fix: ignore stale subagent steer targets 2026-03-24 13:00:48 -05:00
scoootscooob
a395c757ab Chat UI: guard compact retries 2026-03-24 10:58:09 -07:00
scoootscooob
19093112ce Chat UI: tighten compact transport handling 2026-03-24 10:58:09 -07:00
scoootscooob
44e27c6092 Webchat: handle bare /compact as session compaction 2026-03-24 10:58:09 -07:00
scoootscooob
01d3442246 Plugins: sanitize sdk export subpaths 2026-03-24 10:58:06 -07:00
scoootscooob
fc60ced03c Plugins: trust only startup cli sdk roots 2026-03-24 10:58:06 -07:00
scoootscooob
f163759167 Plugins: resolve sdk aliases from the running CLI 2026-03-24 10:58:06 -07:00
scoootscooob
8633c7fa73 Providers: fix kimi fallback normalization 2026-03-24 10:58:03 -07:00
scoootscooob
9acb4c8fbc Providers: fix kimi-coding thinking normalization 2026-03-24 10:58:03 -07:00
Tak Hoffman
d25b4a2943 fix: fail closed when subagent steer remap fails 2026-03-24 12:55:43 -05:00
Vincent Koc
7daaefdb08 test(memory): recycle shared channels batches 2026-03-24 10:54:51 -07:00
Vincent Koc
3b03ff11fc test(memory): isolate slack action-runtime hotspot 2026-03-24 10:51:15 -07:00
Vincent Koc
548c2019f1 test(memory): isolate telegram monitor hotspot 2026-03-24 10:50:32 -07:00
Peter Steinberger
6e9591c4ce test: speed up browser suites 2026-03-24 17:49:25 +00:00
Peter Steinberger
217cb0ac58 test: speed up plugin-sdk and cron suites 2026-03-24 17:49:25 +00:00
Vincent Koc
e7ae7d921a test(memory): isolate telegram fetch hotspot 2026-03-24 10:47:30 -07:00
Tak Hoffman
7ab46301a9 fix: continue subagent kill after session store write failures 2026-03-24 12:46:58 -05:00
Vincent Koc
488ad4ac70 test(memory): isolate telegram bot hotspot 2026-03-24 10:46:17 -07:00
Vincent Koc
86de8b65b1 test(memory): isolate plugin-core hotspot 2026-03-24 10:45:11 -07:00
Vincent Koc
a088109327 test(memory): isolate browser remote-tab hotspot 2026-03-24 10:43:51 -07:00
Vincent Koc
fbe5f45340 test(memory): isolate new unit hotspot files 2026-03-24 10:42:22 -07:00
Tak Hoffman
240479abef fix(ci): stop dropping pending main workflow runs 2026-03-24 12:38:07 -05:00
Peter Steinberger
d58d90074f refactor: isolate ACP final delivery flow 2026-03-24 10:36:46 -07:00
Peter Steinberger
822563d1ab fix: unify pi runner usage snapshot fallback 2026-03-24 10:33:18 -07:00
Peter Steinberger
69a0a6c847 fix: tighten ACP final fallback semantics (#53692) (thanks @w-sss) 2026-03-24 10:29:27 -07:00
w-sss
7b8142997f fix(acp): deliver final result text as fallback when no blocks routed
- Check routedCounts.final to detect prior delivery
- Skip fallback for ttsMode='all' to avoid duplicate TTS processing
- Use delivery.deliver for proper routing in cross-provider turns
- Fixes #46814 where ACP child run results were not delivered
2026-03-24 10:28:33 -07:00
Peter Steinberger
d2e0cfc09f test: speed up media fetch suite 2026-03-24 17:27:02 +00:00
Peter Steinberger
a8bf75f03e test: speed up browser and gateway suites 2026-03-24 17:27:02 +00:00
Vincent Koc
435e2c5967 fix(memory): avoid caching qmd status managers 2026-03-24 10:25:00 -07:00
Peter Steinberger
a37ed72829 test: preserve child_process exports in restart bun mock 2026-03-24 17:24:18 +00:00
Vincent Koc
f2475a7f70 fix(slack): improve interactive reply parity (#53389)
* fix(slack): improve interactive reply parity

* fix(slack): isolate reply interactions from plugins

* docs(changelog): note slack interactive parity fixes

* fix(slack): preserve preview text for local agent replies

* fix(agent): preserve directive text in local previews
2026-03-24 10:23:10 -07:00
Peter Steinberger
398d58fb8a fix: stabilize logging config imports 2026-03-24 17:21:28 +00:00
Vincent Koc
a1c91bdb75 fix(memory): avoid caching status-only managers 2026-03-24 10:21:23 -07:00
Peter Steinberger
f47549c5f6 test: speed up backup and doctor suites 2026-03-24 17:16:25 +00:00
Peter Steinberger
cc9d1103d9 test: speed up command runtime suites 2026-03-24 17:16:25 +00:00
Peter Steinberger
6e20c26397 test: speed up cli and model command suites 2026-03-24 17:16:25 +00:00
Peter Steinberger
4518f6e820 test: speed up slack and telegram suites 2026-03-24 17:16:25 +00:00
Peter Steinberger
b11f4835e2 fix: suppress only recent whatsapp group echoes (#53624) (thanks @w-sss) 2026-03-24 10:10:48 -07:00
w-sss
0d4b47a14e fix(whatsapp): filter fromMe messages in groups to prevent infinite loop (#53386) 2026-03-24 10:10:48 -07:00
Peter Steinberger
f52752889b fix: audit clobbered config reads 2026-03-24 17:10:06 +00:00
Vincent Koc
14f1b65c70 test(memory): enable lower-interval heap snapshots 2026-03-24 10:09:06 -07:00
Tak Hoffman
2990446b21 fix: avoid duplicate orphaned subagent resumes 2026-03-24 12:08:44 -05:00
Peter Steinberger
44d5e6d672 fix(types): add workspace module shims 2026-03-24 10:07:14 -07:00
Vincent Koc
7eefddd0ed test(memory): clear browser and plugin caches between cases 2026-03-24 10:05:32 -07:00
Peter Steinberger
ba95d43e3c refactor: split feishu runtime and inspect secret resolution 2026-03-24 10:05:15 -07:00
Peter Steinberger
8e9e2d2f4e refactor(auth): unify external CLI credential sync 2026-03-24 10:03:00 -07:00
Peter Steinberger
27448c3113 refactor(msteams): split reply and reflection helpers 2026-03-24 10:02:49 -07:00
Peter Steinberger
9f47892bef refactor: centralize google API base URL handling 2026-03-24 10:01:22 -07:00
Tak Hoffman
129b1b5037 fix: return structured errors for subagent control send failures 2026-03-24 11:54:30 -05:00
giulio-leone
bbe6f7fdd9 fix(auth): protect fresher codex reauth state
- invalidate cached Codex CLI credentials when auth.json changes within the TTL window
- skip external CLI sync when the stored Codex OAuth credential is newer
- cover both behaviors with focused regression tests

Refs #53466

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 09:53:24 -07:00
Josh Lehman
559b3a5fd4 test: stabilize preaction process title assertion (#53808)
Regeneration-Prompt: |
  Current origin/main fails src/cli/program/preaction.test.ts because the
  test asserts on process.title directly inside Vitest, where that runtime
  interaction is not stable enough to observe the write reliably. Keep the
  production preaction behavior unchanged. Make the test verify that the
  hook assigns the expected title by wrapping process.title with a local
  getter/setter during each test and restoring the original descriptor
  afterward so other tests keep the real process object behavior.
2026-03-24 09:50:11 -07:00
Peter Steinberger
e727ad6898 fix(msteams): harden feedback reflection follow-ups 2026-03-24 09:50:04 -07:00
Peter Steinberger
72300e8fd0 docs: add changelog for PR #53675 (thanks @hpt) 2026-03-24 09:48:32 -07:00
Peter Steinberger
700ec2f25d fix: use v1beta for migrated google nano banana provider (#53757) (thanks @mahopan) 2026-03-24 09:47:59 -07:00
Maho Pan
2f238b5d7d fix(doctor): add missing baseUrl and models when migrating nano-banana apiKey to google provider
The legacy nano-banana-pro skill migration moves the Gemini API key to
models.providers.google.apiKey but does not populate the required baseUrl
and models fields on the provider entry. When the google provider object
is freshly created (no pre-existing config), the resulting config fails
Zod validation on write:

  Config validation failed: models.providers.google.baseUrl:
  Invalid input: expected string, received undefined

Fix: default baseUrl to 'https://generativelanguage.googleapis.com' and
models to [] when they are not already set, matching the defaults used
elsewhere in the codebase (embeddings-gemini, pdf-native-providers).

Fixes the 'doctor --fix' crash for users who only have a legacy
nano-banana-pro skill entry and no existing models.providers.google.
2026-03-24 09:47:21 -07:00
Han Pingtian
a1cb302c20 Feishu: avoid CLI startup failure on unresolved SecretRef 2026-03-24 09:47:18 -07:00
Tak Hoffman
ada703a7b4 fix: preserve session cleanup hooks after subagent announce 2026-03-24 11:44:10 -05:00
Tak Hoffman
79ef86c305 fix: preserve cleanup hooks after subagent register failure 2026-03-24 11:32:19 -05:00
Peter Steinberger
49e3f2db06 test: speed up core unit suites 2026-03-24 16:26:58 +00:00
Peter Steinberger
27b92f8335 test: speed up google and twitch suites 2026-03-24 16:26:58 +00:00
Peter Steinberger
332d2ebfe8 test: speed up whatsapp and signal suites 2026-03-24 16:26:58 +00:00
Peter Steinberger
5edba12f79 test: speed up discord slack telegram suites 2026-03-24 16:26:58 +00:00
Tak Hoffman
f0761b4914 fix(lockfile): sync discord dependency removal 2026-03-24 11:24:12 -05:00
Tak Hoffman
3e9ff16645 fix(discord): avoid bundling pi-ai runtime deps 2026-03-24 11:17:08 -05:00
Peter Steinberger
49ae71fa62 test: speed up signal and whatsapp extension suites 2026-03-24 15:57:16 +00:00
Peter Steinberger
86921b624c test: speed up telegram extension suites 2026-03-24 15:57:16 +00:00
Peter Steinberger
a29b9f2c20 test: speed up slack extension suites 2026-03-24 15:57:16 +00:00
Peter Steinberger
1d4db9920d test: speed up discord extension suites 2026-03-24 15:57:16 +00:00
Peter Steinberger
781295c14b refactor: dedupe test and script helpers 2026-03-24 15:48:35 +00:00
David Guttman
66e954858b add missing autoArchiveDuration to DiscordGuildChannelConfig type (#43427)
* add missing autoArchiveDuration to DiscordGuildChannelConfig type

The autoArchiveDuration field is present in the Zod schema
(DiscordGuildChannelSchema) and actively used at runtime in
threading.ts and allow-list.ts, but was missing from the
canonical TypeScript type definition.

Add autoArchiveDuration to DiscordGuildChannelConfig to align
the type with the schema and runtime usage.

* Discord: add changelog for config type fix (#43427) (thanks @davidguttman)

---------

Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
2026-03-24 16:30:24 +01:00
David Guttman
aa91000a5d feat(discord): add autoThreadName 'generated' strategy (#43366)
* feat(discord): add autoThreadName 'generated' strategy

Adds async thread title generation for auto-created threads:
- autoThread: boolean - enables/disables auto-threading
- autoThreadName: 'message' | 'generated' - naming strategy
- 'generated' uses LLM to create concise 3-6 word titles
- Includes channel name/description context for better titles
- 10s timeout with graceful fallback

* Discord: support non-key auth for generated thread titles

* Discord: skip fallback auto-thread rename

* Discord: normalize generated thread title first content line

* Discord: split thread title generation helpers

* Discord: tidy thread title generation constants and order

* Discord: use runtime fallback model resolution for thread titles

* Discord: resolve thread-title model aliases

* Discord: fallback thread-title model selection to runtime defaults

* Agents: centralize simple completion runtime

* fix(discord): pass apiKey to complete() for thread title generation

The setRuntimeApiKey approach only works for full agent runs that use
authStorage.getApiKey(). The pi-ai complete() function expects apiKey
directly in options or falls back to env vars — it doesn't read from
authStorage.runtimeOverrides.

Fixes thread title generation for Claude/Anthropic users.

* fix(agents): return exchanged Copilot token from prepareSimpleCompletionModel

The recent thread-title fix (3346ba6) passes prepared.auth.apiKey to
complete(). For github-copilot, this was still the raw GitHub token
rather than the exchanged runtime token, causing auth failures.

Now setRuntimeApiKeyForCompletion returns the resolved token and
prepareSimpleCompletionModel includes it in auth.apiKey, so both the
authStorage path and direct apiKey pass-through work correctly.

* fix(agents): catch auth lookup exceptions in completion model prep

getApiKeyForModel can throw for credential issues (missing profile, etc).
Wrap in try/catch to return { error } for fail-soft handling rather than
propagating rejected promises to callers like thread title generation.

* Discord: strip markdown wrappers from generated thread titles

* Discord/agents: align thread-title model and local no-auth completion headers

* Tests: import fresh modules for mocked thread-title/simple-completion suites

* Agents: apply exchanged Copilot baseUrl in simple completions

* Discord: route thread runtime imports through plugin SDK

* Lockfile: add Discord pi-ai runtime dependency

* Lockfile: regenerate Discord pi-ai runtime dependency entries

* Agents: use published Copilot token runtime module

* Discord: refresh config baseline and lockfile

* Tests: split extension runs by isolation

* Discord: add changelog for generated thread titles (#43366) (thanks @davidguttman)

---------

Co-authored-by: Onur Solmaz <onur@textcortex.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
2026-03-24 16:27:19 +01:00
Tak Hoffman
3f99a30163 fix: clean up attachments when replacing subagent runs 2026-03-24 10:21:15 -05:00
Tak Hoffman
0bda670d9a fix(ci): do not cancel in-progress bun runs on main 2026-03-24 10:19:59 -05:00
Peter Steinberger
d884676dd2 test: speed up whatsapp and shared test suites 2026-03-24 15:16:18 +00:00
Peter Steinberger
83bb647238 test: speed up telegram extension suites 2026-03-24 15:16:18 +00:00
Peter Steinberger
db4572b459 test: speed up slack extension suites 2026-03-24 15:16:18 +00:00
Peter Steinberger
88f49c27a0 test: speed up discord extension suites 2026-03-24 15:16:18 +00:00
Tak Hoffman
df2f900677 fix: clean up attachments for orphaned subagent runs 2026-03-24 10:05:53 -05:00
Tak Hoffman
075ece3dac fix(ci): do not cancel in-progress main runs 2026-03-24 10:02:25 -05:00
Tak Hoffman
938f8f4d83 fix: clean up attachments for released subagent runs 2026-03-24 09:56:20 -05:00
Harold Hunt
35de467b1a Telegram: recover General topic bindings (#53699)
Merged via squash.

Prepared head SHA: 546f0c8134
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
2026-03-24 10:51:26 -04:00
Tak Hoffman
8754d8e330 fix: ci 2026-03-24 09:25:28 -05:00
Sally O'Malley
91adc5e718 feat(cli): support targeting running containerized openclaw instances (#52651)
Signed-off-by: sallyom <somalley@redhat.com>
2026-03-24 10:17:17 -04:00
Tak Hoffman
dd11bdd003 fix: clean up attachments for killed subagent runs 2026-03-24 09:14:06 -05:00
Tak Hoffman
807daf54fe fix: finalize killed delete-mode subagent cleanup 2026-03-24 09:01:55 -05:00
Tak Hoffman
d7e48d4883 fix: ci 2026-03-24 08:40:55 -05:00
Neerav Makwana
f56a79f838 fix: report qmd status counts from real qmd manager (#53683) (thanks @neeravmakwana)
* fix(memory): report qmd status counts from index

* fix(memory): reuse full qmd manager for status

* fix(memory): harden qmd status manager lifecycle
2026-03-24 19:10:20 +05:30
Tak Hoffman
e6e2407cee fix: initialize plugins before killed subagent hooks 2026-03-24 08:27:50 -05:00
Tak Hoffman
b72d0c8459 fix: clean up failed non-thread subagent spawns 2026-03-24 08:26:59 -05:00
Ayaan Zaidi
0a04ef494d fix: merge explicit reply config overrides onto fresh config 2026-03-24 18:52:04 +05:30
Taras Lukavyi
ac07d8814a fix(secrets): prevent unresolved SecretRef from crashing embedded agent runs
Root cause: Telegram channel monitor captures config at startup before secrets
are resolved and passes it as configOverride into the reply pipeline. Since
getReplyFromConfig() uses configOverride directly (skipping loadConfig() which
reads the resolved runtime snapshot), the unresolved SecretRef objects propagate
into FollowupRun.run.config and crash runEmbeddedPiAgent().

Fix (defense in depth):
- get-reply.ts: detect unresolved SecretRefs in configOverride and fall back to
  loadConfig() which returns the resolved runtime snapshot
- message-tool.ts: try-catch around schema/description building at tool creation
  time so channel discovery errors don't crash the agent
- message-tool.ts: detect unresolved SecretRefs in pre-bound config at tool
  execution time and fall back to gateway secret resolution

Fixes: https://github.com/openclaw/openclaw/issues/45838
2026-03-24 18:52:04 +05:30
HollyChou
c84c630b4c fix(docs): correct json55 typo to json5 in IRC channel docs (#50831) (#50842)
Merged via squash.

Prepared head SHA: 0f743bf472
Co-authored-by: Hollychou924 <128659251+Hollychou924@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-24 16:20:49 +03:00
Mariano
922f4e66ea fix(agents): harden edit tool recovery (#52516)
Merged via squash.

Prepared head SHA: e23bde893a
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
2026-03-24 13:19:16 +01:00
Peter Steinberger
60cd98a841 test: defer slack bolt interop for helper-only suites 2026-03-24 09:34:23 +00:00
Peter Steinberger
b1b162fcdb test: harden threaded channel follow-ups 2026-03-24 09:24:29 +00:00
Peter Steinberger
43131dcc08 test: harden threaded shared-worker suites 2026-03-24 08:37:00 +00:00
Peter Steinberger
e7817ad12a test: continue vitest threads migration 2026-03-24 08:37:00 +00:00
Peter Steinberger
2833b27f52 test: continue vitest threads migration 2026-03-24 08:37:00 +00:00
Ayaan Zaidi
d41b92fff2 docs: update CONTRIBUTING.md 2026-03-24 13:36:38 +05:30
Val Alexander
b61a875d56 fix: widen installer regex allowlists and deduplicate safeExternalHref calls
- SAFE_GO_MODULE: allow uppercase in module paths (A-Z)
- SAFE_BREW_FORMULA: allow @ for versioned formulas (python@3.12)
- SAFE_UV_PACKAGE: allow extras [standard] and equality pins ==
- Cache safeExternalHref result in skills detail API key section
2026-03-24 01:46:33 -05:00
Val Alexander
cb58e45130 fix(security): resolve Aisle findings — skill installer validation, terminal sanitization, URL scheme allowlisting (#53471) thanks @BunsDev
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Co-authored-by: Nova <nova@openknot.ai>
2026-03-24 01:43:48 -05:00
Val Alexander
a710366e9e feat(ui): Control UI polish — skills revamp, markdown preview, agent workspace, macOS config tree (#53411) thanks @BunsDev
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Co-authored-by: Nova <nova@openknot.ai>
2026-03-24 01:21:13 -05:00
Peter Steinberger
ecb3aa7fe0 test: sync app chat model override expectation 2026-03-23 23:18:59 -07:00
Peter Steinberger
ff2e9a52ff fix: preserve deferred TUI history sync (#53130) (thanks @joelnishanth) 2026-03-23 23:18:59 -07:00
joelnishanth
cc8ed8d25b fix(tui): preserve user message during slow model responses (#53115)
When a local run ends with an empty final event while another run is active,
skip history reload to prevent clearing the user's pending message from the
chat log. This fixes the 'message disappears' issue with slow models like Ollama.
2026-03-23 23:18:59 -07:00
Tak Hoffman
5e9ea804d4 fix: finalize deferred subagent expiry cleanup 2026-03-24 01:12:54 -05:00
Peter Steinberger
5dc42dfb17 fix: format subagent registry test 2026-03-24 06:10:55 +00:00
Peter Steinberger
fd0fa97952 refactor: centralize plugin install config policy 2026-03-23 23:07:40 -07:00
Tak Hoffman
c3744fbfc4 fix: finalize resumed subagent cleanup give-ups 2026-03-24 01:06:39 -05:00
Peter Steinberger
a2d3b9f317 fix: unblock live harness provider discovery 2026-03-23 23:02:44 -07:00
Tak Hoffman
ab8c834aab fix: report dropped subagent announce queue deliveries 2026-03-24 00:54:46 -05:00
Tak Hoffman
0fc27409c0 fix: preserve direct subagent dispatch failures on abort 2026-03-24 00:47:01 -05:00
Peter Steinberger
687ce31f88 test: harden parallels smoke harness 2026-03-24 05:43:22 +00:00
Peter Steinberger
da10b6026a test: prune low-signal live model sweeps 2026-03-24 05:43:07 +00:00
Tak Hoffman
3689a82494 fix: preserve subagent ended hooks until runtime init 2026-03-24 00:31:48 -05:00
Peter Steinberger
0f84aac487 fix: stabilize matrix and teams ci assertions 2026-03-24 05:29:05 +00:00
Tao Xie
0b54b64fe7 fix(feishu): preserve docx block tree order (openclaw#40524)
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm vitest run extensions/feishu/src/docx.test.ts

Co-authored-by: Tao Xie <7379039+TaoXieSZ@users.noreply.github.com>
2026-03-24 00:28:10 -05:00
Peter Steinberger
9082795b10 refactor(ui): extract chat model resolution state 2026-03-23 22:24:43 -07:00
Peter Steinberger
013385e5c2 refactor: polish trigger and manifest seams 2026-03-23 22:22:48 -07:00
Tak Hoffman
535b792808 fix: delete subagent runs after announce give-up 2026-03-24 00:20:22 -05:00
Peter Steinberger
ec7f564566 fix: clear production dependency advisories 2026-03-24 05:17:34 +00:00
Peter Steinberger
25d52b56a0 fix: keep slash command model qualification on rebase 2026-03-24 05:17:34 +00:00
Peter Steinberger
ef6821a4ca fix: restore teams sdk adapter contracts 2026-03-24 05:17:34 +00:00
Peter Steinberger
a8beb55b74 fix: fetch model catalog for slash command updates 2026-03-24 05:17:34 +00:00
Catalin Lupuleti
cbb11b3662 fix(plugins): address review feedback for Matrix recovery paths (#52899) 2026-03-23 22:16:10 -07:00
Catalin Lupuleti
489797ceaf fix(plugins): address review feedback for Matrix recovery paths (#52899)
1. Narrow loadConfigForInstall() to catch only INVALID_CONFIG errors,
   letting real failures (fs permission, OOM) propagate.
2. Assert allow array is properly cleaned in stale-cleanup test.
3. Add comment clarifying version-resolution is already addressed via
   the shared VERSION constant.
4. Run cleanStaleMatrixPluginConfig() during install so
   persistPluginInstall() → writeConfigFile() does not fail validation
   on stale Matrix load paths.
2026-03-23 22:16:10 -07:00
Catalin Lupuleti
3ae100a8d7 fix(plugins): make Matrix recovery paths tolerate stale plugin config (#52899) 2026-03-23 22:16:10 -07:00
Peter Steinberger
5c9e4cd30a refactor: clarify doctor repair flow 2026-03-23 22:12:56 -07:00
Tak Hoffman
e8ba55adbb test: add exact-stem subagent seam tests 2026-03-24 00:09:36 -05:00
Tak Hoffman
7464330a08 test: audit subagent seam coverage inventory 2026-03-24 00:09:36 -05:00
Peter Steinberger
2e1c88b728 refactor: tighten embedded prompt and sidecar guards 2026-03-23 22:09:22 -07:00
Sid Uppal
cd90130877 msteams: implement Teams AI agent UX best practices (#51808)
Migrates the Teams extension from @microsoft/agents-hosting to the official Teams SDK (@microsoft/teams.apps + @microsoft/teams.api) and implements Microsoft's AI UX best practices for Teams agents.

- AI-generated label on all bot messages (Teams native badge + thumbs up/down)
- Streaming responses in 1:1 chats via Teams streaminfo protocol
- Welcome card with configurable prompt starters on bot install
- Feedback with reflective learning (negative feedback triggers background reflection)
- Typing indicators for personal + group chats (disabled for channels)
- Informative status updates (progress bar while LLM processes)
- JWT validation via Teams SDK createServiceTokenValidator
- User-Agent: teams.ts[apps]/<sdk-version> OpenClaw/<version> on outbound requests
- Fix copy-pasted image downloads (smba.trafficmanager.net auth allowlist)
- Pre-parse auth gate (reject unauthenticated requests before body parsing)
- Reflection dispatcher lifecycle fix (prevent leaked dispatchers)
- Colon-safe session filenames (Windows compatibility)
- Cooldown cache eviction (prevent unbounded memory growth)

Closes #51806
2026-03-23 22:03:39 -07:00
Peter Steinberger
ea62655e19 fix: land cron heartbeat prompt suppression (#53152) (thanks @Protocol-zero-0) 2026-03-23 21:55:34 -07:00
Protocol-zero-0
9f863140d5 test(agents): cover additional heartbeat prompt triggers
Document that default-agent heartbeat prompt injection still applies to memory-triggered and triggerless runs while cron remains excluded.

Made-with: Cursor
2026-03-23 21:55:34 -07:00
Protocol-zero-0
9d21be4193 fix(agents): suppress heartbeat prompt for cron-triggered embedded runs
Prevent cron-triggered embedded runs from inheriting the default heartbeat prompt so non-cron session targets stop reading HEARTBEAT.md and polluting scheduled turns.

Made-with: Cursor
2026-03-23 21:55:34 -07:00
Val Alexander
da5b7ff0af style(ui): polish agent file preview and usage popovers (#53382)
* feat: make workspace links clickable in agent context card and files list

Updated the agent context card and files list to render workspace names as clickable links, allowing users to easily access the corresponding workspace files. This enhances usability by providing direct navigation to the workspace location.

* style(ui): polish markdown preview dialog

* style(ui): reduce markdown preview list indentation

* style(ui): update markdown preview dialog width and alignment

* fix(ui): open usage filter popovers toward the right

* style(ui): adjust positioning of usage filter and export popovers

* style(ui): update sidebar footer padding and modify usage header z-index

* style(ui): adjust positioning of usage filter popover to the left and export popover to the right

* style(ui): simplify workspace link rendering in agent context card

* UI: make workspace paths interactive buttons or plain text

Agent Context card workspace (Channels/Cron panels): replace non-interactive
<div> with a real <button> wired to onSelectPanel('files'), matching the
Overview panel pattern.

Core Files footer workspace: drop workspace-link class since the user is
already on the Files panel — keep as plain text.
2026-03-23 23:55:25 -05:00
HCL
be20eebc21 fix(ui): resolve model provider from catalog instead of stale session default
When the server returns a bare model name (e.g. "deepseek-chat") with
a session-level modelProvider (e.g. "zai"), the UI blindly prepends
the provider — producing "zai/deepseek-chat" instead of the correct
"deepseek/deepseek-chat". This causes "model not allowed" errors
when switching between models from different providers.

Root cause: resolveModelOverrideValue() and resolveDefaultModelValue()
in app-render.helpers.ts, plus the /model slash command handler in
slash-command-executor.ts, all call resolveServerChatModelValue()
which trusts the session's default provider. The session provider
reflects the PREVIOUS model, not the newly selected one.

Fix: for bare model names, create a raw ChatModelOverride and resolve
through normalizeChatModelOverrideValue() which looks up the correct
provider from the model catalog. Falls back to server-provided provider
only if the catalog lookup fails. All 3 call sites are fixed.

Closes #53031

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
2026-03-23 21:54:02 -07:00
Peter Steinberger
5ab3782215 fix: add config clobber forensics 2026-03-24 04:50:30 +00:00
giulio-leone
67c7f98c32 fix(doctor): skip service config repairs during updates
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 21:48:18 -07:00
giulio-leone
d8aada9d45 Preserve no-restart during update doctor fixes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 21:48:18 -07:00
giulio-leone
3359dcfdcf fix(doctor): honor --fix in non-interactive mode
Ensure repair-mode doctor prompts auto-accept recommended fixes even when running non-interactively, while still requiring --force for aggressive rewrites.

This restores the expected behavior for upgrade/doctor flows that rely on 'openclaw doctor --fix --non-interactive' to repair stale gateway service configuration such as entrypoint drift after global updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 21:48:18 -07:00
sudie-codes
6e970010f7 msteams: add message edit and delete support (#49925)
- Add edit/delete action handlers with toolContext.currentChannelId
  fallback for in-thread edits/deletes without explicit target
- Add editMessageMSTeams/deleteMessageMSTeams to channel runtime
- Add updateActivity/deleteActivity to SendContext and MSTeamsTurnContext
- Extend content param with text/content/message fallback chain
- Update test mocks for new SendContext shape

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 23:42:04 -05:00
sudie-codes
9f5d286caf msteams: extract structured quote/reply context (#51647)
* msteams: extract structured quote/reply context from Teams HTML attachments

* msteams: address PR #51647 review feedback
2026-03-23 23:41:51 -05:00
Tak Hoffman
8c89d0e7cd tests: add boundary coverage for media delivery (#53361)
* tests: add boundary coverage for media delivery

* tests: isolate telegram outbound adapter transport

* tests: harden telegram webhook certificate assertion

* tests: fix guardrail false positives on rebased branch
2026-03-23 23:37:34 -05:00
Peter Steinberger
e864421d83 fix: unify live model auth gating 2026-03-24 04:28:53 +00:00
Peter Steinberger
e28e520379 refactor: simplify provider inference and zoned parsing helpers 2026-03-23 21:22:30 -07:00
Peter Steinberger
1bfef17825 ci: balance shards and reuse pr artifacts 2026-03-24 04:19:43 +00:00
Peter Steinberger
26365f7daf fix: hash inline scripts with data-src attributes 2026-03-23 21:14:55 -07:00
Peter Steinberger
0857447a5d fix: reject nonexistent zoned cron at-times 2026-03-23 21:14:55 -07:00
Peter Steinberger
69a317995d fix: fail closed when provider inference drops errored allowlists 2026-03-23 21:14:55 -07:00
Peter Steinberger
3a36fffc15 docs: update mac release automation guidance 2026-03-23 21:12:46 -07:00
Peter Steinberger
dc4d2ca263 build: prepare 2026.3.24 2026-03-23 21:05:59 -07:00
Peter Steinberger
ce49d8bca9 fix: verify global npm correction installs 2026-03-23 21:04:08 -07:00
Tak Hoffman
50d996a6ec tests: cron coverage and NO_REPLY delivery fixes (#53366)
* tools: extend seam audit inventory

* tools: audit cron seam coverage gaps

* test: add cron seam coverage tests

* fix: avoid marking NO_REPLY cron deliveries as delivered

* fix: clean up delete-after-run NO_REPLY cron sessions
2026-03-23 22:52:13 -05:00
Vincent Koc
6142fc1d94 fix(runtime): anchor bundled plugin npm staging to active node 2026-03-23 20:32:48 -07:00
Peter Steinberger
483dc90f05 test: harden linux runtime smoke guards 2026-03-24 03:23:52 +00:00
Peter Steinberger
dd2361a4c4 docs: refresh plugin-sdk api baseline 2026-03-24 03:20:51 +00:00
Ayaan Zaidi
17c1ee7716 fix: preserve command auth resolution errors on empty inferred allowlists 2026-03-24 08:38:27 +05:30
Peter Steinberger
630f1479c4 build: prepare 2026.3.23-2 2026-03-23 20:04:42 -07:00
Peter Steinberger
38137b0cf8 refactor: split tracked ClawHub update flows 2026-03-23 20:01:51 -07:00
Peter Steinberger
b4e392cf9d fix: unblock runtime-api smoke checks 2026-03-24 03:00:51 +00:00
Taras Lukavyi
7ffe7e4822 fix: populate currentThreadTs in threading tool context fallback for Telegram DM topics (#52217)
When a channel plugin lacks a custom buildToolContext (e.g. Telegram),
the fallback path in buildThreadingToolContext did not set currentThreadTs
from the inbound MessageThreadId. This caused resolveTelegramAutoThreadId
to return undefined, so message tool sends without explicit threadId
would route to the main chat instead of the originating DM topic.

Fixes #52217
2026-03-24 08:27:03 +05:30
Peter Steinberger
3ae5d33799 refactor: extract cron schedule and test runner helpers 2026-03-23 19:53:43 -07:00
Taras Lukavyi
d4e3babdcc fix: command auth SecretRef resolution (#52791) (thanks @Lukavyi)
* fix(command-auth): handle unresolved SecretRef in resolveAllowFrom

* fix(command-auth): fall back to config allowlists

* fix(command-auth): avoid duplicate resolution fallback

* fix(command-auth): fail closed on invalid allowlists

* fix(command-auth): isolate fallback resolution errors

* fix: record command auth SecretRef landing notes (#52791) (thanks @Lukavyi)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-24 08:21:30 +05:30
Tak Hoffman
5cd8d43af9 tests: improve boundary audit coverage and safety (#53080)
* tools: extend seam audit inventory

* tools: tighten seam audit heuristics

* tools: refine seam test matching

* tools: refine seam audit review heuristics

* style: format seam audit script

* tools: widen seam audit matcher coverage

* tools: harden seam audit coverage

* tools: tighten boundary audit matchers

* tools: ignore mocked import matches in boundary audit

* test: include native command reply seams in audit
2026-03-23 21:46:53 -05:00
Peter Steinberger
a3f2fbf5a2 refactor: harden extension runtime-api seams 2026-03-23 19:39:20 -07:00
Peter Steinberger
d8e77c423a test: isolate line jiti runtime smoke 2026-03-24 02:38:49 +00:00
Peter Steinberger
9e8abb468d fix: clean changelog merge duplication (#53224) (thanks @RolfHegr) 2026-03-23 19:38:04 -07:00
Peter Steinberger
0cbf6d5fed fix: land cron tz one-shot handling and prerelease config warnings (#53224) (thanks @RolfHegr) 2026-03-23 19:38:04 -07:00
Rolfy
9aac5582d6 fix(cron): make --tz work with --at for one-shot jobs
Previously, `--at` with an offset-less ISO datetime (e.g. `2026-03-23T23:00:00`)
was always interpreted as UTC, even when `--tz` was provided. This caused one-shot
jobs to fire at the wrong time.

Changes:
- `parseAt()` now accepts an optional `tz` parameter
- When `--tz` is provided with `--at`, offset-less datetimes are interpreted in
  that IANA timezone using Intl.DateTimeFormat
- Datetimes with explicit offsets (e.g. `+01:00`, `Z`) are unaffected
- Removed the guard in cron-edit that blocked `--tz` with `--at`
- Updated `--at` help text to mention `--tz` support
- Added 2 tests verifying timezone resolution and offset preservation
2026-03-23 19:38:04 -07:00
Peter Steinberger
8f9799307b test: print failed test lane output tails 2026-03-23 19:36:44 -07:00
Peter Steinberger
7f373823b0 refactor: separate exec policy and execution targets 2026-03-23 19:36:44 -07:00
Val Alexander
a96eded4a0 feat(csp): support inline script hashes in Control UI CSP (#53307) thanks @BunsDev
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Co-authored-by: Nova <nova@openknot.ai>
2026-03-23 21:35:33 -05:00
Peter Steinberger
e530865274 fix: preserve legacy clawhub skill updates (#53206) (thanks @drobison00) 2026-03-23 19:34:05 -07:00
Devin Robison
003752b9b3 Remove lower casing -- preserving prior behavior 2026-03-23 19:34:05 -07:00
Devin Robison
a339d706c1 Formatting fixes and remove trailing dash acceptance 2026-03-23 19:34:05 -07:00
Devin Robison
40071ea23e fix: tighten skill slug validation to ASCII-only 2026-03-23 19:34:05 -07:00
Peter Steinberger
2be3c996fb test: alias all plugin-sdk subpaths in line smoke 2026-03-24 02:31:59 +00:00
Peter Steinberger
d5917d37c5 test: allow line runtime api source fallback 2026-03-24 02:26:17 +00:00
Peter Steinberger
462a7a9ae6 test: allow realpath in shell planner assertions 2026-03-24 02:15:14 +00:00
Drickon
715b13547f fix(line): pre-export clashing symbols to prevent jiti TypeError on startup (#53221)
* fix(line): pre-export clashing symbols to prevent jiti TypeError on startup

When jiti CJS-transforms extensions/line/runtime-api.ts, both
export * from "openclaw/plugin-sdk/line-runtime" and the subsequent
export * from individual source files attempt to define the same 13
symbols via Object.defineProperty with configurable:false. The second
call throws TypeError: Cannot redefine property.

The root cause is that src/plugin-sdk/line-runtime.ts re-exports
these symbols directly from the extension source files, creating a
circular path back to the same files that runtime-api.ts star-exports.

Fix: add named pre-exports for all symbols that plugin-sdk/line-runtime
re-exports from this extension. Named exports register in jiti's
_exportNames map at transform time; the star re-export's hasOwnProperty
guard then skips them, preventing the duplicate Object.defineProperty.

export * reordering cannot fix this: _exportNames is only populated
by named exports, not by export *, so the guard never fires regardless
of order.

This is the same class of bug as the Matrix plugin crash described in
issues #50868, #52780, and #52891, and uses the same fix pattern as
PR #50919.

* test: add LINE runtime-api Jiti regression (#53221) (thanks @Drickon)

* test: stabilize LINE Jiti regression (#53221) (thanks @Drickon)

* test: harden LINE Jiti regression (#53221) (thanks @Drickon)

* chore: retrigger PR checks (#53221)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-03-23 19:13:25 -07:00
Peter Steinberger
d8cef14eb1 fix: split exec and policy resolution for wrapper trust (#53134) (thanks @vincentkoc) 2026-03-23 19:04:04 -07:00
Peter Steinberger
21d480ed92 fix(infra): preserve blocked dispatch policy target
# Conflicts:
#	CHANGELOG.md
2026-03-23 19:04:04 -07:00
Vincent Koc
32e89b4687 Infra: preserve wrapper executable for multiplexer trust 2026-03-23 19:04:04 -07:00
Peter Steinberger
2d5f822ca1 fix: warn on same-base prerelease configs 2026-03-24 02:02:31 +00:00
Peter Steinberger
85ed1a8986 refactor: clean up ClawHub compatibility validation 2026-03-23 18:52:37 -07:00
Val Alexander
9dd0530b97 fix(ui): redact sensitive config values in diff panel
Use isSensitiveConfigPath to detect token/password/secret/apiKey paths
and display REDACTED_PLACEHOLDER instead of raw values in the config
diff panel, preventing credential exposure in the UI.
2026-03-23 20:48:08 -05:00
Val Alexander
21ac4b9a8a style(ui): continue ui clarity pass across theme, config, and usage (#53272) thanks @BunsDev
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Co-authored-by: Nova <nova@openknot.ai>
2026-03-23 20:45:43 -05:00
Peter Steinberger
ecc8fe5dc2 ci: rebalance sharded test lanes 2026-03-24 01:44:26 +00:00
Peter Steinberger
5b4fd6bf31 fix: use runtime version for ClawHub plugin API checks (#53157) (thanks @futhgar) 2026-03-23 18:41:18 -07:00
futhgar
447e074bf4 fix(plugins): use runtime version for plugin API compatibility check
OPENCLAW_PLUGIN_API_VERSION was hardcoded to "1.2.0" while ClawHub-published
plugins require >=2026.3.22, making all plugin installs via ClawHub fail with
"requires plugin API >=2026.3.22, but this OpenClaw runtime exposes 1.2.0".

Use resolveRuntimeServiceVersion() (already imported) to read the actual
version from package.json at runtime.

Fixes #53038
2026-03-23 18:41:18 -07:00
Peter Steinberger
d25ad66069 fix: resolve catalog-backed channel login 2026-03-23 18:25:44 -07:00
Peter Steinberger
69390daa51 test: cover config correction version warnings 2026-03-23 18:23:50 -07:00
Peter Steinberger
b4bda479a4 fix: normalize bundled plugin version reporting 2026-03-23 18:23:50 -07:00
Peter Steinberger
e9905fd696 fix: avoid fd warnings in lock exit cleanup 2026-03-24 01:01:59 +00:00
Val Alexander
6c44b2ea50 fix(cli): guard channel-auth against prototype-chain pollution and control-char injection
- Use hasOwnProperty + isBlockedObjectKey in isConfiguredAuthPlugin to
  prevent __proto__/constructor/prototype keys from matching config
- Sanitize plugin IDs with sanitizeForLog in ambiguity error messages
- Add regression test for __proto__ plugin ID
2026-03-23 19:58:16 -05:00
Val Alexander
c8f4b8533d fix(cli): auto-select login-capable auth channels (#53254) thanks @BunsDev
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Co-authored-by: Nova <nova@openknot.ai>
2026-03-23 19:54:46 -05:00
Peter Steinberger
5cb8e33a31 build: tag correction npm publishes as latest 2026-03-23 17:42:43 -07:00
Peter Steinberger
00d586b2ce test: reduce flaky gemini live probe coverage 2026-03-24 00:40:17 +00:00
Peter Steinberger
dc02a7520f test: stabilize moonshot and minimax live probes 2026-03-24 00:40:17 +00:00
Peter Steinberger
b8bf6c482e ci: cap channel shard workers 2026-03-24 00:38:51 +00:00
Peter Steinberger
9334015262 fix: ship bundled plugin runtime sidecars 2026-03-23 17:38:08 -07:00
Peter Steinberger
ffd722bc2c build: harden local release verification 2026-03-23 17:38:08 -07:00
Peter Steinberger
ce75f60ae9 fix: canonicalize malformed assistant replay content 2026-03-23 17:37:51 -07:00
Val Alexander
a5c35050f3 style: update border-radius values to use CSS variables for consistency across components (#53238) 2026-03-23 19:23:43 -05:00
Peter Steinberger
90fab48416 ci: stabilize sharded channel lanes 2026-03-24 00:21:50 +00:00
Peter Steinberger
e32148f1dd build: publish 2026.3.23 mac appcast 2026-03-23 17:01:06 -07:00
Peter Steinberger
2d19d2acb9 ci: shorten main critical path 2026-03-23 23:45:51 +00:00
Peter Steinberger
36de481541 docs: capture windows parallels install learnings 2026-03-23 16:40:53 -07:00
Peter Steinberger
ea99984e23 test: fix windows parallels agent quoting 2026-03-23 16:39:51 -07:00
Sally O'Malley
34dc712f36 changelog (#53229)
Signed-off-by: sallyom <somalley@redhat.com>
2026-03-23 19:37:59 -04:00
Peter Steinberger
a0483086b9 docs: fix 2026.3.22 and 2026.3.23 release notes 2026-03-23 16:27:14 -07:00
Peter Steinberger
ccfeecb688 test: harden parallels macos dashboard smoke 2026-03-23 16:13:20 -07:00
Peter Steinberger
a921b5bdff test: fix update-cli default path assertion 2026-03-23 23:05:25 +00:00
Peter Steinberger
725a2cc2ca test: expand gemini live transcript stripping 2026-03-23 23:01:22 +00:00
Peter Steinberger
67dbb1ad42 test: update command coverage 2026-03-23 23:01:22 +00:00
Peter Steinberger
d67efbfbd3 test: stabilize test isolation 2026-03-23 23:01:22 +00:00
Sally O'Malley
ae336d1602 Doctor: prune stale plugin allowlist and entry refs (#53187)
Signed-off-by: sallyom <somalley@redhat.com>
2026-03-23 18:58:54 -04:00
Vincent Koc
03231c0633 fix(auth): prevent stale auth store reverts (#53211) 2026-03-23 15:56:46 -07:00
Peter Steinberger
47bdc36831 test: make update-cli checkout path assertion platform-safe 2026-03-23 22:54:32 +00:00
Vincent Koc
19295994f3 fix(ci): stabilize whatsapp extension checks 2026-03-23 15:50:19 -07:00
Vincent Koc
6f5df14308 test(whatsapp): preserve harness session exports 2026-03-23 15:50:19 -07:00
Vincent Koc
7b03502175 test(whatsapp): preserve media test module exports 2026-03-23 15:50:19 -07:00
Vincent Koc
e87a64f8d0 test(whatsapp): preserve session exports in login coverage 2026-03-23 15:50:19 -07:00
Vincent Koc
dad6018230 test(whatsapp): stabilize login coverage in shared workers 2026-03-23 15:50:19 -07:00
Peter Steinberger
e808f8d6ef docs(changelog): reorder release highlights 2026-03-23 15:44:18 -07:00
Onur
6ab6e7a493 Release: document manual macOS asset upload (#53178)
* Release: document manual macOS asset upload

* Release: document macOS smoke-test mode
2026-03-23 23:19:52 +01:00
Robin Waslander
fb6588cb99 fix(diagnostics): redact credentials from cache-trace diagnostic output
Refs #53103
2026-03-23 22:59:09 +01:00
Onur
6e8d5cd578 Release: privatize macOS publish flow (#53166) 2026-03-23 22:35:51 +01:00
George Zhang
ade0182ae0 feat(modelstudio): add standard (pay-as-you-go) DashScope endpoints for Qwen (#43878)
Add Standard API Key auth methods for China (dashscope.aliyuncs.com)
and Global/Intl (dashscope-intl.aliyuncs.com) pay-as-you-go endpoints
alongside the existing Coding Plan (subscription) endpoints.

Also updates group label to 'Qwen (Alibaba Cloud Model Studio)' and
fixes glm-4.7 -> glm-5 in Coding Plan note messages.

Co-authored-by: wenmeng zhou <wenmengzhou@users.noreply.github.com>
2026-03-23 14:35:19 -07:00
Ayush Ojha
64f4df1886 fix: generalize api_error detection for fallback model triggering (#49611)
Co-authored-by: Ayush Ojha <7945279+ayushozha@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
2026-03-24 00:34:27 +03:00
Vincent Koc
3814f956d1 Infra: tighten shell-wrapper positional-argv allowlist matching (#53133)
* Infra: tighten shell carrier allowlist matching

* fix(security): tighten shell carrier allowlist matcher
2026-03-23 14:11:38 -07:00
theo674
dbe7da7684 fix: prevent delivery-mirror re-delivery and raise Slack chunk limit (#45489)
Merged via squash.

Prepared head SHA: c7664c7b6e
Co-authored-by: theo674 <261068216+theo674@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-24 00:11:19 +03:00
Altay
a53715e9d0 test(auth): align device scope expectations (#53151) 2026-03-23 23:56:02 +03:00
Vincent Koc
c1131ba7e0 fix(plugins): accept clawhub uninstall specs 2026-03-23 13:52:27 -07:00
betoblair
1c7e98de16 docs(feishu): replace botName with name in config examples (#52753)
Merged via squash.

Prepared head SHA: 5237726c9c
Co-authored-by: haroldfabla2-hue <229189334+haroldfabla2-hue@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-23 23:48:33 +03:00
Vincent Koc
34c57487b4 fix(subagents): recheck timed-out announce waits (#53127)
Recheck timed-out subagent announce waits against the latest runtime snapshot before announcing timeout, and keep that recheck best-effort so transient gateway failures do not suppress the announcement.

Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
2026-03-23 15:36:49 -05:00
Onur Solmaz
bc95436a43 release: upload macos preflight artifacts (#53105)
* release: upload macos preflight artifacts

* release: speed up macos preflight

* release: use xlarge macos runner

* release: skip dmg path in macos preflight
2026-03-23 21:34:51 +01:00
Peter Steinberger
f698774324 build: finalize 2026.3.23 release 2026-03-23 13:08:33 -07:00
Nimrod Gutman
b98f3634c4 fix(memory): bootstrap lancedb runtime on demand (#53111)
Bootstrap LanceDB into plugin runtime state on first use for packaged/global installs, keep @lancedb/lancedb plugin-local, and add regression coverage for bundled, cached, retry, and Nix fail-fast runtime paths.

Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
2026-03-23 15:07:24 -05:00
Vincent Koc
fd5496d1d3 docs(changelog): note skill secretref runtime fix 2026-03-23 13:05:32 -07:00
Vincent Koc
13e81870bb fix(agents): prefer runtime snapshot for skill secrets 2026-03-23 13:04:12 -07:00
Peter Steinberger
6c58277577 build: prepare 2026.3.23 2026-03-23 12:58:00 -07:00
Val Alexander
3e2b3bd2c5 Fix Control UI operator.read scope handling (#53110)
Preserve Control UI scopes through the device-auth bypass path, normalize implied operator device-auth scopes, ignore cached under-scoped operator tokens, and degrade read-backed main pages gracefully when a connection truly lacks operator.read.

Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
2026-03-23 14:57:21 -05:00
Tak Hoffman
99c84294f3 ci: shard bun test lane 2026-03-23 14:51:47 -05:00
Onur Solmaz
01c1fc797f release: harden preflight workflows (#53087)
* release: harden preflight-only workflows

* release: require main for publish runs

* release: select xcode for macos workflow

* release: retry flaky macos preflight steps
2026-03-23 20:30:46 +01:00
Tak Hoffman
8670f2cead fix(openai-codex): bootstrap proxy on oauth refresh (openclaw#53078)
Verified:
- pnpm install --frozen-lockfile
- pnpm exec vitest run extensions/openai/openai-codex-provider.runtime.test.ts extensions/openai/openai-provider.test.ts
2026-03-23 14:08:04 -05:00
Vincent Koc
cdd797f943 docs(changelog): credit web search runtime fix 2026-03-23 12:01:17 -07:00
Peter Steinberger
e599c66277 test: harden canvas host undici isolation 2026-03-23 11:54:49 -07:00
Peter Steinberger
870b0d216a build: prepare 2026.3.23-beta.1 2026-03-23 11:54:49 -07:00
Vincent Koc
8fa91d283b fix(cli): preserve posix default git dir 2026-03-23 11:49:55 -07:00
Val Alexander
7a459045de fix packaged control ui asset lookup (#53081) 2026-03-23 13:45:04 -05:00
Peter Steinberger
323ae73b30 fix: bound tracked debounce key accounting 2026-03-23 11:33:06 -07:00
scoootscooob
708cccd43b docs(changelog): add missing recent fixes 2026-03-23 11:26:26 -07:00
scoootscooob
cdc8bac466 fix(discord): reply on native command auth failures (#53072) 2026-03-23 11:20:58 -07:00
Peter Steinberger
1b69d9ee1a fix: preserve debounce and followup ordering (#52998) (thanks @osolmaz) 2026-03-23 11:11:06 -07:00
Vincent Koc
9b7cfd0bea fix(inbound): bound tracked debounce keys 2026-03-23 11:11:06 -07:00
Vincent Koc
02e07a157d fix(reply): clear idle followup callbacks 2026-03-23 11:11:06 -07:00
Vincent Koc
3de42e946a fix(reply): preserve no-debounce inbound concurrency 2026-03-23 11:11:06 -07:00
Vincent Koc
7bc8e67d2a Update CHANGELOG.md 2026-03-23 11:11:06 -07:00
Vincent Koc
a35dcf608e fix(reply): refresh followup drain callbacks 2026-03-23 11:11:06 -07:00
Onur Solmaz
bcaadc39ea Telegram: fix fire-and-forget debounce order 2026-03-23 11:11:06 -07:00
Onur Solmaz
9a34a602bd Telegram: preserve inbound debounce order 2026-03-23 11:11:06 -07:00
Vincent Koc
b15462ebaf Revert "fix(plugins): enable bundled Brave web search plugin by default (#52072)"
This reverts commit 0ea3c4d5d8.
2026-03-23 11:04:02 -07:00
Peter Steinberger
80bd5ba728 fix(release): fail empty control ui tarballs 2026-03-23 11:03:14 -07:00
Quinn H.
0ea3c4d5d8 fix(plugins): enable bundled Brave web search plugin by default (#52072)
Brave is a bundled web search plugin but was missing from
BUNDLED_ENABLED_BY_DEFAULT, causing it to be filtered out during
provider resolution. This made web_search unavailable even when
plugins.entries.brave.enabled was configured.

Fixes #51937

Co-authored-by: Ubuntu <ubuntu@ip-172-26-10-234.us-west-2.compute.internal>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-23 11:01:02 -07:00
Vincent Koc
dd586d59ed fix(mistral): repair max-token defaults and doctor migration (#53054)
* fix(mistral): repair max-token defaults and doctor migration

* fix(mistral): add missing small-model repair cap
2026-03-23 10:57:56 -07:00
Peter Steinberger
ffb287e1de fix: harden update dev switch and refresh changelog 2026-03-23 10:56:35 -07:00
Nimrod Gutman
848414d7f2 docs(changelog): add channel catalog override note (#52988) (#53059) 2026-03-23 19:52:32 +02:00
TheRipper
36d6ba55e3 Release: fix npm release preflight under pnpm (#52985)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-23 10:51:09 -07:00
Peter Steinberger
f9a7427e8e test: refresh thread-safe agent fixtures 2026-03-23 10:47:02 -07:00
Vincent Koc
2592eb0796 fix(gateway): guard openrouter auto pricing recursion (#53055) 2026-03-23 10:43:01 -07:00
Vincent Koc
3fe2f0a550 docs: fix CLI command tree, SDK import path, and tool group listing
- Remove non-existent 'secrets migrate' from CLI command tree
- Add actual secrets subcommands: audit, configure, apply
- Add missing plugin subcommands: inspect, uninstall, update, marketplace list
- Fix plugins info -> inspect (actual command name)
- Add message send and broadcast subcommands to command tree
- Remove misleading deprecated import from sdk-overview
- Add sessions_yield and subagents to group:sessions tool group docs
- Fix formatting
2026-03-23 10:40:41 -07:00
Jamil Zakirov
921a147196 Agents: fix runtime web_search provider selection (#53020)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-23 10:30:45 -07:00
scoootscooob
df18063260 fix(clawhub): preserve XDG auth path on macOS 2026-03-23 10:21:28 -07:00
Vincent Koc
d1c95c84b5 docs: fix nav ordering, missing pages, and stale model references
- Sort providers alphabetically in docs.json nav
- Sort channels alphabetically in docs.json nav (slack before synology-chat)
- Add install/migrating-matrix to Maintenance nav section (was orphaned)
- Remove zh-CN/plugins/architecture from nav (file does not exist)
- Add Voice Call to channels index page
- Add missing providers to providers index (DeepSeek, GitHub Copilot, OpenCode Go, Synthetic)
- Sort providers index alphabetically
- Update stale claude-3-5-sonnet model reference to claude-sonnet-4-6 in webhook docs
2026-03-23 10:20:15 -07:00
scoootscooob
5b09463bc3 fix(clawhub): honor macOS auth config path (#53034) 2026-03-23 10:14:23 -07:00
Vincent Koc
b4dd600b37 fix(browser): reuse running loopback browser after probe miss 2026-03-23 10:05:26 -07:00
Vincent Koc
a1df10caac fix(config): ignore stale plugin allow entries 2026-03-23 09:57:46 -07:00
Luke
d98e3a1ea9 Tests: fix fresh-main regressions (#53011)
* Tests: fix fresh-main regressions

* Tests: avoid chat notice cache priming

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-23 09:54:50 -07:00
Vincent Koc
c55d4f63eb fix(release): raise npm pack size budget 2026-03-23 09:54:28 -07:00
Vincent Koc
071de383ff fix(clawhub): resolve auth token for skill browsing (#53017)
* fix(clawhub): resolve auth token for skill browsing

* docs(changelog): note clawhub skill auth fix
2026-03-23 09:53:40 -07:00
Vincent Koc
d5dc6b6573 fix(gateway): require auth for canvas routes 2026-03-23 09:31:40 -07:00
Vincent Koc
b0ce53a79c fix(voice-call): stabilize plivo v2 replay keys 2026-03-23 09:31:40 -07:00
Vincent Koc
50f6a2f136 fix(gateway): require admin for agent session reset 2026-03-23 09:31:39 -07:00
Nimrod Gutman
041c47419f fix(channels): preserve external catalog overrides (#52988)
* fix(channels): preserve external catalog overrides

* fix(channels): clarify catalog precedence

* fix(channels): respect overridden install specs
2026-03-23 18:08:17 +02:00
Vincent Koc
29ad211e76 fix(plugins): unblock Discord/Slack message tool sends and Feishu media (#52991)
* fix(plugins): unblock Discord and Slack message tool payloads

* docs(changelog): note Discord Slack and Feishu message fixes
2026-03-23 09:04:57 -07:00
Vincent Koc
83e86482c6 docs(changelog): note release and matrix fixes 2026-03-23 08:51:46 -07:00
Vincent Koc
93df5f613e fix(gateway): avoid probe false negatives after connect 2026-03-23 08:49:31 -07:00
Vincent Koc
1e5f38a1a8 fix(matrix): avoid duplicate runtime api exports 2026-03-23 08:46:58 -07:00
Vincent Koc
beadd4c553 fix(gateway): harden supervised lock and browser attach readiness 2026-03-23 08:42:00 -07:00
Nimrod Gutman
b84a130788 fix(release): preserve shipped channel surfaces in npm tar (#52913)
* fix(channels): ship official channel catalog (#52838)

* fix(release): keep shipped bundles in npm tar (#52838)

* build(release): fix rebased release-check helpers (#52838)
2026-03-23 17:39:22 +02:00
Vincent Koc
7299b42e2a fix(config): keep built-in auto-enable idempotent 2026-03-23 08:34:37 -07:00
Vincent Koc
2615402a2b Update CHANGELOG.md 2026-03-23 08:29:02 -07:00
Vincent Koc
e68cbea5b4 fix(config): keep built-in channels out of plugin allowlists (#52964)
* fix(config): keep built-in channels out of plugin allowlists

* docs(changelog): note doctor whatsapp allowlist fix

* docs(changelog): move doctor whatsapp fix to top
2026-03-23 08:26:51 -07:00
Vincent Koc
70b235f312 fix(release): ship bundled plugins in pack artifacts 2026-03-23 08:22:00 -07:00
Vincent Koc
31675d65d4 fix(agents): preserve anthropic thinking block order (#52961) 2026-03-23 08:18:15 -07:00
Tak Hoffman
6872e079e6 Improve PR template regression prompts 2026-03-23 10:12:59 -05:00
Vincent Koc
8a8ed34716 Update CHANGELOG.md 2026-03-23 08:10:57 -07:00
Onur Solmaz
8ed33c2aff release: automate macOS publishing (#52853)
* release: automate macOS publishing

* release: keep mac appcast in openclaw repo

* release: add preflight-only release workflow runs

* release: keep appcast updates manual

* release: generate signed appcast as workflow artifact

* release: require preflight before publish

* release: require mac app for every release

* docs: clarify every release ships mac app

* release: document Sparkle feed and SHA rules

* release: keep publish flow tag-based

* release: stabilize mac appcast flow

* release: document local mac fallback
2026-03-23 16:04:53 +01:00
Vincent Koc
e9078b3ff6 fix(msteams): isolate probe test env credentials 2026-03-23 08:00:54 -07:00
Sathvik Veerapaneni
d2e8ed3632 fix: keep session transcript pointers fresh after compaction (#50688)
Co-authored-by: Frank Yang <frank.ekn@gmail.com>
2026-03-23 22:58:07 +08:00
Robin Waslander
dd132ea77b release: add changelog for control UI tarball check 2026-03-23 15:40:36 +01:00
evann
c278e8d7a8 release-check: include stderr/stdout when npm pack fails 2026-03-23 15:40:35 +01:00
evann
fcafd8d4de release: verify control-ui assets are included in npm tarball 2026-03-23 15:40:34 +01:00
Peter Steinberger
b2bf508dc5 docs: refresh generated config baseline 2026-03-23 14:38:57 +00:00
Peter Steinberger
05e31eb9ff test: stabilize channel ci gate 2026-03-23 14:32:10 +00:00
Peter Steinberger
3725b38335 fix: restore ci gates 2026-03-23 14:32:10 +00:00
wangchunyue
4e849ac127 fix: ensure env proxy dispatcher before MiniMax and OpenAI Codex OAuth flows (openclaw#52228)
Verified:
- pnpm install --frozen-lockfile
- NPM_CONFIG_CACHE=/tmp/openclaw-npm-cache-52228 pnpm build
- pnpm check
- pnpm test:macmini (failed on inherited pre-existing plugin contract test: src/plugins/contracts/registry.contract.test.ts missing deepseek in bundled provider contract registry outside this PR surface)

Co-authored-by: openperf <80630709+openperf@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-23 09:26:34 -05:00
Peter Steinberger
8fdb9194d2 build: sync lockfile for mattermost plugin 2026-03-23 12:59:15 +00:00
Peter Steinberger
b393effba6 test: harden channel suite isolation 2026-03-23 12:57:43 +00:00
Peter Steinberger
949d6be1d1 test: fix status plugin pagination expectation 2026-03-23 05:51:19 -07:00
Peter Steinberger
383c61e305 test: stabilize gateway thread harness 2026-03-23 05:44:34 -07:00
Onur Solmaz
cd7d49b48e CI: remove npm release preview workflow (#52825)
* CI: remove npm release preview workflow

* Docs: align release maintainer skill with manual publish

* Docs: expand release maintainer skill flow
2026-03-23 13:35:57 +01:00
Peter Steinberger
41850c3880 refactor: isolate attempt context engine thread helpers 2026-03-23 05:27:44 -07:00
Peter Steinberger
76dc854963 test: remove replaced spawn-workspace monolith 2026-03-23 05:21:59 -07:00
Peter Steinberger
4e661d5c4b test: split attempt spawn-workspace thread fixtures 2026-03-23 05:21:59 -07:00
Peter Steinberger
08b5ba1a12 test: reset line webhook mocks between cases 2026-03-23 12:13:11 +00:00
Ayaan Zaidi
69b9e44762 fix: declare typebox runtime dep for mattermost plugin 2026-03-23 17:39:30 +05:30
Peter Steinberger
9f2330363e fix: refactor deepseek bundled plugin (#48762) (thanks @07akioni) 2026-03-23 05:06:07 -07:00
Bakhtier Sizhaev
a0cb443aa3 fix: document Telegram asDocument alias (#52461) (thanks @bakhtiersizhaev)
* feat(telegram): add asDocument param to message tool

Adds `asDocument` as a user-facing alias for the existing `forceDocument`
parameter in the message tool. When set to `true`, media files (images,
videos, GIFs) are sent via `sendDocument` instead of `sendPhoto`/
`sendVideo`/`sendAnimation`, preserving the original file quality
without Telegram compression.

This is useful when agents need to deliver high-resolution images or
uncompressed files to users via Telegram.

`asDocument` is intentionally an alias rather than a replacement — the
existing `forceDocument` continues to work unchanged.

Changes:
- src/agents/tools/message-tool.ts: add asDocument to send schema
- src/agents/tools/telegram-actions.ts: OR asDocument into forceDocument
- src/infra/outbound/message-action-runner.ts: same OR logic for outbound path
- extensions/telegram/src/channel-actions.ts: read and forward asDocument
- src/channels/plugins/actions/actions.test.ts: add test case

* fix: restore channel-actions.ts to main version (rebase conflict fix)

* fix(test): match asDocument test payload to actual params structure

* fix(telegram): preserve forceDocument alias semantics

* fix: document Telegram asDocument alias (#52461) (thanks @bakhtiersizhaev)

---------

Co-authored-by: Бахтиер Сижаев <bkh@MacBook-Air.local>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-23 17:32:46 +05:30
Peter Steinberger
47db5abece test: inject thread-safe base seams 2026-03-23 04:59:15 -07:00
Peter Steinberger
8fd2fa13c6 test: avoid repo-root perf profile artifacts 2026-03-23 11:51:26 +00:00
Peter Steinberger
f98811a67c fix: harden parallels smoke agent invocation 2026-03-23 04:51:07 -07:00
Peter Steinberger
b3844d920a test: decouple vitest config checks from ambient env 2026-03-23 11:48:19 +00:00
Peter Steinberger
e84ca730a3 test: stabilize e2e module isolation 2026-03-23 11:46:54 +00:00
Peter Steinberger
4f92eaad37 test: inject image-tool provider deps for raw threads 2026-03-23 04:40:58 -07:00
Peter Steinberger
a6c7ad84ba build: prepare 2026.3.23 2026-03-23 04:40:13 -07:00
Peter Steinberger
6a3d4f9fad test: isolate pi model and reset-model thread fixtures 2026-03-23 04:36:06 -07:00
Peter Steinberger
6bcd9a801a test: inject thread-safe gateway and ACP seams 2026-03-23 04:34:42 -07:00
Peter Steinberger
d841d02439 ci: stabilize windows and bun unit lanes 2026-03-23 11:28:25 +00:00
Peter Steinberger
adf63eba9f test: raise timeout for slow provider auth normalization 2026-03-23 11:19:26 +00:00
Peter Steinberger
f182c3a292 test: inject thread-safe deps for agent tools 2026-03-23 04:16:53 -07:00
Peter Steinberger
fc9739313c test: harden channel suite isolation 2026-03-23 11:09:12 +00:00
Peter Steinberger
e7d11f6c33 build: prepare 2026.3.22 2026-03-23 04:08:15 -07:00
Peter Steinberger
6f048f59cb test: stabilize trigger handling and hook e2e tests 2026-03-23 11:06:24 +00:00
Peter Steinberger
b9efba1faf test: target gemini 3.1 flash alias 2026-03-23 10:59:12 +00:00
Peter Steinberger
4dcc39c25c test: snapshot ci timeout investigation 2026-03-23 10:44:34 +00:00
Peter Steinberger
6e012d7feb test: inject model runtime hooks for thread-safe tests 2026-03-23 03:25:42 -07:00
Peter Steinberger
2df10e81c8 test: isolate server-context browser harness imports 2026-03-23 03:23:30 -07:00
Peter Steinberger
9d3d7f9e65 fix: restart windows gateway after npm update 2026-03-23 10:20:59 +00:00
Peter Steinberger
a0ad47440a test: stabilize live provider docker probes 2026-03-23 10:16:06 +00:00
Peter Steinberger
3ff2f85bad fix: stop browser server tests from launching real chrome 2026-03-23 03:15:02 -07:00
Peter Steinberger
203eebec2f test: split pi embedded model thread fixtures 2026-03-23 02:54:10 -07:00
Peter Steinberger
399fae33ca test: add parallels npm update smoke 2026-03-23 09:47:35 +00:00
Peter Steinberger
8067ae50fa fix: restore provider runtime lazy boundary 2026-03-23 09:35:18 +00:00
Peter Steinberger
d8d545bac1 build: prepare 2026.3.22-beta.1 2026-03-23 02:34:30 -07:00
Julia Bush
e94ebfa084 fix: harden gateway SIGTERM shutdown (#51242) (thanks @juliabush)
* fix: increase shutdown timeout to avoid SIGTERM hang

* fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang

* fix(gateway): enforce hard exit on shutdown timeout for SIGTERM

* fix: tighten gateway shutdown watchdog

* fix: harden gateway SIGTERM shutdown (#51242) (thanks @juliabush)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-23 15:01:42 +05:30
Penchan
95fec668a0 fix: preserve Telegram reply context text (#50500) (thanks @p3nchan)
* fix: guard Telegram reply context text (#50500) (thanks @p3nchan)

* fix: preserve Telegram reply caption fallback (#50500) (thanks @p3nchan)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-23 14:54:39 +05:30
Peter Steinberger
75b65c2a35 fix: restore provider runtime lazy boundary 2026-03-23 09:24:20 +00:00
Peter Steinberger
7a92d43d9a test: isolate pi embedded model thread fixtures 2026-03-23 02:22:31 -07:00
Peter Steinberger
9516c72618 docs: sync generated release baselines 2026-03-23 09:19:05 +00:00
Matt Van Horn
988bd782f7 fix: restore Telegram topic announce delivery (#51688) (thanks @mvanhorn)
When `replyLike.text` or `replyLike.caption` is an unexpected
non-string value (edge case from some Telegram API responses),
the reply body was coerced to "[object Object]" via string
concatenation. Add a `typeof === "string"` guard to gracefully
fall back to empty string, matching the existing pattern used
for `quoteText` in the same function.

Co-authored-by: Penchan <penchan@penchan.co>
2026-03-23 14:48:46 +05:30
Peter Steinberger
7ba28d6dba fix: repair runtime seams after rebase 2026-03-23 09:14:17 +00:00
Peter Steinberger
2a06097184 test: update codex test fixtures to gpt-5.4 2026-03-23 02:14:00 -07:00
Ayaan Zaidi
8f8b79496f fix: keep message-tool buttons optional for Telegram and Mattermost (#52589) (thanks @tylerliu612) 2026-03-23 14:43:41 +05:30
liuyang
bf12835995 fix(telegram): make buttons schema optional in message tool
The Telegram plugin injects a `buttons` property into the message tool
schema via `createMessageToolButtonsSchema()`, but without wrapping it
in `Type.Optional()`. This causes TypeBox to include `buttons` in the
JSON Schema `required` array.

In isolated sessions (e.g. cron jobs) where no `currentChannel` is set,
all plugin schemas are merged into the message tool. When the LLM calls
the message tool without a `buttons` parameter, AJV validation fails
with: `buttons: must have required property 'buttons'`.

Wrap the buttons schema in `Type.Optional()` so it is not required.
2026-03-23 14:43:41 +05:30
RichardCao
a835c200f3 fix(status): recompute fallback context window (#51795)
* fix(status): recompute fallback context window

* fix(status): keep live context token caps on fallback

* fix(status): preserve fallback runtime context windows

* fix(status): preserve configured fallback context caps

* fix(status): keep provider-aware transcript context lookups

* fix(status): preserve explicit fallback context caps

* fix(status): clamp fallback configured context caps

* fix(status): keep raw runtime slash ids

* fix(status): refresh plugin-sdk api baseline

* fix(status): preserve fallback context lookup

* test(status): refresh plugin-sdk api baseline

* fix(status): keep runtime slash-id context lookup

---------

Co-authored-by: create <create@createdeMacBook-Pro.local>
Co-authored-by: Frank Yang <frank.ekn@gmail.com>
Co-authored-by: RichardCao <RichardCao@users.noreply.github.com>
2026-03-23 17:08:48 +08:00
Frank Yang
b186d9847c fix(memory-core): register memory tools independently to prevent coupled failure (#52668)
Merged via admin squash because current required CI failures are inherited from base and match latest `main` failures outside this PR's `memory-core` surface.

Prepared head SHA: df7f968581
Co-authored-by: artwalker <44759507+artwalker@users.noreply.github.com>
Reviewed-by: @frankekn
2026-03-23 17:05:37 +08:00
Peter Steinberger
a381e0d115 style: format image-generation runtime tests 2026-03-23 09:04:36 +00:00
Peter Steinberger
a4367eb656 test: finish no-isolate suite hardening 2026-03-23 02:02:03 -07:00
Peter Steinberger
4ea014d581 fix: align websocket stream fallback types 2026-03-23 08:59:16 +00:00
Peter Steinberger
fb602c9b02 test: harden ci isolated mocks 2026-03-23 08:53:35 +00:00
Peter Steinberger
aa02b86a9e docs: clarify unreleased breaking changes 2026-03-23 01:47:38 -07:00
Peter Steinberger
d44a399ae0 fix: alphabetize web search provider listings 2026-03-23 01:46:47 -07:00
Vincent Koc
5637f9b516 fix(changelog): note windows media path guardrails (#52738) 2026-03-23 01:41:53 -07:00
Peter Steinberger
c4420c0324 docs: reorder unreleased changelog 2026-03-23 01:35:20 -07:00
Peter Steinberger
7cee097df9 test: harden no-isolate mocked module resets 2026-03-23 01:33:47 -07:00
Peter Steinberger
abf2157b18 fix: sync agent and autoreply e2e updates 2026-03-23 01:33:40 -07:00
Peter Steinberger
f64f3fdb53 test: isolate base vitest thread blockers 2026-03-23 01:26:31 -07:00
Vincent Koc
8b02ef1332 fix(android): gate canvas bridge to trusted pages (#52722)
* fix(android): gate canvas bridge to trusted pages

* fix(changelog): note android canvas bridge gating

* Update apps/android/app/src/main/java/ai/openclaw/app/node/CanvasActionTrust.kt

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(android): snapshot canvas URL on UI thread

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-23 01:24:51 -07:00
Peter Steinberger
94f397bc5f test: stop leaking image workspace temp dirs 2026-03-23 01:23:10 -07:00
Onur Solmaz
fe459c9084 ACPX: align pinned runtime version (#52730)
* ACPX: align pinned runtime version

* ACPX: drop version example from help text
2026-03-23 09:21:57 +01:00
Vincent Koc
6c60a3773a chore(plugins): remove opik investigation checkpoints 2026-03-23 01:21:19 -07:00
Peter Steinberger
db5369f5f9 test: harden isolated test mocks 2026-03-23 01:13:08 -07:00
Peter Steinberger
a60672b708 test: fix channel summary registry setup 2026-03-23 08:07:44 +00:00
Vincent Koc
d22279d2e8 fix(plugins): preserve live hook registry during gateway runs 2026-03-23 01:05:13 -07:00
Peter Steinberger
9105b3723d test: harden no-isolate test module resets 2026-03-23 01:02:16 -07:00
Peter Steinberger
771a78cc77 test: stabilize ci test harnesses 2026-03-23 07:58:34 +00:00
Peter Steinberger
dc90d3b1d3 refactor(media): share local file access guards 2026-03-23 00:58:23 -07:00
Peter Steinberger
eac93507c3 fix(browser): enforce node browser proxy allowProfiles 2026-03-23 00:56:44 -07:00
Peter Steinberger
3fd5d13315 test: fix ci docs drift and bun qr exit handling 2026-03-23 07:41:23 +00:00
Peter Steinberger
ff54c02b7d test: narrow live transcript scaffolding strip 2026-03-23 07:41:18 +00:00
Peter Steinberger
fe5819887b refactor(gateway): centralize discovery target handling 2026-03-23 00:38:31 -07:00
Peter Steinberger
9fbb840c79 docs(changelog): add Windows media security fix 2026-03-23 00:37:45 -07:00
Peter Steinberger
5f05c92922 test: harden no-isolate reply teardown 2026-03-23 00:37:32 -07:00
Peter Steinberger
93880717f1 fix(media): harden secondary local path seams 2026-03-23 00:29:46 -07:00
Peter Steinberger
4fd7feb0fd fix(media): block remote-host file URLs in loaders 2026-03-23 00:29:46 -07:00
Peter Steinberger
abbd1b6b8a feat: add slash plugin installs 2026-03-23 00:29:20 -07:00
Peter Steinberger
deecf68b59 fix(gateway): fail closed on unresolved discovery endpoints 2026-03-23 00:27:37 -07:00
Peter Steinberger
0b58829364 test: fix ci env-sensitive assertions 2026-03-23 07:27:03 +00:00
Vincent Koc
83e715cdaa fix(plugin-sdk): normalize hashed diagnostic event exports 2026-03-23 00:24:47 -07:00
Vincent Koc
b23e9c577d fix(plugin-sdk): resolve hashed diagnostic events chunks 2026-03-23 00:18:19 -07:00
Peter Steinberger
04c69ea3a0 refactor: reuse canonical setup bootstrap profile 2026-03-23 00:15:55 -07:00
Peter Steinberger
6686f1cb2c refactor: centralize bootstrap profile handling 2026-03-23 00:15:55 -07:00
scoootscooob
43557668d2 Infra: support shell carrier allow-always approvals 2026-03-23 00:14:58 -07:00
Peter Steinberger
fe3663a9fe refactor: remove embedded runner cwd mutation 2026-03-23 00:11:55 -07:00
Peter Steinberger
65405edaab docs: reorder unreleased changelog by user impact 2026-03-23 00:11:08 -07:00
Peter Steinberger
ddf823036b fix: harden Windows Parallels smoke installs 2026-03-23 07:07:50 +00:00
Peter Steinberger
4a26f10f68 docs: sync minimax m2.7 references 2026-03-23 00:02:35 -07:00
Peter Steinberger
80cd8cd6be refactor: unify minimax model and failover live policies 2026-03-23 00:02:35 -07:00
Peter Steinberger
ebc2b711ea docs(synology-chat): clarify multi-account webhook paths 2026-03-23 00:01:29 -07:00
Peter Steinberger
937f78b69f refactor(synology-chat): type startup webhook path policy 2026-03-23 00:01:07 -07:00
Peter Steinberger
535263572e fix(tlon): unify settings reconciliation semantics 2026-03-22 23:59:04 -07:00
Peter Steinberger
a600c72ed7 fix: bind bootstrap setup codes to node profile 2026-03-22 23:57:15 -07:00
scoootscooob
4580d585ff Gateway: resolve fallback plugin context lazily 2026-03-22 23:52:21 -07:00
scoootscooob
52b92f2973 Test: isolate qr dashboard integration suite 2026-03-22 23:52:21 -07:00
Peter Steinberger
47186c50a2 fix(ci): restore stale guardrails and baselines 2026-03-23 06:50:23 +00:00
ruochen
b62fed0ea7 Docs: align MiniMax examples with M2.7 2026-03-22 23:44:11 -07:00
Peter Steinberger
202b588db5 fix: harden plugin docker e2e 2026-03-22 23:42:34 -07:00
Peter Steinberger
d2a1b24b83 test: honor env auth in gateway live probes 2026-03-23 06:42:09 +00:00
Peter Steinberger
ed614938d7 test(voice-call): accept oversize webhook socket resets 2026-03-23 06:37:57 +00:00
Peter Steinberger
37c2166f52 test: finish base vitest thread fixture fixes 2026-03-22 23:37:31 -07:00
Peter Steinberger
3fac0d11fa test: fix base vitest thread regressions 2026-03-22 23:37:31 -07:00
Peter Steinberger
2349693924 docs: credit nexrin in synology changelog 2026-03-22 23:36:30 -07:00
Peter Steinberger
980940aa58 fix(synology-chat): fail closed shared webhook paths 2026-03-22 23:34:21 -07:00
Peter Steinberger
651dc7450b fix(voice-call): harden webhook pre-auth guards 2026-03-22 23:33:30 -07:00
Peter Steinberger
2467fa4c5b fix: normalize scoped vitest filter paths 2026-03-22 23:32:22 -07:00
Peter Steinberger
dda347eda3 refactor: dedupe synology config schema 2026-03-22 23:32:22 -07:00
Peter Steinberger
c42cb1ca66 refactor: audit synology dangerous name matching 2026-03-22 23:32:22 -07:00
Peter Steinberger
fb6454c543 refactor: narrow synology legacy name lookup 2026-03-22 23:32:21 -07:00
Peter Steinberger
677a821a2f refactor: centralize synology dangerous name matching 2026-03-22 23:32:21 -07:00
Peter Steinberger
ea800dd4ef refactor: clarify synology delivery identity names 2026-03-22 23:32:21 -07:00
scoootscooob
6c1ea41472 Docs: refresh config baseline for Synology Chat 2026-03-22 23:32:03 -07:00
Peter Steinberger
f3de580ca1 test: trim docker live auth mounts 2026-03-23 06:31:41 +00:00
Peter Steinberger
957fff443f fix: include .npmrc in onboard docker build 2026-03-23 06:27:58 +00:00
Peter Steinberger
cef7d14861 refactor(exec): rename wrapper plans for trust semantics 2026-03-22 23:18:54 -07:00
Peter Steinberger
0b40ec38ab refactor(exec): share wrapper trust planning 2026-03-22 23:18:54 -07:00
Peter Steinberger
6ba5595004 refactor(exec): make dispatch wrapper semantics spec-driven 2026-03-22 23:18:54 -07:00
Peter Steinberger
c041f8587b refactor(exec): split wrapper resolution modules 2026-03-22 23:18:54 -07:00
Peter Steinberger
97abc6db55 docs: clarify sessions_spawn ACP vs subagent policies 2026-03-22 23:08:06 -07:00
Peter Steinberger
7ade3553b7 fix: gate synology chat reply name matching 2026-03-22 23:06:38 -07:00
Peter Steinberger
55ad5d7bd7 fix(security): harden explicit-proxy SSRF pinning 2026-03-22 23:05:42 -07:00
Peter Steinberger
f52eb934d6 fix(security): unify dispatch wrapper approval hardening 2026-03-22 23:01:49 -07:00
Peter Steinberger
72e58ca260 test(models): refresh example model fixtures 2026-03-22 23:00:18 -07:00
Peter Steinberger
4d50084c6e fix(exec): escape invisible approval filler chars 2026-03-22 22:52:14 -07:00
Peter Steinberger
78175aeb0a docs(agents): prefer current test model examples 2026-03-22 22:48:30 -07:00
Vincent Koc
e1d4c38cee fix(runtime): skip peer resolution for bundled plugin deps 2026-03-22 22:47:46 -07:00
Peter Steinberger
c036e4d176 fix: restrict remote marketplace plugin sources 2026-03-22 22:47:08 -07:00
Peter Steinberger
09faed6bd8 fix(gateway): gate internal command persistence mutations 2026-03-22 22:46:49 -07:00
Peter Steinberger
81445a9010 fix(media): bound remote error-body snippet reads 2026-03-22 22:43:42 -07:00
Vincent Koc
fd5555d5be fix(runtime): make dist-runtime staging idempotent 2026-03-22 22:41:27 -07:00
Peter Steinberger
5822892fee docs: refresh plugin-sdk api baseline 2026-03-23 05:35:51 +00:00
Peter Steinberger
c82fc9a0fd docs(changelog): note time exec approval fix 2026-03-22 22:32:02 -07:00
Peter Steinberger
a55f371cc5 fix(ci): skip docs-only preflight pnpm audit 2026-03-23 05:29:27 +00:00
Vincent Koc
be3a2e2eb6 fix(plugin-sdk): fall back to src root alias files 2026-03-22 22:26:18 -07:00
Peter Steinberger
39409b6a6d fix(security): unwrap time dispatch wrappers 2026-03-22 22:25:57 -07:00
Peter Steinberger
af9de86286 perf: trim vitest hot imports and refresh manifests 2026-03-23 05:25:05 +00:00
Peter Steinberger
7fcbf383d8 fix(ci): write dist build stamp after builds 2026-03-22 22:23:25 -07:00
Peter Steinberger
ea579ef858 fix(gateway): preserve async hook ingress provenance 2026-03-22 22:21:49 -07:00
Peter Steinberger
c5a941a506 refactor!: remove moltbot state-dir migration fallback 2026-03-22 22:19:35 -07:00
Peter Steinberger
6b9915a106 refactor!: drop legacy CLAWDBOT env compatibility 2026-03-22 22:13:39 -07:00
oliviareid-svg
5ff60cc39f fix(build): add stable memory-cli dist entry (#51759)
Co-authored-by: oliviareid-svg <269669958+oliviareid-svg@users.noreply.github.com>
Co-authored-by: Frank <vibespecs@gmail.com>
2026-03-23 13:11:06 +08:00
Vincent Koc
91f2c92960 docs(tools): soften DDG wording (scrapes -> pulls/gathers) 2026-03-22 22:10:56 -07:00
Rick_Xu
2fe1ff8ea8 Usage: include reset and deleted session archives (#43215)
Merged via squash.

Prepared head SHA: 49ed6c2fa3
Co-authored-by: rcrick <23069968+rcrick@users.noreply.github.com>
Co-authored-by: frankekn <4488090+frankekn@users.noreply.github.com>
Reviewed-by: @frankekn
2026-03-23 13:10:26 +08:00
Peter Steinberger
30ed4342b3 fix(agents): deny local MEDIA paths for MCP results 2026-03-22 22:10:13 -07:00
Vincent Koc
5730865f08 docs(tools): update DuckDuckGo Search for landed plugin code
- Mark as experimental (not just unofficial)
- Add region and safeSearch tool parameters (from DDG schema)
- Add plugin config example for region/safeSearch defaults
- Document auto-detection order (100 = last)
- Note SafeSearch defaults to moderate
- Verified against extensions/duckduckgo/src/
2026-03-22 22:09:47 -07:00
Vincent Koc
d43e26e399 fix(web-search): mark DuckDuckGo experimental 2026-03-22 22:07:53 -07:00
Peter Steinberger
8791aaae2b refactor: extract gateway install token helpers 2026-03-22 22:05:56 -07:00
Peter Steinberger
c15282062f refactor: split durable service env helpers 2026-03-22 22:05:56 -07:00
Vincent Koc
c6ca11e5a5 feat(web-search): add DuckDuckGo bundled plugin (#52629)
* feat(web-search): add DuckDuckGo bundled plugin

* chore(changelog): restore main changelog

* fix(web-search): harden DuckDuckGo challenge detection
2026-03-22 22:05:33 -07:00
Peter Steinberger
827c441902 fix(test): isolate flaky extension lanes 2026-03-23 05:02:07 +00:00
Peter Steinberger
dc6c22b812 fix: narrow exec exit failure kind typing 2026-03-23 04:58:46 +00:00
Kevin ONeill
dd860e76aa fix: normalize env var keys and isolate tests from real .env
- Apply normalizeEnvVarKey({ portable: true }) before security
  filtering, matching the established pattern in env-vars.ts.
  Rejects non-portable key names (spaces, special chars) that
  would produce invalid plist/systemd syntax.

- Isolate existing tests from the developer's real ~/.openclaw/.env
  by providing a temp HOME directory, preventing flaky failures
  when the test machine has a populated .env file.
2026-03-22 21:55:58 -07:00
Kevin ONeill
77ec7b4adf fix: include .env file vars in gateway service environment on install
When building the gateway install plan, read and parse
~/.openclaw/.env (or $OPENCLAW_STATE_DIR/.env) and merge those
key-value pairs into the service environment at the lowest
priority — below config env vars, auth-profile refs, and the
core service environment (HOME, PATH, OPENCLAW_*).

This ensures that user-defined secrets stored in .env (e.g.
BRAVE_API_KEY, OPENROUTER_API_KEY, DISCORD_BOT_TOKEN) are
embedded in the LaunchAgent plist (macOS), systemd unit (Linux),
and Scheduled Task (Windows) at install time, rather than
relying solely on the gateway process loading them via
dotenv.config() at startup.

Previously, on macOS the LaunchAgent plist never included .env
vars, which meant:
- launchctl print did not show user secrets (hard to debug)
- Child processes spawned before dotenv loaded had no access
- If the same key existed in both .env and the plist, the stale
  plist value won via dotenv override:false semantics

Dangerous host env vars (NODE_OPTIONS, LD_PRELOAD, etc.) are
filtered using the same security policy applied to config env
vars.

Fixes #37101
Relates to #22663
2026-03-22 21:55:58 -07:00
Vincent Koc
3afb6a2b95 fix(exec): accept runtime failure kind in formatter 2026-03-22 21:54:02 -07:00
Vincent Koc
8a59b09fc8 refactor(ci): collapse fast setup jobs into preflight 2026-03-22 21:52:19 -07:00
Peter Steinberger
97e4f37171 fix: keep status --json stdout clean (#52449) (thanks @cgdusek) 2026-03-22 21:51:08 -07:00
Charles Dusek
03c4bacbfb fix(cli): route deferred plugin logs to stderr in status --json 2026-03-22 21:51:08 -07:00
Charles Dusek
0e1da034c2 fix(cli): route plugin logs to stderr during --json output 2026-03-22 21:51:08 -07:00
Peter Steinberger
46a455d9e3 perf: enable vitest fs module cache by default 2026-03-23 04:48:31 +00:00
2926 changed files with 150312 additions and 66682 deletions

View File

@@ -16,13 +16,35 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- Pass `--json` for machine-readable summaries.
- Per-phase logs land under `/tmp/openclaw-parallels-*`.
- Do not run local and gateway agent turns in parallel on the same fresh workspace or session.
- For `prlctl exec`, pass the VM name before `--current-user` (`prlctl exec "$VM" --current-user ...`), not the other way around.
- If the workflow installs OpenClaw from a repo checkout instead of the site installer/npm release, finish by installing a real guest CLI shim and verifying it in a fresh guest shell. `pnpm openclaw ...` inside the repo is not enough for handoff parity.
- On macOS guests, prefer a user-global install plus a stable PATH-visible shim:
- install with `NPM_CONFIG_PREFIX="$HOME/.npm-global" npm install -g .`
- make sure `~/.local/bin/openclaw` exists or `~/.npm-global/bin` is on PATH
- verify from a brand-new guest shell with `which openclaw` and `openclaw --version`
## npm install then update
- Preferred entrypoint: `pnpm test:parallels:npm-update`
- Flow: fresh snapshot -> install npm package baseline -> smoke -> install current main tgz on the same guest -> smoke again.
- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh explicit `--session-id` so old session model state does not leak into the check.
- The aggregate npm-update wrapper must resolve the Linux VM with the same Ubuntu fallback policy as `parallels-linux-smoke.sh` before both fresh and update lanes. On Peter's current host, missing `Ubuntu 24.04.3 ARM64` should fall back to `Ubuntu 25.10`.
- On Windows same-guest update checks, restart the gateway after the npm upgrade before `gateway status` / `agent`; in-place global npm updates can otherwise leave stale hashed `dist/*` module imports alive in the running service.
- For Windows same-guest update checks, prefer the done-file/log-drain PowerShell runner pattern over one long-lived `prlctl exec ... powershell -EncodedCommand ...` transport. The guest can finish successfully while the outer `prlctl exec` still hangs.
- Linux same-guest update verification should also export `HOME=/root`, pass `OPENAI_API_KEY` via `prlctl exec ... /usr/bin/env`, and use `openclaw agent --local`; the fresh Linux baseline does not rely on persisted gateway credentials.
## CLI invocation footgun
- The Parallels smoke shell scripts should tolerate a literal bare `--` arg so `pnpm test:parallels:* -- --json` and similar forwarded invocations work without needing to call `bash scripts/e2e/...` directly.
## macOS flow
- Preferred entrypoint: `pnpm test:parallels:macos`
- Default to the snapshot closest to `macOS 26.3.1 latest`.
- On Peter's Tahoe VM, `fresh-latest-march-2026` can hang in `prlctl snapshot-switch`; if restore times out there, rerun with `--snapshot-hint 'macOS 26.3.1 latest'` before blaming auth or the harness.
- The macOS smoke should include a dashboard load phase after gateway health: resolve the tokenized URL with `openclaw dashboard --no-open`, verify the served HTML contains the Control UI title/root shell, then open Safari and require an established localhost TCP connection from Safari to the gateway port.
- `prlctl exec` is fine for deterministic repo commands, but use the guest Terminal or `prlctl enter` when installer parity or shell-sensitive behavior matters.
- Multi-word `openclaw agent --message ...` checks should go through a guest shell wrapper (`guest_current_user_sh` / `guest_current_user_cli` or `/bin/sh -lc ...`), not raw `prlctl exec ... node openclaw.mjs ...`, or the message can be split into extra argv tokens and Commander reports `too many arguments for 'agent'`.
- On the fresh Tahoe snapshot, `brew` exists but `node` may be missing from PATH in noninteractive exec. Use `/opt/homebrew/bin/node` when needed.
- Fresh host-served tgz installs should install as guest root with `HOME=/var/root`, then run onboarding as the desktop user via `prlctl exec --current-user`.
- Root-installed tgz smoke can log plugin blocks for world-writable `extensions/*`; do not treat that as an onboarding or gateway failure unless plugin loading is the task.
@@ -34,7 +56,11 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- Always use `prlctl exec --current-user`; plain `prlctl exec` lands in `NT AUTHORITY\\SYSTEM`.
- Prefer explicit `npm.cmd` and `openclaw.cmd`.
- Use PowerShell only as the transport with `-ExecutionPolicy Bypass`, then call the `.cmd` shims from inside it.
- Multi-word `openclaw agent --message ...` checks should call `& $openclaw ...` inside PowerShell, not `Start-Process ... -ArgumentList` against `openclaw.cmd`, or Commander can see split argv and throw `too many arguments for 'agent'`.
- Windows installer/tgz phases now retry once after guest-ready recheck; keep new Windows smoke steps idempotent so a transport-flake retry is safe.
- Windows global `npm install -g` phases can stay quiet for a minute or more even when healthy; inspect the phase log before calling it hung, and only treat it as a regression once the retry wrapper or timeout trips.
- Keep onboarding and status output ASCII-clean in logs; fancy punctuation becomes mojibake in current capture paths.
- If you hit an older run with `rc=255` plus an empty `fresh.install-main.log` or `upgrade.install-main.log`, treat it as a likely `prlctl exec` transport drop after guest start-up, not immediate proof of an npm/package failure.
## Linux flow

View File

@@ -11,7 +11,9 @@ Use this skill for release and publish-time workflow. Keep ordinary development
- Do not change version numbers without explicit operator approval.
- Ask permission before any npm publish or release step.
- Use the private maintainer release docs for the actual runbook and `docs/reference/RELEASING.md` for public policy.
- This skill should be sufficient to drive the normal release flow end-to-end.
- Use the private maintainer release docs for credentials, recovery steps, and mac signing/notary specifics, and use `docs/reference/RELEASING.md` for public policy.
- Core `openclaw` publish is manual `workflow_dispatch`; creating or pushing a tag does not publish by itself.
## Keep release channel naming aligned
@@ -31,8 +33,19 @@ Use this skill for release and publish-time workflow. Keep ordinary development
- `apps/macos/Sources/OpenClaw/Resources/Info.plist`
- `docs/install/updating.md`
- Peekaboo Xcode project and plist version fields
- Before creating a release tag, make every version location above match the version encoded by that tag.
- For fallback correction tags like `vYYYY.M.D-N`, the repo version locations still stay at `YYYY.M.D`.
- “Bump version everywhere” means all version locations above except `appcast.xml`.
- Release signing and notary credentials live outside the repo in the private maintainer docs.
- Every OpenClaw release ships the npm package and macOS app together.
- The production Sparkle feed lives at `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`, and the canonical published file is `appcast.xml` on `main` in the `openclaw` repo.
- That shared production Sparkle feed is stable-only. Beta mac releases may
upload assets to the GitHub prerelease, but they must not replace the shared
`appcast.xml` unless a separate beta feed exists.
- For fallback correction tags like `vYYYY.M.D-N`, the repo version still stays
at `YYYY.M.D`, but the mac release must use a strictly higher numeric
`APP_BUILD` / Sparkle build than the original release so existing installs
see it as newer.
## Build changelog-backed release notes
@@ -59,16 +72,166 @@ pnpm test:install:smoke
For a non-root smoke path:
```bash
OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke
OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke
```
After npm publish, run:
```bash
node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
```
- This verifies the published registry install path in a fresh temp prefix.
- For stable correction releases like `YYYY.M.D-N`, it also verifies the
upgrade path from `YYYY.M.D` to `YYYY.M.D-N` so a correction publish cannot
silently leave existing global installs on the old base stable payload.
## Check all relevant release builds
- Always validate the OpenClaw npm release path before creating the tag.
- Default release checks:
- `pnpm check`
- `pnpm build`
- `node --import tsx scripts/release-check.ts`
- `pnpm release:check`
- `OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke`
- Check all release-related build surfaces touched by the release, not only the npm package.
- Include mac release readiness in preflight by running the public validation
workflow in `openclaw/openclaw` and the real mac preflight in
`openclaw/releases-private` for every release.
- Treat the `appcast.xml` update on `main` as part of mac release readiness, not an optional follow-up.
- The workflows remain tag-based. The agent is responsible for making sure
preflight runs complete successfully before any publish run starts.
- Any fix after preflight means a new commit. Delete and recreate the tag and
matching GitHub release from the fixed commit, then rerun preflight from
scratch before publishing.
- For stable mac releases, generate the signed `appcast.xml` before uploading
public release assets so the updater feed cannot lag the published binaries.
- Serialize stable appcast-producing runs across tags so two releases do not
generate replacement `appcast.xml` files from the same stale seed.
- For stable releases, confirm the latest beta already passed the broader release workflows before cutting stable.
- If any required build, packaging step, or release workflow is red, do not say the release is ready.
## Use the right auth flow
- Core `openclaw` publish uses GitHub trusted publishing.
- Do not use `NPM_TOKEN` or the plugin OTP flow for core releases.
- OpenClaw publish uses GitHub trusted publishing.
- The publish run must be started manually with `workflow_dispatch`.
- The npm workflow and the private mac publish workflow accept
`preflight_only=true` to run validation/build/package steps without uploading
public release assets.
- The private mac workflow also accepts `smoke_test_only=true` for branch-safe
workflow smoke tests that use ad-hoc signing, skip notarization, skip shared
appcast generation, and do not prove release readiness.
- `preflight_only=true` on the npm workflow is also the right way to validate an
existing tag after publish; it should keep running the build checks even when
the npm version is already published.
- Validation-only runs may be dispatched from a branch when you are testing a
workflow change before merge.
- `.github/workflows/macos-release.yml` in `openclaw/openclaw` is now a
public validation-only handoff. It validates the tag/release state and points
operators to the private repo; it does not build or publish macOS artifacts.
- Real mac preflight and real mac publish both use
`openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml`.
- The private mac workflow runs on GitHub's xlarge macOS runner and uses a
SwiftPM cache because the Swift build/test/package path is CPU-heavy.
- Private mac preflight uploads notarized build artifacts as workflow artifacts
instead of uploading public GitHub release assets.
- Private smoke-test runs upload ad-hoc, non-notarized build artifacts as
workflow artifacts and intentionally skip stable `appcast.xml` generation.
- npm preflight, public mac validation, and private mac preflight must all pass
before any real publish run starts.
- Real publish runs must be dispatched from `main`; branch-dispatched publish
attempts should fail before the protected environment is reached.
- The release workflows stay tag-based; rely on the documented release sequence
rather than workflow-level SHA pinning.
- The `npm-release` environment must be approved by `@openclaw/openclaw-release-managers` before publish continues.
- Mac publish uses
`openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml` for
build, signing, notarization, packaged mac artifact generation, and
stable-feed `appcast.xml` artifact generation.
- Real private mac publish uploads the packaged `.zip`, `.dmg`, and
`.dSYM.zip` assets to the existing GitHub release in `openclaw/openclaw`
automatically when `OPENCLAW_PUBLIC_REPO_RELEASE_TOKEN` is present in the
private repo `mac-release` environment.
- For stable releases, the agent must also download the signed
`macos-appcast-<tag>` artifact from the successful private mac workflow and
then update `appcast.xml` on `main`.
- For beta mac releases, do not update the shared production `appcast.xml`
unless a separate beta Sparkle feed exists.
- The private repo targets a dedicated `mac-release` environment. If the GitHub
plan does not yet support required reviewers there, do not assume the
environment alone is the approval boundary; rely on private repo access and
CODEOWNERS until those settings can be enabled.
- Do not use `NPM_TOKEN` or the plugin OTP flow for OpenClaw releases.
- `@openclaw/*` plugin publishes use a separate maintainer-only flow.
- Only publish plugins that already exist on npm; bundled disk-tree-only plugins stay unpublished.
## Fallback local mac publish
- Keep the original local macOS publish workflow available as a fallback in case
CI/CD mac publishing is unavailable or broken.
- Preserve the existing maintainer workflow Peter uses: run it on a real Mac
with local signing, notary, and Sparkle credentials already configured.
- Follow the private maintainer macOS runbook for the local steps:
`scripts/package-mac-dist.sh` to build, sign, notarize, and package the app;
manual GitHub release asset upload; then `scripts/make_appcast.sh` plus the
`appcast.xml` commit to `main`.
- `scripts/package-mac-dist.sh` now fails closed for release builds if the
bundled app comes out with a debug bundle id, an empty Sparkle feed URL, or a
`CFBundleVersion` below the canonical Sparkle build floor for that short
version. For correction tags, set a higher explicit `APP_BUILD`.
- `scripts/make_appcast.sh` first uses `generate_appcast` from `PATH`, then
falls back to the SwiftPM Sparkle tool output under `apps/macos/.build`.
- For stable tags, the local fallback may update the shared production
`appcast.xml`.
- For beta tags, the local fallback still publishes the mac assets but must not
update the shared production `appcast.xml` unless a separate beta feed exists.
- Treat the local workflow as fallback only. Prefer the CI/CD publish workflow
when it is working.
- After any stable mac publish, verify all of the following before you call the
release finished:
- the GitHub release has `.zip`, `.dmg`, and `.dSYM.zip` assets
- `appcast.xml` on `main` points at the new stable zip
- the packaged app reports the expected short version and a numeric
`CFBundleVersion` at or above the canonical Sparkle build floor
## Run the release sequence
1. Confirm the operator explicitly wants to cut a release.
2. Choose the exact target version and git tag.
3. Make every repo version location match that tag before creating it.
4. Update `CHANGELOG.md` and assemble the matching GitHub release notes.
5. Run the full preflight for all relevant release builds, including mac readiness.
6. Confirm the target npm version is not already published.
7. Create and push the git tag.
8. Create or refresh the matching GitHub release.
9. Start `.github/workflows/openclaw-npm-release.yml` with `preflight_only=true`
and wait for it to pass.
10. Start `.github/workflows/macos-release.yml` in `openclaw/openclaw` and wait
for the public validation-only run to pass.
11. Start
`openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml`
with `preflight_only=true` and wait for it to pass.
12. If any preflight or validation run fails, fix the issue on a new commit,
delete the tag and matching GitHub release, recreate them from the fixed
commit, and rerun all relevant preflights from scratch before continuing.
Never reuse old preflight results after the commit changes.
13. Start `.github/workflows/openclaw-npm-release.yml` with the same tag for
the real publish.
14. Wait for `npm-release` approval from `@openclaw/openclaw-release-managers`.
15. Start
`openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml`
for the real publish and wait for success.
16. Verify the successful real private mac run uploaded the `.zip`, `.dmg`,
and `.dSYM.zip` artifacts to the existing GitHub release in
`openclaw/openclaw`.
17. For stable releases, download `macos-appcast-<tag>` from the successful
private mac run, update `appcast.xml` on `main`, and verify the feed.
18. For beta releases, publish the mac assets but expect no shared production
`appcast.xml` artifact and do not update the shared production feed unless a
separate beta feed exists.
19. After publish, verify npm and the attached release artifacts.
## GHSA advisory work
- Use `openclaw-ghsa-maintainer` for GHSA advisory inspection, patch/publish flow, private-fork validation, and GHSA API-specific publish checks.

View File

@@ -9,6 +9,8 @@ body:
value: |
Thanks for filing this report. Keep every answer concise, reproducible, and grounded in observed evidence.
Do not speculate or infer beyond the evidence. If a narrative section cannot be answered from the available evidence, respond with exactly `NOT_ENOUGH_INFO`.
If this is a plugin beta-release blocker, rename the issue title to `Beta blocker: <plugin-name> - <summary>` and apply the `beta-blocker` label after filing.
- type: dropdown
id: bug_type
attributes:
@@ -20,6 +22,19 @@ body:
- Behavior bug (incorrect output/state without crash)
validations:
required: true
- type: dropdown
id: beta_blocker
attributes:
label: Beta release blocker
description: >
Choose `Yes` only if this blocks plugin compatibility during the current beta release window.
Selecting `Yes` does not apply the label automatically. You must also rename the issue title
to `Beta blocker: <plugin-name> - <summary>` for the automation to apply the `beta-blocker` label.
options:
- "No"
- "Yes"
validations:
required: true
- type: textarea
id: summary
attributes:

View File

@@ -23,6 +23,16 @@ runs:
exit 0
fi
if ! [[ "$BASE_SHA" =~ ^[0-9a-fA-F]{7,40}$ ]]; then
echo "::error title=ensure-base-commit invalid base sha::Refusing invalid base SHA: $BASE_SHA"
exit 2
fi
if ! git check-ref-format --branch "$FETCH_REF" >/dev/null 2>&1; then
echo "::error title=ensure-base-commit invalid fetch ref::Refusing invalid fetch ref: $FETCH_REF"
exit 2
fi
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
echo "Base commit already present: $BASE_SHA"
exit 0
@@ -30,7 +40,7 @@ runs:
for deepen_by in 25 100 300; do
echo "Base commit missing; deepening $FETCH_REF by $deepen_by."
if ! git fetch --no-tags --deepen="$deepen_by" origin "$FETCH_REF"; then
if ! git fetch --no-tags --deepen="$deepen_by" origin -- "$FETCH_REF"; then
echo "::warning title=ensure-base-commit fetch failed::Failed to deepen $FETCH_REF by $deepen_by while looking for $BASE_SHA"
fi
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
@@ -40,7 +50,7 @@ runs:
done
echo "Base commit still missing; fetching full history for $FETCH_REF."
if ! git fetch --no-tags origin "$FETCH_REF"; then
if ! git fetch --no-tags origin -- "$FETCH_REF"; then
echo "::warning title=ensure-base-commit fetch failed::Failed to fetch full history for $FETCH_REF while looking for $BASE_SHA"
fi
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then

View File

@@ -1,7 +1,7 @@
name: Setup Node environment
description: >
Initialize submodules with retry, install Node 24 by default, pnpm, optionally Bun,
and optionally run pnpm install. Requires actions/checkout to run first.
Install Node 24 by default, pnpm, optionally Bun, and optionally run pnpm
install. Requires actions/checkout to run first.
inputs:
node-version:
description: Node.js version to install.
@@ -34,20 +34,6 @@ inputs:
runs:
using: composite
steps:
- name: Checkout submodules (retry)
shell: bash
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Setup Node.js
uses: actions/setup-node@v6
with:

12
.github/labeler.yml vendored
View File

@@ -221,14 +221,14 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/open-prose/**"
"extensions: qwen-portal-auth":
- changed-files:
- any-glob-to-any-file:
- "extensions/qwen-portal-auth/**"
"extensions: device-pair":
- changed-files:
- any-glob-to-any-file:
- "extensions/device-pair/**"
"extensions: duckduckgo":
- changed-files:
- any-glob-to-any-file:
- "extensions/duckduckgo/**"
"extensions: acpx":
- changed-files:
- any-glob-to-any-file:
@@ -237,6 +237,10 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/byteplus/**"
"extensions: deepseek":
- changed-files:
- any-glob-to-any-file:
- "extensions/deepseek/**"
"extensions: anthropic":
- changed-files:
- any-glob-to-any-file:

View File

@@ -2,6 +2,8 @@
Describe the problem and fix in 25 bullets:
If this PR fixes a plugin beta-release blocker, title it `fix(<plugin-id>): beta blocker - <summary>` and link the matching `Beta blocker: <plugin-name> - <summary>` issue labeled `beta-blocker`. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.
- Problem:
- Why it matters:
- What changed:
@@ -31,12 +33,50 @@ Describe the problem and fix in 25 bullets:
- Closes #
- Related #
- [ ] This PR fixes a bug or regression
## Root Cause / Regression History (if applicable)
For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write `N/A`. If the cause is unclear, write `Unknown`.
- Root cause:
- Missing detection / guardrail:
- Prior context (`git blame`, prior PR, issue, or refactor if known):
- Why this regressed now:
- If unknown, what was ruled out:
## Regression Test Plan (if applicable)
For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise write `N/A`.
- Coverage level that should have caught this:
- [ ] Unit test
- [ ] Seam / integration test
- [ ] End-to-end test
- [ ] Existing coverage already sufficient
- Target test or file:
- Scenario the test should lock in:
- Why this is the smallest reliable guardrail:
- Existing test that already covers this (if any):
- If no new test is added, why not:
## User-visible / Behavior Changes
List user-visible changes (including defaults/config).
If none, write `None`.
## Diagram (if applicable)
For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write `N/A`.
```text
Before:
[user action] -> [old state]
After:
[user action] -> [new state] -> [result]
```
## Security Impact (required)
- New permissions/capabilities? (`Yes/No`)
@@ -101,12 +141,6 @@ If a bot review conversation is addressed by this PR, resolve that conversation
- Migration needed? (`Yes/No`)
- If yes, exact upgrade steps:
## Failure Recovery (if this breaks)
- How to disable/revert this change quickly:
- Files/config to restore:
- Known bad symptoms reviewers should watch for:
## Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write `None`.

View File

@@ -11,6 +11,10 @@ on:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request_target' }}
permissions: {}
jobs:

108
.github/workflows/ci-bun.yml vendored Normal file
View File

@@ -0,0 +1,108 @@
name: CI Bun
on:
push:
branches: [main]
concurrency:
group: ci-bun-push-${{ github.run_id }}
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
preflight:
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
run_bun_checks: ${{ steps.manifest.outputs.run_bun_checks }}
bun_checks_matrix: ${{ steps.manifest.outputs.bun_checks_matrix }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
install-deps: "false"
use-sticky-disk: "false"
- name: Build Bun CI manifest
id: manifest
env:
OPENCLAW_CI_DOCS_ONLY: "false"
OPENCLAW_CI_DOCS_CHANGED: "false"
OPENCLAW_CI_RUN_NODE: "true"
OPENCLAW_CI_RUN_MACOS: "false"
OPENCLAW_CI_RUN_ANDROID: "false"
OPENCLAW_CI_RUN_WINDOWS: "false"
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false"
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "false"
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[]}'
run: node scripts/ci-write-manifest-outputs.mjs --workflow ci-bun
build-bun-artifacts:
needs: [preflight]
if: needs.preflight.outputs.run_bun_checks == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
use-sticky-disk: "false"
- name: Build A2UI bundle
run: pnpm canvas:a2ui:bundle
- name: Upload A2UI bundle artifact
uses: actions/upload-artifact@v4
with:
name: canvas-a2ui-bundle
path: src/canvas-host/a2ui/
bun-checks:
name: ${{ matrix.check_name }}
needs: [preflight, build-bun-artifacts]
if: needs.preflight.outputs.run_bun_checks == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.bun_checks_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "true"
use-sticky-disk: "false"
- name: Download A2UI bundle artifact
uses: actions/download-artifact@v8
with:
name: canvas-a2ui-bundle
path: src/canvas-host/a2ui/
- name: Run Bun test shard
env:
SHARD_COUNT: ${{ matrix.shard_count }}
SHARD_INDEX: ${{ matrix.shard_index }}
shell: bash
run: |
set -euo pipefail
OPENCLAW_TEST_ISOLATE=1 bunx vitest run --config vitest.unit.config.ts --shard "$SHARD_INDEX/$SHARD_COUNT"

View File

@@ -6,72 +6,75 @@ on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review, converted_to_draft]
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('ci-pr-{0}', github.event.pull_request.number) || format('ci-push-{0}', github.run_id) }}
group: ${{ github.event_name == 'pull_request' && format('{0}-{1}', github.workflow, github.event.pull_request.number) || format('{0}-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
# Preflight: establish the fast global truth for this revision before the
# expensive platform and test lanes fan out.
# Detect docs-only changes to skip heavy jobs (test, build, Windows, macOS, Android).
# Lint and format always run. Fail-safe: if detection fails, run everything.
docs-scope:
# Preflight: establish routing truth and planner-owned matrices once, then let
# real work fan out from a single source of truth.
preflight:
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
docs_only: ${{ steps.check.outputs.docs_only }}
docs_changed: ${{ steps.check.outputs.docs_changed }}
docs_only: ${{ steps.manifest.outputs.docs_only }}
docs_changed: ${{ steps.manifest.outputs.docs_changed }}
run_node: ${{ steps.manifest.outputs.run_node }}
run_macos: ${{ steps.manifest.outputs.run_macos }}
run_android: ${{ steps.manifest.outputs.run_android }}
run_skills_python: ${{ steps.manifest.outputs.run_skills_python }}
run_skills_python_job: ${{ steps.manifest.outputs.run_skills_python_job }}
run_windows: ${{ steps.manifest.outputs.run_windows }}
has_changed_extensions: ${{ steps.manifest.outputs.has_changed_extensions }}
changed_extensions_matrix: ${{ steps.manifest.outputs.changed_extensions_matrix }}
run_build_artifacts: ${{ steps.manifest.outputs.run_build_artifacts }}
run_release_check: ${{ steps.manifest.outputs.run_release_check }}
run_checks_fast: ${{ steps.manifest.outputs.run_checks_fast }}
checks_fast_matrix: ${{ steps.manifest.outputs.checks_fast_matrix }}
run_checks: ${{ steps.manifest.outputs.run_checks }}
checks_matrix: ${{ steps.manifest.outputs.checks_matrix }}
run_extension_fast: ${{ steps.manifest.outputs.run_extension_fast }}
extension_fast_matrix: ${{ steps.manifest.outputs.extension_fast_matrix }}
run_check: ${{ steps.manifest.outputs.run_check }}
run_check_additional: ${{ steps.manifest.outputs.run_check_additional }}
run_build_smoke: ${{ steps.manifest.outputs.run_build_smoke }}
run_check_docs: ${{ steps.manifest.outputs.run_check_docs }}
run_checks_windows: ${{ steps.manifest.outputs.run_checks_windows }}
checks_windows_matrix: ${{ steps.manifest.outputs.checks_windows_matrix }}
run_macos_node: ${{ steps.manifest.outputs.run_macos_node }}
macos_node_matrix: ${{ steps.manifest.outputs.macos_node_matrix }}
run_macos_swift: ${{ steps.manifest.outputs.run_macos_swift }}
run_android_job: ${{ steps.manifest.outputs.run_android_job }}
android_matrix: ${{ steps.manifest.outputs.android_matrix }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Ensure docs-scope base commit
- name: Ensure preflight base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect docs-only changes
id: check
id: docs_scope
uses: ./.github/actions/detect-docs-changes
# Detect which heavy areas are touched so CI can skip unrelated expensive jobs.
# Fail-safe: if detection fails, downstream jobs run.
changed-scope:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_only != 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
run_node: ${{ steps.scope.outputs.run_node }}
run_macos: ${{ steps.scope.outputs.run_macos }}
run_android: ${{ steps.scope.outputs.run_android }}
run_skills_python: ${{ steps.scope.outputs.run_skills_python }}
run_windows: ${{ steps.scope.outputs.run_windows }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
submodules: false
- name: Ensure changed-scope base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect changed scopes
id: scope
id: changed_scope
if: steps.docs_scope.outputs.docs_only != 'true'
shell: bash
run: |
set -euo pipefail
@@ -84,29 +87,8 @@ jobs:
node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD
changed-extensions:
needs: [docs-scope, changed-scope]
if: needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_node == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
has_changed_extensions: ${{ steps.changed.outputs.has_changed_extensions }}
changed_extensions_matrix: ${{ steps.changed.outputs.changed_extensions_matrix }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
submodules: false
- name: Ensure changed-extensions base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Setup Node environment
if: steps.docs_scope.outputs.docs_only != 'true'
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
@@ -114,7 +96,8 @@ jobs:
use-sticky-disk: "false"
- name: Detect changed extensions
id: changed
id: changed_extensions
if: steps.docs_scope.outputs.docs_only != 'true' && steps.changed_scope.outputs.run_node == 'true'
env:
BASE_SHA: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
BASE_REF: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
@@ -135,28 +118,59 @@ jobs:
appendFileSync(process.env.GITHUB_OUTPUT, `changed_extensions_matrix=${matrix}\n`, "utf8");
EOF
secrets:
- name: Build CI manifest
id: manifest
env:
OPENCLAW_CI_DOCS_ONLY: ${{ steps.docs_scope.outputs.docs_only }}
OPENCLAW_CI_DOCS_CHANGED: ${{ steps.docs_scope.outputs.docs_changed }}
OPENCLAW_CI_RUN_NODE: ${{ steps.changed_scope.outputs.run_node || 'false' }}
OPENCLAW_CI_RUN_MACOS: ${{ steps.changed_scope.outputs.run_macos || 'false' }}
OPENCLAW_CI_RUN_ANDROID: ${{ steps.changed_scope.outputs.run_android || 'false' }}
OPENCLAW_CI_RUN_WINDOWS: ${{ steps.changed_scope.outputs.run_windows || 'false' }}
OPENCLAW_CI_RUN_SKILLS_PYTHON: ${{ steps.changed_scope.outputs.run_skills_python || 'false' }}
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: ${{ steps.changed_extensions.outputs.has_changed_extensions || 'false' }}
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: ${{ steps.changed_extensions.outputs.changed_extensions_matrix || '{"include":[]}' }}
run: node scripts/ci-write-manifest-outputs.mjs --workflow ci
# Run the fast security/SCM checks in parallel with scope detection so the
# main Node jobs do not have to wait for Python/pre-commit setup.
security-fast:
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
env:
PRE_COMMIT_CACHE_KEY_SUFFIX: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.sha }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Ensure secrets base commit
- name: Ensure security base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Prepare trusted pre-commit config
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
set -euo pipefail
trusted_config="$RUNNER_TEMP/pre-commit-base.yaml"
git show "${BASE_SHA}:.pre-commit-config.yaml" > "$trusted_config"
echo "PRE_COMMIT_CONFIG_PATH=$trusted_config" >> "$GITHUB_ENV"
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
use-sticky-disk: "false"
install-deps: "false"
use-sticky-disk: "false"
- name: Setup Python
id: setup-python
@@ -173,15 +187,17 @@ jobs:
uses: actions/cache@v5
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
key: pre-commit-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}-${{ env.PRE_COMMIT_CACHE_KEY_SUFFIX }}
restore-keys: |
pre-commit-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}-
- name: Install pre-commit
run: |
python -m pip install --upgrade pip
python -m pip install pre-commit
python -m pip install pre-commit==4.2.0
- name: Detect committed private keys
run: pre-commit run --all-files detect-private-key
run: pre-commit run --config "${PRE_COMMIT_CONFIG_PATH:-.pre-commit-config.yaml}" --all-files detect-private-key
- name: Audit changed GitHub workflows with zizmor
env:
@@ -208,44 +224,24 @@ jobs:
fi
printf 'Auditing workflow files:\n%s\n' "${workflow_files[@]}"
pre-commit run zizmor --files "${workflow_files[@]}"
pre-commit run --config "${PRE_COMMIT_CONFIG_PATH:-.pre-commit-config.yaml}" zizmor --files "${workflow_files[@]}"
- name: Audit production dependencies
run: pre-commit run --all-files pnpm-audit-prod
run: pre-commit run --config "${PRE_COMMIT_CONFIG_PATH:-.pre-commit-config.yaml}" --all-files pnpm-audit-prod
# Preflight hub: collapse setup jobs into one explicit fanout node so the
# graph matches operator mental model instead of raw YAML ordering.
preflight:
needs: [docs-scope, changed-scope, changed-extensions, secrets]
if: always() && needs.docs-scope.result == 'success' && (needs.changed-scope.result == 'success' || needs.changed-scope.result == 'skipped') && (needs.changed-extensions.result == 'success' || needs.changed-extensions.result == 'skipped') && needs.secrets.result == 'success'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
outputs:
docs_only: ${{ needs.docs-scope.outputs.docs_only }}
docs_changed: ${{ needs.docs-scope.outputs.docs_changed }}
run_node: ${{ needs.changed-scope.outputs.run_node || 'false' }}
run_macos: ${{ needs.changed-scope.outputs.run_macos || 'false' }}
run_android: ${{ needs.changed-scope.outputs.run_android || 'false' }}
run_skills_python: ${{ needs.changed-scope.outputs.run_skills_python || 'false' }}
run_windows: ${{ needs.changed-scope.outputs.run_windows || 'false' }}
has_changed_extensions: ${{ needs.changed-extensions.outputs.has_changed_extensions || 'false' }}
changed_extensions_matrix: ${{ needs.changed-extensions.outputs.changed_extensions_matrix || '{"include":[]}' }}
steps:
- name: Preflight complete
run: echo "Preflight scope complete."
# Fanout: downstream lanes branch from preflight outputs instead of waiting
# on unrelated Linux checks.
# Build dist once for Node-relevant changes and share it with downstream jobs.
# Keep this overlapping with the fast correctness lanes so green PRs get heavy
# test/build feedback sooner instead of waiting behind a full `check` pass.
build-artifacts:
needs: [preflight]
if: github.event_name == 'push' && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true'
if: needs.preflight.outputs.run_build_artifacts == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Ensure secrets base commit (PR fast path)
@@ -264,6 +260,9 @@ jobs:
- name: Build dist
run: pnpm build
- name: Build Control UI
run: pnpm ui:build
- name: Upload dist artifact
uses: actions/upload-artifact@v7
with:
@@ -271,16 +270,25 @@ jobs:
path: dist/
retention-days: 1
- name: Upload A2UI bundle artifact
uses: actions/upload-artifact@v7
with:
name: canvas-a2ui-bundle
path: src/canvas-host/a2ui/
include-hidden-files: true
retention-days: 1
# Validate npm pack contents after build (only on push to main, not PRs).
release-check:
needs: [preflight, build-artifacts]
if: github.event_name == 'push' && needs.preflight.outputs.docs_only != 'true'
if: needs.preflight.outputs.run_release_check == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
@@ -298,74 +306,82 @@ jobs:
- name: Check release contents
run: pnpm release:check
checks:
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
checks-fast:
name: ${{ matrix.check_name }}
needs: [preflight]
if: needs.preflight.outputs.run_checks_fast == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- runtime: node
task: test
shard_index: 1
shard_count: 2
command: pnpm canvas:a2ui:bundle && pnpm test
- runtime: node
task: test
shard_index: 2
shard_count: 2
command: pnpm canvas:a2ui:bundle && pnpm test
- runtime: node
task: extensions
command: pnpm test:extensions
- runtime: node
task: channels
command: pnpm test:channels
- runtime: node
task: contracts
command: pnpm test:contracts
- runtime: node
task: protocol
command: pnpm protocol:check
- runtime: bun
task: test
command: pnpm canvas:a2ui:bundle && bunx vitest run --config vitest.unit.config.ts
- runtime: node
task: compat-node22
node_version: "22.x"
cache_key_suffix: "node22"
command: |
pnpm build
node openclaw.mjs --help
node openclaw.mjs status --json --timeout 1
pnpm test:build:singleton
node scripts/stage-bundled-plugin-runtime-deps.mjs
node --import tsx scripts/release-check.ts
matrix: ${{ fromJson(needs.preflight.outputs.checks_fast_matrix) }}
steps:
- name: Skip compatibility lanes on pull requests
if: github.event_name == 'pull_request' && (matrix.runtime == 'bun' || matrix.task == 'compat-node22')
run: echo "Skipping push-only lane on pull requests."
- name: Checkout
if: github.event_name != 'pull_request' || (matrix.runtime != 'bun' && matrix.task != 'compat-node22')
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
if: github.event_name != 'pull_request' || (matrix.runtime != 'bun' && matrix.task != 'compat-node22')
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
use-sticky-disk: "false"
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
env:
TASK: ${{ matrix.task }}
shell: bash
run: |
set -euo pipefail
case "$TASK" in
extensions)
pnpm test:extensions
;;
contracts|contracts-protocol)
pnpm test:contracts
pnpm protocol:check
;;
*)
echo "Unsupported checks-fast task: $TASK" >&2
exit 1
;;
esac
checks:
name: ${{ matrix.check_name }}
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.run_checks == 'true' && needs.build-artifacts.result == 'success'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.checks_matrix) }}
steps:
- name: Skip compatibility lanes on pull requests
if: github.event_name == 'pull_request' && matrix.task == 'compat-node22'
run: echo "Skipping push-only lane on pull requests."
- name: Checkout
if: github.event_name != 'pull_request' || matrix.task != 'compat-node22'
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
if: github.event_name != 'pull_request' || matrix.task != 'compat-node22'
uses: ./.github/actions/setup-node-env
with:
node-version: "${{ matrix.node_version || '24.x' }}"
cache-key-suffix: "${{ matrix.cache_key_suffix || 'node24' }}"
install-bun: "${{ matrix.runtime == 'bun' }}"
install-bun: "false"
use-sticky-disk: "false"
- name: Configure Node test resources
if: (github.event_name != 'pull_request' || (matrix.runtime != 'bun' && matrix.task != 'compat-node22')) && matrix.runtime == 'node' && (matrix.task == 'test' || matrix.task == 'compat-node22')
if: (github.event_name != 'pull_request' || matrix.task != 'compat-node22') && matrix.runtime == 'node' && (matrix.task == 'test' || matrix.task == 'channels' || matrix.task == 'compat-node22')
env:
TASK: ${{ matrix.task }}
SHARD_COUNT: ${{ matrix.shard_count || '' }}
SHARD_INDEX: ${{ matrix.shard_index || '' }}
run: |
@@ -373,39 +389,72 @@ jobs:
# Default heap limits have been too low on Linux CI (V8 OOM near 4GB).
echo "OPENCLAW_TEST_WORKERS=2" >> "$GITHUB_ENV"
echo "OPENCLAW_TEST_MAX_OLD_SPACE_SIZE_MB=6144" >> "$GITHUB_ENV"
if [ "$TASK" = "channels" ]; then
echo "OPENCLAW_TEST_WORKERS=1" >> "$GITHUB_ENV"
echo "OPENCLAW_TEST_ISOLATE=1" >> "$GITHUB_ENV"
fi
if [ -n "$SHARD_COUNT" ] && [ -n "$SHARD_INDEX" ]; then
echo "OPENCLAW_TEST_SHARDS=$SHARD_COUNT" >> "$GITHUB_ENV"
echo "OPENCLAW_TEST_SHARD_INDEX=$SHARD_INDEX" >> "$GITHUB_ENV"
fi
- name: Download dist artifact
if: github.event_name == 'push' && matrix.task == 'test'
if: matrix.task == 'test'
uses: actions/download-artifact@v8
with:
name: dist-build
path: dist/
- name: Build dist
if: github.event_name != 'push' && matrix.task == 'test' && matrix.runtime == 'node'
run: pnpm build
- name: Download A2UI bundle artifact
if: matrix.task == 'test' || matrix.task == 'channels'
uses: actions/download-artifact@v8
with:
name: canvas-a2ui-bundle
path: src/canvas-host/a2ui/
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
if: github.event_name != 'pull_request' || (matrix.runtime != 'bun' && matrix.task != 'compat-node22')
run: ${{ matrix.command }}
if: github.event_name != 'pull_request' || matrix.task != 'compat-node22'
env:
TASK: ${{ matrix.task }}
shell: bash
run: |
set -euo pipefail
case "$TASK" in
test)
pnpm test
;;
channels)
pnpm test:channels
;;
compat-node22)
pnpm build
pnpm ui:build
node openclaw.mjs --help
node openclaw.mjs status --json --timeout 1
pnpm test:build:singleton
node scripts/stage-bundled-plugin-runtime-deps.mjs
node --import tsx scripts/release-check.ts
;;
*)
echo "Unsupported checks task: $TASK" >&2
exit 1
;;
esac
extension-fast:
name: "extension-fast"
needs: [preflight]
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && needs.preflight.outputs.has_changed_extensions == 'true'
if: needs.preflight.outputs.run_extension_fast == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.changed_extensions_matrix) }}
matrix: ${{ fromJson(needs.preflight.outputs.extension_fast_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
@@ -423,13 +472,14 @@ jobs:
check:
name: "check"
needs: [preflight]
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true'
if: always() && needs.preflight.outputs.run_check == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
@@ -447,13 +497,14 @@ jobs:
check-additional:
name: "check-additional"
needs: [preflight]
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true'
if: always() && needs.preflight.outputs.run_check_additional == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
@@ -545,13 +596,14 @@ jobs:
build-smoke:
name: "build-smoke"
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
if: always() && needs.preflight.outputs.run_build_smoke == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
@@ -586,13 +638,14 @@ jobs:
# Validate docs (format, lint, broken links) only when docs files changed.
check-docs:
needs: [preflight]
if: needs.preflight.outputs.docs_changed == 'true'
if: needs.preflight.outputs.run_check_docs == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
@@ -606,13 +659,14 @@ jobs:
skills-python:
needs: [preflight]
if: needs.preflight.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.preflight.outputs.run_skills_python == 'true')
if: needs.preflight.outputs.run_skills_python_job == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Python
@@ -632,8 +686,9 @@ jobs:
run: python -m pytest -q skills
checks-windows:
name: ${{ matrix.check_name }}
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_windows == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
if: always() && needs.preflight.outputs.run_checks_windows == 'true' && needs.build-artifacts.result == 'success'
runs-on: blacksmith-32vcpu-windows-2025
timeout-minutes: 20
env:
@@ -646,42 +701,12 @@ jobs:
shell: bash
strategy:
fail-fast: false
matrix:
include:
- runtime: node
task: test
shard_index: 1
shard_count: 6
command: pnpm test
- runtime: node
task: test
shard_index: 2
shard_count: 6
command: pnpm test
- runtime: node
task: test
shard_index: 3
shard_count: 6
command: pnpm test
- runtime: node
task: test
shard_index: 4
shard_count: 6
command: pnpm test
- runtime: node
task: test
shard_index: 5
shard_count: 6
command: pnpm test
- runtime: node
task: test
shard_index: 6
shard_count: 6
command: pnpm test
matrix: ${{ fromJson(needs.preflight.outputs.checks_windows_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Try to exclude workspace from Windows Defender (best-effort)
@@ -745,41 +770,57 @@ jobs:
- name: Configure test shard (Windows)
if: matrix.task == 'test'
env:
SHARD_COUNT: ${{ matrix.shard_count }}
SHARD_INDEX: ${{ matrix.shard_index }}
run: |
echo "OPENCLAW_TEST_SHARDS=${{ matrix.shard_count }}" >> "$GITHUB_ENV"
echo "OPENCLAW_TEST_SHARD_INDEX=${{ matrix.shard_index }}" >> "$GITHUB_ENV"
- name: Build A2UI bundle (Windows)
if: matrix.task == 'test'
run: pnpm canvas:a2ui:bundle
echo "OPENCLAW_TEST_SHARDS=$SHARD_COUNT" >> "$GITHUB_ENV"
echo "OPENCLAW_TEST_SHARD_INDEX=$SHARD_INDEX" >> "$GITHUB_ENV"
- name: Download dist artifact
if: github.event_name == 'push' && matrix.task == 'test'
if: matrix.task == 'test'
uses: actions/download-artifact@v8
with:
name: dist-build
path: dist/
- name: Build dist (Windows)
if: github.event_name != 'push' && matrix.task == 'test'
run: pnpm build
- name: Download A2UI bundle artifact
if: matrix.task == 'test'
uses: actions/download-artifact@v8
with:
name: canvas-a2ui-bundle
path: src/canvas-host/a2ui/
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
run: ${{ matrix.command }}
env:
TASK: ${{ matrix.task }}
shell: bash
run: |
set -euo pipefail
case "$TASK" in
test)
pnpm test
;;
*)
echo "Unsupported Windows checks task: $TASK" >&2
exit 1
;;
esac
# Consolidated macOS job: runs TS tests + Swift lint/build/test sequentially
# on a single runner. GitHub limits macOS concurrent jobs to 5 per org;
# running 4 separate jobs per PR (as before) starved the queue. One job
# per PR allows 5 PRs to run macOS checks simultaneously.
macos:
needs: [preflight]
if: github.event_name == 'pull_request' && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_macos == 'true'
macos-node:
name: ${{ matrix.check_name }}
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.run_macos_node == 'true' && needs.build-artifacts.result == 'success'
runs-on: macos-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.macos_node_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
@@ -787,16 +828,56 @@ jobs:
with:
install-bun: "false"
- name: Build dist (macOS)
run: pnpm build
- name: Download dist artifact
uses: actions/download-artifact@v8
with:
name: dist-build
path: dist/
- name: Download A2UI bundle artifact
uses: actions/download-artifact@v8
with:
name: canvas-a2ui-bundle
path: src/canvas-host/a2ui/
- name: Configure test shard (macOS)
env:
SHARD_COUNT: ${{ matrix.shard_count }}
SHARD_INDEX: ${{ matrix.shard_index }}
run: |
echo "OPENCLAW_TEST_SHARDS=$SHARD_COUNT" >> "$GITHUB_ENV"
echo "OPENCLAW_TEST_SHARD_INDEX=$SHARD_INDEX" >> "$GITHUB_ENV"
# --- Run all checks sequentially (fast gates first) ---
- name: TS tests (macOS)
env:
NODE_OPTIONS: --max-old-space-size=4096
run: pnpm test
TASK: ${{ matrix.task }}
shell: bash
run: |
set -euo pipefail
case "$TASK" in
test)
pnpm test
;;
*)
echo "Unsupported macOS node task: $TASK" >&2
exit 1
;;
esac
macos-swift:
name: "macos-swift"
needs: [preflight]
if: needs.preflight.outputs.run_macos_swift == 'true'
runs-on: macos-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
# --- Xcode/Swift setup ---
- name: Select Xcode 26.1
run: |
sudo xcode-select -s /Applications/Xcode_26.1.app
@@ -805,6 +886,14 @@ jobs:
- name: Install XcodeGen / SwiftLint / SwiftFormat
run: brew install xcodegen swiftlint swiftformat
- name: Cache SwiftPM
uses: actions/cache@v5
with:
path: ~/Library/Caches/org.swift.swiftpm
key: ${{ runner.os }}-swiftpm-${{ hashFiles('apps/macos/Package.resolved') }}
restore-keys: |
${{ runner.os }}-swiftpm-
- name: Show toolchain
run: |
sw_vers
@@ -816,14 +905,6 @@ jobs:
swiftlint --config .swiftlint.yml
swiftformat --lint apps/macos/Sources --config .swiftformat
- name: Cache SwiftPM
uses: actions/cache@v5
with:
path: ~/Library/Caches/org.swift.swiftpm
key: ${{ runner.os }}-swiftpm-${{ hashFiles('apps/macos/Package.resolved') }}
restore-keys: |
${{ runner.os }}-swiftpm-
- name: Swift build (release)
run: |
set -euo pipefail
@@ -849,26 +930,19 @@ jobs:
exit 1
android:
name: ${{ matrix.check_name }}
needs: [preflight]
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_android == 'true'
if: needs.preflight.outputs.run_android_job == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- task: test-play
command: ./gradlew --no-daemon :app:testPlayDebugUnitTest
- task: test-third-party
command: ./gradlew --no-daemon :app:testThirdPartyDebugUnitTest
- task: build-play
command: ./gradlew --no-daemon :app:assemblePlayDebug
- task: build-third-party
command: ./gradlew --no-daemon :app:assembleThirdPartyDebug
matrix: ${{ fromJson(needs.preflight.outputs.android_matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Java
@@ -898,7 +972,7 @@ jobs:
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with:
gradle-version: 8.11.1
@@ -912,4 +986,26 @@ jobs:
- name: Run Android ${{ matrix.task }}
working-directory: apps/android
run: ${{ matrix.command }}
env:
TASK: ${{ matrix.task }}
shell: bash
run: |
set -euo pipefail
case "$TASK" in
test-play)
./gradlew --no-daemon :app:testPlayDebugUnitTest
;;
test-third-party)
./gradlew --no-daemon :app:testThirdPartyDebugUnitTest
;;
build-play)
./gradlew --no-daemon :app:assemblePlayDebug
;;
build-third-party)
./gradlew --no-daemon :app:assembleThirdPartyDebug
;;
*)
echo "Unsupported Android task: $TASK" >&2
exit 1
;;
esac

View File

@@ -2,8 +2,6 @@ name: Docker Release
on:
push:
branches:
- main
tags:
- "v*"
paths-ignore:
@@ -20,7 +18,7 @@ on:
type: string
concurrency:
group: docker-release-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
group: ${{ github.event_name == 'workflow_dispatch' && format('docker-release-manual-{0}', inputs.tag) || format('docker-release-push-{0}', github.run_id) }}
cancel-in-progress: false
env:

View File

@@ -8,56 +8,41 @@ on:
workflow_dispatch:
concurrency:
group: ${{ github.event_name == 'pull_request' && format('install-smoke-pr-{0}', github.event.pull_request.number) || format('install-smoke-push-{0}', github.run_id) }}
group: ${{ github.event_name == 'pull_request' && format('{0}-{1}', github.workflow, github.event.pull_request.number) || format('{0}-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
docs-scope:
preflight:
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
outputs:
docs_only: ${{ steps.check.outputs.docs_only }}
docs_only: ${{ steps.manifest.outputs.docs_only }}
run_install_smoke: ${{ steps.manifest.outputs.run_install_smoke }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Ensure docs-scope base commit
- name: Ensure preflight base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect docs-only changes
id: check
id: docs_scope
uses: ./.github/actions/detect-docs-changes
changed-smoke:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_only != 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
outputs:
run_changed_smoke: ${{ steps.scope.outputs.run_changed_smoke }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1
fetch-tags: false
- name: Ensure changed-smoke base commit
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
- name: Detect changed smoke scope
id: scope
id: changed_scope
if: steps.docs_scope.outputs.docs_only != 'true'
shell: bash
run: |
set -euo pipefail
@@ -70,9 +55,32 @@ jobs:
node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD
- name: Setup Node environment
if: steps.docs_scope.outputs.docs_only != 'true'
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
install-deps: "false"
use-sticky-disk: "false"
- name: Build install-smoke CI manifest
id: manifest
env:
OPENCLAW_CI_DOCS_ONLY: ${{ steps.docs_scope.outputs.docs_only }}
OPENCLAW_CI_DOCS_CHANGED: "false"
OPENCLAW_CI_RUN_NODE: "false"
OPENCLAW_CI_RUN_MACOS: "false"
OPENCLAW_CI_RUN_ANDROID: "false"
OPENCLAW_CI_RUN_WINDOWS: "false"
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false"
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "false"
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[]}'
OPENCLAW_CI_RUN_CHANGED_SMOKE: ${{ steps.changed_scope.outputs.run_changed_smoke || 'false' }}
run: node scripts/ci-write-manifest-outputs.mjs --workflow install-smoke
install-smoke:
needs: [docs-scope, changed-smoke]
if: (github.event_name != 'pull_request' || !github.event.pull_request.draft) && needs.docs-scope.outputs.docs_only != 'true' && needs.changed-smoke.outputs.run_changed_smoke == 'true'
needs: [preflight]
if: needs.preflight.outputs.run_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
env:
DOCKER_BUILD_SUMMARY: "false"
@@ -186,12 +194,12 @@ jobs:
- name: Run installer docker tests
env:
CLAWDBOT_INSTALL_URL: https://openclaw.ai/install.sh
CLAWDBOT_INSTALL_CLI_URL: https://openclaw.ai/install-cli.sh
CLAWDBOT_NO_ONBOARD: "1"
CLAWDBOT_INSTALL_SMOKE_SKIP_CLI: "1"
CLAWDBOT_INSTALL_SMOKE_SKIP_IMAGE_BUILD: "1"
CLAWDBOT_INSTALL_NONROOT_SKIP_IMAGE_BUILD: ${{ github.event_name == 'pull_request' && '0' || '1' }}
CLAWDBOT_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }}
CLAWDBOT_INSTALL_SMOKE_SKIP_PREVIOUS: "1"
OPENCLAW_INSTALL_URL: https://openclaw.ai/install.sh
OPENCLAW_INSTALL_CLI_URL: https://openclaw.ai/install-cli.sh
OPENCLAW_NO_ONBOARD: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_CLI: "1"
OPENCLAW_INSTALL_SMOKE_SKIP_IMAGE_BUILD: "1"
OPENCLAW_INSTALL_NONROOT_SKIP_IMAGE_BUILD: ${{ github.event_name == 'pull_request' && '0' || '1' }}
OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }}
OPENCLAW_INSTALL_SMOKE_SKIP_PREVIOUS: "1"
run: bash scripts/test-install-sh-docker.sh

View File

@@ -2,9 +2,9 @@ name: Labeler
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] maintainer-owned triage workflow; no untrusted checkout or PR code execution
types: [opened, synchronize, reopened]
types: [opened, synchronize, reopened, edited]
issues:
types: [opened]
types: [opened, edited]
workflow_dispatch:
inputs:
max_prs:
@@ -19,6 +19,10 @@ on:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request_target' }}
permissions: {}
jobs:
@@ -205,6 +209,59 @@ jobs:
// labels: [trustedLabel],
// });
// }
- name: Apply beta-blocker title label
uses: actions/github-script@v8
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
const pullRequest = context.payload.pull_request;
if (!pullRequest) {
return;
}
const labelName = "beta-blocker";
const matchesBetaBlocker = /\bbeta blocker\b/i.test(pullRequest.title ?? "");
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: labelName,
});
} catch (error) {
if (error?.status !== 404) {
throw error;
}
core.info(`Skipping ${labelName} labeling because the label does not exist in the repository.`);
return;
}
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
per_page: 100,
});
const hasLabel = currentLabels.some((label) => label.name === labelName);
if (matchesBetaBlocker && !hasLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
labels: [labelName],
});
return;
}
if (!matchesBetaBlocker && hasLabel) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
name: labelName,
});
}
- name: Apply too-many-prs label
uses: actions/github-script@v8
with:
@@ -415,6 +472,7 @@ jobs:
const maxCount = processAll ? Number.POSITIVE_INFINITY : Math.max(1, maxPrs);
const sizeLabels = ["size: XS", "size: S", "size: M", "size: L", "size: XL"];
const betaBlockerLabel = "beta-blocker";
const labelColor = "b76e79";
// const trustedLabel = "trusted-contributor";
// const experiencedLabel = "experienced-contributor";
@@ -445,6 +503,22 @@ jobs:
}
}
async function hasBetaBlockerLabel() {
try {
await github.rest.issues.getLabel({
owner,
repo,
name: betaBlockerLabel,
});
return true;
} catch (error) {
if (error?.status !== 404) {
throw error;
}
return false;
}
}
async function resolveContributorLabel(login) {
if (contributorCache.has(login)) {
return contributorCache.get(login);
@@ -576,7 +650,37 @@ jobs:
labelNames.add(label);
}
async function applyBetaBlockerTitleLabel(pullRequest, labelNames) {
const matchesBetaBlocker = /\bbeta blocker\b/i.test(pullRequest.title ?? "");
if (matchesBetaBlocker) {
if (!labelNames.has(betaBlockerLabel)) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: pullRequest.number,
labels: [betaBlockerLabel],
});
labelNames.add(betaBlockerLabel);
}
return;
}
if (!labelNames.has(betaBlockerLabel)) {
return;
}
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: pullRequest.number,
name: betaBlockerLabel,
});
labelNames.delete(betaBlockerLabel);
}
await ensureSizeLabels();
const betaBlockerLabelExists = await hasBetaBlockerLabel();
let page = 1;
let processed = 0;
@@ -614,6 +718,9 @@ jobs:
await applySizeLabel(pullRequest, currentLabels, labelNames);
await applyContributorLabel(pullRequest, labelNames);
if (betaBlockerLabelExists) {
await applyBetaBlockerTitleLabel(pullRequest, labelNames);
}
processed += 1;
}
@@ -715,3 +822,56 @@ jobs:
// labels: [trustedLabel],
// });
// }
- name: Apply beta-blocker title label
uses: actions/github-script@v8
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
const issue = context.payload.issue;
if (!issue || issue.pull_request) {
return;
}
const labelName = "beta-blocker";
const matchesBetaBlocker = /^beta blocker:/i.test(issue.title ?? "");
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: labelName,
});
} catch (error) {
if (error?.status !== 404) {
throw error;
}
core.info(`Skipping ${labelName} labeling because the label does not exist in the repository.`);
return;
}
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
per_page: 100,
});
const hasLabel = currentLabels.some((label) => label.name === labelName);
if (matchesBetaBlocker && !hasLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [labelName],
});
return;
}
if (!matchesBetaBlocker && hasLabel) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
name: labelName,
});
}

86
.github/workflows/macos-release.yml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: macOS Release
on:
workflow_dispatch:
inputs:
tag:
description: Existing release tag to validate for macOS release handoff (for example v2026.3.22 or v2026.3.22-beta.1)
required: true
type: string
preflight_only:
description: Retained for operator compatibility; this public workflow is validation-only
required: true
default: true
type: boolean
concurrency:
group: macos-release-${{ inputs.tag }}
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "24.x"
PNPM_VERSION: "10.23.0"
jobs:
validate_macos_release_request:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Validate tag input format
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-beta\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]]; then
echo "Invalid release tag format: ${RELEASE_TAG}"
exit 1
fi
- name: Checkout selected tag
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
install-bun: "false"
use-sticky-disk: "false"
- name: Ensure matching GitHub release exists
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
run: gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_MAIN_REF: origin/main
run: |
set -euo pipefail
RELEASE_SHA=$(git rev-parse HEAD)
export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
pnpm release:openclaw:npm:check
- name: Summarize next step
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
{
echo "## Public macOS validation only"
echo
echo "This workflow no longer builds, signs, notarizes, or uploads macOS assets."
echo
echo "Next step:"
echo "- Run \`openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml\` with tag \`${RELEASE_TAG}\`."
echo "- Use \`preflight_only=true\` there for the full private mac preflight."
echo "- For the real publish path, the private run uploads the packaged \`.zip\`, \`.dmg\`, and \`.dSYM.zip\` files to the existing GitHub release in \`openclaw/openclaw\` automatically."
echo "- For stable releases, also download \`macos-appcast-${RELEASE_TAG}\` from the successful private run and commit \`appcast.xml\` back to \`main\` in \`openclaw/openclaw\`."
} >> "$GITHUB_STEP_SUMMARY"

View File

@@ -1,15 +1,17 @@
name: OpenClaw NPM Release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: Release tag to publish (for example v2026.3.22, v2026.3.22-beta.1, or fallback v2026.3.22-1)
required: true
type: string
preflight_only:
description: Run validation/build only and skip the gated publish job
required: true
default: false
type: boolean
concurrency:
group: openclaw-npm-release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
@@ -21,15 +23,25 @@ env:
PNPM_VERSION: "10.23.0"
jobs:
preview_openclaw_npm:
if: github.event_name == 'push'
preflight_openclaw_npm:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Validate tag input format
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-beta\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]]; then
echo "Invalid release tag format: ${RELEASE_TAG}"
exit 1
fi
- name: Checkout
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
- name: Setup Node environment
@@ -40,38 +52,12 @@ jobs:
install-bun: "false"
use-sticky-disk: "false"
- name: Print release plan
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
RELEASE_SHA=$(git rev-parse HEAD)
PACKAGE_VERSION=$(node -p "require('./package.json').version")
if [[ "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*-[1-9][0-9]*$ ]]; then
TAG_KIND="fallback correction"
else
TAG_KIND="standard"
fi
echo "Release plan for ${RELEASE_TAG}:"
echo "Resolved release SHA: ${RELEASE_SHA}"
echo "Resolved package version: ${PACKAGE_VERSION}"
echo "Resolved tag kind: ${TAG_KIND}"
if [[ "${TAG_KIND}" == "fallback correction" ]]; then
echo "Correction tag note: npm version remains ${PACKAGE_VERSION}"
fi
echo "Would run: git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main"
echo "Would run with env: RELEASE_SHA=${RELEASE_SHA} RELEASE_TAG=${RELEASE_TAG} RELEASE_MAIN_REF=origin/main pnpm release:openclaw:npm:check"
echo "Would run: npm view openclaw@${PACKAGE_VERSION} version"
echo "Would run: pnpm check"
echo "Would run: pnpm build"
echo "Would run: pnpm release:check"
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ github.ref_name }}
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_MAIN_REF: origin/main
run: |
set -euxo pipefail
set -euo pipefail
RELEASE_SHA=$(git rev-parse HEAD)
export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF
# Fetch the full main ref so merge-base ancestry checks keep working
@@ -81,52 +67,51 @@ jobs:
- name: Ensure version is not already published
env:
RELEASE_TAG: ${{ github.ref_name }}
PREFLIGHT_ONLY: ${{ inputs.preflight_only }}
run: |
set -euxo pipefail
set -euo pipefail
PACKAGE_VERSION=$(node -p "require('./package.json').version")
IS_CORRECTION_TAG=0
if [[ "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*-[1-9][0-9]*$ ]]; then
IS_CORRECTION_TAG=1
fi
if npm view "openclaw@${PACKAGE_VERSION}" version >/dev/null 2>&1; then
if [[ "${IS_CORRECTION_TAG}" == "1" ]]; then
echo "openclaw@${PACKAGE_VERSION} is already published on npm."
echo "Correction tag ${RELEASE_TAG} is allowed as a fallback release tag, so preview will continue without treating this as an error."
if [[ "${PREFLIGHT_ONLY}" == "true" ]]; then
echo "openclaw@${PACKAGE_VERSION} is already published on npm; continuing because preflight_only=true."
exit 0
fi
echo "openclaw@${PACKAGE_VERSION} is already published on npm."
exit 1
fi
if [[ "${IS_CORRECTION_TAG}" == "1" ]]; then
echo "Previewing fallback correction tag ${RELEASE_TAG} for npm version openclaw@${PACKAGE_VERSION}"
else
echo "Previewing openclaw@${PACKAGE_VERSION}"
fi
echo "Publishing openclaw@${PACKAGE_VERSION}"
- name: Check
run: |
set -euxo pipefail
pnpm check
run: pnpm check
- name: Build
run: |
set -euxo pipefail
pnpm build
run: pnpm build
- name: Verify release contents
run: |
set -euxo pipefail
pnpm release:check
run: pnpm release:check
- name: Preview publish command
run: bash scripts/openclaw-npm-publish.sh --dry-run
validate_publish_dispatch_ref:
if: ${{ !inputs.preflight_only }}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Require main workflow ref for publish
env:
WORKFLOW_REF: ${{ github.ref }}
run: |
set -euo pipefail
if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]]; then
echo "Real publish runs must be dispatched from main. Use preflight_only=true for branch validation."
exit 1
fi
publish_openclaw_npm:
if: github.event_name == 'workflow_dispatch'
# npm trusted publishing + provenance requires a GitHub-hosted runner.
needs: [preflight_openclaw_npm, validate_publish_dispatch_ref]
if: ${{ !inputs.preflight_only }}
runs-on: ubuntu-latest
environment: npm-release
permissions:
@@ -182,14 +167,5 @@ jobs:
echo "Publishing openclaw@${PACKAGE_VERSION}"
- name: Check
run: pnpm check
- name: Build
run: pnpm build
- name: Verify release contents
run: pnpm release:check
- name: Publish
run: bash scripts/openclaw-npm-publish.sh --publish

View File

@@ -15,7 +15,7 @@ on:
- scripts/sandbox-common-setup.sh
concurrency:
group: sandbox-common-smoke-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:

View File

@@ -7,7 +7,7 @@ on:
workflow_dispatch:
concurrency:
group: workflow-sanity-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:

View File

@@ -113,14 +113,15 @@
- Framework: Vitest with V8 coverage thresholds (70% lines/branches/functions/statements).
- Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`.
- When tests need example Anthropic/OpenAI model constants, prefer `sonnet-4.6` and `gpt-5.4`; update older Anthropic/GPT examples when you touch those tests.
- Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic.
- Write tests to clean up timers, env, globals, mocks, sockets, temp dirs, and module state so `--isolate=false` stays green.
- Agents MUST NOT modify baseline, inventory, ignore, snapshot, or expected-failure files to silence failing checks without explicit approval in this chat.
- For targeted/local debugging, keep using the wrapper: `pnpm test -- <path-or-filter> [vitest args...]` (for example `pnpm test -- src/commands/onboard-search.test.ts -t "shows registered plugin providers"`); do not default to raw `pnpm vitest run ...` because it bypasses wrapper config/profile/pool routing.
- Do not set test workers above 16; tried already.
- Do not reintroduce Vitest VM pools by default without fresh green evidence on current `main`; keep CI on `forks`.
- If local Vitest runs cause memory pressure (common on non-Mac-Studio hosts), use `OPENCLAW_TEST_PROFILE=low OPENCLAW_TEST_SERIAL_GATEWAY=1 pnpm test` for land/gate runs.
- Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
- Keep Vitest on `forks` only. Do not introduce or reintroduce any non-`forks` Vitest pool or alternate execution mode in configs, wrapper scripts, or default test commands without explicit approval in this chat. This includes `threads`, `vmThreads`, `vmForks`, and any future/nonstandard pool variant.
- If local Vitest runs cause memory pressure, the wrapper now derives budgets from host capabilities (CPU, memory band, current load). For a conservative explicit override during land/gate runs, use `OPENCLAW_TEST_PROFILE=serial OPENCLAW_TEST_SERIAL_GATEWAY=1 pnpm test`.
- Live tests (real keys): `OPENCLAW_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
- Full kit + whats covered: `docs/help/testing.md`.
- Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process).
- Changelog placement: in the active version block, append new entries to the end of the target section (`### Changes` or `### Fixes`); do not insert new entries at the top of a section.

View File

@@ -4,109 +4,434 @@ Docs: https://docs.openclaw.ai
## Unreleased
### Breaking
- Providers/Qwen: remove the deprecated `qwen-portal-auth` OAuth integration for `portal.qwen.ai`; migrate to Model Studio with `openclaw onboard --auth-choice modelstudio-api-key`. (#52709) Thanks @pomelo-nwu.
### Changes
- Breaking/Plugins: bare `openclaw plugins install <package>` now prefers ClawHub before npm for npm-safe names, and only falls back to npm when ClawHub does not have that package or version.
- Breaking/Tools/image generation: deprecate the bundled `openai-image-gen` skill, standardize on the native `image` and `image_generate` tools, clarify provider-specific image auth requirements in runtime output and docs, and harden provider-auth hint lookups against prototype-like provider ids. (#52552) Thanks @vincentkoc.
- ClawHub/install: add native `openclaw skills search|install|update` flows plus `openclaw plugins install clawhub:<package>` with tracked update metadata, gateway skill-install/update support for ClawHub-backed requests, and regression coverage/docs for the new source path.
- Models/Anthropic Vertex: add core `anthropic-vertex` provider support for Claude via Google Vertex AI, including GCP auth/discovery and main run-path routing. (#43356) Thanks @sallyom and @yossiovadia.
- Commands/btw: add `/btw` side questions for quick tool-less answers about the current session without changing future session context, with dismissible in-session TUI answers and explicit BTW replies on external channels. (#45444) Thanks @ngutman.
- Gateway/docs: clarify that empty URL input allowlists are treated as unset, document `allowUrl: false` as the deny-all switch, and add regression coverage for the normalization path.
- Sandbox/runtime: add pluggable sandbox backends, ship an OpenShell backend with `mirror` and `remote` workspace modes, and make sandbox list/recreate/prune backend-aware instead of Docker-only.
- Sandbox/SSH: add a core SSH sandbox backend with secret-backed key, certificate, and known_hosts inputs, move shared remote exec/filesystem tooling into core, and keep OpenShell focused on sandbox lifecycle plus optional `mirror` mode.
- Web tools/Firecrawl: add Firecrawl as an `onboard`/configure search provider via a bundled plugin, expose explicit `firecrawl_search` and `firecrawl_scrape` tools, and align core `web_fetch` fallback behavior with Firecrawl base-URL/env fallback plus guarded endpoint fetches.
- Plugins/bundles: add compatible Codex, Claude, and Cursor bundle discovery/install support, map bundle skills into OpenClaw skills, and apply Claude bundle `settings.json` defaults to embedded Pi with shell overrides sanitized.
- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy.
- Plugins/agent integrations: broaden the plugin surface for app-server integrations with channel-aware commands, interactive callbacks, inbound claims, and Discord/Telegram conversation binding support. (#45318) Thanks @huntharo and @vincentkoc.
- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs. (#47630) Thanks @vincentkoc.
- Gateway/health monitor: add configurable stale-event thresholds and restart limits, plus per-channel and per-account `healthMonitor.enabled` overrides, while keeping the existing global disable path on `gateway.channelHealthCheckMinutes=0`. (#42107) Thanks @rstar327.
- Android/mobile: add a system-aware dark theme across onboarding and post-onboarding screens so the app follows the device theme through setup, chat, and voice flows. (#46249) Thanks @sibbl.
- Feishu/ACP: add current-conversation ACP and subagent session binding for supported DMs and topic conversations, including completion delivery back to the originating Feishu conversation. (#46819) Thanks @Takhoffman.
- Plugins/marketplaces: add Claude marketplace registry resolution, `plugin@marketplace` installs, marketplace listing, and update support, plus Docker E2E coverage for local and official marketplace flows. (#48058) Thanks @vincentkoc.
- Commands/plugins: add owner-gated `/plugins` and `/plugin` chat commands for plugin list/show and enable/disable flows, alongside explicit `commands.plugins` config gating. Thanks @vincentkoc.
- Feishu/cards: add structured interactive approval and quick-action launcher cards, preserve callback user and conversation context through routing, and keep legacy card-action fallback behavior so common actions can run without typing raw commands. (#47873) Thanks @Takhoffman.
- Feishu/streaming: add `onReasoningStream` and `onReasoningEnd` support to streaming cards, so `/reasoning stream` renders thinking tokens as markdown blockquotes in the same card — matching the Telegram channel's reasoning lane behavior. (#46029) Thanks @day253.
- Feishu/cards: add identity-aware structured card headers and note footers for Feishu replies and direct sends, while keeping that presentation wired through the shared outbound identity path. (#29938) Thanks @nszhsl.
- Android/nodes: add `callLog.search` plus shared Call Log permission wiring so Android nodes can search recent call history through the gateway. (#44073) Thanks @lixuankai.
- Android/nodes: add `sms.search` plus shared SMS permission wiring so Android nodes can search device text messages through the gateway. (#48299) Thanks @lixuankai.
- Plugins/MiniMax: merge the bundled MiniMax API and MiniMax OAuth plugin surfaces into a single default-on `minimax` plugin, while keeping legacy `minimax-portal-auth` config ids aliased for compatibility.
- Agents/Pi compatibility: align OpenClaw's bundled MiniMax runtime behavior with the current upstream Pi 0.61.1 release so embedded runs stay in sync with the latest published Pi SDK semantics. Thanks @vincentkoc.
- Models/MiniMax defaults: raise bundled MiniMax M2.5/M2.7 context-window, max-token, and pricing metadata to the higher defaults shipped by the current upstream Pi SDK. Thanks @vincentkoc.
- MiniMax/fast mode: map shared `/fast` and `params.fastMode` to MiniMax `-highspeed` models for M2.1, M2.5, and M2.7 API-key and OAuth runs. Thanks @vincentkoc.
- Models/MiniMax: add bundled `MiniMax-M2`, `MiniMax-M2.1`, and `MiniMax-M2.1-highspeed` catalog entries so OpenClaw's provider metadata and OAuth aliases stay aligned with the current upstream Pi SDK. Thanks @vincentkoc.
- xAI/models: sync the bundled Grok catalog to current Pi-backed IDs, limits, and pricing metadata, while keeping older Grok fast and 4.20 aliases resolving cleanly at runtime. Thanks @vincentkoc.
- Z.AI/models: sync the bundled GLM catalog to current Pi metadata, including newer 4.5/4.6 model families, updated multimodal entries, and current pricing and token limits. Thanks @vincentkoc.
- Mistral/models: sync the bundled default Mistral metadata to current Pi pricing so the built-in default no longer advertises zero-cost usage. Thanks @vincentkoc.
- xAI/fast mode: map shared `/fast` and `params.fastMode` to the current xAI Grok fast model family so direct Grok runs can opt into the faster Pi-backed variants. Thanks @vincentkoc.
- Agents/steering docs: update embedded Pi steering docs and runner comments for the current upstream behavior, where queued steering is injected after the active assistant turn finishes its tool calls instead of skipping the remaining tools mid-turn. Thanks @vincentkoc.
- Telegram/actions: add `topic-edit` for forum-topic renames and icon updates while sharing the same Telegram topic-edit transport used by the plugin runtime. (#47798) Thanks @obviyus.
- Telegram/error replies: add a default-off `channels.telegram.silentErrorReplies` setting so bot error replies can be delivered silently across regular replies, native commands, and fallback sends. (#19776) Thanks @ImLukeF.
- Doctor/refactor: start splitting doctor provider checks into `src/commands/doctor/providers/*` by extracting Telegram first-run and group allowlist warnings into a provider-specific module, keeping the current setup guidance and warning behavior intact. Thanks @vincentkoc.
- Refactor/channels: remove the legacy channel shim directories and point channel-specific imports directly at the extension-owned implementations. (#45967) Thanks @scoootscooob.
- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898.
- secrets: harden read-only SecretRef command paths and diagnostics. (#47794) Thanks @joshavant.
- Browser/existing-session: support `browser.profiles.<name>.userDataDir` so Chrome DevTools MCP can attach to Brave, Edge, and other Chromium-based browsers through their own user data directories. (#48170) Thanks @velvet-shark.
- Skills/prompt budget: preserve all registered skills via a compact catalog fallback before dropping entries when the full prompt format exceeds `maxSkillsPromptChars`. (#47553) Thanks @snese.
- Models/OpenAI: add native forward-compat support for `gpt-5.4-mini` and `gpt-5.4-nano` in the OpenAI provider catalog, runtime resolution, and reasoning capability gates. Thanks @vincentkoc.
- Plugins/bundles: make enabled bundle MCP servers expose runnable tools in embedded Pi, and default relative bundle MCP launches to the bundle root so marketplace bundles like Context7 work through Pi instead of stopping at config import.
- Scope message SecretRef resolution and harden doctor/status paths. (#48728) Thanks @joshavant.
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` surface for plugin-author test helpers, and move bundled-extension-only test bridges out of `extensions/` into private repo test helpers.
- Plugins/Chutes: add a bundled Chutes provider with plugin-owned OAuth/API-key auth, dynamic model discovery, and default-on extension wiring. (#41416) Thanks @Veightor.
- Plugins/binding: add `onConversationBindingResolved(...)` so plugins can react immediately after bind approvals or denies without blocking channel interaction acknowledgements. (#48678) Thanks @huntharo.
- CLI/config: expand `config set` with SecretRef and provider builder modes, JSON/batch assignment support, and `--dry-run` validation with structured JSON output. (#49296) Thanks @joshavant.
- Control UI/appearance: unify theme border radii across Claw, Knot, and Dash, and add a Roundness slider to the Appearance settings so users can adjust corner radius from sharp to fully rounded. Thanks @BunsDev.
- Control UI/chat: add an expand-to-canvas button on assistant chat bubbles and in-app session navigation from Sessions and Cron views. Thanks @BunsDev.
- Plugins/context engines: expose `delegateCompactionToRuntime(...)` on the public plugin SDK, refactor the legacy engine to use the shared helper, and clarify `ownsCompaction` delegation semantics for non-owning engines. (#49061) Thanks @jalehman.
- Plugins/MiniMax: add MiniMax-M2.7 and MiniMax-M2.7-highspeed models and update the default model from M2.5 to M2.7. (#49691) Thanks @liyuan97.
- Plugins/Xiaomi: switch the bundled Xiaomi provider to the `/v1` OpenAI-compatible endpoint and add MiMo V2 Pro plus MiMo V2 Omni to the built-in catalog. (#49214) thanks @DJjjjhao.
- Android/Talk: move Talk speech synthesis behind gateway `talk.speak`, keep Talk secrets on the gateway, and switch Android playback to final-response audio instead of device-local ElevenLabs streaming. (#50849)
- Plugins/Matrix: add `allowBots` room policy so configured Matrix bot accounts can talk to each other, with optional mention-only gating. Thanks @gumadeiras.
- Plugins/Matrix: add per-account `allowPrivateNetwork` opt-in for private/internal homeservers, while keeping public cleartext homeservers blocked. Thanks @gumadeiras.
- Web tools/Tavily: add Tavily as a bundled web-search provider with dedicated `tavily_search` and `tavily_extract` tools, using canonical plugin-owned config under `plugins.entries.tavily.config.webSearch.*`. (#49200) thanks @lakshyaag-tavily.
- Docs/plugins: add the community DingTalk plugin listing to the docs catalog. (#29913) Thanks @sliverp.
- Docs/plugins: add the community QQbot plugin listing to the docs catalog. (#29898) Thanks @sliverp.
- Plugins/context engines: pass the embedded runner `modelId` into context-engine `assemble()` so plugins can adapt context formatting per model. (#47437) thanks @jscianna.
- Plugins/context engines: add transcript maintenance rewrites for context engines, preserve active-branch transcript metadata during rewrites, and harden overflow-recovery truncation to rewrite sessions under the normal session write lock. (#51191) Thanks @jalehman.
- Telegram/apiRoot: add per-account custom Bot API endpoint support across send, probe, setup, doctor repair, and inbound media download paths so proxied or self-hosted Telegram deployments work end to end. (#48842) Thanks @Cypherm.
- Telegram/topics: auto-rename DM forum topics on first message with LLM-generated labels, with per-account and per-DM `autoTopicLabel` overrides. (#51502) Thanks @Lukavyi.
- Docs/plugins: add the community wecom plugin listing to the docs catalog. (#29905) Thanks @sliverp.
- Models/GitHub Copilot: allow forward-compat dynamic model ids without code updates, while preserving configured provider and per-model overrides for those synthetic models. (#51325) Thanks @fuller-stack-dev.
- Agents/compaction: notify users when followup auto-compaction starts and finishes, keeping those notices out of TTS and preserving reply threading for the real assistant reply. (#38805) Thanks @zidongdesign.
- Models/OpenAI: switch the default OpenAI setup model to `openai/gpt-5.4`, keep Codex on `openai-codex/gpt-5.4`, and centralize OpenAI chat, image, TTS, transcription, and embedding defaults in one shared module so future default-model updates stay low-churn. Thanks @vincentkoc.
- Memory/plugins: let the active memory plugin register its own system-prompt section while preserving cache-clear and snapshot-load prompt isolation. (#40126) Thanks @jarimustonen.
- Control UI/usage: improve usage overview styling, localization, and responsive chat/context-notice presentation, including safer theme color handling and unclipped usage-header menus. (#51951) Thanks @BunsDev.
- Agents: add per-agent thinking/reasoning/fast defaults and auto-revert disallowed model overrides to the agent's default selection. Thanks @xuanmingguo and @vincentkoc.
- Control UI/usage: drop the empty session-detail placeholder card so the usage view stays single-column until a real session detail panel is selected. (#52013) Thanks @BunsDev.
- Hooks/workspace: keep repo-local `<workspace>/hooks` disabled until explicitly enabled, block workspace hook name collisions from shadowing bundled/managed/plugin hooks, and treat `hooks.internal.load.extraDirs` as trusted managed hook sources.
- Web tools/Exa: add Exa as a bundled web-search plugin with Exa-native date filters, search-mode selection, and optional content extraction under `plugins.entries.exa.config.webSearch.*`. Thanks @V-Gutierrez and @vincentkoc.
- CLI/hooks: route hook-pack install and update through `openclaw plugins`, keep `openclaw hooks` focused on hook visibility and per-hook controls, and show plugin-managed hook details in CLI output.
- MiniMax: add image generation provider for `image-01` model, supporting generate and image-to-image editing with aspect ratio control. (#54487) Thanks @liyuan97.
- Slack/tool actions: add an explicit `upload-file` Slack action that routes file uploads through the existing Slack upload transport, with optional filename/title/comment overrides for channels and DMs.
- Plugins/Matrix TTS: send auto-TTS replies as native Matrix voice bubbles instead of generic audio attachments. (#37080) thanks @Matthew19990919.
- Memory/plugins: move the pre-compaction memory flush plan behind the active memory plugin contract so `memory-core` owns flush prompts and target-path policy instead of hardcoded core logic.
- MiniMax: trim model catalog to M2.7 only, removing legacy M2, M2.1, M2.5, and VL-01 models. (#54487) Thanks @liyuan97.
- CLI: add `openclaw config schema` to print the generated JSON schema for `openclaw.json`. (#54523) Thanks @kvokka.
- Plugins/runtime: expose `runHeartbeatOnce` in the plugin runtime `system` namespace so plugins can trigger a single heartbeat cycle with an explicit delivery target override (e.g. `heartbeat: { target: "last" }`). (#40299) Thanks @loveyana.
- Agents/compaction: preserve the post-compaction AGENTS refresh on stale-usage preflight compaction for both immediate replies and queued followups. (#49479) Thanks @jared596.
- Agents/compaction: surface safeguard-specific cancel reasons and relabel benign manual `/compact` no-op cases as skipped instead of failed. (#51072) Thanks @afurm.
- Plugins/CLI backends: move bundled Claude CLI, Codex CLI, and Gemini CLI inference defaults onto the plugin surface, add bundled Gemini CLI backend support, and replace `gateway run --claude-cli-logs` with generic `--cli-backend-logs` while keeping the old flag as a compatibility alias.
- Plugins/startup: auto-load bundled provider and CLI-backend plugins from explicit config refs, so bundled Claude CLI, Codex CLI, and Gemini CLI message-provider setups no longer need manual `plugins.allow` entries.
### Fixes
- WhatsApp: fix infinite echo loop in self-chat DM mode where the bot's own outbound replies were re-processed as new inbound user messages. (#54570) Thanks @joelnishanth
- OpenAI Codex/image tools: register Codex for media understanding and route image prompts through Codex instructions so image analysis no longer fails on missing provider registration or missing `instructions`. (#54829) Thanks @neeravmakwana.
- Agents/image tool: restore the generic image-runtime fallback when no provider-specific media-understanding provider is registered, so image analysis works again for providers like `openrouter` and `minimax-portal`. (#54858) Thanks @MonkeyLeeT.
- Telegram: deliver verbose tool summaries inside forum topic sessions again, so threaded topic chats now match DM verbose behavior. (#43236) Thanks @frankbuild.
- BlueBubbles/CLI agents: restore inbound prompt image refs for CLI routed turns, reapply embedded runner image size guardrails, and cover both CLI image transport paths with regression tests. (#51373)
- BlueBubbles/groups: optionally enrich unnamed participant lists with local macOS Contacts names after group gating passes, so group member context can show names instead of only raw phone numbers.
- Discord/reconnect: drain stale gateway sockets, clear cached resume state before forced fresh reconnects, and fail closed when old sockets refuse to die so Discord recovery stops looping on poisoned resume state. (#54697) Thanks @ngutman.
- iMessage: stop leaking inline `[[reply_to:...]]` tags into delivered text by sending `reply_to` as RPC metadata and stripping stray directive tags from outbound messages. (#39512) Thanks @mvanhorn.
- CLI/plugins: make routed commands use the same auto-enabled bundled-channel snapshot as gateway startup, so configured bundled channels like Slack load without requiring a prior config rewrite. (#54809) Thanks @neeravmakwana.
- CLI/message send: write manual `openclaw message send` deliveries into the resolved agent session transcript again by always threading the default CLI agent through outbound mirroring. (#54187) Thanks @KevInTheCloud5617.
- CLI/onboarding: show the Kimi Code API key option again in the Moonshot setup menu so the interactive picker includes all Kimi setup paths together. Fixes #54412 Thanks @sparkyrider
- Agents/status: use provider-aware context window lookup for fresh Anthropic 4.6 model overrides so `/status` shows the correct 1.0m window instead of an underreported shared-cache minimum. (#54796) Thanks @neeravmakwana.
- Agents/errors: surface provider quota/reset details when available, but keep HTML/Cloudflare rate-limit pages on the generic fallback so raw error pages are not shown to users. (#54512) Thanks @bugkill3r.
- Agents/embedded replies: surface mid-turn 429 and overload failures when embedded runs end without a user-visible reply, while preserving successful media-only replies that still use legacy `mediaUrl`. (#50930) Thanks @infichen.
- WhatsApp/allowFrom: show a specific allowFrom policy error for valid blocked targets instead of the misleading `<E.164|group JID>` format hint. Thanks @mcaxtr.
- Agents/cooldowns: scope rate-limit cooldowns per model so one 429 no longer blocks every model on the same auth profile, replace the exponential 1 min -> 1 h escalation with a stepped 30 s / 1 min / 5 min ladder, and surface a user-facing countdown message when all models are rate-limited. (#49834) Thanks @kiranvk-2011.
- Agents/embedded transport errors: distinguish common network failures like connection refused, DNS lookup failure, and interrupted sockets from true timeouts in embedded-run user messaging and lifecycle diagnostics. (#51419) Thanks @scoootscooob.
- Telegram/pairing: ignore self-authored DM `message` updates so bot-pinned status cards and similar service updates do not trigger bogus pairing requests or re-enter inbound dispatch. (#54530) thanks @huntharo
- Mattermost/replies: keep pairing replies, slash-command fallback replies, and model-picker messages on the resolved config path so `exec:` SecretRef bot tokens work across all outbound reply branches. (#48347) thanks @mathiasnagler.
- Microsoft Teams/config: accept the existing `welcomeCard`, `groupWelcomeCard`, `promptStarters`, and feedback/reflection keys in strict config validation so already-supported Teams runtime settings stop failing schema checks. (#54679) Thanks @gumclaw.
- Plugins/SDK: thread `moduleUrl` through plugin-sdk alias resolution so user-installed plugins outside the openclaw directory (e.g. `~/.openclaw/extensions/`) correctly resolve `openclaw/plugin-sdk/*` subpath imports, and gate `plugin-sdk:check-exports` in `release:check`. (#54283) Thanks @xieyongliang.
- Config/web fetch: allow the documented `tools.web.fetch.maxResponseBytes` setting in runtime schema validation so valid configs no longer fail with unrecognized-key errors. (#53401) Thanks @erhhung.
- Message tool/buttons: keep the shared `buttons` schema optional in merged tool definitions so plain `action=send` calls stop failing validation when no buttons are provided. (#54418) Thanks @adzendo.
- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026.
- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.
- Plugins/context engines: retry strict legacy `assemble()` calls without the new `prompt` field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.
- CLI/update status: explicitly say `up to date` when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.
- Daemon/Linux: stop flagging non-gateway systemd services as duplicate gateways just because their unit files mention OpenClaw, reducing false-positive doctor/log noise. (#45328) Thanks @gregretkowski.
- Feishu: close WebSocket connections on monitor stop/abort so ghost connections no longer persist, preventing duplicate event processing and resource leaks across restart cycles. (#52844) Thanks @schumilin.
- Feishu: use the original message `create_time` instead of `Date.now()` for inbound timestamps so offline-retried messages carry the correct authoring time, preventing mis-targeted agent actions on stale instructions. (#52809) Thanks @schumilin.
- 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.
- Agents/sandbox: make blocked-tool guidance glob-aware again, redact/sanitize session-specific explain hints for safer copy-paste, and avoid leaking control-character session keys in those hints. (#54684) Thanks @ngutman.
- Agents/compaction: trigger timeout recovery compaction before retrying high-context LLM timeouts so embedded runs stop repeating oversized requests. (#46417) thanks @joeykrug.
- Agents/compaction: reconcile `sessions.json.compactionCount` after a late embedded auto-compaction success so persisted session counts catch up once the handler reports completion. (#45493) Thanks @jackal092927.
- Agents/failover: classify Codex accountId token extraction failures as auth errors so model fallback continues to the next configured candidate. (#55206) Thanks @cosmicnet.
- Talk/macOS: stop direct system-voice failures from replaying system speech, use app-locale fallback for shared watchdog timing, and add regression coverage for the macOS fallback route and language-aware timeout policy. (#53511) thanks @hongsw.
- Discord/gateway cleanup: keep late Carbon reconnect-exhausted errors suppressed through startup/dispose cleanup so Discord monitor shutdown no longer crashes on late gateway close events. (#55373) Thanks @Takhoffman.
## 2026.3.24
### Breaking
### Changes
- Gateway/OpenAI compatibility: add `/v1/models` and `/v1/embeddings`, and forward explicit model overrides through `/v1/chat/completions` and `/v1/responses` for broader client and RAG compatibility. Thanks @vincentkoc.
- Agents/tools: make `/tools` show the tools the current agent can actually use right now, add a compact default view with an optional detailed mode, and add a live "Available Right Now" section in the Control UI so it is easier to see what will work before you ask.
- Microsoft Teams: migrate to the official Teams SDK and add AI-agent UX best practices including streaming 1:1 replies, welcome cards with prompt starters, feedback/reflection, informative status updates, typing indicators, and native AI labeling. (#51808)
- Microsoft Teams: add message edit and delete support for sent messages, including in-thread fallbacks when no explicit target is provided. (#49925)
- Skills/install metadata: add one-click install recipes to bundled skills (coding-agent, gh-issues, openai-whisper-api, session-logs, tmux, trello, weather) so the CLI and Control UI can offer dependency installation when requirements are missing. (#53411) Thanks @BunsDev.
- Control UI/skills: add status-filter tabs (All / Ready / Needs Setup / Disabled) with counts, replace inline skill cards with a click-to-detail dialog showing requirements, toggle switch, install action, API key entry, source metadata, and homepage link. (#53411) Thanks @BunsDev.
- Slack/interactive replies: restore rich reply parity for direct deliveries, auto-render simple trailing `Options:` lines as buttons/selects, improve Slack interactive setup defaults, and isolate reply controls from plugin interactive handlers. (#53389) Thanks @vincentkoc.
- CLI/containers: add `--container` and `OPENCLAW_CONTAINER` to run `openclaw` commands inside a running Docker or Podman OpenClaw container. (#52651) Thanks @sallyom.
- Discord/auto threads: add optional `autoThreadName: "generated"` naming so new auto-created threads can be renamed asynchronously with concise LLM-generated titles while keeping the existing message-based naming as the default. (#43366) Thanks @davidguttman.
- Plugins/hooks: add `before_dispatch` with canonical inbound metadata and route handled replies through the normal final-delivery path, preserving TTS and routed delivery semantics. (#50444) Thanks @gfzhx.
- Control UI/agents: convert agent workspace file rows to expandable `<details>` with lazy-loaded inline markdown preview, and add comprehensive `.sidebar-markdown` styles for headings, lists, code blocks, tables, blockquotes, and details/summary elements. (#53411) Thanks @BunsDev.
- Control UI/markdown preview: restyle the agent workspace file preview dialog with a frosted backdrop, sized panel, and styled header, and integrate `@create-markdown/preview` v2 system theme for rich markdown rendering (headings, tables, code blocks, callouts, blockquotes) that auto-adapts to the app's light/dark design tokens. (#53411) Thanks @BunsDev.
- macOS app/config: replace horizontal pill-based subsection navigation with a collapsible tree sidebar using disclosure chevrons and indented subsection rows. (#53411) Thanks @BunsDev.
- CLI/skills: soften missing-requirements label from "missing" to "needs setup" and surface API key setup guidance (where to get a key, CLI save command, storage path) in `openclaw skills info` output. (#53411) Thanks @BunsDev.
- macOS app/skills: add "Get your key" homepage link and storage-path hint to the API key editor dialog, and show the config path in save confirmation messages. (#53411) Thanks @BunsDev.
- Control UI/agents: add a "Not set" placeholder to the default agent model selector dropdown. (#53411) Thanks @BunsDev.
- Runtime/install: lower the supported Node 22 floor to `22.14+` while continuing to recommend Node 24, so npm installs and self-updates do not strand Node 22.14 users on older releases.
- CLI/update: preflight the target npm package `engines.node` before `openclaw update` runs a global package install, so outdated Node runtimes fail with a clear upgrade message instead of attempting an unsupported latest release.
### Fixes
- Outbound media/local files: align outbound media access with the configured fs policy so host-local files and inbound-media paths keep sending when `workspaceOnly` is off, while strict workspace-only agents remain sandboxed.
- Security/sandbox media dispatch: close the `mediaUrl`/`fileUrl` alias bypass so outbound tool and message actions cannot escape media-root restrictions. (#54034)
- Gateway/restart sentinel: wake the interrupted agent session via heartbeat after restart instead of only sending a best-effort restart note, retry outbound delivery once on transient failure, and preserve explicit thread/topic routing through the wake path so replies land in the correct Telegram topic or Slack thread. (#53940) Thanks @VACInc.
- Docker/setup: avoid the pre-start `openclaw-cli` shared-network namespace loop by routing setup-time onboard/config writes through `openclaw-gateway`, so fresh Docker installs stop failing before the gateway comes up. (#53385) Thanks @amsminn.
- Gateway/channels: keep channel startup sequential while isolating per-channel boot failures, so one broken channel no longer blocks later channels from starting. (#54215) Thanks @JonathanJing.
- Embedded runs/secrets: stop unresolved `SecretRef` config from crashing embedded agent runs by falling back to the resolved runtime snapshot when needed. Fixes #45838.
- WhatsApp/groups: track recent gateway-sent message IDs and suppress only matching group echoes, preserving owner `/status`, `/new`, and `/activation` commands from linked-account `fromMe` traffic. (#53624) Thanks @w-sss.
- WhatsApp/reply-to-bot detection: restore implicit group reply detection by unwrapping `botInvokeMessage` payloads and reading `selfLid` from `creds.json`, so reply-based mentions reach the bot again in linked-account group chats.
- Telegram/forum topics: recover `#General` topic `1` routing when Telegram omits forum metadata, including native commands, interactive callbacks, inbound message context, and fallback error replies. (#53699) thanks @huntharo
- Discord/gateway supervision: centralize gateway error handling behind a lifetime-owned supervisor so early, active, and late-teardown Carbon gateway errors stay classified consistently and stop surfacing as process-killing teardown crashes.
- Discord/timeouts: send a visible timeout reply when the inbound Discord worker times out before a final reply starts, including created auto-thread targets and queued-run ordering. (#53823) Thanks @Kimbo7870.
- ACP/direct chats: always deliver a terminal ACP result when final TTS does not yield audio, even if block text already streamed earlier, and skip redundant empty-text final synthesis. (#53692) Thanks @w-sss.
- Telegram/outbound errors: preserve actionable 403 membership/block/kick details and treat `bot not a member` as a permanent delivery failure so Telegram sends stop retrying doomed chats. (#53635) Thanks @w-sss.
- Telegram/photos: preflight Telegram photo dimension and aspect-ratio rules, and fall back to document sends when image metadata is invalid or unavailable so photo uploads stop failing with `PHOTO_INVALID_DIMENSIONS`. (#52545) Thanks @hnshah.
- Slack/runtime defaults: trim Slack DM reply overhead, restore Codex auto transport, and tighten Slack/web-search runtime defaults around DM preview threading, cache scoping, warning dedupe, and explicit web-search opt-in. (#53957) Thanks @vincentkoc.
## 2026.3.24-beta.2
### Breaking
### Changes
### Fixes
- Outbound media/local files: align outbound media access with the configured fs policy so host-local files and inbound-media paths keep sending when `workspaceOnly` is off, while strict workspace-only agents remain sandboxed.
- Runtime/install: lower the supported Node 22 floor to `22.14+` while continuing to recommend Node 24, so npm installs and self-updates do not strand Node 22.14 users on older releases.
- CLI/update: preflight the target npm package `engines.node` before `openclaw update` runs a global package install, so outdated Node runtimes fail with a clear upgrade message instead of attempting an unsupported latest release.
- Tests/security audit: isolate audit-test home and personal skill resolution so local `~/.agents/skills` installs no longer make maintainer prep runs fail nondeterministically. (#54473) thanks @huntharo
## 2026.3.24-beta.1
### Breaking
### Changes
- Gateway/OpenAI compatibility: add `/v1/models` and `/v1/embeddings`, and forward explicit model overrides through `/v1/chat/completions` and `/v1/responses` for broader client and RAG compatibility. Thanks @vincentkoc.
- Agents/tools: make `/tools` show the tools the current agent can actually use right now, add a compact default view with an optional detailed mode, and add a live “Available Right Now” section in the Control UI so it is easier to see what will work before you ask.
- Microsoft Teams: migrate to the official Teams SDK and add AI-agent UX best practices including streaming 1:1 replies, welcome cards with prompt starters, feedback/reflection, informative status updates, typing indicators, and native AI labeling. (#51808)
- Microsoft Teams: add message edit and delete support for sent messages, including in-thread fallbacks when no explicit target is provided. (#49925)
- Skills/install metadata: add one-click install recipes to bundled skills (coding-agent, gh-issues, openai-whisper-api, session-logs, tmux, trello, weather) so the CLI and Control UI can offer dependency installation when requirements are missing. (#53411) Thanks @BunsDev.
- Control UI/skills: add status-filter tabs (All / Ready / Needs Setup / Disabled) with counts, replace inline skill cards with a click-to-detail dialog showing requirements, toggle switch, install action, API key entry, source metadata, and homepage link. (#53411) Thanks @BunsDev.
- Slack/interactive replies: restore rich reply parity for direct deliveries, auto-render simple trailing `Options:` lines as buttons/selects, improve Slack interactive setup defaults, and isolate reply controls from plugin interactive handlers. (#53389) Thanks @vincentkoc.
- CLI/containers: add `--container` and `OPENCLAW_CONTAINER` to run `openclaw` commands inside a running Docker or Podman OpenClaw container. (#52651) Thanks @sallyom.
- Discord/auto threads: add optional `autoThreadName: "generated"` naming so new auto-created threads can be renamed asynchronously with concise LLM-generated titles while keeping the existing message-based naming as the default. (#43366) Thanks @davidguttman.
- Plugins/hooks: add `before_dispatch` with canonical inbound metadata and route handled replies through the normal final-delivery path, preserving TTS and routed delivery semantics. (#50444) Thanks @gfzhx.
- Control UI/agents: convert agent workspace file rows to expandable `<details>` with lazy-loaded inline markdown preview, and add comprehensive `.sidebar-markdown` styles for headings, lists, code blocks, tables, blockquotes, and details/summary elements. (#53411) Thanks @BunsDev.
- Control UI/markdown preview: restyle the agent workspace file preview dialog with a frosted backdrop, sized panel, and styled header, and integrate `@create-markdown/preview` v2 system theme for rich markdown rendering (headings, tables, code blocks, callouts, blockquotes) that auto-adapts to the app's light/dark design tokens. (#53411) Thanks @BunsDev.
- macOS app/config: replace horizontal pill-based subsection navigation with a collapsible tree sidebar using disclosure chevrons and indented subsection rows. (#53411) Thanks @BunsDev.
- CLI/skills: soften missing-requirements label from "missing" to "needs setup" and surface API key setup guidance (where to get a key, CLI save command, storage path) in `openclaw skills info` output. (#53411) Thanks @BunsDev.
- macOS app/skills: add "Get your key" homepage link and storage-path hint to the API key editor dialog, and show the config path in save confirmation messages. (#53411) Thanks @BunsDev.
- Control UI/agents: add a "Not set" placeholder to the default agent model selector dropdown. (#53411) Thanks @BunsDev.
### Fixes
- Security/sandbox media dispatch: close the `mediaUrl`/`fileUrl` alias bypass so outbound tool and message actions cannot escape media-root restrictions. (#54034)
- Gateway/restart sentinel: wake the interrupted agent session via heartbeat after restart instead of only sending a best-effort restart note, retry outbound delivery once on transient failure, and preserve explicit thread/topic routing through the wake path so replies land in the correct Telegram topic or Slack thread. (#53940) Thanks @VACInc.
- Docker/setup: avoid the pre-start `openclaw-cli` shared-network namespace loop by routing setup-time onboard/config writes through `openclaw-gateway`, so fresh Docker installs stop failing before the gateway comes up. (#53385) Thanks @amsminn.
- Gateway/channels: keep channel startup sequential while isolating per-channel boot failures, so one broken channel no longer blocks later channels from starting. (#54215) Thanks @JonathanJing.
- Embedded runs/secrets: stop unresolved `SecretRef` config from crashing embedded agent runs by falling back to the resolved runtime snapshot when needed. Fixes #45838.
- WhatsApp/groups: track recent gateway-sent message IDs and suppress only matching group echoes, preserving owner `/status`, `/new`, and `/activation` commands from linked-account `fromMe` traffic. (#53624) Thanks @w-sss.
- WhatsApp/reply-to-bot detection: restore implicit group reply detection by unwrapping `botInvokeMessage` payloads and reading `selfLid` from `creds.json`, so reply-based mentions reach the bot again in linked-account group chats.
- Telegram/forum topics: recover `#General` topic `1` routing when Telegram omits forum metadata, including native commands, interactive callbacks, inbound message context, and fallback error replies. (#53699) thanks @huntharo
- Discord/gateway supervision: centralize gateway error handling behind a lifetime-owned supervisor so early, active, and late-teardown Carbon gateway errors stay classified consistently and stop surfacing as process-killing teardown crashes.
- Discord/timeouts: send a visible timeout reply when the inbound Discord worker times out before a final reply starts, including created auto-thread targets and queued-run ordering. (#53823) Thanks @Kimbo7870.
- ACP/direct chats: always deliver a terminal ACP result when final TTS does not yield audio, even if block text already streamed earlier, and skip redundant empty-text final synthesis. (#53692) Thanks @w-sss.
- Telegram/outbound errors: preserve actionable 403 membership/block/kick details and treat `bot not a member` as a permanent delivery failure so Telegram sends stop retrying doomed chats. (#53635) Thanks @w-sss.
- Telegram/photos: preflight Telegram photo dimension and aspect-ratio rules, and fall back to document sends when image metadata is invalid or unavailable so photo uploads stop failing with `PHOTO_INVALID_DIMENSIONS`. (#52545) Thanks @hnshah.
- Slack/runtime defaults: trim Slack DM reply overhead, restore Codex auto transport, and tighten Slack/web-search runtime defaults around DM preview threading, cache scoping, warning dedupe, and explicit web-search opt-in. (#53957) Thanks @vincentkoc.
- Doctor/image generation: seed migrated legacy Nano Banana Google provider config with the `/v1beta` API root and an empty model list so `openclaw doctor --fix` completes and the migrated native Google image path keeps hitting the correct endpoint. (#53757) Thanks @mahopan.
- Models/google: normalize bare Google Generative AI API roots for custom provider names, and keep built-in Google model-id rewrites working when `api` is declared only on individual models, so custom Google lanes and older configs stop missing `/v1beta` or preview-id normalization. (#44969) Thanks @Kathie-yu.
- Feishu/startup: treat unresolved `SecretRef` app credentials as not configured during account resolution so CLI startup and read-only Feishu config surfaces stop crashing before runtime-backed secret resolution is available. (#53675) Thanks @hpt.
- Feishu/groups: when `groupPolicy` is `open`, stop implicitly requiring @mentions for unset `requireMention`, so image, file, audio, and other non-text group messages reach the bot unless operators explicitly keep mention gating on. (#54058) Thanks @byungsker.
- Feishu/startup: keep `requireMention` enforcement strict when bot identity startup probes fail, raise the startup bot-info timeout to 30s, and add cancellable background identity recovery so mention-gated groups recover without noisy fallback. (#43788) Thanks @lefarcen.
- Feishu/MSTeams message tool: keep provider-native `card` payloads optional in merged tool schemas so media-only sends stop failing validation before channel runtime dispatch. (#53715) Thanks @lndyzwdxhs.
- Feishu/docx block ordering: preserve the document tree order from `docx.document.convert` when inserting blocks, fixing heading/paragraph/list misordering in newly written Feishu documents. (#40524) Thanks @TaoXieSZ.
- Telegram/native commands: run native slash-command execution against the resolved runtime snapshot so DM commands still reply when fresh config reads surface unresolved SecretRefs. (#53179) Thanks @nimbleenigma.
- Gateway/ports: parse Docker Compose-style `OPENCLAW_GATEWAY_PORT` host publish values correctly without reviving the legacy `CLAWDBOT_GATEWAY_PORT` override. (#44083) Thanks @bebule.
- Plugins/memory-lancedb: bootstrap the env-configured HTTP/HTTPS proxy dispatcher before OpenAI embeddings requests so memory capture and recall work in proxy-required environments again. (#54119) Thanks @neeravmakwana.
- Runtime/build: stabilize long-lived lazy `dist` runtime entry paths and harden bundled plugin npm staging so local rebuilds stop breaking on missing hashed chunks or broken shell `npm` shims. (#53855) Thanks @vincentkoc.
- Security/skills: validate skill installer metadata against strict regex allowlists per package manager, sanitize skill metadata for terminal output, add URL protocol allowlisting in markdown preview and skill homepage links, warn on non-bundled skill install sources, and remove unsafe `file://` workspace links. (#53471) Thanks @BunsDev.
- Memory/builtin sqlite: cut redundant sync and status query churn by snapshotting file state once per source, reusing sync statements, and consolidating status aggregation reads, which reduces builtin memory overhead on sync/status/doctor-style paths. Thanks @vincentkoc.
- TUI/chat: preserve pending user messages when a slow local run emits an empty final event, but still defer and flush the needed history reload after the newer active run finishes so silent/tool-only runs do not stay incomplete. (#53130) Thanks @joelnishanth.
- DeepSeek/pricing: replace the zero-cost DeepSeek catalog rates with the current DeepSeek V3.2 pricing so usage totals stop showing `$0.00` for DeepSeek sessions. (#54143) Thanks @arkyu2077.
- CLI/logging: make pretty log timestamps always include an explicit timezone offset in default UTC and `--local-time` modes, so incident triage no longer mixes ambiguous clock displays. (#38904) Thanks @sahilsatralkar.
- Browser/default detection: recognize macOS LaunchServices Edge bundle ids so default Chromium detection stops falling back to Chrome when Edge is the system default. (#48561) Thanks @zoherghadyali.
- CLI/Telegram topics: route `message thread create` through Telegram `topic-create` with the required topic `name` field so Telegram forum topic creation works from the CLI again. (#54336) Thanks @andyliu.
- Telegram/pairing: render pairing codes and approval commands as Telegram-only code blocks while keeping shared pairing replies plain text for other channels. (#52784) Thanks @sumukhj1219.
- Agents/cron: suppress the default heartbeat system prompt for cron-triggered embedded runs even when they target non-cron session keys, so cron tasks stop reading `HEARTBEAT.md` and polluting unrelated threads. (#53152) Thanks @Protocol-zero-0.
- Agents/cron: mark best-effort announce runs as not delivered when any payload fails, and log those partial delivery failures instead of silently reporting success. (#42535) Thanks @MoerAI.
- Plugins: enforce terminal hook decision semantics for tool/message guards (#54241) Thanks @joshavant.
- Marketplace/agents: correct the ClawHub skill URL in agent docs and stream marketplace archive downloads to disk so installs avoid excess memory use and fail cleanly on empty responses. (#54160) Thanks @QuinnH496.
- Discord/config types: add missing `autoArchiveDuration` to `DiscordGuildChannelConfig` so TypeScript config definitions match the existing schema and runtime support. (#43427) Thanks @davidguttman.
- Docs/IRC: fix five `json55` code-fence typos in the IRC channel examples so Mintlify applies JSON5 syntax highlighting correctly. (#50842) Thanks @Hollychou924.
- Discord/commands: trim overlong slash-command descriptions to Discord's 100-character limit and map rejected deploy indexes from Discord validation payloads back to command names/descriptions, so deploys stop failing on long descriptions and startup logs identify the rejected commands. (#54118) thanks @huntharo
## 2026.3.23
### Breaking
### Changes
- ModelStudio/Qwen: add standard (pay-as-you-go) DashScope endpoints for China and global Qwen API keys alongside the existing Coding Plan endpoints, and relabel the provider group to `Qwen (Alibaba Cloud Model Studio)`. (#43878)
- UI/clarity: consolidate button primitives (`btn--icon`, `btn--ghost`, `btn--xs`), refine the Knot theme to a black-and-red palette with WCAG 2.1 AA contrast, add config icons for Diagnostics/CLI/Secrets/ACP/MCP sections, replace the roundness slider with discrete stops, and improve accessibility with aria-labels across usage filters. (#53272) Thanks @BunsDev.
- CSP/Control UI: compute SHA-256 hashes for inline `<script>` blocks in the served `index.html` and include them in the `script-src` CSP directive, keeping inline scripts blocked by default while allowing explicitly hashed bootstrap code. (#53307) Thanks @BunsDev.
### Fixes
- Plugins/bundled runtimes: ship bundled plugin runtime sidecars like WhatsApp `light-runtime-api.js`, Matrix `runtime-api.js`, and other plugin runtime entry files in the npm package again, so global installs stop failing on missing bundled plugin runtime surfaces.
- CLI/channel auth: auto-select the single configured login-capable channel for `channels login`/`logout`, harden channel ids against prototype-chain and control-character abuse, and fall back cleanly to catalog-backed channel installs, so channel auth works again for single-channel setups and on-demand channel installs. (#53254) Thanks @BunsDev.
- Auth/OpenAI tokens: stop live gateway auth-profile writes from reverting freshly saved credentials back to stale in-memory values, and make `models auth paste-token` write to the resolved agent store, so Configure, Onboard, and token-paste flows stop snapping back to expired OpenAI tokens. Fixes #53207. Related to #45516.
- Control UI/auth: preserve operator scopes through the device-auth bypass path, ignore cached under-scoped operator tokens, and show a clear `operator.read` fallback message when a connection really lacks read scope, so operator sessions stop failing or blanking on read-backed pages. (#53110) Thanks @BunsDev.
- Plugins/ClawHub: resolve plugin API compatibility against the active runtime version at install time, and add regression coverage for current `>=2026.3.22` ClawHub package checks so installs no longer fail behind the stale `1.2.0` constant. (#53157) Thanks @futhgar.
- Plugins/uninstall: accept installed `clawhub:` specs and versionless ClawHub package names as uninstall targets, so `openclaw plugins uninstall clawhub:<package>` works again even when the recorded install was pinned to a version.
- Browser/Chrome MCP: wait for existing-session browser tabs to become usable after attach instead of treating the initial Chrome MCP handshake as ready, which reduces user-profile timeouts and repeated consent churn on macOS Chrome attach flows. Fixes #52930. Thanks @vincentkoc.
- Browser/CDP: reuse an already-running loopback browser after a short initial reachability miss instead of immediately falling back to relaunch detection, which fixes second-run browser start/open regressions on slower headless Linux setups. Fixes #53004. Thanks @vincentkoc.
- Agents/web_search: use the active runtime `web_search` provider instead of stale/default selection, so agent turns keep hitting the provider you actually configured. Fixes #53020. Thanks @jzakirov.
- Mistral/models: lower bundled Mistral max-token defaults to safe output budgets and teach `openclaw doctor --fix` to repair old persisted Mistral provider configs that still carry context-sized output limits, avoiding deterministic Mistral 422 rejects on fresh and existing setups. Fixes #52599. Thanks @vincentkoc.
- ClawHub/macOS auth: honor macOS auth config and XDG auth paths for saved ClawHub credentials, so `openclaw skills ...` and gateway skill browsing keep using the signed-in auth state instead of silently falling back to unauthenticated mode. Fixes #53034.
- ClawHub/macOS: read the local ClawHub login from the macOS Application Support path and still honor XDG config on macOS, so skill browsing uses the logged-in token on both default and XDG-style setups. Fixes #52949. Thanks @scoootscooob.
- ClawHub/skills: resolve the local ClawHub auth token for gateway skill browsing and switch browse-all requests to search so ClawControl stops falling into unauthenticated 429s and empty authenticated skill lists. Fixes #52949. Thanks @vincentkoc.
- Config/warnings: suppress the confusing “newer OpenClaw” warning when a config written by a same-base correction release like `2026.3.23-2` is read by `2026.3.23`, while still warning for truly newer or incompatible versions.
- CLI/cron: make `openclaw cron add|edit --at ... --tz <iana>` honor the requested local wall-clock time for offset-less one-shot datetimes, including DST boundaries, and keep `--tz` rejected for `--every`. (#53224) Thanks @RolfHegr.
- Commands/auth: stop slash-command authorization from crashing or dropping valid allowlists when channel `allowFrom` resolution hits unresolved SecretRef-backed accounts, and fail closed only for the affected provider inference path. (#52791) Thanks @Lukavyi.
- Agents/failover: classify generic `api_error` payloads as retryable only when they include transient failure signals, so MiniMax-style backend failures still trigger model fallback without misclassifying billing, auth, or format/context errors. (#49611) Thanks @ayushozha.
- LINE/runtime-api: pre-export overlapping runtime symbols before the `line-runtime` star export so jiti no longer throws `TypeError: Cannot redefine property` on startup. (#53221) Thanks @Drickon.
- Telegram/threading: populate `currentThreadTs` in the threading tool-context fallback for Telegram DM topics so thread-aware tools still receive the active topic context when the main thread metadata is missing. (#52217)
- Diagnostics/cache trace: strip credential fields from cache-trace JSONL output while preserving non-sensitive diagnostic fields and image redaction metadata.
- Docs/Feishu: replace `botName` with `name` in the channel config examples so the docs match the strict account schema for per-account display names. (#52753) Thanks @haroldfabla2-hue.
- Doctor/plugins: make `openclaw doctor --fix` remove stale `plugins.allow` and `plugins.entries` refs left behind after plugin removal. Thanks @sallyom
- Agents/replay: canonicalize malformed assistant transcript content before session-history sanitization so legacy or corrupted assistant turns stop crashing Pi replay and subagent recovery paths.
- ClawHub/skills: keep updating already-tracked legacy Unicode slugs after the ASCII-only slug hardening, so older installs do not get stuck behind `Invalid skill slug` errors during `openclaw skills update`. (#53206) Thanks @drobison00.
- Infra/exec trust: preserve shell-multiplexer wrapper binaries for policy checks without breaking approved-command reconstruction, so BusyBox/ToyBox allowlist and audit flows bind to the real wrapper while execution plans stay coherent. (#53134) Thanks @vincentkoc.
- Plugins/message tool: make Discord `components` and Slack `blocks` optional again, and route Feishu `message(..., media=...)` sends through the outbound media path, so pin/unpin/react flows stop failing schema validation and Feishu file/image attachments actually send. Fixes #52970 and #52962. Thanks @vincentkoc.
- Gateway/model pricing: stop `openrouter/auto` pricing refresh from recursing indefinitely during bootstrap, so OpenRouter auto routes can populate cached pricing and `usage.cost` again. Fixes #53035. Thanks @vincentkoc.
- Models/OpenAI Codex OAuth: bootstrap the env-configured HTTP/HTTPS proxy dispatcher on the stored-credential refresh path before token renewal runs, so expired Codex OAuth profiles can refresh successfully in proxy-required environments instead of locking users out after the first token expiry.
- Models/OpenAI Codex OAuth and Plugins/MiniMax OAuth: ensure env-configured HTTP/HTTPS proxy dispatchers are initialized before OAuth preflight and token exchange requests so proxy-required environments can complete MiniMax and OpenAI Codex sign-in flows again. (#52228; fixes #51619, #51569) Thanks @openperf.
- Plugins/memory-lancedb: bootstrap LanceDB into plugin runtime state on first use when the bundled npm install does not already have it, so `plugins.slots.memory="memory-lancedb"` works again after global npm installs without moving LanceDB into OpenClaw core dependencies. Fixes #26100.
- Config/plugins: treat stale unknown `plugins.allow` ids as warnings instead of fatal config errors, so recovery commands like `plugins install`, `doctor --fix`, and `status` still run when a plugin is missing locally. Fixes #52992. Thanks @vincentkoc.
- Doctor/WhatsApp: stop auto-enable from appending built-in channel ids like `whatsapp` to `plugins.allow`, so `openclaw doctor --fix` no longer writes schema-invalid plugin allowlist entries when repairing built-in channels. Fixes #52931. Thanks @vincentkoc.
- Telegram/auto-reply: preserve same-chat inbound debounce order without stranding stale busy-session followups, and keep same-key overflow turns ordered when tracked debounce keys are saturated. (#52998) Thanks @osolmaz.
- Telegram/message tool: add `asDocument` as a user-facing alias for `forceDocument` on image and GIF sends, while preserving explicit `forceDocument` precedence when both flags are present. (#52461) Thanks @bakhtiersizhaev.
- Discord/commands: return an explicit unauthorized reply for privileged native slash commands instead of falling through to Discord's misleading generic completion when auth gates reject the sender. Fixes #53041. Thanks @scoootscooob.
- Channels/catalog: let external channel catalogs override shipped fallback metadata and honor overridden npm specs during channel setup, so custom channel catalogs no longer fall back to bundled packages when a channel id matches. (#52988)
- Voice-call/Plivo: stabilize Plivo v2 replay keys so webhook retries and replay protection stop colliding on valid follow-up deliveries.
- Agents/skills: prefer the active resolved runtime snapshot for embedded skill config and env injection, so `skills.entries.<skill>.apiKey` SecretRefs resolve correctly during embedded startup instead of failing on raw source config. Fixes #53098. Thanks @vincentkoc.
- Agents/subagents: recheck timed-out worker waits against the latest runtime snapshot before sending completion events, so fast-finishing workers stop being reported as timed out when they actually succeeded. Fixes #53106. Thanks @vincentkoc.
- Agents/Anthropic: preserve latest assistant thinking and redacted-thinking block ordering during transcript image sanitization so follow-up turns do not trip Anthropic's unmodified-thinking validation. (#52961) Thanks @vincentkoc.
- Plugins/DeepSeek: refactor the bundled DeepSeek provider onto the shared single-provider plugin entry, move its coverage into the extension test lane, and keep bundled auth env-var metadata on the generated manifest path. (#48762) Thanks @07akioni.
- Plugins/Matrix: avoid duplicate `resolveMatrixAccountStringValues` runtime-api exports under Jiti so bundled Matrix installs no longer crash at startup with `Cannot redefine property: resolveMatrixAccountStringValues`. Fixes #52909 and #52891. Thanks @vincentkoc.
- Security/exec approvals: keep shell-wrapper positional-argv allowlist matching on real direct carriers only by rejecting single-quoted `$0`/`$n` tokens, disallowing newline-separated `exec`, and still accepting `exec --` carrier forms. Thanks @vincentkoc.
- Gateway/probe: stop successful gateway handshakes from timing out as unreachable while post-connect detail RPCs are still loading, so slow devices report a reachable RPC failure instead of a false negative dead gateway. Fixes #52927. Thanks @vincentkoc.
- Gateway/supervision: stop lock conflicts from crash-looping under launchd and systemd by keeping the duplicate process in a retry wait instead of exiting as a failure while another healthy gateway still owns the lock. Fixes #52922. Thanks @vincentkoc.
- Gateway/auth: require auth for canvas routes and admin scope for agent session reset, so anonymous canvas access and non-admin reset requests fail closed.
- Release/install: keep previously released bundled plugins and Control UI assets in published openclaw npm installs, and fail release checks when those shipped artifacts are missing. Thanks @vincentkoc.
- WhatsApp/outbound sends: keep the active Web listener on a direct process-global symbol so split runtime chunks keep sharing the connected Baileys session and `openclaw message send --channel whatsapp` stops failing after connect. Fixes #52574. Thanks @MonkeyLeeT.
- Agents/process: fail loud when `send-keys` tries cursor-sensitive keys before a background PTY reports its cursor mode, so startup races no longer silently send the wrong arrow/Home/End sequences. (#51490) Thanks @liuy.
## 2026.3.22
### Breaking
- Plugins/install: bare `openclaw plugins install <package>` now prefers ClawHub before npm for npm-safe names, and only falls back to npm when ClawHub does not have that package or version. Docs: https://docs.openclaw.ai/tools/clawhub
- Browser/Chrome MCP: remove the legacy Chrome extension relay path, bundled extension assets, `driver: "extension"`, and `browser.relayBindHost`. Run `openclaw doctor --fix` to migrate host-local browser config to `existing-session` / `user`; Docker, headless, sandbox, and remote browser flows still use raw CDP. Docs: https://docs.openclaw.ai/gateway/doctor and https://docs.openclaw.ai/tools/browser (#47893) Thanks @vincentkoc.
- Tools/image generation: standardize the stock image create/edit path on the core `image_generate` tool. The old `nano-banana-pro` docs/examples are gone; if you previously copied that sample-skill config, switch to `agents.defaults.imageGenerationModel` for built-in image generation or install a separate third-party skill explicitly.
- Skills/image generation: remove the bundled `nano-banana-pro` skill wrapper. Use `agents.defaults.imageGenerationModel.primary: "google/gemini-3-pro-image-preview"` for the native Nano Banana-style path instead.
- Plugins/SDK: the new public plugin SDK surface is `openclaw/plugin-sdk/*`; `openclaw/extension-api` is removed with no compatibility shim. Bundled plugins must use injected runtime for host-side operations (for example `api.runtime.agent.runEmbeddedPiAgent`) and any remaining direct imports must come from narrow `openclaw/plugin-sdk/*` subpaths instead of the monolithic SDK root. Docs: https://docs.openclaw.ai/plugins/sdk-migration and https://docs.openclaw.ai/plugins/sdk-overview
- Plugins/message discovery: require `ChannelMessageActionAdapter.describeMessageTool(...)` for shared `message` tool discovery. The legacy `listActions`, `getCapabilities`, and `getToolSchema` adapter methods are removed. Plugin authors should migrate message discovery to `describeMessageTool(...)` and keep channel-specific action runtime code inside the owning plugin package. Thanks @gumadeiras.
- Plugins/Matrix: add a new Matrix plugin backed by the official `matrix-js-sdk`. If you are upgrading from the previous public Matrix plugin, follow the migration guide: https://docs.openclaw.ai/install/migrating-matrix Thanks @gumadeiras.
- Config/env: remove legacy `CLAWDBOT_*` and `MOLTBOT_*` compatibility env names across runtime, installers, and test tooling. Use the matching `OPENCLAW_*` env names instead.
- Config/state: remove legacy `.moltbot` state-dir and `moltbot.json` auto-detection/migration fallback. If you still keep state under `~/.moltbot`, move it to `~/.openclaw` or set `OPENCLAW_STATE_DIR` / `OPENCLAW_CONFIG_PATH` explicitly. Docs: https://docs.openclaw.ai/install/migrating and https://docs.openclaw.ai/start/getting-started
- Exec/env sandbox: block build-tool JVM injection (`MAVEN_OPTS`, `SBT_OPTS`, `GRADLE_OPTS`, `ANT_OPTS`), glibc tunable exploitation (`GLIBC_TUNABLES`), and .NET dependency resolution hijack (`DOTNET_ADDITIONAL_DEPS`) from the host exec environment, and restrict Gradle init script redirect (`GRADLE_USER_HOME`) as an override-only block so user-configured Gradle homes still propagate. (#49702)
- Discord/commands: switch native command deployment to Carbon reconcile by default so Discord restarts stop churning slash commands through OpenClaws local deploy path. (#46597) Thanks @huntharo and @thewilloftheshadow.
- Security/exec approvals: treat `time` as a transparent dispatch wrapper during allowlist evaluation and allow-always persistence so approved `time ...` commands bind the inner executable instead of the wrapper path. Thanks @YLChen-007 for reporting.
- Voice-call/webhooks: reject missing provider signature headers before body reads, drop the pre-auth body budget to `64 KB` / `5s`, and cap concurrent pre-auth requests per source IP so unauthenticated callers cannot force the old `1 MB` / `30s` buffering path. Thanks @SEORY0 for reporting.
- Plugins/Matrix: stop mention-gated or otherwise dropped room chatter from refreshing focused thread bindings before the message is actually routed, so idle ACP and session bindings can still expire normally in mention-required rooms. Thanks @vincentkoc, @dinakars777 and @mvanhorn.
- Plugins/Matrix: durably dedupe inbound room events across gateway restarts so previously handled Matrix messages are not replayed as new, while preserving clean-restart backlog delivery for unseen events. (#50922) thanks @gumadeiras
- Agents/media replies: migrate the remaining browser, canvas, and nodes snapshot outputs onto `details.media` so generated media keeps attaching to assistant replies after the collect-then-attach refactor. (#51731) Thanks @christianklotz.
- Android/contacts search: escape literal `%` and `_` in contact-name queries so searches like `100%` or `_id` no longer match unrelated contacts through SQL `LIKE` wildcards. (#41891) Thanks @Kaneki-x.
- Gateway/usage: include reset and deleted archived session transcripts in usage totals, session discovery, and archived-only session detail fallback so the Usage view no longer undercounts rotated sessions. (#43215) Thanks @rcrick.
### Changes
- ClawHub/install: add native `openclaw skills search|install|update` flows plus `openclaw plugins install clawhub:<package>` with tracked update metadata, gateway skill-install/update support for ClawHub-backed requests, and regression coverage/docs for the new source path.
- Plugins/marketplaces: add Claude marketplace registry resolution, `plugin@marketplace` installs, marketplace listing, and update support, plus Docker E2E coverage for local and official marketplace flows. (#48058) Thanks @vincentkoc.
- Commands/plugins: add owner-gated `/plugins` and `/plugin` chat commands for plugin list/show and enable/disable flows, alongside explicit `commands.plugins` config gating. Thanks @vincentkoc.
- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs. (#47630) Thanks @vincentkoc.
- Plugins/bundles: add compatible Codex, Claude, and Cursor bundle discovery/install support, map bundle skills into OpenClaw skills, and apply Claude bundle `settings.json` defaults to embedded Pi with shell overrides sanitized.
- CLI/hooks: route hook-pack install and update through `openclaw plugins`, keep `openclaw hooks` focused on hook visibility and per-hook controls, and show plugin-managed hook details in CLI output.
- Models/OpenAI: switch the default OpenAI setup model to `openai/gpt-5.4`, keep Codex on `openai-codex/gpt-5.4`, and centralize OpenAI chat, image, TTS, transcription, and embedding defaults in one shared module so future default-model updates stay low-churn. Thanks @vincentkoc.
- Agents: add per-agent thinking/reasoning/fast defaults and auto-revert disallowed model overrides to the agent's default selection. Thanks @xuanmingguo and @vincentkoc.
- Commands/btw: add `/btw` side questions for quick tool-less answers about the current session without changing future session context, with dismissible in-session TUI answers and explicit BTW replies on external channels. (#45444) Thanks @ngutman.
- Sandbox/runtime: add pluggable sandbox backends, ship an OpenShell backend with `mirror` and `remote` workspace modes, and make sandbox list/recreate/prune backend-aware instead of Docker-only.
- Sandbox/SSH: add a core SSH sandbox backend with secret-backed key, certificate, and known_hosts inputs, move shared remote exec/filesystem tooling into core, and keep OpenShell focused on sandbox lifecycle plus optional `mirror` mode.
- Browser/existing-session: support `browser.profiles.<name>.userDataDir` so Chrome DevTools MCP can attach to Brave, Edge, and other Chromium-based browsers through their own user data directories. (#48170) Thanks @velvet-shark.
- Plugins/bundles: make enabled bundle MCP servers expose runnable tools in embedded Pi, and default relative bundle MCP launches to the bundle root so marketplace bundles like Context7 work through Pi instead of stopping at config import.
- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy.
- Models/Anthropic Vertex: add core `anthropic-vertex` provider support for Claude via Google Vertex AI, including GCP auth/discovery and main run-path routing. (#43356) Thanks @sallyom and @yossiovadia.
- Plugins/Chutes: add a bundled Chutes provider with plugin-owned OAuth/API-key auth, dynamic model discovery, and default-on extension wiring. (#41416) Thanks @Veightor.
- Web tools/Exa: add Exa as a bundled web-search plugin with Exa-native date filters, search-mode selection, and optional content extraction under `plugins.entries.exa.config.webSearch.*`. Thanks @V-Gutierrez and @vincentkoc.
- Web tools/Tavily: add Tavily as a bundled web-search provider with dedicated `tavily_search` and `tavily_extract` tools, using canonical plugin-owned config under `plugins.entries.tavily.config.webSearch.*`. (#49200) thanks @lakshyaag-tavily.
- Web tools/Firecrawl: add Firecrawl as an `onboard`/configure search provider via a bundled plugin, expose explicit `firecrawl_search` and `firecrawl_scrape` tools, and align core `web_fetch` fallback behavior with Firecrawl base-URL/env fallback plus guarded endpoint fetches.
- Models/OpenAI: add native forward-compat support for `gpt-5.4-mini` and `gpt-5.4-nano` in the OpenAI provider catalog, runtime resolution, and reasoning capability gates. Thanks @vincentkoc.
- Control UI/chat: add an expand-to-canvas button on assistant chat bubbles and in-app session navigation from Sessions and Cron views. Thanks @BunsDev.
- Control UI/appearance: unify theme border radii across Claw, Knot, and Dash, and add a Roundness slider to the Appearance settings so users can adjust corner radius from sharp to fully rounded. Thanks @BunsDev.
- Control UI/usage: improve usage overview styling, localization, and responsive chat/context-notice presentation, including safer theme color handling and unclipped usage-header menus. (#51951) Thanks @BunsDev.
- Control UI/usage: drop the empty session-detail placeholder card so the usage view stays single-column until a real session detail panel is selected. (#52013) Thanks @BunsDev.
- Android/mobile: add a system-aware dark theme across onboarding and post-onboarding screens so the app follows the device theme through setup, chat, and voice flows. (#46249) Thanks @sibbl.
- Android/Talk: move Talk speech synthesis behind gateway `talk.speak`, keep Talk secrets on the gateway, and switch Android playback to final-response audio instead of device-local ElevenLabs streaming. (#50849)
- Android/nodes: add `callLog.search` plus shared Call Log permission wiring so Android nodes can search recent call history through the gateway. (#44073) Thanks @lixuankai.
- Android/nodes: add `sms.search` plus shared SMS permission wiring so Android nodes can search device text messages through the gateway. (#48299) Thanks @lixuankai.
- Telegram/apiRoot: add per-account custom Bot API endpoint support across send, probe, setup, doctor repair, and inbound media download paths so proxied or self-hosted Telegram deployments work end to end. (#48842) Thanks @Cypherm.
- Telegram/topics: auto-rename DM forum topics on first message with LLM-generated labels, with per-account and per-DM `autoTopicLabel` overrides. (#51502) Thanks @Lukavyi.
- Telegram/actions: add `topic-edit` for forum-topic renames and icon updates while sharing the same Telegram topic-edit transport used by the plugin runtime. (#47798) Thanks @obviyus.
- Telegram/error replies: add a default-off `channels.telegram.silentErrorReplies` setting so bot error replies can be delivered silently across regular replies, native commands, and fallback sends. (#19776) Thanks @ImLukeF.
- Feishu/cards: add structured interactive approval and quick-action launcher cards, preserve callback user and conversation context through routing, and keep legacy card-action fallback behavior so common actions can run without typing raw commands. (#47873) Thanks @Takhoffman.
- Feishu/ACP: add current-conversation ACP and subagent session binding for supported DMs and topic conversations, including completion delivery back to the originating Feishu conversation. (#46819) Thanks @Takhoffman.
- Feishu/streaming: add `onReasoningStream` and `onReasoningEnd` support to streaming cards, so `/reasoning stream` renders thinking tokens as markdown blockquotes in the same card — matching the Telegram channel's reasoning lane behavior. (#46029) Thanks @day253.
- Feishu/cards: add identity-aware structured card headers and note footers for Feishu replies and direct sends, while keeping that presentation wired through the shared outbound identity path. (#29938) Thanks @nszhsl.
- Plugins/Matrix: add `allowBots` room policy so configured Matrix bot accounts can talk to each other, with optional mention-only gating. Thanks @gumadeiras.
- Plugins/Matrix: add per-account `allowPrivateNetwork` opt-in for private/internal homeservers, while keeping public cleartext homeservers blocked. Thanks @gumadeiras.
- Plugins/MiniMax: add MiniMax-M2.7 and MiniMax-M2.7-highspeed models and update the default model from M2.5 to M2.7. (#49691) Thanks @liyuan97.
- MiniMax/fast mode: map shared `/fast` and `params.fastMode` to MiniMax `-highspeed` models for M2.1, M2.5, and M2.7 API-key and OAuth runs. Thanks @vincentkoc.
- Models/MiniMax defaults: raise bundled MiniMax M2.5/M2.7 context-window, max-token, and pricing metadata to the higher defaults shipped by the current upstream Pi SDK. Thanks @vincentkoc.
- Models/MiniMax: add bundled `MiniMax-M2`, `MiniMax-M2.1`, and `MiniMax-M2.1-highspeed` catalog entries so OpenClaw's provider metadata and OAuth aliases stay aligned with the current upstream Pi SDK. Thanks @vincentkoc.
- Plugins/MiniMax: merge the bundled MiniMax API and MiniMax OAuth plugin surfaces into a single default-on `minimax` plugin, while keeping legacy `minimax-portal-auth` config ids aliased for compatibility.
- Agents/Pi compatibility: align OpenClaw's bundled MiniMax runtime behavior with the current upstream Pi 0.61.1 release so embedded runs stay in sync with the latest published Pi SDK semantics. Thanks @vincentkoc.
- Models/GitHub Copilot: allow forward-compat dynamic model ids without code updates, while preserving configured provider and per-model overrides for those synthetic models. (#51325) Thanks @fuller-stack-dev.
- xAI/models: sync the bundled Grok catalog to current Pi-backed IDs, limits, and pricing metadata, while keeping older Grok fast and 4.20 aliases resolving cleanly at runtime. Thanks @vincentkoc.
- xAI/fast mode: map shared `/fast` and `params.fastMode` to the current xAI Grok fast model family so direct Grok runs can opt into the faster Pi-backed variants. Thanks @vincentkoc.
- CLI/config: expand `config set` with SecretRef and provider builder modes, JSON/batch assignment support, and `--dry-run` validation with structured JSON output. (#49296) Thanks @joshavant.
- Z.AI/models: sync the bundled GLM catalog to current Pi metadata, including newer 4.5/4.6 model families, updated multimodal entries, and current pricing and token limits. Thanks @vincentkoc.
- Mistral/models: sync the bundled default Mistral metadata to current Pi pricing so the built-in default no longer advertises zero-cost usage. Thanks @vincentkoc.
- Plugins/Xiaomi: switch the bundled Xiaomi provider to the `/v1` OpenAI-compatible endpoint and add MiMo V2 Pro plus MiMo V2 Omni to the built-in catalog. (#49214) thanks @DJjjjhao.
- Agents/compaction: notify users when followup auto-compaction starts and finishes, keeping those notices out of TTS and preserving reply threading for the real assistant reply. (#38805) Thanks @zidongdesign.
- Memory/plugins: let the active memory plugin register its own system-prompt section while preserving cache-clear and snapshot-load prompt isolation. (#40126) Thanks @jarimustonen.
- Gateway/health monitor: add configurable stale-event thresholds and restart limits, plus per-channel and per-account `healthMonitor.enabled` overrides, while keeping the existing global disable path on `gateway.channelHealthCheckMinutes=0`. (#42107) Thanks @rstar327.
- Plugins/agent integrations: broaden the plugin surface for app-server integrations with channel-aware commands, interactive callbacks, inbound claims, and Discord/Telegram conversation binding support. (#45318) Thanks @huntharo and @vincentkoc.
- Plugins/binding: add `onConversationBindingResolved(...)` so plugins can react immediately after bind approvals or denies without blocking channel interaction acknowledgements. (#48678) Thanks @huntharo.
- Plugins/context engines: expose `delegateCompactionToRuntime(...)` on the public plugin SDK, refactor the legacy engine to use the shared helper, and clarify `ownsCompaction` delegation semantics for non-owning engines. (#49061) Thanks @jalehman.
- Plugins/context engines: pass the embedded runner `modelId` into context-engine `assemble()` so plugins can adapt context formatting per model. (#47437) thanks @jscianna.
- Plugins/context engines: add transcript maintenance rewrites for context engines, preserve active-branch transcript metadata during rewrites, and harden overflow-recovery truncation to rewrite sessions under the normal session write lock. (#51191) Thanks @jalehman.
- Skills/prompt budget: preserve all registered skills via a compact catalog fallback before dropping entries when the full prompt format exceeds `maxSkillsPromptChars`. (#47553) Thanks @snese.
- Hooks/workspace: keep repo-local `<workspace>/hooks` disabled until explicitly enabled, block workspace hook name collisions from shadowing bundled/managed/plugin hooks, and treat `hooks.internal.load.extraDirs` as trusted managed hook sources.
- Security/plugins: reject remote marketplace manifest entries that expand installation outside the cloned marketplace repo, including external git/GitHub sources, HTTP archives, and absolute paths.
- Gateway/docs: clarify that empty URL input allowlists are treated as unset, document `allowUrl: false` as the deny-all switch, and add regression coverage for the normalization path.
- secrets: harden read-only SecretRef command paths and diagnostics. (#47794) Thanks @joshavant.
- Scope message SecretRef resolution and harden doctor/status paths. (#48728) Thanks @joshavant.
- Build/memory tools: emit `dist/cli/memory-cli.js` as a stable core entry so runtime `memory_search` loading no longer depends on hashed `memory-cli-*` bundle names. (#51759) Thanks @oliviareid-svg.
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` surface for plugin-author test helpers, and move bundled-extension-only test bridges out of `extensions/` into private repo test helpers.
- Agents/steering docs: update embedded Pi steering docs and runner comments for the current upstream behavior, where queued steering is injected after the active assistant turn finishes its tool calls instead of skipping the remaining tools mid-turn. Thanks @vincentkoc.
- Doctor/refactor: start splitting doctor provider checks into `src/commands/doctor/providers/*` by extracting Telegram first-run and group allowlist warnings into a provider-specific module, keeping the current setup guidance and warning behavior intact. Thanks @vincentkoc.
- Refactor/channels: remove the legacy channel shim directories and point channel-specific imports directly at the extension-owned implementations. (#45967) Thanks @scoootscooob.
- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898.
- Docs/plugins: add the community DingTalk plugin listing to the docs catalog. (#29913) Thanks @sliverp.
- Docs/plugins: add the community QQbot plugin listing to the docs catalog. (#29898) Thanks @sliverp.
- Docs/plugins: add the community wecom plugin listing to the docs catalog. (#29905) Thanks @sliverp.
### Fixes
- Web tools/search provider lists: keep onboarding, configure, and docs provider lists alphabetical while preserving the separate runtime auto-detect precedence used for credential-based provider selection.
- Media/Windows security: block remote-host `file://` media URLs and UNC/network paths before local filesystem resolution in core media loading and adjacent prompt/sandbox attachment seams, so the next release no longer allows structured local-media inputs to trigger outbound SMB credential handshakes on Windows. Thanks @RacerZ-fighting for reporting.
- Gateway/discovery: fail closed on unresolved Bonjour and DNS-SD service endpoints in CLI discovery, onboarding, and `gateway status` so TXT-only hints can no longer steer routing or SSH auto-target selection. Thanks @nexrin for reporting.
- Security/pairing: bind iOS setup codes to the intended node profile and reject first-use bootstrap redemption that asks for broader roles or scopes. Thanks @tdjackey.
- Memory/core tools: register `memory_search` and `memory_get` independently so one unavailable memory tool no longer suppresses the other in new sessions. (#50198) Thanks @artwalker.
- Web tools/Exa: align the bundled Exa plugin with the current Exa API by supporting newer search types and richer `contents` options, while fixing the result-count cap to honor Exa's higher limit. Thanks @vincentkoc.
- Plugins/Matrix: move bundled plugin `KeyedAsyncQueue` imports onto the stable `plugin-sdk/core` surface so Matrix Docker/runtime builds do not depend on the brittle keyed-async-queue subpath. Thanks @ecohash-co and @vincentkoc.
- Nostr/security: enforce inbound DM policy before decrypt, route Nostr DMs through the standard reply pipeline, and add pre-crypto rate and size guards so unknown senders cannot bypass pairing or force unbounded crypto work. Thanks @kuranikaran.
- Synology Chat/security: keep reply delivery bound to stable numeric `user_id` by default, and gate mutable username/nickname recipient lookup behind `dangerouslyAllowNameMatching` with new regression coverage. Thanks @nexrin.
- Agents/default timeout: raise the shared default agent timeout from `600s` to `48h` so long-running ACP and agent sessions do not fail unless you configure a shorter limit.
- Gateway/Linux: auto-detect nvm-managed Node TLS CA bundle needs before CLI startup and refresh installed services that are missing `NODE_EXTRA_CA_CERTS`. (#51146) Thanks @GodsBoy.
- Gateway/startup: load bundled channel plugins from compiled `dist/extensions` entries in built installs, so gateway boot no longer recompiles bundled extension TypeScript on every startup and WhatsApp-class cold starts drop back to seconds instead of tens of seconds or worse. (#47560) Thanks @ngutman.
- Gateway/startup: prewarm the configured primary model before channel startup and retry one transient provider-runtime miss so the first Telegram or Discord message after boot no longer fails with `Unknown model: openai-codex/gpt-5.4`. Thanks @vincentkoc.
- CLI/startup: lazy-load channel add and root help startup paths to trim avoidable RSS and help latency on constrained hosts. (#46784) Thanks @vincentkoc.
- Configure/startup: move outbound send-deps resolution into a lightweight helper so `openclaw configure` no longer stalls after the banner while eagerly loading channel plugins. (#46301) Thanks @scoootscooob.
- CLI/auth choice: lazy-load plugin/provider fallback resolution so mapped auth choices stay on the static path and only unknown choices pay the heavy provider load. (#47495) Thanks @vincentkoc.
- Gateway/Discord startup: load only configured channel plugins during gateway boot, and lazy-load Discord provider/session runtime setup so startup stops importing unrelated providers and trims cold-start delay. Thanks @vincentkoc.
- Agents/inbound: lazy-load media and link understanding for plain-text turns and cache synced auth stores by auth-file state so ordinary inbound replies avoid unnecessary startup churn. Thanks @vincentkoc.
- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026.
- Agents/openai-responses: strip `prompt_cache_key` and `prompt_cache_retention` for non-OpenAI-compatible Responses endpoints while keeping them on direct OpenAI and Azure OpenAI paths, so third-party OpenAI-compatible providers no longer reject those requests with HTTP 400. (#49877) Thanks @ShaunTsai.
- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.
- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao.
- Control UI/session routing: preserve established external delivery routes when webchat views or sends in externally originated sessions, so subagent completions still return to the original channel instead of the dashboard. (#47797) Thanks @brokemac79.
- Telegram/replies: set `allow_sending_without_reply` on reply-targeted sends and media-error notices so deleted parent messages no longer drop otherwise valid replies. (#52524) Thanks @moltbot886.
- Telegram/polling: hard-timeout stuck `getUpdates` requests so wedged network paths fail over sooner instead of waiting for the polling stall watchdog. Thanks @vincentkoc.
- Android/location: make current-location requests drop late callbacks after timeout instead of crashing with `Already resumed`. (#52318) Thanks @Kaneki-x.
- Android/pairing: resolve portless secure setup URLs to `443` while preserving direct cleartext gateway defaults and explicit `:80` manual endpoints in onboarding. (#43540) Thanks @fmercurio.
- Android/canvas: ignore bridge messages from pages outside the bundled scaffold and trusted A2UI surfaces. Thanks @vincentkoc.
- CLI/status: keep `status --json` stdout clean by skipping plugin compatibility scans that were not rendered in the JSON payload. (#52449) Thanks @cgdusek.
- WhatsApp/reconnect: restore the append recency filter in the extension inbox monitor and handle protobuf `Long` timestamps correctly, so fresh post-reconnect append messages are processed while stale history sync stays suppressed. (#42588) Thanks @MonkeyLeeT.
- WhatsApp/login: wait for pending creds writes before reopening after Baileys `515` pairing restarts in both QR login and `channels login` flows, and keep the restart coverage pinned to the real wrapped error shape plus per-account creds queues. (#27910) Thanks @asyncjason.
- Android/canvas: serialize A2UI action-status event strings before evaluating WebView JS, so action ids and multiline errors do not break the callback dispatch. (#43784) Thanks @Kaneki-x.
- Android/camera: recycle intermediate and final snap bitmaps in `camera.snap` so repeated captures do not leak native image memory. (#41902) Thanks @Kaneki-x.
- Control UI/logging: make browser-safe logger imports avoid eager temp-dir resolution so the bundled Control UI no longer crashes to a blank screen when logging reaches `tmp-openclaw-dir`. (#48469) Fixes #48062. Thanks @7inspire.
- Control UI/chat sessions: show human-readable labels in the grouped session dropdown again, keep unique scoped fallbacks when metadata is missing, and disambiguate duplicate labels only when needed. (#45130) Thanks @luzhidong.
- Telegram/replies: ignore malformed non-string reply text and caption fields when describing reply context, so unexpected Telegram reply payloads no longer break inbound context assembly. (#50500) Thanks @p3nchan.
- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46580) Fixes #46532. Thanks @vincentkoc.
- Android/chat: theme the thinking dropdown and TLS trust dialogs explicitly so popup surfaces match the active app theme instead of falling back to mismatched Material defaults.
- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46515) Fixes #46411. Thanks @ademczuk.
- Slack/startup: harden `@slack/bolt` import interop across current bundled runtime shapes so Slack monitors no longer crash with `App is not a constructor` after plugin-sdk bundling changes. (#45953) Thanks @merc1305.
- Control UI/model switching: preserve the selected provider prefix when switching models from the chat dropdown, so multi-provider setups no longer send `anthropic/gpt-5.2`-style mismatches when the user picked `openai/gpt-5.2`. (#47581) Thanks @chrishham.
- Control UI/storage: scope persisted settings keys by gateway base path, with migration from the legacy shared key, so multiple gateways under one domain stop overwriting each other's dashboard preferences. (#47932) Thanks @bobBot-claw.
- Control UI/overview: keep the language dropdown aligned with the persisted locale during dashboard startup so refreshing the page does not fall back to English before locale hydration completes. (#48019) Thanks @git-jxj.
- macOS/node service startup: use `openclaw node start/stop --json` from the Mac app instead of the removed `openclaw service node ...` command shape, so current CLI installs expose the full node exec surface again. (#46843) Fixes #43171. Thanks @Br1an67.
- ACP/gateway startup: use direct Telegram and Discord startup/status helpers instead of routing probes through the plugin runtime, and prepend the selected daemon Node bin dir to service PATH so plugin-local installs can still find `npm` and `pnpm`.
- WhatsApp/active-listener: pin the active listener registry to a `globalThis` singleton so split WhatsApp bundle chunks share one listener map and outbound sends stop missing the registered session. (#47433) Thanks @clawdia67.
- Gateway/probe: honor caller `--timeout` for active local loopback probes in `gateway status`, keep inactive remote-mode loopback probes fast, and clamp probe timers to JS-safe bounds so slow local/container gateways stop reporting false timeouts. (#47533) Thanks @MonkeyLeeT.
- Config/startup: keep bundled web-search allowlist compatibility on a lightweight manifest path so config validation no longer pulls bundled web-search registry imports into startup, while still avoiding accidental auto-allow of config-loaded override plugins. (#51574) Thanks @RichardCao.
- Gateway/chat.send: persist uploaded image references across reloads and compaction without delaying first-turn dispatch or double-submitting the same image to vision models. (#51324) Thanks @fuller-stack-dev.
- Android/canvas: recycle captured and scaled snapshot bitmaps so repeated canvas snapshots do not leak native image memory. (#41889) Thanks @Kaneki-x.
- Android/theme: switch status bar icon contrast with the active system theme so Android light mode no longer leaves unreadable light icons over the app header. (#51098) Thanks @goweii.
- Gateway/openresponses: preserve assistant commentary and session continuity across hosted-tool `/v1/responses` turns, and emit streamed tool-call payloads before finalization so client tool loops stay resumable. (#52171) Thanks @CharZhou.
- Android/Talk: serialize `TalkModeManager` player teardown so rapid interrupt/restart cycles stop double-releasing or overlapping TTS playback. (#52310) Thanks @Kaneki-x.
- WhatsApp/reconnect: preserve the last inbound timestamp across reconnect attempts so the watchdog can still recycle linked-but-dead listeners after a restart instead of leaving them stuck connected forever.
- Gateway/network discovery: guard LAN, tailnet, and pairing interface enumeration so WSL2 and restricted hosts degrade to missing-address fallbacks instead of crashing on `uv_interface_addresses` errors. (#44180, #47590)
- Gateway/bonjour: suppress the non-fatal `@homebridge/ciao` IPv4-loss assertion during interface churn so WiFi/VPN/sleep-wake changes no longer take down the gateway. (#38628, #47159, #52431)
- Browser/launch: stop forcing an extra blank tab on browser launch so managed browser startup no longer opens an unwanted empty page. (#52451) Thanks @rogerdigital.
- CLI/onboarding: import static provider definitions directly for onboarding model/config helpers so those paths no longer pull provider discovery just for built-in defaults. (#47467) Thanks @vincentkoc.
- Agents/exec: return plain-text failed tool output for timeouts and other non-success exec outcomes so models no longer parrot raw JSON error payloads back to users. (#52508) Thanks @martingarramon.
- CLI/config: make `config set --strict-json` enforce real JSON, prefer `JSON.parse` with JSON5 fallback for machine-written cron/subagent stores, and relabel raw config surfaces as `JSON/JSON5` to match actual compatibility. Related: #48415, #43127, #14529, #21332. Thanks @adhitShet and @vincentkoc.
- CLI/Ollama onboarding: keep the interactive model picker for explicit `openclaw onboard --auth-choice ollama` runs so setup still selects a default model without reintroducing pre-picker auto-pulls. (#49249) Thanks @BruceMacD.
- Android/location: make current-location requests drop late callbacks after timeout instead of crashing with `Already resumed`. (#52318) Thanks @Kaneki-x.
- Plugins/bundler TDZ: fix `RESERVED_COMMANDS` temporal dead zone error that prevented device-pair, phone-control, and talk-voice plugins from registering when the bundler placed the commands module after call sites in the same output chunk. Thanks @BunsDev.
- Plugins/imports: fix stale googlechat runtime-api import paths and signal SDK circular re-exports broken by recent plugin-sdk refactors. Thanks @BunsDev.
- Telegram/setup: seed fresh setups with `channels.telegram.groups["*"].requireMention=true` so new bots stay mention-gated in groups unless you explicitly open them up. Thanks @vincentkoc.
- Security/exec safe bins: remove `jq` from the default safe-bin allowlist and fail closed on the `jq` `env` builtin when operators explicitly opt `jq` back in, so `jq -n env` cannot dump host secrets without an explicit trust path. Thanks @gladiator9797 for reporting.
- Google auth/Node 25: patch `gaxios` to use native fetch without injecting `globalThis.window`, while translating proxy and mTLS transport settings so Google Vertex and Google Chat auth keep working on Node 25. (#47914) Thanks @pdd-cli.
- Gateway/startup: load bundled channel plugins from compiled `dist/extensions` entries in built installs, so gateway boot no longer recompiles bundled extension TypeScript on every startup and WhatsApp-class cold starts drop back to seconds instead of tens of seconds or worse. (#47560) Thanks @ngutman.
- Agents/openai-responses: strip `prompt_cache_key` and `prompt_cache_retention` for non-OpenAI-compatible Responses endpoints while keeping them on direct OpenAI and Azure OpenAI paths, so third-party OpenAI-compatible providers no longer reject those requests with HTTP 400. (#49877) Thanks @ShaunTsai.
- Plugins/context engines: enforce owner-aware context-engine registration on both loader and public SDK paths so plugins cannot spoof privileged ownership, claim the core `legacy` engine id, or overwrite an existing engine id through direct SDK imports. (#47595) Thanks @vincentkoc.
- Browser/remote CDP: honor strict browser SSRF policy during remote CDP reachability and `/json/version` discovery checks, redact sensitive `cdpUrl` tokens from status output, and warn when remote CDP targets private/internal hosts.
- Gateway/plugins: pin runtime webhook routes to the gateway startup registry so channel webhooks keep working across plugin-registry churn, and make plugin auth + dispatch resolve routes from the same live HTTP-route registry. (#47902) Fixes #46924 and #47041. Thanks @steipete.
- Gateway/auth: ignore spoofed loopback hops in trusted forwarding chains and block device approvals that request scopes above the caller session. (#46800) Thanks @vincentkoc.
- Gateway/restart: defer externally signaled unmanaged restarts through the in-process idle drain, and preserve the restored subagent run as remap fallback during orphan recovery so resumed sessions do not duplicate work. (#47719) Thanks @joeykrug.
- Control UI/session routing: preserve established external delivery routes when webchat views or sends in externally originated sessions, so subagent completions still return to the original channel instead of the dashboard. (#47797) Thanks @brokemac79.
- Configure/startup: move outbound send-deps resolution into a lightweight helper so `openclaw configure` no longer stalls after the banner while eagerly loading channel plugins. (#46301) Thanks @scoootscooob.
- CLI/configure: clarify fresh-setup memory-search warnings so they say semantic recall needs at least one embedding provider, and scope the initial model allowlist picker to the provider selected in configure. Thanks @vincentkoc.
- Mattermost/threading: honor `replyToMode: "off"` for already-threaded inbound posts so threaded follow-ups can fall back to top-level replies when configured. (#52543) Thanks @RichardCao.
- Onboarding/custom providers: store Azure OpenAI and Azure AI Foundry custom endpoints with the Responses API config shape, normalized `/openai/v1` base URLs, and Azure-safe defaults so TUI and agent runs work after setup. (#49543) Thanks @kunalk16.
- CLI/completion: reduce recursive completion-script string churn and fix nested PowerShell command-path matching so generated nested completions resolve on PowerShell too. (#45537) Thanks @yiShanXin and @vincentkoc.
- macOS/launch at login: stop emitting `KeepAlive` for the desktop app launch agent so OpenClaw no longer relaunches immediately after a manual quit while launch at login remains enabled. (#40213) Thanks @stablegenius49.
- Mattermost/DM send: retry transient direct-channel creation failures for DM deliveries, with configurable backoff and per-request timeout. (#42398) Thanks @JonathanJing.
- Secrets/exec refs: require explicit `--allow-exec` for `secrets apply` write plans that contain exec SecretRefs/providers, and align audit/configure/apply dry-run behavior to skip exec checks unless opted in to prevent unexpected command side effects. (#49417) Thanks @restriction and @joshavant.
- Signal/runtime API: re-export `SignalAccountConfig` so Signal account resolution type-checks again. (#49470) Thanks @scoootscooob.
- Google Chat/runtime API: thin the private runtime barrel onto the curated public SDK surface while keeping public Google Chat exports intact. (#49504) Thanks @scoootscooob.
- Onboarding/custom providers: keep Azure AI Foundry `*.services.ai.azure.com` custom endpoints on the selected compatibility path instead of forcing Responses, so chat-completions Foundry models still work after setup. Fixes #50528. (#50535) Thanks @obviyus.
- make `openclaw update status` explicitly say `up to date` when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.
- Agents/embedded transport errors: distinguish common network failures like connection refused, DNS lookup failure, and interrupted sockets from true timeouts in embedded-run user messaging and lifecycle diagnostics. (#51419) Thanks @scoootscooob.
- Security/pairing: bind iOS setup codes to the intended node profile and reject first-use bootstrap redemption that asks for broader roles or scopes. Thanks @tdjackey.
- Nostr/security: enforce inbound DM policy before decrypt, route Nostr DMs through the standard reply pipeline, and add pre-crypto rate and size guards so unknown senders cannot bypass pairing or force unbounded crypto work. Thanks @kuranikaran.
- Synology Chat/security: keep reply delivery bound to stable numeric `user_id` by default, and gate mutable username/nickname recipient lookup behind `dangerouslyAllowNameMatching` with new regression coverage. Thanks @nexrin.
- Browser/node proxy: enforce `nodeHost.browserProxy.allowProfiles` across `query.profile` and `body.profile`, block proxy-side profile create/delete when the allowlist is set, and keep the default full proxy surface when the allowlist is empty.
- Security/device pairing: harden `device.token.rotate` deny handling by keeping public failures generic while logging internal deny reasons and preserving approved-baseline enforcement. (`GHSA-7jrw-x62h-64p8`)
- Security/exec safe bins: remove `jq` from the default safe-bin allowlist and fail closed on the `jq` `env` builtin when operators explicitly opt `jq` back in, so `jq -n env` cannot dump host secrets without an explicit trust path. Thanks @gladiator9797 for reporting.
- Security/exec approvals: escape blank Hangul filler code points in approval prompts across gateway/chat and the macOS native approval UI so visually empty Unicode padding cannot hide reviewed command text.
- Security/exec approvals: unify transparent dispatch-wrapper handling across resolution and allow-always persistence so wrapper metadata cannot silently drift and broaden approvals.
- Security/exec: harden macOS allowlist resolution against wrapper and `env` spoofing, require fresh approval for inline interpreter eval with `tools.exec.strictInlineEval`, wrap Discord guild message bodies as untrusted external content, and add audit findings for risky exec approval and open-channel combinations.
- Security/network: harden explicit-proxy SSRF pinning by translating target-hop transport hints onto HTTPS proxy tunnels and failing closed for plain HTTP guarded fetches that cannot preserve pinned DNS.
- Security/Synology Chat: require explicit per-account webhook paths for multi-account setups by default, reject duplicate exact webhook paths fail-closed, and keep inherited-path behavior behind an explicit dangerous opt-in so shared routes can no longer collapse DM policy contexts across accounts. Thanks @tdjackey for reporting.
- Browser/remote CDP: honor strict browser SSRF policy during remote CDP reachability and `/json/version` discovery checks, redact sensitive `cdpUrl` tokens from status output, and warn when remote CDP targets private/internal hosts.
- Telegram/replies: set `allow_sending_without_reply` on reply-targeted sends and media-error notices so deleted parent messages no longer drop otherwise valid replies. (#52524) Thanks @moltbot886.
- Gateway/status: resolve env-backed `gateway.auth.*` SecretRefs before read-only probe auth checks so status no longer reports false probe failures when auth is configured through SecretRef. (#52513) Thanks @CodeForgeNet.
- Agents/exec: return plain-text failed tool output for timeouts and other non-success exec outcomes so models no longer parrot raw JSON error payloads back to users. (#52508) Thanks @martingarramon.
@@ -115,15 +440,26 @@ Docs: https://docs.openclaw.ai
- CLI/configure: clarify fresh-setup memory-search warnings so they say semantic recall needs at least one embedding provider, and scope the initial model allowlist picker to the provider selected in configure. Thanks @vincentkoc.
- CLI/auth choice: lazy-load plugin/provider fallback resolution so mapped auth choices stay on the static path and only unknown choices pay the heavy provider load. (#47495) Thanks @vincentkoc.
- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc.
- CLI/status: keep `status --json` stdout clean by skipping plugin compatibility scans that were not rendered in the JSON payload. (#52449) Thanks @cgdusek.
- Agents/Telegram: avoid rebuilding the full model catalog on ordinary inbound replies so Telegram message handling no longer pays multi-second core startup latency before reply generation. Thanks @vincentkoc.
- Media/security: bound remote-media error-body snippets with the same streaming caps and idle timeouts as successful downloads, so malicious HTTP error responses cannot force unbounded buffering before OpenClaw throws.
- Gateway/auth: ignore spoofed loopback hops in trusted forwarding chains and block device approvals that request scopes above the caller session. (#46800) Thanks @vincentkoc.
- Gateway/auth: clear self-declared scopes for device-less trusted-proxy Control UI sessions so proxy-authenticated connects cannot claim admin or secrets scopes without a bound device identity.
- Hardening: refresh stale device pairing requests and pending metadata (#50695) Thanks @smaeljaish771 and @joshavant.
- Gateway/auth: add regression coverage that keeps device-less trusted-proxy Control UI sessions off privileged pairing approval RPCs. Thanks @vincentkoc.
- Media/Windows security: block remote-host `file://` media URLs and UNC/network paths before local filesystem resolution in core media loading and adjacent prompt/sandbox attachment seams, so the next release no longer allows structured local-media inputs to trigger outbound SMB credential handshakes on Windows. Thanks @RacerZ-fighting for reporting.
- Web tools/Exa: align the bundled Exa plugin with the current Exa API by supporting newer search types and richer `contents` options, while fixing the result-count cap to honor Exa's higher limit. Thanks @vincentkoc.
- Agents/default timeout: raise the shared default agent timeout from `600s` to `48h` so long-running ACP and agent sessions do not fail unless you configure a shorter limit.
- CLI: avoid loading provider discovery during startup model normalization. (#46522) Thanks @ItsAditya-xyz and @vincentkoc.
- Agents/Telegram: avoid rebuilding the full model catalog on ordinary inbound replies so Telegram message handling no longer pays multi-second core startup latency before reply generation. Thanks @vincentkoc.
- Gateway/Discord startup: load only configured channel plugins during gateway boot, and lazy-load Discord provider/session runtime setup so startup stops importing unrelated providers and trims cold-start delay. Thanks @vincentkoc.
- Security/exec: harden macOS allowlist resolution against wrapper and `env` spoofing, require fresh approval for inline interpreter eval with `tools.exec.strictInlineEval`, wrap Discord guild message bodies as untrusted external content, and add audit findings for risky exec approval and open-channel combinations.
- Gateway/install: treat state-dir `.env` password vars as durable install auth inputs so token inference stays aligned with the service environment during gateway install and onboarding. (#52492) Thanks @Chevron7Locked.
- Agents/inbound: lazy-load media and link understanding for plain-text turns and cache synced auth stores by auth-file state so ordinary inbound replies avoid unnecessary startup churn. Thanks @vincentkoc.
- Telegram/polling: hard-timeout stuck `getUpdates` requests so wedged network paths fail over sooner instead of waiting for the polling stall watchdog. Thanks @vincentkoc.
- Agents/models: cache `models.json` readiness by config and auth-file state so embedded runner turns stop paying repeated model-catalog startup work before replies. Thanks @vincentkoc.
- Security/device pairing: harden `device.token.rotate` deny handling by keeping public failures generic while logging internal deny reasons and preserving approved-baseline enforcement. (`GHSA-7jrw-x62h-64p8`)
- Gateway/status: tolerate network interface discovery failures in status, onboarding control-UI links, and self-presence display paths so those surfaces fall back cleanly instead of crashing. (#52195) Thanks @meng-clb.
- Gateway/Linux: auto-detect nvm-managed Node TLS CA bundle needs before CLI startup and refresh installed services that are missing `NODE_EXTRA_CA_CERTS`. (#51146) Thanks @GodsBoy.
- Google auth/Node 25: patch `gaxios` to use native fetch without injecting `globalThis.window`, while translating proxy and mTLS transport settings so Google Vertex and Google Chat auth keep working on Node 25. (#47914) Thanks @pdd-cli.
- Gateway/status: resolve env-backed `gateway.auth.*` SecretRefs before read-only probe auth checks so status no longer reports false probe failures when auth is configured through SecretRef. (#52513) Thanks @CodeForgeNet.
- Gateway/plugins: pin runtime webhook routes to the gateway startup registry so channel webhooks keep working across plugin-registry churn, and make plugin auth + dispatch resolve routes from the same live HTTP-route registry. (#47902) Fixes #46924 and #47041. Thanks @steipete.
- Gateway/restart: defer externally signaled unmanaged restarts through the in-process idle drain, and preserve the restored subagent run as remap fallback during orphan recovery so resumed sessions do not duplicate work. (#47719) Thanks @joeykrug.
- Telegram/setup: seed fresh setups with `channels.telegram.groups["*"].requireMention=true` so new bots stay mention-gated in groups unless you explicitly open them up. Thanks @vincentkoc.
- Inbound policy hardening: tighten callback and webhook sender checks across Mattermost and Google Chat, match Nextcloud Talk rooms by stable room token, and treat explicit empty Twitch allowlists as deny-all. (#46787) Thanks @zpbrent, @ijxpwastaken and @vincentkoc.
- Webhooks/runtime: move auth earlier and tighten pre-auth body limits and timeouts across bundled webhook handlers, including slow-body handling for Mattermost slash commands. (#46802) Thanks @vincentkoc.
- Email/webhook wrapping: sanitize sender and subject metadata before external-content wrapping so metadata fields cannot break the wrapper structure. (#46816) Thanks @vincentkoc.
@@ -136,165 +472,116 @@ Docs: https://docs.openclaw.ai
- Web search/onboarding: clarify provider labels, key prompts, and missing-key notes so setup/configure more clearly names the required provider credential for Gemini, Kimi, Grok, Brave Search, Firecrawl, Perplexity, and Tavily. Thanks @vincentkoc.
- macOS/canvas actions: keep unattended local agent actions on trusted in-app canvas surfaces only, and stop exposing the deep-link fallback key to arbitrary page scripts. (#46790) Thanks @vincentkoc.
- Agents/compaction: extend the enclosing run deadline once while compaction is actively in flight, and abort the underlying SDK compaction on timeout/cancel so large-session compactions stop freezing mid-run. (#46889) Thanks @asyncjason.
- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026.
- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.
- Gateway/Telegram shutdown: abort stalled Telegram polling fetches on shutdown, clean up per-cycle abort listeners, and keep the in-process watchdog ahead of supervisor stop timeouts so SIGTERM no longer leaves zombie gateways behind. (#51242) Thanks @juliabush.
- Telegram/setup: warn when setup leaves DMs on pairing without an allowlist, and show valid account-scoped remediation commands. (#50710) Thanks @ernestodeoliveira.
- Doctor/Telegram: replace the fresh-install empty group-allowlist false positive with first-run guidance that explains DM pairing approval and the next group setup steps, so new Telegram installs get actionable setup help instead of a broken-config warning. Thanks @vincentkoc.
- Doctor/extensions: keep Matrix DM `allowFrom` repairs on the canonical `dm.allowFrom` path and stop treating Zalouser group sender gating as if it fell back to `allowFrom`, so doctor warnings and `--fix` stay aligned with runtime access control. Thanks @vincentkoc.
- Doctor/refactor: centralize built-in channel doctor semantics in one static capability registry with conservative fallback behavior for unknown/external channels, so future extension changes stop depending on scattered shared string checks. Thanks @vincentkoc.
- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao.
- Gateway/startup: prewarm the configured primary model before channel startup and retry one transient provider-runtime miss so the first Telegram or Discord message after boot no longer fails with `Unknown model: openai-codex/gpt-5.4`. Thanks @vincentkoc.
- Channels/plugins: keep shared interactive payloads merge-ready by fixing Slack custom callback routing and repeat-click dedupe, allowing interactive-only sends, and preserving ordered Discord shared text blocks. (#47715) Thanks @vincentkoc.
- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc.
- Feishu/actions: expand the runtime action surface with message read/edit, explicit thread replies, pinning, and operator-facing chat/member inspection so Feishu can operate more of the workspace directly. (#47968) Thanks @Takhoffman.
- Feishu/topic threads: fetch full thread context, including prior bot replies, when starting a topic-thread session so follow-up turns in Feishu topics keep the right conversation state. (#45254) Thanks @Coobiw.
- Feishu/media: keep native image, file, audio, and video/media handling aligned across outbound sends, inbound downloads, thread replies, directory/action aliases, and capability docs so unsupported areas are explicit instead of implied. (#47968) Thanks @Takhoffman.
- Feishu/webhooks: harden signed webhook verification to use constant-time signature comparison and keep malformed short signatures fail-closed in webhook E2E coverage.
- WhatsApp/reconnect: restore the append recency filter in the extension inbox monitor and handle protobuf `Long` timestamps correctly, so fresh post-reconnect append messages are processed while stale history sync stays suppressed. (#42588) Thanks @MonkeyLeeT.
- WhatsApp/login: wait for pending creds writes before reopening after Baileys `515` pairing restarts in both QR login and `channels login` flows, and keep the restart coverage pinned to the real wrapped error shape plus per-account creds queues. (#27910) Thanks @asyncjason.
- Telegram/message send: forward `--force-document` through the `sendPayload` path as well as `sendMedia`, so Telegram payload sends with `channelData` keep uploading images as documents instead of silently falling back to compressed photo sends. (#47119) Thanks @thepagent.
- Android/canvas: serialize A2UI action-status event strings before evaluating WebView JS, so action ids and multiline errors do not break the callback dispatch. (#43784) Thanks @Kaneki-x.
- Android/camera: recycle intermediate and final snap bitmaps in `camera.snap` so repeated captures do not leak native image memory. (#41902) Thanks @Kaneki-x.
- Telegram/message chunking: preserve spaces, paragraph separators, and word boundaries when HTML overflow rechunking splits formatted replies. (#47274) Thanks @obviyus.
- Z.AI/onboarding: detect a working default model even for explicit `zai-coding-*` endpoint choices, so Coding Plan setup can keep the selected endpoint while defaulting to `glm-5` when available or `glm-4.7` as fallback. (#45969) Thanks @obviyus.
- CI/onboarding smoke: surface `ensure-base-commit` fetch failures as workflow warnings and fail the onboarding Docker smoke when expected setup prompts drift instead of continuing silently. Thanks @Takhoffman.
- Z.AI/onboarding: add `glm-5-turbo` to the default Z.AI provider catalog so onboarding-generated configs expose the new model alongside the existing GLM defaults. (#46670) Thanks @tomsun28.
- Zalo Personal/group gating: stop reapplying `dmPolicy.allowFrom` as a sender gate for already-allowlisted groups when `groupAllowFrom` is unset, so any member of an allowed group can trigger replies while DMs stay restricted. (#46663) Fixes #40146. Thanks @Takhoffman.
- Zalo/plugin runtime: export `resolveClientIp` from `openclaw/plugin-sdk/zalo` so installed builds no longer crash on startup when the webhook monitor loads from the packaged extension instead of the monorepo source tree. (#46549) Thanks @No898.
- Onboarding/custom providers: store Azure OpenAI and Azure AI Foundry custom endpoints with the Responses API config shape, normalized `/openai/v1` base URLs, and Azure-safe defaults so TUI and agent runs work after setup. (#49543) Thanks @kunalk16.
- Docker/live tests: mount external CLI auth homes into writable container copies, derive Codex OAuth expiry from JWT `exp`, refresh synced CLI creds instead of trusting stale cached expiry, and make gateway live probes wait on transcript output so `pnpm test:docker:all` stays green in Linux.
- Plugins/install precedence: keep bundled plugins ahead of auto-discovered globals by default, but let an explicitly installed plugin record win its own duplicate-id tie so installed channel plugins load from `~/.openclaw/extensions` after `openclaw plugins install`. (#46722) Thanks @Takhoffman.
- Control UI/logging: make browser-safe logger imports avoid eager temp-dir resolution so the bundled Control UI no longer crashes to a blank screen when logging reaches `tmp-openclaw-dir`. (#48469) Fixes #48062. Thanks @7inspire.
- Plugins/scoped ids: preserve scoped plugin ids during install and config keying, and keep bundled plugins ahead of discovered duplicate ids by default so `@scope/name` plugins no longer collide with unscoped installs. (#47413) Thanks @vincentkoc.
- Gateway/watch mode: restart on bundled-plugin package and manifest metadata changes, rebuild `dist` for extension source and `tsdown.config.ts` changes, and still ignore extension docs. (#47571) Thanks @gumadeiras.
- Gateway/watch mode: recreate bundled plugin runtime metadata after clean or stale `dist` states, so `pnpm gateway:watch` no longer fails on missing `dist/extensions/*/openclaw.plugin.json` manifests after a rebuild. Thanks @gumadeiras.
- Control UI/chat sessions: show human-readable labels in the grouped session dropdown again, keep unique scoped fallbacks when metadata is missing, and disambiguate duplicate labels only when needed. (#45130) Thanks @luzhidong.
- Control UI: scope persisted session selection per gateway, prevent stale session bleed across tokenized gateway opens, and cap stored gateway session history. (#47453) Thanks @sallyom.
- Control UI/dashboard: preserve structured gateway shutdown reasons across restart disconnects so config-triggered restarts no longer fall back to `disconnected (1006): no reason`. (#46580) Fixes #46532. Thanks @vincentkoc.
- Models/OpenAI Codex OAuth: start the remote manual-input race for Codex login and keep the pasted-input prompt aligned with the actual accepted values, so remote/VPS auth no longer stalls waiting on an unreachable localhost callback. (#51631) Thanks @cash-echo-bot.
- Android/chat: theme the thinking dropdown and TLS trust dialogs explicitly so popup surfaces match the active app theme instead of falling back to mismatched Material defaults.
- Group mention gating: reject invalid and unsafe nested-repetition `mentionPatterns`, reuse the shared safe config-regex compiler across mention stripping and detection, and cache strip-time regex compilation so noisy groups avoid repeated recompiles.
- Browser/profiles: drop the auto-created `chrome-relay` browser profile; users who need the Chrome extension relay must now create their own profile via `openclaw browser create-profile`. (#46596) Fixes #45777. Thanks @odysseus0.
- CI/channel test routing: move the built-in channel suites into `test:channels` and keep them out of `test:extensions`, so extension CI no longer fails after the channel migration while targeted test routing still sends Slack, Signal, and iMessage suites to the right lane. (#46066) Thanks @scoootscooob.
- Docs/Mintlify: fix MDX marker syntax on Perplexity, Model Providers, Moonshot, and exec approvals pages so local docs preview no longer breaks rendering or leaves stale pages unpublished. (#46695) Thanks @velvet-shark.
- Plugins/runtime barrels: route bundled extension runtime imports through public `openclaw/plugin-sdk/*` subpaths and block relative cross-package escapes so packaged extensions stop depending on monorepo-only relative paths. (#51939) Thanks @vincentkoc.
- Gateway/config validation: stop treating the implicit default memory slot as a required explicit plugin config, so startup no longer fails with `plugins.slots.memory: plugin not found: memory-core` when `memory-core` was only inferred. (#47494) Thanks @ngutman.
- Tlon: honor explicit empty allowlists and defer cite expansion. (#46788) Thanks @zpbrent and @vincentkoc.
- Tlon/DM auth: defer cited-message expansion until after DM authorization and owner command handling, so unauthorized DMs and owner approval/admin commands no longer trigger cross-channel cite fetches before the deny or command path.
- Gateway/agent events: stop broadcasting false end-of-run `seq gap` errors to clients, and isolate node-driven ingress turns with per-turn run IDs so stale tail events cannot leak into later session runs. (#43751) Thanks @caesargattuso.
- Docs/security audit: spell out that `gateway.controlUi.allowedOrigins: ["*"]` is an explicit allow-all browser-origin policy and should be avoided outside tightly controlled local testing.
- Gateway/auth: clear self-declared scopes for device-less trusted-proxy Control UI sessions so proxy-authenticated connects cannot claim admin or secrets scopes without a bound device identity.
- Nodes/pending actions: re-check queued foreground actions against the current node command policy before returning them to the node. (#46815) Thanks @zpbrent and @vincentkoc.
- Node/startup: remove leftover debug `console.log("node host PATH: ...")` that printed the resolved PATH on every `openclaw node run` invocation. (#46515) Fixes #46411. Thanks @ademczuk.
- CLI/completion: reduce recursive completion-script string churn and fix nested PowerShell command-path matching so generated nested completions resolve on PowerShell too. (#45537) Thanks @yiShanXin and @vincentkoc.
- Slack/startup: harden `@slack/bolt` import interop across current bundled runtime shapes so Slack monitors no longer crash with `App is not a constructor` after plugin-sdk bundling changes. (#45953) Thanks @merc1305.
- Windows/gateway status: accept `schtasks` `Last Result` output as an alias for `Last Run Result`, so running scheduled-task installs no longer show `Runtime: unknown`. (#47844) Thanks @MoerAI.
- ACP/acpx: resolve the bundled plugin root from the actual plugin directory so plugin-local installs stay under `dist/extensions/acpx` instead of escaping to `dist/extensions` and failing runtime setup. (#47601) Thanks @ngutman.
- Gateway/WS handshake: raise the default pre-auth handshake timeout to 10 seconds and add `OPENCLAW_HANDSHAKE_TIMEOUT_MS` as a runtime override so busy local gateways stop dropping healthy CLI connections at 3 seconds. (#49262) Thanks @fuller-stack-dev.
- Gateway/websocket pairing bypass for disabled auth: skip device-pairing enforcement for Control UI operator sessions when `gateway.auth.mode=none`, so reverse-proxied dashboards no longer get stuck on `pairing required` despite auth being explicitly disabled. (#47148) Thanks @ademczuk.
- Control UI/model switching: preserve the selected provider prefix when switching models from the chat dropdown, so multi-provider setups no longer send `anthropic/gpt-5.2`-style mismatches when the user picked `openai/gpt-5.2`. (#47581) Thanks @chrishham.
- Control UI/storage: scope persisted settings keys by gateway base path, with migration from the legacy shared key, so multiple gateways under one domain stop overwriting each other's dashboard preferences. (#47932) Thanks @bobBot-claw.
- Agents/usage tracking: stop forcing `supportsUsageInStreaming: false` on non-native OpenAI-completions providers so compatible backends report token usage and cost again instead of showing all zeros. (#46500) Fixes #46142. Thanks @ademczuk.
- ACP/acpx: keep plugin-local backend installs under `extensions/acpx` in live repo checkouts so rebuilds no longer delete the runtime binary, and avoid package-lock churn during runtime repair.
- Plugins/subagents: preserve gateway-owned plugin subagent access across runtime, tool, and embedded-runner load paths so gateway plugin tools and context engines can still spawn and manage subagents after the loader cache split. (#46648) Thanks @jalehman.
- Control UI/overview: keep the language dropdown aligned with the persisted locale during dashboard startup so refreshing the page does not fall back to English before locale hydration completes. (#48019) Thanks @git-jxj.
- Agents/compaction: rerun transcript repair after `session.compact()` so orphaned `tool_result` blocks cannot survive compaction and break later Anthropic requests. (#16095) thanks @claw-sylphx.
- Agents/compaction: trigger overflow recovery from the tool-result guard once post-compaction context still exceeds the safe threshold, so long tool loops compact before the next model call hard-fails. (#29371) thanks @keshav55.
- macOS/exec approvals: harden exec-host request HMAC verification to use a timing-safe compare and keep malformed or truncated signatures fail-closed in focused IPC auth coverage.
- Gateway/exec approvals: surface requested env override keys in gateway-host approval prompts so operators can review surviving env context without inheriting noisy base host env.
- Telegram/network: preserve sticky IPv4 fallback state across polling restarts so hosts with unstable IPv6 to `api.telegram.org` stop re-triggering repeated Telegram timeouts after each restart. (#48282) Thanks @yassinebkr.
- Plugins/subagents: forward per-run provider and model overrides through gateway plugin subagent dispatch so plugin-launched agent delegations honor explicit model selection again. (#48277) Thanks @jalehman.
- Agents/compaction: write minimal boundary summaries for empty preparations while keeping split-turn prefixes on the normal path, so no-summarizable-message sessions stop retriggering the safeguard loop. (#42215) thanks @lml2468.
- Models/chat commands: keep `/model ...@YYYYMMDD` version suffixes intact by default, but still honor matching stored numeric auth-profile overrides for the same provider. (#48896) Thanks @Alix-007.
- Gateway/channels: serialize per-account channel startup so overlapping starts do not boot the same provider twice, preventing MS Teams `EADDRINUSE` crash loops during startup and restart. (#49583) Thanks @sudie-codes.
- Tests/OpenAI Codex auth: align login expectations with the default `gpt-5.4` model so CI coverage stays consistent with the current OpenAI Codex default. (#44367) Thanks @jrrcdev.
- Discord: enforce strict DM component allowlist auth (#49997) Thanks @joshavant.
- Stabilize plugin loader and Docker extension smoke (#50058) Thanks @joshavant.
- Telegram: stabilize pairing/session/forum routing and reply formatting tests (#50155) Thanks @joshavant.
- Hardening: refresh stale device pairing requests and pending metadata (#50695) Thanks @smaeljaish771 and @joshavant.
- Gateway: harden OpenResponses file-context escaping (#50782) Thanks @YLChen-007 and @joshavant.
- LINE: harden Express webhook parsing to verified raw body (#51202) Thanks @gladiator9797 and @joshavant.
- Exec: harden host env override handling across gateway and node (#51207) Thanks @gladiator9797 and @joshavant.
- Voice Call: enforce spoken-output contract and fix stream TTS silence regression (#51500) Thanks @joshavant.
- xAI/models: rename the bundled Grok 4.20 catalog entries to the GA IDs and normalize saved deprecated beta IDs at runtime so existing configs and sessions keep resolving. (#50772) thanks @Jaaneek
- Plugins/Matrix TTS: send auto-TTS replies as native Matrix voice bubbles instead of generic audio attachments. (#37080) thanks @Matthew19990919.
- Plugins/discovery: distinguish missing package entry files from package-path escape violations so startup skips absent plugin entry paths without raising false security diagnostics. (#52491) Thanks @hclsys.
### Fixes
- Agents/bootstrap warnings: move bootstrap truncation warnings out of the system prompt and into the per-turn prompt body so prompt-cache reuse stays stable when truncation warnings appear or disappear. (#48753) Thanks @scoootscooob and @obviyus.
- Plugins/Matrix: accept shared send-tool media aliases (`mediaUrl`, `filePath`, `path`) and preserve `asVoice` / `audioAsVoice` through Matrix action dispatch so media-only sends and voice-message intents reach the plugin send layer correctly. Thanks @psacc and @vincentkoc.
- Telegram/DM topic session keys: route named-account DM topics through the same per-account base session key across inbound messages, native commands, and session-state lookups so `/status` and thread recovery stop creating phantom `agent:main:main:thread:...` sessions. (#48204) Thanks @vincentkoc.
- macOS/node service startup: use `openclaw node start/stop --json` from the Mac app instead of the removed `openclaw service node ...` command shape, so current CLI installs expose the full node exec surface again. (#46843) Fixes #43171. Thanks @Br1an67.
- macOS/launch at login: stop emitting `KeepAlive` for the desktop app launch agent so OpenClaw no longer relaunches immediately after a manual quit while launch at login remains enabled. (#40213) Thanks @stablegenius49.
- ACP/gateway startup: use direct Telegram and Discord startup/status helpers instead of routing probes through the plugin runtime, and prepend the selected daemon Node bin dir to service PATH so plugin-local installs can still find `npm` and `pnpm`.
- ACP/configured bindings: reinitialize configured ACP sessions that are stuck in `error` state instead of reusing the failed runtime.
- Mattermost/DM send: retry transient direct-channel creation failures for DM deliveries, with configurable backoff and per-request timeout. (#42398) Thanks @JonathanJing.
- Telegram/network: unify API and media fetches under the same sticky IPv4 and pinned-IP fallback chain, and re-validate pinned override addresses against SSRF policy. (#49148) Thanks @obviyus.
- Agents/prompt composition: append bootstrap truncation warnings to the current-turn prompt and add regression coverage for stable system-prompt cache invariants. (#49237) Thanks @scoootscooob.
- Gateway/auth: add regression coverage that keeps device-less trusted-proxy Control UI sessions off privileged pairing approval RPCs. Thanks @vincentkoc.
- Plugins/runtime-api: pin extension runtime-api export surfaces with explicit guardrail coverage so future surface creep becomes a deliberate diff. Thanks @vincentkoc.
- Synology Chat/multi-account: scope direct-message sessions by account and sender so identical webhook `user_id` values on different Synology accounts no longer share transcript or delivery state.
- Telegram/security: add regression coverage proving pinned fallback host overrides stay bound to Telegram and delegate non-matching hostnames back to the original lookup path. Thanks @vincentkoc.
- Secrets/exec refs: require explicit `--allow-exec` for `secrets apply` write plans that contain exec SecretRefs/providers, and align audit/configure/apply dry-run behavior to skip exec checks unless opted in to prevent unexpected command side effects. (#49417) Thanks @restriction and @joshavant.
- Tools/image generation: add bundled fal image generation support so `image_generate` can target `fal/*` models with `FAL_KEY`, including single-image edit flows via FLUX image-to-image. Thanks @vincentkoc.
- Gateway/hooks: preserve immutable hook ingress provenance across async isolated-agent dispatch so normalized hook session routes keep external wrapping, Gmail-specific policy, and Gmail model selection intact.
- Messages/polls: treat zero-valued poll params on `message.send` as unset defaults while keeping non-zero poll params on the poll validation path. (#52150) Fixes #52118. Thanks @Bartok9.
- xAI/web search: add missing Grok credential metadata so the bundled provider registration type-checks again. (#49472) thanks @scoootscooob.
- Agents/session cache: opportunistically sweep expired embedded-runner session cache entries during later cache activity, so one-shot session files do not accumulate forever. (#52427) Thanks @karanuppal.
- Signal/runtime API: re-export `SignalAccountConfig` so Signal account resolution type-checks again. (#49470) Thanks @scoootscooob.
- Google Chat/runtime API: thin the private runtime barrel onto the curated public SDK surface while keeping public Google Chat exports intact. (#49504) Thanks @scoootscooob.
- WhatsApp: stabilize inbound monitor and setup tests (#50007) Thanks @joshavant.
- Matrix: make onboarding status runtime-safe (#49995) Thanks @joshavant.
- Channels: stabilize lane harness and monitor tests (#50167) Thanks @joshavant.
- WhatsApp/active-listener: pin the active listener registry to a `globalThis` singleton so split WhatsApp bundle chunks share one listener map and outbound sends stop missing the registered session. (#47433) Thanks @clawdia67.
- Plugins/WhatsApp: share split-load singleton state for plugin command registration and active WhatsApp listeners so duplicate module graphs no longer lose native plugin commands or outbound listener state. (#50418) Thanks @huntharo.
- Onboarding/custom providers: keep Azure AI Foundry `*.services.ai.azure.com` custom endpoints on the selected compatibility path instead of forcing Responses, so chat-completions Foundry models still work after setup. Fixes #50528. (#50535) Thanks @obviyus.
- Plugins/update: let `openclaw plugins update <npm-spec>` target tracked npm installs by dist-tag or exact version, and preserve the recorded npm spec for later id-based updates. (#49998) Thanks @huntharo.
- Tests/CLI: reduce command-secret gateway test import pressure while keeping the real protocol payload validator in place, so the isolated lane no longer carries the heavier runtime-web and message-channel graphs. (#50663) Thanks @huntharo.
- Gateway/plugins: share plugin interactive callback routing and plugin bind approval state across duplicate module graphs so Telegram Codex picker buttons and plugin bind approvals no longer fall through to normal inbound message routing. (#50722) Thanks @huntharo.
- Agents/compaction: add an opt-in post-compaction session JSONL truncation step that drops summarized transcript entries while preserving the retained branch tail and live session metadata. (#41021) thanks @thirumaleshp.
- Telegram/routing: fail loud when `message send` targets an unknown non-default Telegram `accountId`, instead of silently falling back to the channel-level bot token and sending through the wrong bot. (#50853) Thanks @hclsys.
- Web search: align onboarding, configure, and finalize with plugin-owned provider contracts, including disabled-provider recovery, config-aware credential hooks, and runtime-visible summaries. (#50935) Thanks @gumadeiras.
- Agents/replay: sanitize malformed assistant tool-call replay blocks before provider replay so follow-up Anthropic requests do not inherit the downstream `replace` crash. (#50005) Thanks @jalehman.
- Plugins/context engines: retry strict legacy `assemble()` calls without the new `prompt` field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.
- make `openclaw update status` explicitly say `up to date` when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.
- Agents/embedded transport errors: distinguish common network failures like connection refused, DNS lookup failure, and interrupted sockets from true timeouts in embedded-run user messaging and lifecycle diagnostics. (#51419) Thanks @scoootscooob.
- Discord/startup logging: report client initialization while the gateway is still connecting instead of claiming Discord is logged in before readiness is reached. (#51425) Thanks @scoootscoob.
- Gateway/probe: honor caller `--timeout` for active local loopback probes in `gateway status`, keep inactive remote-mode loopback probes fast, and clamp probe timers to JS-safe bounds so slow local/container gateways stop reporting false timeouts. (#47533) Thanks @MonkeyLeeT.
- Config/startup: keep bundled web-search allowlist compatibility on a lightweight manifest path so config validation no longer pulls bundled web-search registry imports into startup, while still avoiding accidental auto-allow of config-loaded override plugins. (#51574) Thanks @RichardCao.
- Gateway/chat.send: persist uploaded image references across reloads and compaction without delaying first-turn dispatch or double-submitting the same image to vision models. (#51324) Thanks @fuller-stack-dev.
- Plugins/runtime state: share plugin-facing infra singleton state across duplicate module graphs and keep session-binding adapter ownership stable until the active owner unregisters. (#50725) thanks @huntharo.
- Agents/compaction safeguard: preserve split-turn context and preserved recent turns when capped retry fallback reuses the last successful summary. (#27727) thanks @Pandadadadazxf.
- Discord/pickers: keep `/codex_resume --browse-projects` picker callbacks alive in Discord by sharing component callback state across duplicate module graphs, preserving callback fallbacks, and acknowledging matched plugin interactions before dispatch. (#51260) Thanks @huntharo.
- Agents/memory flush: keep transcript-hash dedup active across memory-flush fallback retries so a write-then-throw flush attempt cannot append duplicate `MEMORY.md` entries before the fallback cycle completes. (#34222) Thanks @lml2468.
- make `openclaw update status` explicitly say `up to date` when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.
- Android/canvas: recycle captured and scaled snapshot bitmaps so repeated canvas snapshots do not leak native image memory. (#41889) Thanks @Kaneki-x.
- Android/theme: switch status bar icon contrast with the active system theme so Android light mode no longer leaves unreadable light icons over the app header. (#51098) Thanks @goweii.
- Discord/ACP: forward worker abort signals into ACP turns so timed-out Discord jobs cancel the running turn instead of silently leaving the bound ACP session working in the background.
- Gateway/openresponses: preserve assistant commentary and session continuity across hosted-tool `/v1/responses` turns, and emit streamed tool-call payloads before finalization so client tool loops stay resumable. (#52171) Thanks @CharZhou.
- Android/Talk: serialize `TalkModeManager` player teardown so rapid interrupt/restart cycles stop double-releasing or overlapping TTS playback. (#52310) Thanks @Kaneki-x.
- WhatsApp/reconnect: preserve the last inbound timestamp across reconnect attempts so the watchdog can still recycle linked-but-dead listeners after a restart instead of leaving them stuck connected forever.
- Gateway/network discovery: guard LAN, tailnet, and pairing interface enumeration so WSL2 and restricted hosts degrade to missing-address fallbacks instead of crashing on `uv_interface_addresses` errors. (#44180, #47590)
- Gateway/bonjour: suppress the non-fatal `@homebridge/ciao` IPv4-loss assertion during interface churn so WiFi/VPN/sleep-wake changes no longer take down the gateway. (#38628, #47159, #52431)
- Browser/launch: stop forcing an extra blank tab on browser launch so managed browser startup no longer opens an unwanted empty page. (#52451) Thanks @rogerdigital.
- ACP/Codex session replay: preserve hidden assistant thinking when loading or rebinding existing ACP sessions so stored thought chunks do not replay into visible assistant text. Thanks @vincentkoc.
- Gateway/commands: keep internal `chat.send` slash-command UX while requiring `operator.admin` before internal callers can persist `/exec` defaults or mutate `phone-control` node policy through `/phone arm|disarm`.
- Plugins/Matrix: move bundled plugin `KeyedAsyncQueue` imports onto the stable `plugin-sdk/core` surface so Matrix Docker/runtime builds do not depend on the brittle keyed-async-queue subpath. Thanks @ecohash-co and @vincentkoc.
- Plugins/context engines: enforce owner-aware context-engine registration on both loader and public SDK paths so plugins cannot spoof privileged ownership, claim the core `legacy` engine id, or overwrite an existing engine id through direct SDK imports. (#47595) Thanks @vincentkoc.
- Plugins/bundler TDZ: fix `RESERVED_COMMANDS` temporal dead zone error that prevented device-pair, phone-control, and talk-voice plugins from registering when the bundler placed the commands module after call sites in the same output chunk. Thanks @BunsDev.
- Plugins/imports: fix stale googlechat runtime-api import paths and signal SDK circular re-exports broken by recent plugin-sdk refactors. Thanks @BunsDev.
- Plugins/install precedence: keep bundled plugins ahead of auto-discovered globals by default, but let an explicitly installed plugin record win its own duplicate-id tie so installed channel plugins load from `~/.openclaw/extensions` after `openclaw plugins install`. (#46722) Thanks @Takhoffman.
- Plugins/scoped ids: preserve scoped plugin ids during install and config keying, and keep bundled plugins ahead of discovered duplicate ids by default so `@scope/name` plugins no longer collide with unscoped installs. (#47413) Thanks @vincentkoc.
- Docs/Mintlify: fix MDX marker syntax on Perplexity, Model Providers, Moonshot, and exec approvals pages so local docs preview no longer breaks rendering or leaves stale pages unpublished. (#46695) Thanks @velvet-shark.
- Plugins/runtime barrels: route bundled extension runtime imports through public `openclaw/plugin-sdk/*` subpaths and block relative cross-package escapes so packaged extensions stop depending on monorepo-only relative paths. (#51939) Thanks @vincentkoc.
- Docs/security audit: spell out that `gateway.controlUi.allowedOrigins: ["*"]` is an explicit allow-all browser-origin policy and should be avoided outside tightly controlled local testing.
- Plugins/subagents: preserve gateway-owned plugin subagent access across runtime, tool, and embedded-runner load paths so gateway plugin tools and context engines can still spawn and manage subagents after the loader cache split. (#46648) Thanks @jalehman.
- Plugins/subagents: forward per-run provider and model overrides through gateway plugin subagent dispatch so plugin-launched agent delegations honor explicit model selection again. (#48277) Thanks @jalehman.
- Tests/OpenAI Codex auth: align login expectations with the default `gpt-5.4` model so CI coverage stays consistent with the current OpenAI Codex default. (#44367) Thanks @jrrcdev.
- Plugins/Matrix TTS: send auto-TTS replies as native Matrix voice bubbles instead of generic audio attachments. (#37080) thanks @Matthew19990919.
- Plugins/discovery: distinguish missing package entry files from package-path escape violations so startup skips absent plugin entry paths without raising false security diagnostics. (#52491) Thanks @hclsys.
- Plugins/Matrix: accept shared send-tool media aliases (`mediaUrl`, `filePath`, `path`) and preserve `asVoice` / `audioAsVoice` through Matrix action dispatch so media-only sends and voice-message intents reach the plugin send layer correctly. Thanks @psacc and @vincentkoc.
- Plugins/runtime-api: pin extension runtime-api export surfaces with explicit guardrail coverage so future surface creep becomes a deliberate diff. Thanks @vincentkoc.
- Plugins/WhatsApp: share split-load singleton state for plugin command registration and active WhatsApp listeners so duplicate module graphs no longer lose native plugin commands or outbound listener state. (#50418) Thanks @huntharo.
- Plugins/update: let `openclaw plugins update <npm-spec>` target tracked npm installs by dist-tag or exact version, and preserve the recorded npm spec for later id-based updates. (#49998) Thanks @huntharo.
- Tests/CLI: reduce command-secret gateway test import pressure while keeping the real protocol payload validator in place, so the isolated lane no longer carries the heavier runtime-web and message-channel graphs. (#50663) Thanks @huntharo.
- Gateway/plugins: share plugin interactive callback routing and plugin bind approval state across duplicate module graphs so Telegram Codex picker buttons and plugin bind approvals no longer fall through to normal inbound message routing. (#50722) Thanks @huntharo.
- Plugins/context engines: retry strict legacy `assemble()` calls without the new `prompt` field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.
- Plugins/runtime state: share plugin-facing infra singleton state across duplicate module graphs and keep session-binding adapter ownership stable until the active owner unregisters. (#50725) thanks @huntharo.
- Discord/pickers: keep `/codex_resume --browse-projects` picker callbacks alive in Discord by sharing component callback state across duplicate module graphs, preserving callback fallbacks, and acknowledging matched plugin interactions before dispatch. (#51260) Thanks @huntharo.
- Memory/core tools: register `memory_search` and `memory_get` independently so one unavailable memory tool no longer suppresses the other in new sessions. (#50198) Thanks @artwalker.
- Telegram/Mattermost message tool: keep plugin button schemas optional in isolated and cron sessions so plain sends do not fail validation when no current channel is active. (#52589) Thanks @tylerliu612.
- Release/npm publish: fail the npm release check when `dist/control-ui/index.html` is missing from the packed tarball, so broken Control UI asset releases are blocked before publish. Fixes #52808. (#52852) Thanks @kevinheinrichs.
- Slack/embedded delivery: suppress transcript-only `delivery-mirror` assistant messages before embedded re-delivery and raise the default Slack chunk fallback so messages just over 4000 characters stay in a single post. (#45489) Thanks @theo674.
- Slack/embedded delivery: suppress transcript-only `delivery-mirror` assistant messages before embedded re-delivery and raise the default Slack chunk fallback so messages just over 4000 characters stay in a single post. (#45489) Thanks @theo674.
### Breaking
### Fixes
- Skills/image generation: remove the bundled `nano-banana-pro` skill wrapper. Use `agents.defaults.imageGenerationModel.primary: "google/gemini-3-pro-image-preview"` for the native Nano Banana-style path instead.
- Browser/Chrome MCP: remove the legacy Chrome extension relay path, bundled extension assets, `driver: "extension"`, and `browser.relayBindHost`. Run `openclaw doctor --fix` to migrate host-local browser config to `existing-session` / `user`; Docker, headless, sandbox, and remote browser flows still use raw CDP. (#47893) Thanks @vincentkoc.
- Plugins/runtime: remove the public `openclaw/extension-api` surface with no compatibility shim. Bundled plugins must use injected runtime for host-side operations (for example `api.runtime.agent.runEmbeddedPiAgent`) and any remaining direct imports must come from narrow `openclaw/plugin-sdk/*` subpaths instead of the monolithic SDK root.
- Tools/image generation: standardize the stock image create/edit path on the core `image_generate` tool. The old `nano-banana-pro` docs/examples are gone; if you previously copied that sample-skill config, switch to `agents.defaults.imageGenerationModel` for built-in image generation or install a separate third-party skill explicitly.
- Skills/image generation: remove the bundled `nano-banana-pro` skill wrapper. Use `agents.defaults.imageGenerationModel.primary: "google/gemini-3-pro-image-preview"` for the native Nano Banana-style path instead.
- Plugins/message discovery: require `ChannelMessageActionAdapter.describeMessageTool(...)` for shared `message` tool discovery. The legacy `listActions`, `getCapabilities`, and `getToolSchema` adapter methods are removed. Plugin authors should migrate message discovery to `describeMessageTool(...)` and keep channel-specific action runtime code inside the owning plugin package. Thanks @gumadeiras.
- Exec/env sandbox: block build-tool JVM injection (`MAVEN_OPTS`, `SBT_OPTS`, `GRADLE_OPTS`, `ANT_OPTS`), glibc tunable exploitation (`GLIBC_TUNABLES`), and .NET dependency resolution hijack (`DOTNET_ADDITIONAL_DEPS`) from the host exec environment, and restrict Gradle init script redirect (`GRADLE_USER_HOME`) as an override-only block so user-configured Gradle homes still propagate. (#49702)
- Plugins/Matrix: add a new Matrix plugin backed by the official `matrix-js-sdk`. If you are upgrading from the previous public Matrix plugin, follow the migration guide: https://docs.openclaw.ai/install/migrating-matrix Thanks @gumadeiras.
- Plugins/Matrix: stop mention-gated or otherwise dropped room chatter from refreshing focused thread bindings before the message is actually routed, so idle ACP and session bindings can still expire normally in mention-required rooms. Thanks @vincentkoc, @dinakars777 and @mvanhorn.
- Discord/commands: switch native command deployment to Carbon reconcile by default so Discord restarts stop churning slash commands through OpenClaws local deploy path. (#46597) Thanks @huntharo and @thewilloftheshadow.
- Plugins/Matrix: durably dedupe inbound room events across gateway restarts so previously handled Matrix messages are not replayed as new, while preserving clean-restart backlog delivery for unseen events. (#50922) thanks @gumadeiras
- Agents/media replies: migrate the remaining browser, canvas, and nodes snapshot outputs onto `details.media` so generated media keeps attaching to assistant replies after the collect-then-attach refactor. (#51731) Thanks @christianklotz.
- Android/contacts search: escape literal `%` and `_` in contact-name queries so searches like `100%` or `_id` no longer match unrelated contacts through SQL `LIKE` wildcards. (#41891) Thanks @Kaneki-x.
- Agents/edit tool: accept common path/text alias spellings, show current file contents on exact-match failures, and avoid false edit failures after successful writes. (#52516) thanks @mbelinky.
## 2026.3.13

View File

@@ -24,7 +24,7 @@ Welcome to the lobster tank! 🦞
- GitHub: [@joshp123](https://github.com/joshp123) · X: [@jjpcodes](https://x.com/jjpcodes)
- **Ayaan Zaidi** - Telegram subsystem, Android app
- GitHub: [@obviyus](https://github.com/obviyus) · X: [@0bviyus](https://x.com/0bviyus)
- GitHub: [@obviyus](https://github.com/obviyus) · X: [@obviyus](https://x.com/obviyus)
- **Tyler Yust** - Agents/subagents, cron, BlueBubbles, macOS app
- GitHub: [@tyler6204](https://github.com/tyler6204) · X: [@tyleryust](https://x.com/tyleryust)
@@ -58,6 +58,7 @@ Welcome to the lobster tank! 🦞
- **Jonathan Taylor** - ACP subsystem, Gateway features/bugs, Gog/Mog/Sog CLI's, SEDMAT
- GitHub [@visionik](https://github.com/visionik) · X: [@visionik](https://x.com/visionik)
- **Josh Lehman** - Compaction, Tlon/Urbit subsystem
- GitHub [@jalehman](https://github.com/jalehman) · X: [@jlehman\_](https://x.com/jlehman_)
@@ -76,9 +77,6 @@ Welcome to the lobster tank! 🦞
- **Tengji (George) Zhang** - Chinese model APIs, cloud, pi
- GitHub: [@odysseus0](https://github.com/odysseus0) · X: [@odysseus0z](https://x.com/odysseus0z)
- **Andrew (Bubbles) Demczuk** - Agents/Gateway/TTS/VTT
- GitHub: [@ademczuk](https://github.com/ademczuk) · X: [@ademczuk](https://x.com/ademczuk)
## How to Contribute
1. **Bugs & small fixes** → Open a PR!

View File

@@ -19,7 +19,7 @@
</p>
**OpenClaw** is a _personal AI assistant_ you run on your own devices.
It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WebChat). It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WeChat, WebChat). It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
@@ -74,7 +74,7 @@ openclaw gateway --port 18789 --verbose
# Send a message
openclaw message send --to +1234567890 --message "Hello from OpenClaw"
# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/IRC/Microsoft Teams/Matrix/Feishu/LINE/Mattermost/Nextcloud Talk/Nostr/Synology Chat/Tlon/Twitch/Zalo/Zalo Personal/WebChat)
# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/IRC/Microsoft Teams/Matrix/Feishu/LINE/Mattermost/Nextcloud Talk/Nostr/Synology Chat/Tlon/Twitch/Zalo/Zalo Personal/WeChat/WebChat)
openclaw agent --message "Ship checklist" --thinking high
```
@@ -126,7 +126,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
## Highlights
- **[Local-first Gateway](https://docs.openclaw.ai/gateway)** — single control plane for sessions, channels, tools, and events.
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WeChat, WebChat, macOS, iOS/Android.
- **[Multi-agent routing](https://docs.openclaw.ai/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — wake words on macOS/iOS and continuous voice on Android (ElevenLabs + system TTS fallback).
- **[Live Canvas](https://docs.openclaw.ai/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
@@ -150,7 +150,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
### Channels
- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (iMessage, recommended), [iMessage](https://docs.openclaw.ai/channels/imessage) (legacy imsg), [IRC](https://docs.openclaw.ai/channels/irc), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams), [Matrix](https://docs.openclaw.ai/channels/matrix), [Feishu](https://docs.openclaw.ai/channels/feishu), [LINE](https://docs.openclaw.ai/channels/line), [Mattermost](https://docs.openclaw.ai/channels/mattermost), [Nextcloud Talk](https://docs.openclaw.ai/channels/nextcloud-talk), [Nostr](https://docs.openclaw.ai/channels/nostr), [Synology Chat](https://docs.openclaw.ai/channels/synology-chat), [Tlon](https://docs.openclaw.ai/channels/tlon), [Twitch](https://docs.openclaw.ai/channels/twitch), [Zalo](https://docs.openclaw.ai/channels/zalo), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser), [WebChat](https://docs.openclaw.ai/web/webchat).
- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (iMessage, recommended), [iMessage](https://docs.openclaw.ai/channels/imessage) (legacy imsg), [IRC](https://docs.openclaw.ai/channels/irc), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams), [Matrix](https://docs.openclaw.ai/channels/matrix), [Feishu](https://docs.openclaw.ai/channels/feishu), [LINE](https://docs.openclaw.ai/channels/line), [Mattermost](https://docs.openclaw.ai/channels/mattermost), [Nextcloud Talk](https://docs.openclaw.ai/channels/nextcloud-talk), [Nostr](https://docs.openclaw.ai/channels/nostr), [Synology Chat](https://docs.openclaw.ai/channels/synology-chat), [Tlon](https://docs.openclaw.ai/channels/tlon), [Twitch](https://docs.openclaw.ai/channels/twitch), [Zalo](https://docs.openclaw.ai/channels/zalo), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser), WeChat (`@tencent-weixin/openclaw-weixin`), [WebChat](https://docs.openclaw.ai/web/webchat).
- [Group routing](https://docs.openclaw.ai/channels/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.openclaw.ai/channels).
### Apps + nodes
@@ -185,7 +185,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
## How it works (short)
```
WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBubbles / IRC / Microsoft Teams / Matrix / Feishu / LINE / Mattermost / Nextcloud Talk / Nostr / Synology Chat / Tlon / Twitch / Zalo / Zalo Personal / WebChat
WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBubbles / IRC / Microsoft Teams / Matrix / Feishu / LINE / Mattermost / Nextcloud Talk / Nostr / Synology Chat / Tlon / Twitch / Zalo / Zalo Personal / WeChat / WebChat
┌───────────────────────────────┐
@@ -397,6 +397,12 @@ Details: [Security guide](https://docs.openclaw.ai/gateway/security) · [Docker
- Configure a Teams app + Bot Framework, then add a `msteams` config section.
- Allowlist who can talk via `msteams.allowFrom`; group access via `msteams.groupAllowFrom` or `msteams.groupPolicy: "open"`.
### WeChat
- Official Tencent plugin via [`@tencent-weixin/openclaw-weixin`](https://www.npmjs.com/package/@tencent-weixin/openclaw-weixin) (iLink Bot API). Private chats only; v2.x requires OpenClaw `>=2026.3.22`.
- Install: `openclaw plugins install "@tencent-weixin/openclaw-weixin"`, then `openclaw channels login --channel openclaw-weixin` to scan the QR code.
- Requires the WeChat ClawBot plugin (WeChat > Me > Settings > Plugins); gradual rollout by Tencent.
### [WebChat](https://docs.openclaw.ai/web/webchat)
- Uses the Gateway WebSocket; no separate WebChat port/config.

View File

@@ -2,6 +2,99 @@
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>OpenClaw</title>
<item>
<title>2026.3.24</title>
<pubDate>Wed, 25 Mar 2026 17:06:31 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026032490</sparkle:version>
<sparkle:shortVersionString>2026.3.24</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.3.24</h2>
<h3>Breaking</h3>
<h3>Changes</h3>
<ul>
<li>Gateway/OpenAI compatibility: add <code>/v1/models</code> and <code>/v1/embeddings</code>, and forward explicit model overrides through <code>/v1/chat/completions</code> and <code>/v1/responses</code> for broader client and RAG compatibility. Thanks @vincentkoc.</li>
<li>Agents/tools: make <code>/tools</code> show the tools the current agent can actually use right now, add a compact default view with an optional detailed mode, and add a live "Available Right Now" section in the Control UI so it is easier to see what will work before you ask.</li>
<li>Microsoft Teams: migrate to the official Teams SDK and add AI-agent UX best practices including streaming 1:1 replies, welcome cards with prompt starters, feedback/reflection, informative status updates, typing indicators, and native AI labeling. (#51808)</li>
<li>Microsoft Teams: add message edit and delete support for sent messages, including in-thread fallbacks when no explicit target is provided. (#49925)</li>
<li>Skills/install metadata: add one-click install recipes to bundled skills (coding-agent, gh-issues, openai-whisper-api, session-logs, tmux, trello, weather) so the CLI and Control UI can offer dependency installation when requirements are missing. (#53411) Thanks @BunsDev.</li>
<li>Control UI/skills: add status-filter tabs (All / Ready / Needs Setup / Disabled) with counts, replace inline skill cards with a click-to-detail dialog showing requirements, toggle switch, install action, API key entry, source metadata, and homepage link. (#53411) Thanks @BunsDev.</li>
<li>Slack/interactive replies: restore rich reply parity for direct deliveries, auto-render simple trailing <code>Options:</code> lines as buttons/selects, improve Slack interactive setup defaults, and isolate reply controls from plugin interactive handlers. (#53389) Thanks @vincentkoc.</li>
<li>CLI/containers: add <code>--container</code> and <code>OPENCLAW_CONTAINER</code> to run <code>openclaw</code> commands inside a running Docker or Podman OpenClaw container. (#52651) Thanks @sallyom.</li>
<li>Discord/auto threads: add optional <code>autoThreadName: "generated"</code> naming so new auto-created threads can be renamed asynchronously with concise LLM-generated titles while keeping the existing message-based naming as the default. (#43366) Thanks @davidguttman.</li>
<li>Plugins/hooks: add <code>before_dispatch</code> with canonical inbound metadata and route handled replies through the normal final-delivery path, preserving TTS and routed delivery semantics. (#50444) Thanks @gfzhx.</li>
<li>Control UI/agents: convert agent workspace file rows to expandable <code><details></code> with lazy-loaded inline markdown preview, and add comprehensive <code>.sidebar-markdown</code> styles for headings, lists, code blocks, tables, blockquotes, and details/summary elements. (#53411) Thanks @BunsDev.</li>
<li>Control UI/markdown preview: restyle the agent workspace file preview dialog with a frosted backdrop, sized panel, and styled header, and integrate <code>@create-markdown/preview</code> v2 system theme for rich markdown rendering (headings, tables, code blocks, callouts, blockquotes) that auto-adapts to the app's light/dark design tokens. (#53411) Thanks @BunsDev.</li>
<li>macOS app/config: replace horizontal pill-based subsection navigation with a collapsible tree sidebar using disclosure chevrons and indented subsection rows. (#53411) Thanks @BunsDev.</li>
<li>CLI/skills: soften missing-requirements label from "missing" to "needs setup" and surface API key setup guidance (where to get a key, CLI save command, storage path) in <code>openclaw skills info</code> output. (#53411) Thanks @BunsDev.</li>
<li>macOS app/skills: add "Get your key" homepage link and storage-path hint to the API key editor dialog, and show the config path in save confirmation messages. (#53411) Thanks @BunsDev.</li>
<li>Control UI/agents: add a "Not set" placeholder to the default agent model selector dropdown. (#53411) Thanks @BunsDev.</li>
<li>Runtime/install: lower the supported Node 22 floor to <code>22.14+</code> while continuing to recommend Node 24, so npm installs and self-updates do not strand Node 22.14 users on older releases.</li>
<li>CLI/update: preflight the target npm package <code>engines.node</code> before <code>openclaw update</code> runs a global package install, so outdated Node runtimes fail with a clear upgrade message instead of attempting an unsupported latest release.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Outbound media/local files: align outbound media access with the configured fs policy so host-local files and inbound-media paths keep sending when <code>workspaceOnly</code> is off, while strict workspace-only agents remain sandboxed.</li>
<li>Security/sandbox media dispatch: close the <code>mediaUrl</code>/<code>fileUrl</code> alias bypass so outbound tool and message actions cannot escape media-root restrictions. (#54034)</li>
<li>Gateway/restart sentinel: wake the interrupted agent session via heartbeat after restart instead of only sending a best-effort restart note, retry outbound delivery once on transient failure, and preserve explicit thread/topic routing through the wake path so replies land in the correct Telegram topic or Slack thread. (#53940) Thanks @VACInc.</li>
<li>Docker/setup: avoid the pre-start <code>openclaw-cli</code> shared-network namespace loop by routing setup-time onboard/config writes through <code>openclaw-gateway</code>, so fresh Docker installs stop failing before the gateway comes up. (#53385) Thanks @amsminn.</li>
<li>Gateway/channels: keep channel startup sequential while isolating per-channel boot failures, so one broken channel no longer blocks later channels from starting. (#54215) Thanks @JonathanJing.</li>
<li>Embedded runs/secrets: stop unresolved <code>SecretRef</code> config from crashing embedded agent runs by falling back to the resolved runtime snapshot when needed. Fixes #45838.</li>
<li>WhatsApp/groups: track recent gateway-sent message IDs and suppress only matching group echoes, preserving owner <code>/status</code>, <code>/new</code>, and <code>/activation</code> commands from linked-account <code>fromMe</code> traffic. (#53624) Thanks @w-sss.</li>
<li>WhatsApp/reply-to-bot detection: restore implicit group reply detection by unwrapping <code>botInvokeMessage</code> payloads and reading <code>selfLid</code> from <code>creds.json</code>, so reply-based mentions reach the bot again in linked-account group chats.</li>
<li>Telegram/forum topics: recover <code>#General</code> topic <code>1</code> routing when Telegram omits forum metadata, including native commands, interactive callbacks, inbound message context, and fallback error replies. (#53699) thanks @huntharo</li>
<li>Discord/gateway supervision: centralize gateway error handling behind a lifetime-owned supervisor so early, active, and late-teardown Carbon gateway errors stay classified consistently and stop surfacing as process-killing teardown crashes.</li>
<li>Discord/timeouts: send a visible timeout reply when the inbound Discord worker times out before a final reply starts, including created auto-thread targets and queued-run ordering. (#53823) Thanks @Kimbo7870.</li>
<li>ACP/direct chats: always deliver a terminal ACP result when final TTS does not yield audio, even if block text already streamed earlier, and skip redundant empty-text final synthesis. (#53692) Thanks @w-sss.</li>
<li>Telegram/outbound errors: preserve actionable 403 membership/block/kick details and treat <code>bot not a member</code> as a permanent delivery failure so Telegram sends stop retrying doomed chats. (#53635) Thanks @w-sss.</li>
<li>Telegram/photos: preflight Telegram photo dimension and aspect-ratio rules, and fall back to document sends when image metadata is invalid or unavailable so photo uploads stop failing with <code>PHOTO_INVALID_DIMENSIONS</code>. (#52545) Thanks @hnshah.</li>
<li>Slack/runtime defaults: trim Slack DM reply overhead, restore Codex auto transport, and tighten Slack/web-search runtime defaults around DM preview threading, cache scoping, warning dedupe, and explicit web-search opt-in. (#53957) Thanks @vincentkoc.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.24/OpenClaw-2026.3.24.zip" length="24749233" type="application/octet-stream" sparkle:edSignature="gLm2VvI+PPEnNy4klYSs9WmZLkJTF5BcfFparrtPdnmeE4xgc8kFfICg445I039ev9/A6xGav7pm08reUHDcAg=="/>
</item>
<item>
<title>2026.3.23</title>
<pubDate>Mon, 23 Mar 2026 16:59:51 -0700</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026032390</sparkle:version>
<sparkle:shortVersionString>2026.3.23</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.3.23</h2>
<h3>Breaking</h3>
<h3>Changes</h3>
<h3>Fixes</h3>
<ul>
<li>Browser/Chrome MCP: wait for existing-session browser tabs to become usable after attach instead of treating the initial Chrome MCP handshake as ready, which reduces user-profile timeouts and repeated consent churn on macOS Chrome attach flows. Fixes #52930. Thanks @vincentkoc.</li>
<li>Browser/CDP: reuse an already-running loopback browser after a short initial reachability miss instead of immediately falling back to relaunch detection, which fixes second-run browser start/open regressions on slower headless Linux setups. Fixes #53004. Thanks @vincentkoc.</li>
<li>ClawHub/macOS auth: honor macOS auth config and XDG auth paths for saved ClawHub credentials, so <code>openclaw skills ...</code> and gateway skill browsing keep using the signed-in auth state instead of silently falling back to unauthenticated mode. Fixes #53034.</li>
<li>ClawHub/macOS: read the local ClawHub login from the macOS Application Support path and still honor XDG config on macOS, so skill browsing uses the logged-in token on both default and XDG-style setups. Fixes #52949. Thanks @scoootscooob.</li>
<li>ClawHub/skills: resolve the local ClawHub auth token for gateway skill browsing and switch browse-all requests to search so ClawControl stops falling into unauthenticated 429s and empty authenticated skill lists. Fixes #52949. Thanks @vincentkoc.</li>
<li>Plugins/message tool: make Discord <code>components</code> and Slack <code>blocks</code> optional again, and route Feishu <code>message(..., media=...)</code> sends through the outbound media path, so pin/unpin/react flows stop failing schema validation and Feishu file/image attachments actually send. Fixes #52970 and #52962. Thanks @vincentkoc.</li>
<li>Gateway/model pricing: stop <code>openrouter/auto</code> pricing refresh from recursing indefinitely during bootstrap, so OpenRouter auto routes can populate cached pricing and <code>usage.cost</code> again. Fixes #53035. Thanks @vincentkoc.</li>
<li>Mistral/models: lower bundled Mistral max-token defaults to safe output budgets and teach <code>openclaw doctor --fix</code> to repair old persisted Mistral provider configs that still carry context-sized output limits, avoiding deterministic Mistral 422 rejects on fresh and existing setups. Fixes #52599. Thanks @vincentkoc.</li>
<li>Agents/web_search: use the active runtime <code>web_search</code> provider instead of stale/default selection, so agent turns keep hitting the provider you actually configured. Fixes #53020. Thanks @jzakirov.</li>
<li>Models/OpenAI Codex OAuth: bootstrap the env-configured HTTP/HTTPS proxy dispatcher on the stored-credential refresh path before token renewal runs, so expired Codex OAuth profiles can refresh successfully in proxy-required environments instead of locking users out after the first token expiry.</li>
<li>Plugins/memory-lancedb: bootstrap LanceDB into plugin runtime state on first use when the bundled npm install does not already have it, so <code>plugins.slots.memory="memory-lancedb"</code> works again after global npm installs without moving LanceDB into OpenClaw core dependencies. Fixes #26100.</li>
<li>Config/plugins: treat stale unknown <code>plugins.allow</code> ids as warnings instead of fatal config errors, so recovery commands like <code>plugins install</code>, <code>doctor --fix</code>, and <code>status</code> still run when a plugin is missing locally. Fixes #52992. Thanks @vincentkoc.</li>
<li>Doctor/WhatsApp: stop auto-enable from appending built-in channel ids like <code>whatsapp</code> to <code>plugins.allow</code>, so <code>openclaw doctor --fix</code> no longer writes schema-invalid plugin allowlist entries when repairing built-in channels. Fixes #52931. Thanks @vincentkoc.</li>
<li>Telegram/auto-reply: preserve same-chat inbound debounce order without stranding stale busy-session followups, and keep same-key overflow turns ordered when tracked debounce keys are saturated. (#52998) Thanks @osolmaz.</li>
<li>Discord/commands: return an explicit unauthorized reply for privileged native slash commands instead of falling through to Discord's misleading generic completion when auth gates reject the sender. Fixes #53041. Thanks @scoootscooob.</li>
<li>Channels/catalog: let external channel catalogs override shipped fallback metadata and honor overridden npm specs during channel setup, so custom channel catalogs no longer fall back to bundled packages when a channel id matches. (#52988)</li>
<li>Voice-call/Plivo: stabilize Plivo v2 replay keys so webhook retries and replay protection stop colliding on valid follow-up deliveries.</li>
<li>Agents/skills: prefer the active resolved runtime snapshot for embedded skill config and env injection, so <code>skills.entries.<skill>.apiKey</code> SecretRefs resolve correctly during embedded startup instead of failing on raw source config. Fixes #53098. Thanks @vincentkoc.</li>
<li>Agents/subagents: recheck timed-out worker waits against the latest runtime snapshot before sending completion events, so fast-finishing workers stop being reported as timed out when they actually succeeded. Fixes #53106. Thanks @vincentkoc.</li>
<li>Agents/Anthropic: preserve latest assistant thinking and redacted-thinking block ordering during transcript image sanitization so follow-up turns do not trip Anthropic's unmodified-thinking validation. (#52961) Thanks @vincentkoc.</li>
<li>Gateway/probe: stop successful gateway handshakes from timing out as unreachable while post-connect detail RPCs are still loading, so slow devices report a reachable RPC failure instead of a false negative dead gateway. Fixes #52927. Thanks @vincentkoc.</li>
<li>Gateway/supervision: stop lock conflicts from crash-looping under launchd and systemd by keeping the duplicate process in a retry wait instead of exiting as a failure while another healthy gateway still owns the lock. Fixes #52922. Thanks @vincentkoc.</li>
<li>Gateway/auth: require auth for canvas routes and admin scope for agent session reset, so anonymous canvas access and non-admin reset requests fail closed.</li>
<li>Release/install: keep previously released bundled plugins and Control UI assets in published openclaw npm installs, and fail release checks when those shipped artifacts are missing. Thanks @vincentkoc.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.23/OpenClaw-2026.3.23.zip" length="24522883" type="application/octet-stream" sparkle:edSignature="ptBgHYLBqq/TSdONYCfIB5d6aP/ij/9G0gYQ5mJI9jf8Y31sbQIh5CqpJVxEEWLTMIGQKsHQir/kXZjtRvvZAg=="/>
</item>
<item>
<title>2026.3.13</title>
<pubDate>Sat, 14 Mar 2026 05:19:48 +0000</pubDate>
@@ -78,171 +171,5 @@
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.13/OpenClaw-2026.3.13.zip" length="23640917" type="application/octet-stream" sparkle:edSignature="Me63UHSpFLocTo5Lt7Iqsl0Hq61y3jTcZ9DUkiFl9xQvTE0+ORuqRMFWqPgYwfaKMgcgQmUbrV/uFzEoTIRHBA=="/>
</item>
<item>
<title>2026.3.12</title>
<pubDate>Fri, 13 Mar 2026 04:25:50 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026031290</sparkle:version>
<sparkle:shortVersionString>2026.3.12</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.3.12</h2>
<h3>Changes</h3>
<ul>
<li>Control UI/dashboard-v2: refresh the gateway dashboard with modular overview, chat, config, agent, and session views, plus a command palette, mobile bottom tabs, and richer chat tools like slash commands, search, export, and pinned messages. (#41503) Thanks @BunsDev.</li>
<li>OpenAI/GPT-5.4 fast mode: add configurable session-level fast toggles across <code>/fast</code>, TUI, Control UI, and ACP, with per-model config defaults and OpenAI/Codex request shaping.</li>
<li>Anthropic/Claude fast mode: map the shared <code>/fast</code> toggle and <code>params.fastMode</code> to direct Anthropic API-key <code>service_tier</code> requests, with live verification for both Anthropic and OpenAI fast-mode tiers.</li>
<li>Models/plugins: move Ollama, vLLM, and SGLang onto the provider-plugin architecture, with provider-owned onboarding, discovery, model-picker setup, and post-selection hooks so core provider wiring is more modular.</li>
<li>Docs/Kubernetes: Add a starter K8s install path with raw manifests, Kind setup, and deployment docs. Thanks @sallyom @dzianisv @egkristi</li>
<li>Agents/subagents: add <code>sessions_yield</code> so orchestrators can end the current turn immediately, skip queued tool work, and carry a hidden follow-up payload into the next session turn. (#36537) thanks @jriff</li>
<li>Slack/agent replies: support <code>channelData.slack.blocks</code> in the shared reply delivery path so agents can send Block Kit messages through standard Slack outbound delivery. (#44592) Thanks @vincentkoc.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Security/device pairing: switch <code>/pair</code> and <code>openclaw qr</code> setup codes to short-lived bootstrap tokens so the next release no longer embeds shared gateway credentials in chat or QR pairing payloads. Thanks @lintsinghua.</li>
<li>Security/plugins: disable implicit workspace plugin auto-load so cloned repositories cannot execute workspace plugin code without an explicit trust decision. (<code>GHSA-99qw-6mr3-36qr</code>)(#44174) Thanks @lintsinghua and @vincentkoc.</li>
<li>Models/Kimi Coding: send <code>anthropic-messages</code> tools in native Anthropic format again so <code>kimi-coding</code> stops degrading tool calls into XML/plain-text pseudo invocations instead of real <code>tool_use</code> blocks. (#38669, #39907, #40552) Thanks @opriz.</li>
<li>TUI/chat log: reuse the active assistant message component for the same streaming run so <code>openclaw tui</code> no longer renders duplicate assistant replies. (#35364) Thanks @lisitan.</li>
<li>Telegram/model picker: make inline model button selections persist the chosen session model correctly, clear overrides when selecting the configured default, and include effective fallback models in <code>/models</code> button validation. (#40105) Thanks @avirweb.</li>
<li>Cron/proactive delivery: keep isolated direct cron sends out of the write-ahead resend queue so transient-send retries do not replay duplicate proactive messages after restart. (#40646) Thanks @openperf and @vincentkoc.</li>
<li>Models/Kimi Coding: send the built-in <code>User-Agent: claude-code/0.1.0</code> header by default for <code>kimi-coding</code> while still allowing explicit provider headers to override it, so Kimi Code subscription auth can work without a local header-injection proxy. (#30099) Thanks @Amineelfarssi and @vincentkoc.</li>
<li>Models/OpenAI Codex Spark: keep <code>gpt-5.3-codex-spark</code> working on the <code>openai-codex/*</code> path via resolver fallbacks and clearer Codex-only handling, while continuing to suppress the stale direct <code>openai/*</code> Spark row that OpenAI rejects live.</li>
<li>Ollama/Kimi Cloud: apply the Moonshot Kimi payload compatibility wrapper to Ollama-hosted Kimi models like <code>kimi-k2.5:cloud</code>, so tool routing no longer breaks when thinking is enabled. (#41519) Thanks @vincentkoc.</li>
<li>Moonshot CN API: respect explicit <code>baseUrl</code> (api.moonshot.cn) in implicit provider resolution so platform.moonshot.cn API keys authenticate correctly instead of returning HTTP 401. (#33637) Thanks @chengzhichao-xydt.</li>
<li>Kimi Coding/provider config: respect explicit <code>models.providers["kimi-coding"].baseUrl</code> when resolving the implicit provider so custom Kimi Coding endpoints no longer get overwritten by the built-in default. (#36353) Thanks @2233admin.</li>
<li>Gateway/main-session routing: keep TUI and other <code>mode:UI</code> main-session sends on the internal surface when <code>deliver</code> is enabled, so replies no longer inherit the session's persisted Telegram/WhatsApp route. (#43918) Thanks @obviyus.</li>
<li>BlueBubbles/self-chat echo dedupe: drop reflected duplicate webhook copies only when a matching <code>fromMe</code> event was just seen for the same chat, body, and timestamp, preventing self-chat loops without broad webhook suppression. Related to #32166. (#38442) Thanks @vincentkoc.</li>
<li>iMessage/self-chat echo dedupe: drop reflected duplicate copies only when a matching <code>is_from_me</code> event was just seen for the same chat, text, and <code>created_at</code>, preventing self-chat loops without broad text-only suppression. Related to #32166. (#38440) Thanks @vincentkoc.</li>
<li>Subagents/completion announce retries: raise the default announce timeout to 90 seconds and stop retrying gateway-timeout failures for externally delivered completion announces, preventing duplicate user-facing completion messages after slow gateway responses. Fixes #41235. Thanks @vasujain00 and @vincentkoc.</li>
<li>Mattermost/block streaming: fix duplicate message delivery (one threaded, one top-level) when block streaming is active by excluding <code>replyToId</code> from the block reply dedup key and adding an explicit <code>threading</code> dock to the Mattermost plugin. (#41362) Thanks @mathiasnagler and @vincentkoc.</li>
<li>Mattermost/reply media delivery: pass agent-scoped <code>mediaLocalRoots</code> through shared reply delivery so allowed local files upload correctly from button, slash-command, and model-picker replies. (#44021) Thanks @LyleLiu666.</li>
<li>macOS/Reminders: add the missing <code>NSRemindersUsageDescription</code> to the bundled app so <code>apple-reminders</code> can trigger the system permission prompt from OpenClaw.app. (#8559) Thanks @dinakars777.</li>
<li>Gateway/session discovery: discover disk-only and retired ACP session stores under custom templated <code>session.store</code> roots so ACP reconciliation, session-id/session-label targeting, and run-id fallback keep working after restart. (#44176) thanks @gumadeiras.</li>
<li>Plugins/env-scoped roots: fix plugin discovery/load caches and provenance tracking so same-process <code>HOME</code>/<code>OPENCLAW_HOME</code> changes no longer reuse stale plugin state or misreport <code>~/...</code> plugins as untracked. (#44046) thanks @gumadeiras.</li>
<li>Models/OpenRouter native ids: canonicalize native OpenRouter model keys across config writes, runtime lookups, fallback management, and <code>models list --plain</code>, and migrate legacy duplicated <code>openrouter/openrouter/...</code> config entries forward on write.</li>
<li>Windows/native update: make package installs use the npm update path instead of the git path, carry portable Git into native Windows updates, and mirror the installer's Windows npm env so <code>openclaw update</code> no longer dies early on missing <code>git</code> or <code>node-llama-cpp</code> download setup.</li>
<li>Sandbox/write: preserve pinned mutation-helper payload stdin so sandboxed <code>write</code> no longer reports success while creating empty files. (#43876) Thanks @glitch418x.</li>
<li>Security/exec approvals: escape invisible Unicode format characters in approval prompts so zero-width command text renders as visible <code>\u{...}</code> escapes instead of spoofing the reviewed command. (<code>GHSA-pcqg-f7rg-xfvv</code>)(#43687) Thanks @EkiXu and @vincentkoc.</li>
<li>Hooks/loader: fail closed when workspace hook paths cannot be resolved with <code>realpath</code>, so unreadable or broken internal hook paths are skipped instead of falling back to unresolved imports. (#44437) Thanks @vincentkoc.</li>
<li>Hooks/agent deliveries: dedupe repeated hook requests by optional idempotency key so webhook retries can reuse the first run instead of launching duplicate agent executions. (#44438) Thanks @vincentkoc.</li>
<li>Security/exec detection: normalize compatibility Unicode and strip invisible formatting code points before obfuscation checks so zero-width and fullwidth command tricks no longer suppress heuristic detection. (<code>GHSA-9r3v-37xh-2cf6</code>)(#44091) Thanks @wooluo and @vincentkoc.</li>
<li>Security/exec allowlist: preserve POSIX case sensitivity and keep <code>?</code> within a single path segment so exact-looking allowlist patterns no longer overmatch executables across case or directory boundaries. (<code>GHSA-f8r2-vg7x-gh8m</code>)(#43798) Thanks @zpbrent and @vincentkoc.</li>
<li>Security/commands: require sender ownership for <code>/config</code> and <code>/debug</code> so authorized non-owner senders can no longer reach owner-only config and runtime debug surfaces. (<code>GHSA-r7vr-gr74-94p8</code>)(#44305) Thanks @tdjackey and @vincentkoc.</li>
<li>Security/gateway auth: clear unbound client-declared scopes on shared-token WebSocket connects so device-less shared-token operators cannot self-declare elevated scopes. (<code>GHSA-rqpp-rjj8-7wv8</code>)(#44306) Thanks @LUOYEcode and @vincentkoc.</li>
<li>Security/browser.request: block persistent browser profile create/delete routes from write-scoped <code>browser.request</code> so callers can no longer persist admin-only browser profile changes through the browser control surface. (<code>GHSA-vmhq-cqm9-6p7q</code>)(#43800) Thanks @tdjackey and @vincentkoc.</li>
<li>Security/agent: reject public spawned-run lineage fields and keep workspace inheritance on the internal spawned-session path so external <code>agent</code> callers can no longer override the gateway workspace boundary. (<code>GHSA-2rqg-gjgv-84jm</code>)(#43801) Thanks @tdjackey and @vincentkoc.</li>
<li>Security/session_status: enforce sandbox session-tree visibility and shared agent-to-agent access guards before reading or mutating target session state, so sandboxed subagents can no longer inspect parent session metadata or write parent model overrides via <code>session_status</code>. (<code>GHSA-wcxr-59v9-rxr8</code>)(#43754) Thanks @tdjackey and @vincentkoc.</li>
<li>Security/agent tools: mark <code>nodes</code> as explicitly owner-only and document/test that <code>canvas</code> remains a shared trusted-operator surface unless a real boundary bypass exists.</li>
<li>Security/exec approvals: fail closed for Ruby approval flows that use <code>-r</code>, <code>--require</code>, or <code>-I</code> so approval-backed commands no longer bind only the main script while extra local code-loading flags remain outside the reviewed file snapshot.</li>
<li>Security/device pairing: cap issued and verified device-token scopes to each paired device's approved scope baseline so stale or overbroad tokens cannot exceed approved access. (<code>GHSA-2pwv-x786-56f8</code>)(#43686) Thanks @tdjackey and @vincentkoc.</li>
<li>Docs/onboarding: align the legacy wizard reference and <code>openclaw onboard</code> command docs with the Ollama onboarding flow so all onboarding reference paths now document <code>--auth-choice ollama</code>, Cloud + Local mode, and non-interactive usage. (#43473) Thanks @BruceMacD.</li>
<li>Models/secrets: enforce source-managed SecretRef markers in generated <code>models.json</code> so runtime-resolved provider secrets are not persisted when runtime projection is skipped. (#43759) Thanks @joshavant.</li>
<li>Security/WebSocket preauth: shorten unauthenticated handshake retention and reject oversized pre-auth frames before application-layer parsing to reduce pre-pairing exposure on unsupported public deployments. (<code>GHSA-jv4g-m82p-2j93</code>)(#44089) (<code>GHSA-xwx2-ppv2-wx98</code>)(#44089) Thanks @ez-lbz and @vincentkoc.</li>
<li>Security/proxy attachments: restore the shared media-store size cap for persisted browser proxy files so oversized payloads are rejected instead of overriding the intended 5 MB limit. (<code>GHSA-6rph-mmhp-h7h9</code>)(#43684) Thanks @tdjackey and @vincentkoc.</li>
<li>Security/host env: block inherited <code>GIT_EXEC_PATH</code> from sanitized host exec environments so Git helper resolution cannot be steered by host environment state. (<code>GHSA-jf5v-pqgw-gm5m</code>)(#43685) Thanks @zpbrent and @vincentkoc.</li>
<li>Security/Feishu webhook: require <code>encryptKey</code> alongside <code>verificationToken</code> in webhook mode so unsigned forged events are rejected instead of being processed with token-only configuration. (<code>GHSA-g353-mgv3-8pcj</code>)(#44087) Thanks @lintsinghua and @vincentkoc.</li>
<li>Security/Feishu reactions: preserve looked-up group chat typing and fail closed on ambiguous reaction context so group authorization and mention gating cannot be bypassed through synthetic <code>p2p</code> reactions. (<code>GHSA-m69h-jm2f-2pv8</code>)(#44088) Thanks @zpbrent and @vincentkoc.</li>
<li>Security/LINE webhook: require signatures for empty-event POST probes too so unsigned requests no longer confirm webhook reachability with a <code>200</code> response. (<code>GHSA-mhxh-9pjm-w7q5</code>)(#44090) Thanks @TerminalsandCoffee and @vincentkoc.</li>
<li>Security/Zalo webhook: rate limit invalid secret guesses before auth so weak webhook secrets cannot be brute-forced through unauthenticated churned requests without pre-auth <code>429</code> responses. (<code>GHSA-5m9r-p9g7-679c</code>)(#44173) Thanks @zpbrent and @vincentkoc.</li>
<li>Security/Zalouser groups: require stable group IDs for allowlist auth by default and gate mutable group-name matching behind <code>channels.zalouser.dangerouslyAllowNameMatching</code>. Thanks @zpbrent.</li>
<li>Security/Slack and Teams routing: require stable channel and team IDs for allowlist routing by default, with mutable name matching only via each channel's <code>dangerouslyAllowNameMatching</code> break-glass flag.</li>
<li>Security/exec approvals: fail closed for ambiguous inline loader and shell-payload script execution, bind the real script after POSIX shell value-taking flags, and unwrap <code>pnpm</code>/<code>npm exec</code>/<code>npx</code> script runners before approval binding. (<code>GHSA-57jw-9722-6rf2</code>)(<code>GHSA-jvqh-rfmh-jh27</code>)(<code>GHSA-x7pp-23xv-mmr4</code>)(<code>GHSA-jc5j-vg4r-j5jx</code>)(#44247) Thanks @tdjackey and @vincentkoc.</li>
<li>Doctor/gateway service audit: canonicalize service entrypoint paths before comparing them so symlink-vs-realpath installs no longer trigger false "entrypoint does not match the current install" repair prompts. (#43882) Thanks @ngutman.</li>
<li>Doctor/gateway service audit: earlier groundwork for this fix landed in the superseded #28338 branch. Thanks @realriphub.</li>
<li>Gateway/session stores: regenerate the Swift push-test protocol models and align Windows native session-store realpath handling so protocol checks and sync session discovery stop drifting on Windows. (#44266) thanks @jalehman.</li>
<li>Context engine/session routing: forward optional <code>sessionKey</code> through context-engine lifecycle calls so plugins can see structured routing metadata during bootstrap, assembly, post-turn ingestion, and compaction. (#44157) thanks @jalehman.</li>
<li>Agents/failover: classify z.ai <code>network_error</code> stop reasons as retryable timeouts so provider connectivity failures trigger fallback instead of surfacing raw unhandled-stop-reason errors. (#43884) Thanks @hougangdev.</li>
<li>Memory/session sync: add mode-aware post-compaction session reindexing with <code>agents.defaults.compaction.postIndexSync</code> plus <code>agents.defaults.memorySearch.sync.sessions.postCompactionForce</code>, so compacted session memory can refresh immediately without forcing every deployment into synchronous reindexing. (#25561) thanks @rodrigouroz.</li>
<li>Telegram/model picker: make inline model button selections persist the chosen session model correctly, clear overrides when selecting the configured default, and include effective fallback models in <code>/models</code> button validation. (#40105) Thanks @avirweb.</li>
<li>Telegram/native command sync: suppress expected <code>BOT_COMMANDS_TOO_MUCH</code> retry error noise, add a final fallback summary log, and document the difference between command-menu overflow and real Telegram network failures.</li>
<li>Mattermost/reply media delivery: pass agent-scoped <code>mediaLocalRoots</code> through shared reply delivery so allowed local files upload correctly from button, slash-command, and model-picker replies. (#44021) Thanks @LyleLiu666.</li>
<li>Plugins/env-scoped roots: fix plugin discovery/load caches and provenance tracking so same-process <code>HOME</code>/<code>OPENCLAW_HOME</code> changes no longer reuse stale plugin state or misreport <code>~/...</code> plugins as untracked. (#44046) thanks @gumadeiras.</li>
<li>Gateway/session discovery: discover disk-only and retired ACP session stores under custom templated <code>session.store</code> roots so ACP reconciliation, session-id/session-label targeting, and run-id fallback keep working after restart. (#44176) thanks @gumadeiras.</li>
<li>Models/OpenRouter native ids: canonicalize native OpenRouter model keys across config writes, runtime lookups, fallback management, and <code>models list --plain</code>, and migrate legacy duplicated <code>openrouter/openrouter/...</code> config entries forward on write.</li>
<li>Gateway/hooks: bucket hook auth failures by forwarded client IP behind trusted proxies and warn when <code>hooks.allowedAgentIds</code> leaves hook routing unrestricted.</li>
<li>Agents/compaction: skip the post-compaction <code>cache-ttl</code> marker write when a compaction completed in the same attempt, preventing the next turn from immediately triggering a second tiny compaction. (#28548) thanks @MoerAI.</li>
<li>Native chat/macOS: add <code>/new</code>, <code>/reset</code>, and <code>/clear</code> reset triggers, keep shared main-session aliases aligned, and ignore stale model-selection completions so native chat state stays in sync across reset and fast model changes. (#10898) Thanks @Nachx639.</li>
<li>Agents/compaction safeguard: route missing-model and missing-API-key cancellation warnings through the shared subsystem logger so they land in structured and file logs. (#9974) Thanks @dinakars777.</li>
<li>Cron/doctor: stop flagging canonical <code>agentTurn</code> and <code>systemEvent</code> payload kinds as legacy cron storage, while still normalizing whitespace-padded and non-canonical variants. (#44012) Thanks @shuicici.</li>
<li>ACP/client final-message delivery: preserve terminal assistant text snapshots before resolving <code>end_turn</code>, so ACP clients no longer drop the last visible reply when the gateway sends the final message body on the terminal chat event. (#17615) Thanks @pjeby.</li>
<li>Telegram/Discord status reactions: show a temporary compacting reaction during auto-compaction pauses and restore thinking afterward so the bot no longer appears frozen while context is being compacted. (#35474) thanks @Cypherm.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.12/OpenClaw-2026.3.12.zip" length="23628700" type="application/octet-stream" sparkle:edSignature="o6Zdcw36l3I0jUg14H+RBqNwrhuuSsq1WMDi4tBRa1+5TC3VCVdFKZ2hzmH2Xjru9lDEzVMP8v2A6RexSbOCBQ=="/>
</item>
<item>
<title>2026.3.8-beta.1</title>
<pubDate>Mon, 09 Mar 2026 07:19:57 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026030801</sparkle:version>
<sparkle:shortVersionString>2026.3.8-beta.1</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.3.8-beta.1</h2>
<h3>Changes</h3>
<ul>
<li>CLI/backup: add <code>openclaw backup create</code> and <code>openclaw backup verify</code> for local state archives, including <code>--only-config</code>, <code>--no-include-workspace</code>, manifest/payload validation, and backup guidance in destructive flows. (#40163) thanks @shichangs.</li>
<li>macOS/onboarding: add a remote gateway token field for remote mode, preserve existing non-plaintext <code>gateway.remote.token</code> config values until explicitly replaced, and warn when the loaded token shape cannot be used directly from the macOS app. (#40187, supersedes #34614) Thanks @cgdusek.</li>
<li>Talk mode: add top-level <code>talk.silenceTimeoutMs</code> config so Talk waits a configurable amount of silence before auto-sending the current transcript, while keeping each platform's existing default pause window when unset. (#39607) Thanks @danodoesdesign. Fixes #17147.</li>
<li>TUI: infer the active agent from the current workspace when launched inside a configured agent workspace, while preserving explicit <code>agent:</code> session targets. (#39591) thanks @arceus77-7.</li>
<li>Tools/Brave web search: add opt-in <code>tools.web.search.brave.mode: "llm-context"</code> so <code>web_search</code> can call Brave's LLM Context endpoint and return extracted grounding snippets with source metadata, plus config/docs/test coverage. (#33383) Thanks @thirumaleshp.</li>
<li>CLI/install: include the short git commit hash in <code>openclaw --version</code> output when metadata is available, and keep installer version checks compatible with the decorated format. (#39712) thanks @sourman.</li>
<li>CLI/backup: improve archive naming for date sorting, add config-only backup mode, and harden backup planning, publication, and verification edge cases. (#40163) Thanks @gumadeiras.</li>
<li>ACP/Provenance: add optional ACP ingress provenance metadata and visible receipt injection (<code>openclaw acp --provenance off|meta|meta+receipt</code>) so OpenClaw agents can retain and report ACP-origin context with session trace IDs. (#40473) thanks @mbelinky.</li>
<li>Tools/web search: alphabetize provider ordering across runtime selection, onboarding/configure pickers, and config metadata, so provider lists stay neutral and multi-key auto-detect now prefers Grok before Kimi. (#40259) thanks @kesku.</li>
<li>Docs/Web search: restore $5/month free-credit details, replace defunct "Data for Search"/"Data for AI" plan names with current "Search" plan, and note legacy subscription validity in Brave setup docs. Follows up on #26860. (#40111) Thanks @remusao.</li>
<li>Extensions/ACPX tests: move the shared runtime fixture helper from <code>src/runtime-internals/</code> to <code>src/test-utils/</code> so the test-only helper no longer looks like shipped runtime code.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>macOS app/chat UI: route browser proxy through the local node browser service, preserve plain-text paste semantics, strip completed assistant trace/debug wrapper noise from transcripts, refresh permission state after returning from System Settings, and tolerate malformed cron rows in the macOS tab. (#39516) Thanks @Imhermes1.</li>
<li>Android/Play distribution: remove self-update, background location, <code>screen.record</code>, and background mic capture from the Android app, narrow the foreground service to <code>dataSync</code> only, and clean up the legacy <code>location.enabledMode=always</code> preference migration. (#39660) Thanks @obviyus.</li>
<li>Telegram/DM routing: dedupe inbound Telegram DMs per agent instead of per session key so the same DM cannot trigger duplicate replies when both <code>agent:main:main</code> and <code>agent:main:telegram:direct:<id></code> resolve for one agent. Fixes #40005. Supersedes #40116. (#40519) thanks @obviyus.</li>
<li>Cron/Telegram announce delivery: route text-only announce jobs through the real outbound adapters after finalizing descendant output so plain Telegram targets no longer report <code>delivered: true</code> when no message actually reached Telegram. (#40575) thanks @obviyus.</li>
<li>Matrix/DM routing: add safer fallback detection for broken <code>m.direct</code> homeservers, honor explicit room bindings over DM classification, and preserve room-bound agent selection for Matrix DM rooms. (#19736) Thanks @derbronko.</li>
<li>Feishu/plugin onboarding: clear the short-lived plugin discovery cache before reloading the registry after installing a channel plugin, so onboarding no longer re-prompts to download Feishu immediately after a successful install. Fixes #39642. (#39752) Thanks @GazeKingNuWu.</li>
<li>Plugins/channel onboarding: prefer bundled channel plugins over duplicate npm-installed copies during onboarding and release-channel sync, preventing bundled plugins from being shadowed by npm installs with the same plugin ID. (#40092)</li>
<li>Config/runtime snapshots: keep secrets-runtime-resolved config and auth-profile snapshots intact after config writes so follow-up reads still see file-backed secret values while picking up the persisted config update. (#37313) thanks @bbblending.</li>
<li>Gateway/Control UI: resolve bundled dashboard assets through symlinked global wrappers and auto-detected package roots, while keeping configured and custom roots on the strict hardlink boundary. (#40385) Thanks @LarytheLord.</li>
<li>Browser/extension relay: add <code>browser.relayBindHost</code> so the Chrome relay can bind to an explicit non-loopback address for WSL2 and other cross-namespace setups, while preserving loopback-only defaults. (#39364) Thanks @mvanhorn.</li>
<li>Browser/CDP: normalize loopback direct WebSocket CDP URLs back to HTTP(S) for <code>/json/*</code> tab operations so local <code>ws://</code> / <code>wss://</code> profiles can still list, focus, open, and close tabs after the new direct-WS support lands. (#31085) Thanks @shrey150.</li>
<li>Browser/CDP: rewrite wildcard <code>ws://0.0.0.0</code> and <code>ws://[::]</code> debugger URLs from remote <code>/json/version</code> responses back to the external CDP host/port, fixing Browserless-style container endpoints. (#17760) Thanks @joeharouni.</li>
<li>Browser/extension relay: wait briefly for a previously attached Chrome tab to reappear after transient relay drops before failing with <code>tab not found</code>, reducing noisy reconnect flakes. (#32461) Thanks @AaronWander.</li>
<li>macOS/Tailscale gateway discovery: keep Tailscale Serve probing alive when other remote gateways are already discovered, prefer direct transport for resolved <code>.ts.net</code> and Tailscale Serve gateways, and set <code>TERM=dumb</code> for GUI-launched Tailscale CLI discovery. (#40167) thanks @ngutman.</li>
<li>TUI/theme: detect light terminal backgrounds via <code>COLORFGBG</code> and pick a WCAG AA-compliant light palette, with <code>OPENCLAW_THEME=light|dark</code> override for terminals without auto-detection. (#38636) Thanks @ademczuk and @vincentkoc.</li>
<li>Agents/openai-codex: normalize <code>gpt-5.4</code> fallback transport back to <code>openai-codex-responses</code> on <code>chatgpt.com/backend-api</code> when config drifts to the generic OpenAI responses endpoint. (#38736) Thanks @0xsline.</li>
<li>Models/openai-codex GPT-5.4 forward-compat: use the GPT-5.4 1,050,000-token context window and 128,000 max tokens for <code>openai-codex/gpt-5.4</code> instead of inheriting stale legacy Codex limits in resolver fallbacks and model listing. (#37876) thanks @yuweuii.</li>
<li>Tools/web search: restore Perplexity OpenRouter/Sonar compatibility for legacy <code>OPENROUTER_API_KEY</code>, <code>sk-or-...</code>, and explicit <code>perplexity.baseUrl</code> / <code>model</code> setups while keeping direct Perplexity keys on the native Search API path. (#39937) Thanks @obviyus.</li>
<li>Agents/failover: detect Amazon Bedrock <code>Too many tokens per day</code> quota errors as rate limits across fallback, cron retry, and memory embeddings while keeping context-window <code>too many tokens per request</code> errors out of the rate-limit lane. (#39377) Thanks @gambletan.</li>
<li>Mattermost replies: keep <code>root_id</code> pinned to the existing thread root when an agent replies inside a thread, while still using reply-target threading for top-level posts. (#27744) thanks @hnykda.</li>
<li>Telegram/DM partial streaming: keep DM preview lanes on real message edits instead of native draft materialization so final replies no longer flash a second duplicate copy before collapsing back to one.</li>
<li>macOS overlays: fix VoiceWake, Talk, and Notify overlay exclusivity crashes by removing shared <code>inout</code> visibility mutation from <code>OverlayPanelFactory.present</code>, and add a repeated Talk overlay smoke test. (#39275, #39321) Thanks @fellanH.</li>
<li>macOS Talk Mode: set the speech recognition request <code>taskHint</code> to <code>.dictation</code> for mic capture, and add regression coverage for the request defaults. (#38445) Thanks @dmiv.</li>
<li>macOS release packaging: default <code>scripts/package-mac-app.sh</code> to universal binaries for <code>BUILD_CONFIG=release</code>, and clarify that <code>scripts/package-mac-dist.sh</code> already produces the release zip + DMG. (#33891) Thanks @cgdusek.</li>
<li>Hooks/session-memory: keep <code>/new</code> and <code>/reset</code> memory artifacts in the bound agent workspace and align saved reset session keys with that workspace when stale main-agent keys leak into the hook path. (#39875) thanks @rbutera.</li>
<li>Sessions/model switch: clear stale cached <code>contextTokens</code> when a session changes models so status and runtime paths recompute against the active model window. (#38044) thanks @yuweuii.</li>
<li>ACP/session history: persist transcripts for successful ACP child runs, preserve exact transcript text, record ACP spawned-session lineage, and keep spawn-time transcript-path persistence best-effort so history storage failures do not block execution. (#40137) thanks @mbelinky.</li>
<li>Docs/browser: add a layered WSL2 + Windows remote Chrome CDP troubleshooting guide, including Control UI origin pitfalls and extension-relay bind-address guidance. (#39407) Thanks @Owlock.</li>
<li>Context engine registry/bundled builds: share the registry state through a <code>globalThis</code> singleton so duplicated bundled module copies can resolve engines registered by each other at runtime, with regression coverage for duplicate-module imports. (#40115) thanks @jalehman.</li>
<li>Podman/setup: fix <code>cannot chdir: Permission denied</code> in <code>run_as_user</code> when <code>setup-podman.sh</code> is invoked from a directory the target user cannot access, by wrapping user-switch calls in a subshell that cd's to <code>/tmp</code> with <code>/</code> fallback. (#39435) Thanks @langdon and @jlcbk.</li>
<li>Podman/SELinux: auto-detect SELinux enforcing/permissive mode and add <code>:Z</code> relabel to bind mounts in <code>run-openclaw-podman.sh</code> and the Quadlet template, fixing <code>EACCES</code> on Fedora/RHEL hosts. Supports <code>OPENCLAW_BIND_MOUNT_OPTIONS</code> override. (#39449) Thanks @langdon and @githubbzxs.</li>
<li>Agents/context-engine plugins: bootstrap runtime plugins once at embedded-run, compaction, and subagent boundaries so plugin-provided context engines and hooks load from the active workspace before runtime resolution. (#40232)</li>
<li>Docs/Changelog: correct the contributor credit for the bundled Control UI global-install fix to @LarytheLord. (#40420) Thanks @velvet-shark.</li>
<li>Telegram/media downloads: time out only stalled body reads so polling recovers from hung file downloads without aborting slow downloads that are still streaming data. (#40098) thanks @tysoncung.</li>
<li>Docker/runtime image: prune dev dependencies, strip build-only dist metadata for smaller Docker images. (#40307) Thanks @vincentkoc.</li>
<li>Gateway/restart timeout recovery: exit non-zero when restart-triggered shutdown drains time out so launchd/systemd restart the gateway instead of treating the failed restart as a clean stop. Landed from contributor PR #40380 by @dsantoreis. Thanks @dsantoreis.</li>
<li>Gateway/config restart guard: validate config before service start/restart and keep post-SIGUSR1 startup failures from crashing the gateway process, reducing invalid-config restart loops and macOS permission loss. Landed from contributor PR #38699 by @lml2468. Thanks @lml2468.</li>
<li>Gateway/launchd respawn detection: treat <code>XPC_SERVICE_NAME</code> as a launchd supervision hint so macOS restarts exit cleanly under launchd instead of attempting detached self-respawn. Landed from contributor PR #20555 by @dimat. Thanks @dimat.</li>
<li>Telegram/poll restart cleanup: abort the in-flight Telegram API fetch when shutdown or forced polling restarts stop a runner, preventing stale <code>getUpdates</code> long polls from colliding with the replacement runner. Landed from contributor PR #23950 by @Gkinthecodeland. Thanks @Gkinthecodeland.</li>
<li>Cron/restart catch-up staggering: limit immediate missed-job replay on startup and reschedule the deferred remainder from the post-catchup clock so restart bursts do not starve the gateway or silently skip overdue recurring jobs. Landed from contributor PR #18925 by @rexlunae. Thanks @rexlunae.</li>
<li>Cron/owner-only tools: pass trusted isolated cron runs into the embedded agent with owner context so <code>cron</code>/<code>gateway</code> tooling remains available after the owner-auth hardening narrowed direct-message ownership inference.</li>
<li>Browser/SSRF: block private-network intermediate redirect hops in strict browser navigation flows and fail closed when remote tab-open paths cannot inspect redirect chains. Thanks @zpbrent.</li>
<li>MS Teams/authz: keep <code>groupPolicy: "allowlist"</code> enforcing sender allowlists even when a team/channel route allowlist is configured, so route matches no longer widen group access to every sender in that route. Thanks @zpbrent.</li>
<li>Security/system.run: bind approved <code>bun</code> and <code>deno run</code> script operands to on-disk file snapshots so post-approval script rewrites are denied before execution.</li>
<li>Skills/download installs: pin the validated per-skill tools root before writing downloaded archives, so rebinding the lexical tools path cannot redirect download writes outside the intended tools directory. Thanks @tdjackey.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.8-beta.1/OpenClaw-2026.3.8-beta.1.zip" length="23407015" type="application/octet-stream" sparkle:edSignature="KCqhSmu4b0tHf55RqcQOHorsc55CgBI5BUmK/NTizxNq04INn/7QvsamHYQou9DbB2IW6B2nawBC4nn4au5yDA=="/>
</item>
</channel>
</rss>
</rss>

View File

@@ -65,8 +65,8 @@ android {
applicationId = "ai.openclaw.app"
minSdk = 31
targetSdk = 36
versionCode = 2026032200
versionName = "2026.3.22"
versionCode = 2026032500
versionName = "2026.3.25"
ndk {
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")

View File

@@ -237,6 +237,10 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
ensureRuntime().handleCanvasA2UIActionFromWebView(payloadJson)
}
fun isTrustedCanvasActionUrl(rawUrl: String?): Boolean {
return ensureRuntime().isTrustedCanvasActionUrl(rawUrl)
}
fun requestCanvasRehydrate(source: String = "screen_tab") {
ensureRuntime().requestCanvasRehydrate(source = source, force = true)
}

View File

@@ -904,6 +904,10 @@ class NodeRuntime(
}
}
fun isTrustedCanvasActionUrl(rawUrl: String?): Boolean {
return a2uiHandler.isTrustedCanvasActionUrl(rawUrl)
}
fun loadChat(sessionKey: String) {
val key = sessionKey.trim().ifEmpty { resolveMainSessionKey() }
chat.load(key)

View File

@@ -13,6 +13,13 @@ class A2UIHandler(
private val getNodeCanvasHostUrl: () -> String?,
private val getOperatorCanvasHostUrl: () -> String?,
) {
fun isTrustedCanvasActionUrl(rawUrl: String?): Boolean {
return CanvasActionTrust.isTrustedCanvasActionUrl(
rawUrl = rawUrl,
trustedA2uiUrls = listOfNotNull(resolveA2uiHostUrl()),
)
}
fun resolveA2uiHostUrl(): String? {
val nodeRaw = getNodeCanvasHostUrl()?.trim().orEmpty()
val operatorRaw = getOperatorCanvasHostUrl()?.trim().orEmpty()

View File

@@ -0,0 +1,50 @@
package ai.openclaw.app.node
import java.net.URI
object CanvasActionTrust {
const val scaffoldAssetUrl: String = "file:///android_asset/CanvasScaffold/scaffold.html"
fun isTrustedCanvasActionUrl(rawUrl: String?, trustedA2uiUrls: List<String>): Boolean {
val candidate = rawUrl?.trim().orEmpty()
if (candidate.isEmpty()) return false
if (candidate == scaffoldAssetUrl) return true
val candidateUri = parseUri(candidate) ?: return false
if (candidateUri.scheme.equals("file", ignoreCase = true)) {
return false
}
return trustedA2uiUrls.any { trusted ->
isTrustedA2uiPage(candidateUri, trusted)
}
}
private fun isTrustedA2uiPage(candidateUri: URI, trustedUrl: String): Boolean {
val trustedUri = parseUri(trustedUrl) ?: return false
if (!candidateUri.scheme.equals(trustedUri.scheme, ignoreCase = true)) return false
if (candidateUri.host?.equals(trustedUri.host, ignoreCase = true) != true) return false
if (effectivePort(candidateUri) != effectivePort(trustedUri)) return false
val trustedPath = trustedUri.rawPath?.takeIf { it.isNotBlank() } ?: return false
val candidatePath = candidateUri.rawPath?.takeIf { it.isNotBlank() } ?: return false
val trustedPrefix = if (trustedPath.endsWith("/")) trustedPath else "$trustedPath/"
return candidatePath == trustedPath || candidatePath.startsWith(trustedPrefix)
}
private fun effectivePort(uri: URI): Int {
if (uri.port >= 0) return uri.port
return when (uri.scheme?.lowercase()) {
"https" -> 443
"http" -> 80
else -> -1
}
}
private fun parseUri(raw: String): URI? =
try {
URI(raw)
} catch (_: Throwable) {
null
}
}

View File

@@ -22,6 +22,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature
import ai.openclaw.app.MainViewModel
import java.util.concurrent.atomic.AtomicReference
@SuppressLint("SetJavaScriptEnabled")
@Composable
@@ -29,6 +30,7 @@ fun CanvasScreen(viewModel: MainViewModel, visible: Boolean, modifier: Modifier
val context = LocalContext.current
val isDebuggable = (context.applicationInfo.flags and android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE) != 0
val webViewRef = remember { mutableStateOf<WebView?>(null) }
val currentPageUrlRef = remember { AtomicReference<String?>(null) }
DisposableEffect(viewModel) {
onDispose {
@@ -68,6 +70,14 @@ fun CanvasScreen(viewModel: MainViewModel, visible: Boolean, modifier: Modifier
isHorizontalScrollBarEnabled = true
webViewClient =
object : WebViewClient() {
override fun onPageStarted(
view: WebView,
url: String?,
favicon: android.graphics.Bitmap?,
) {
currentPageUrlRef.set(url)
}
override fun onReceivedError(
view: WebView,
request: WebResourceRequest,
@@ -90,6 +100,7 @@ fun CanvasScreen(viewModel: MainViewModel, visible: Boolean, modifier: Modifier
}
override fun onPageFinished(view: WebView, url: String?) {
currentPageUrlRef.set(url)
if (isDebuggable) {
Log.d("OpenClawWebView", "onPageFinished: $url")
}
@@ -122,7 +133,12 @@ fun CanvasScreen(viewModel: MainViewModel, visible: Boolean, modifier: Modifier
}
}
val bridge = CanvasA2UIActionBridge { payload -> viewModel.handleCanvasA2UIActionFromWebView(payload) }
val bridge =
CanvasA2UIActionBridge(
isTrustedPage = { viewModel.isTrustedCanvasActionUrl(currentPageUrlRef.get()) },
) { payload ->
viewModel.handleCanvasA2UIActionFromWebView(payload)
}
addJavascriptInterface(bridge, CanvasA2UIActionBridge.interfaceName)
viewModel.canvas.attach(this)
webViewRef.value = this
@@ -147,11 +163,15 @@ private fun disableForceDarkIfSupported(settings: WebSettings) {
WebSettingsCompat.setForceDark(settings, WebSettingsCompat.FORCE_DARK_OFF)
}
private class CanvasA2UIActionBridge(private val onMessage: (String) -> Unit) {
private class CanvasA2UIActionBridge(
private val isTrustedPage: () -> Boolean,
private val onMessage: (String) -> Unit,
) {
@JavascriptInterface
fun postMessage(payload: String?) {
val msg = payload?.trim().orEmpty()
if (msg.isEmpty()) return
if (!isTrustedPage()) return
onMessage(msg)
}

View File

@@ -0,0 +1,42 @@
package ai.openclaw.app.node
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class CanvasActionTrustTest {
@Test
fun acceptsBundledScaffoldAsset() {
assertTrue(CanvasActionTrust.isTrustedCanvasActionUrl(CanvasActionTrust.scaffoldAssetUrl, emptyList()))
}
@Test
fun acceptsTrustedA2uiPageOnAdvertisedCanvasHost() {
assertTrue(
CanvasActionTrust.isTrustedCanvasActionUrl(
rawUrl = "https://canvas.example.com:9443/__openclaw__/cap/token/__openclaw__/a2ui/?platform=android",
trustedA2uiUrls = listOf("https://canvas.example.com:9443/__openclaw__/cap/token/__openclaw__/a2ui/?platform=android"),
),
)
}
@Test
fun rejectsDifferentOriginEvenIfPathMatches() {
assertFalse(
CanvasActionTrust.isTrustedCanvasActionUrl(
rawUrl = "https://evil.example.com:9443/__openclaw__/cap/token/__openclaw__/a2ui/?platform=android",
trustedA2uiUrls = listOf("https://canvas.example.com:9443/__openclaw__/cap/token/__openclaw__/a2ui/?platform=android"),
),
)
}
@Test
fun rejectsUntrustedCanvasPagePathOnTrustedOrigin() {
assertFalse(
CanvasActionTrust.isTrustedCanvasActionUrl(
rawUrl = "https://canvas.example.com:9443/untrusted/index.html",
trustedA2uiUrls = listOf("https://canvas.example.com:9443/__openclaw__/cap/token/__openclaw__/a2ui/?platform=android"),
),
)
}
}

View File

@@ -1,8 +1,8 @@
// Shared iOS version defaults.
// Generated overrides live in build/Version.xcconfig (git-ignored).
OPENCLAW_GATEWAY_VERSION = 2026.3.22
OPENCLAW_MARKETING_VERSION = 2026.3.22
OPENCLAW_BUILD_VERSION = 202603220
OPENCLAW_GATEWAY_VERSION = 2026.3.25
OPENCLAW_MARKETING_VERSION = 2026.3.25
OPENCLAW_BUILD_VERSION = 202603250
#include? "../build/Version.xcconfig"

View File

@@ -46,6 +46,13 @@ struct IOSGatewayChatTransport: OpenClawChatTransport, Sendable {
_ = try await self.gateway.request(method: "sessions.reset", paramsJSON: json, timeoutSeconds: 10)
}
func compactSession(sessionKey: String) async throws {
struct Params: Codable { var key: String }
let data = try JSONEncoder().encode(Params(key: sessionKey))
let json = String(data: data, encoding: .utf8)
_ = try await self.gateway.request(method: "sessions.compact", paramsJSON: json, timeoutSeconds: 10)
}
func requestHistory(sessionKey: String) async throws -> OpenClawChatHistoryPayload {
struct Params: Codable { var sessionKey: String }
let data = try JSONEncoder().encode(Params(sessionKey: sessionKey))

View File

@@ -99,7 +99,7 @@ def normalize_release_version(raw_value)
version = raw_value.to_s.strip.sub(/\Av/, "")
UI.user_error!("Missing root package.json version.") unless env_present?(version)
unless version.match?(/\A\d+\.\d+\.\d+(?:[.-]?beta[.-]\d+)?\z/i)
UI.user_error!("Invalid package.json version '#{raw_value}'. Expected 2026.3.22 or 2026.3.22-beta.1.")
UI.user_error!("Invalid package.json version '#{raw_value}'. Expected YYYY.M.D or YYYY.M.D-beta.N.")
end
version

View File

@@ -73,7 +73,7 @@ extension ConfigSettings {
private var sidebar: some View {
SettingsSidebarScroll {
LazyVStack(alignment: .leading, spacing: 8) {
LazyVStack(alignment: .leading, spacing: 4) {
if self.sections.isEmpty {
Text("No config sections available.")
.font(.caption)
@@ -82,7 +82,7 @@ extension ConfigSettings {
.padding(.vertical, 4)
} else {
ForEach(self.sections) { section in
self.sidebarRow(section)
self.sidebarSection(section)
}
}
}
@@ -128,7 +128,6 @@ extension ConfigSettings {
}
self.actionRow
self.sectionHeader(section)
self.subsectionNav(section)
self.sectionForm(section)
if self.store.configDirty, !self.isNixMode {
Text("Unsaved changes")
@@ -182,78 +181,76 @@ extension ConfigSettings {
.buttonStyle(.bordered)
}
private func sidebarRow(_ section: ConfigSection) -> some View {
let isSelected = self.activeSectionKey == section.key
return Button {
self.selectSection(section)
} label: {
VStack(alignment: .leading, spacing: 2) {
Text(section.label)
if let help = section.help {
Text(help)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(2)
private func sidebarSection(_ section: ConfigSection) -> some View {
let isExpanded = self.activeSectionKey == section.key
let subsections = isExpanded ? self.resolveSubsections(for: section) : []
return VStack(alignment: .leading, spacing: 2) {
Button {
self.selectSection(section)
} label: {
HStack(spacing: 6) {
Image(systemName: "chevron.right")
.font(.caption2.weight(.semibold))
.foregroundStyle(.tertiary)
.rotationEffect(.degrees(isExpanded ? 90 : 0))
Text(section.label)
.lineLimit(1)
}
.padding(.vertical, 5)
.padding(.horizontal, 8)
.frame(maxWidth: .infinity, alignment: .leading)
.background(isExpanded && subsections.isEmpty
? Color.accentColor.opacity(0.18)
: Color.clear)
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
.contentShape(Rectangle())
}
.padding(.vertical, 6)
.padding(.horizontal, 8)
.frame(maxWidth: .infinity, alignment: .leading)
.background(isSelected ? Color.accentColor.opacity(0.18) : Color.clear)
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
.background(Color.clear)
.buttonStyle(.plain)
.contentShape(Rectangle())
if isExpanded, !subsections.isEmpty {
VStack(alignment: .leading, spacing: 1) {
self.sidebarSubRow(title: "All", key: nil, sectionKey: section.key)
ForEach(subsections) { sub in
self.sidebarSubRow(title: sub.label, key: sub.key, sectionKey: section.key)
}
}
.padding(.leading, 20)
.transition(.opacity.combined(with: .move(edge: .top)))
}
}
.animation(.easeInOut(duration: 0.18), value: isExpanded)
}
private func sidebarSubRow(title: String, key: String?, sectionKey: String) -> some View {
let isSelected: Bool = {
guard self.activeSectionKey == sectionKey else { return false }
if let key { return self.activeSubsection == .key(key) }
return self.activeSubsection == .all
}()
return Button {
if let key {
self.activeSubsection = .key(key)
} else {
self.activeSubsection = .all
}
} label: {
Text(title)
.font(.callout)
.lineLimit(1)
.padding(.vertical, 4)
.padding(.horizontal, 8)
.frame(maxWidth: .infinity, alignment: .leading)
.background(isSelected ? Color.accentColor.opacity(0.18) : Color.clear)
.clipShape(RoundedRectangle(cornerRadius: 7, style: .continuous))
.contentShape(Rectangle())
}
.frame(maxWidth: .infinity, alignment: .leading)
.buttonStyle(.plain)
.contentShape(Rectangle())
}
@ViewBuilder
private func subsectionNav(_ section: ConfigSection) -> some View {
let subsections = self.resolveSubsections(for: section)
if subsections.isEmpty {
EmptyView()
} else {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
self.subsectionButton(
title: "All",
isSelected: self.activeSubsection == .all)
{
self.activeSubsection = .all
}
ForEach(subsections) { subsection in
self.subsectionButton(
title: subsection.label,
isSelected: self.activeSubsection == .key(subsection.key))
{
self.activeSubsection = .key(subsection.key)
}
}
}
.padding(.vertical, 2)
}
}
}
private func subsectionButton(
title: String,
isSelected: Bool,
action: @escaping () -> Void) -> some View
{
Button(action: action) {
Text(title)
.font(.callout.weight(.semibold))
.foregroundStyle(isSelected ? Color.accentColor : .primary)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(isSelected ? Color.accentColor.opacity(0.18) : Color(nsColor: .controlBackgroundColor))
.clipShape(Capsule())
}
.buttonStyle(.plain)
}
private func sectionForm(_ section: ConfigSection) -> some View {
let subsection = self.activeSubsection
let defaultPath: ConfigPath = [.key(section.key)]

View File

@@ -0,0 +1,31 @@
import Foundation
enum ExecApprovalCommandDisplaySanitizer {
private static let invisibleCodePoints: Set<UInt32> = [
0x115F,
0x1160,
0x3164,
0xFFA0,
]
static func sanitize(_ text: String) -> String {
var sanitized = ""
sanitized.reserveCapacity(text.count)
for scalar in text.unicodeScalars {
if self.shouldEscape(scalar) {
sanitized.append(self.escape(scalar))
} else {
sanitized.append(String(scalar))
}
}
return sanitized
}
private static func shouldEscape(_ scalar: UnicodeScalar) -> Bool {
scalar.properties.generalCategory == .format || self.invisibleCodePoints.contains(scalar.value)
}
private static func escape(_ scalar: UnicodeScalar) -> String {
"\\u{\(String(scalar.value, radix: 16, uppercase: true))}"
}
}

View File

@@ -271,7 +271,7 @@ enum ExecApprovalsPromptPresenter {
commandText.drawsBackground = true
commandText.backgroundColor = NSColor.textBackgroundColor
commandText.font = NSFont.monospacedSystemFont(ofSize: NSFont.systemFontSize, weight: .regular)
commandText.string = request.command
commandText.string = ExecApprovalCommandDisplaySanitizer.sanitize(request.command)
commandText.textContainerInset = NSSize(width: 6, height: 6)
commandText.textContainer?.lineFragmentPadding = 0
commandText.textContainer?.widthTracksTextView = true

View File

@@ -1,9 +1,11 @@
import CryptoKit
import Foundation
import OpenClawProtocol
enum OpenClawConfigFile {
private static let logger = Logger(subsystem: "ai.openclaw", category: "config")
private static let configAuditFileName = "config-audit.jsonl"
private static let configHealthFileName = "config-health.json"
static func url() -> URL {
OpenClawPaths.configURL
@@ -23,9 +25,11 @@ enum OpenClawConfigFile {
do {
let data = try Data(contentsOf: url)
guard let root = self.parseConfigData(data) else {
self.observeConfigRead(data: data, root: nil, configURL: url, valid: false)
self.logger.warning("config JSON root invalid")
return [:]
}
self.observeConfigRead(data: data, root: root, configURL: url, valid: true)
return root
} catch {
self.logger.warning("config read failed: \(error.localizedDescription)")
@@ -76,6 +80,7 @@ enum OpenClawConfigFile {
"gatewayModeAfter": gatewayModeAfter ?? NSNull(),
"suspicious": suspicious,
])
self.observeConfigRead(data: data, root: output, configURL: url, valid: true)
} catch {
self.logger.error("config save failed: \(error.localizedDescription)")
self.appendConfigWriteAudit([
@@ -316,6 +321,215 @@ enum OpenClawConfigFile {
.appendingPathComponent(self.configAuditFileName, isDirectory: false)
}
private static func configHealthStateURL() -> URL {
self.stateDirURL()
.appendingPathComponent("logs", isDirectory: true)
.appendingPathComponent(self.configHealthFileName, isDirectory: false)
}
private static func readConfigHealthState() -> [String: Any] {
let url = self.configHealthStateURL()
guard let data = try? Data(contentsOf: url),
let root = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
else {
return [:]
}
return root
}
private static func writeConfigHealthState(_ root: [String: Any]) {
guard JSONSerialization.isValidJSONObject(root),
let data = try? JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys])
else {
return
}
let url = self.configHealthStateURL()
do {
try FileManager().createDirectory(
at: url.deletingLastPathComponent(),
withIntermediateDirectories: true)
try data.write(to: url, options: [.atomic])
} catch {
// best-effort
}
}
private static func configHealthEntry(state: [String: Any], configPath: String) -> [String: Any] {
let entries = state["entries"] as? [String: Any]
return entries?[configPath] as? [String: Any] ?? [:]
}
private static func setConfigHealthEntry(
state: [String: Any],
configPath: String,
entry: [String: Any]) -> [String: Any]
{
var next = state
var entries = next["entries"] as? [String: Any] ?? [:]
entries[configPath] = entry
next["entries"] = entries
return next
}
private static func isUpdateChannelOnlyRoot(_ root: [String: Any]) -> Bool {
let keys = Array(root.keys)
guard keys.count == 1, keys.first == "update" else { return false }
guard let update = root["update"] as? [String: Any] else { return false }
let updateKeys = Array(update.keys)
return updateKeys.count == 1 && update["channel"] is String
}
private static func fileTimestampMs(_ value: Any?) -> Double? {
guard let date = value as? Date else { return nil }
return date.timeIntervalSince1970 * 1000
}
private static func configFingerprint(
data: Data,
root: [String: Any]?,
configURL: URL,
observedAt: String) -> [String: Any]
{
let attributes = try? FileManager().attributesOfItem(atPath: configURL.path)
return [
"hash": SHA256.hash(data: data).compactMap { String(format: "%02x", $0) }.joined(),
"bytes": data.count,
"mtimeMs": self.fileTimestampMs(attributes?[.modificationDate]) ?? NSNull(),
"ctimeMs": self.fileTimestampMs(attributes?[.creationDate]) ?? NSNull(),
"hasMeta": self.hasMeta(root),
"gatewayMode": self.gatewayMode(root) ?? NSNull(),
"observedAt": observedAt,
]
}
private static func sameFingerprint(_ left: [String: Any]?, _ right: [String: Any]) -> Bool {
guard let left else { return false }
return (left["hash"] as? String) == (right["hash"] as? String) &&
(left["bytes"] as? Int) == (right["bytes"] as? Int) &&
(left["mtimeMs"] as? Double) == (right["mtimeMs"] as? Double) &&
(left["ctimeMs"] as? Double) == (right["ctimeMs"] as? Double) &&
(left["hasMeta"] as? Bool) == (right["hasMeta"] as? Bool) &&
(left["gatewayMode"] as? String) == (right["gatewayMode"] as? String)
}
private static func observeSuspiciousReasons(
root: [String: Any]?,
bytes: Int,
lastKnownGood: [String: Any]?) -> [String]
{
guard let lastKnownGood else { return [] }
var reasons: [String] = []
if let previousBytes = lastKnownGood["bytes"] as? Int,
previousBytes >= 512,
bytes < max(1, previousBytes / 2)
{
reasons.append("size-drop-vs-last-good:\(previousBytes)->\(bytes)")
}
if (lastKnownGood["hasMeta"] as? Bool) == true, !self.hasMeta(root) {
reasons.append("missing-meta-vs-last-good")
}
if (lastKnownGood["gatewayMode"] as? String) != nil, self.gatewayMode(root) == nil {
reasons.append("gateway-mode-missing-vs-last-good")
}
if let root, (lastKnownGood["gatewayMode"] as? String) != nil, self.isUpdateChannelOnlyRoot(root) {
reasons.append("update-channel-only-root")
}
return reasons
}
private static func readConfigFingerprint(at url: URL) -> [String: Any]? {
guard let data = try? Data(contentsOf: url) else { return nil }
let root = self.parseConfigData(data)
return self.configFingerprint(
data: data,
root: root,
configURL: url,
observedAt: ISO8601DateFormatter().string(from: Date()))
}
private static func configTimestampToken(_ timestamp: String) -> String {
timestamp.replacingOccurrences(of: ":", with: "-")
.replacingOccurrences(of: ".", with: "-")
}
private static func persistClobberedSnapshot(data: Data, configURL: URL, observedAt: String) -> String? {
let url = configURL.deletingLastPathComponent()
.appendingPathComponent("\(configURL.lastPathComponent).clobbered.\(self.configTimestampToken(observedAt))")
guard !FileManager().fileExists(atPath: url.path) else { return url.path }
do {
try data.write(to: url, options: [])
return url.path
} catch {
return nil
}
}
private static func observeConfigRead(data: Data, root: [String: Any]?, configURL: URL, valid: Bool) {
let observedAt = ISO8601DateFormatter().string(from: Date())
let current = self.configFingerprint(data: data, root: root, configURL: configURL, observedAt: observedAt)
var state = self.readConfigHealthState()
let entry = self.configHealthEntry(state: state, configPath: configURL.path)
let lastKnownGood = entry["lastKnownGood"] as? [String: Any]
let suspicious = self.observeSuspiciousReasons(
root: root,
bytes: current["bytes"] as? Int ?? 0,
lastKnownGood: lastKnownGood)
if suspicious.isEmpty {
guard valid else { return }
let nextEntry: [String: Any] = [
"lastKnownGood": current,
"lastObservedSuspiciousSignature": NSNull(),
]
if !self.sameFingerprint(lastKnownGood, current) || entry["lastObservedSuspiciousSignature"] != nil {
state = self.setConfigHealthEntry(state: state, configPath: configURL.path, entry: nextEntry)
self.writeConfigHealthState(state)
}
return
}
let signature = "\((current["hash"] as? String) ?? ""):\(suspicious.joined(separator: ","))"
if (entry["lastObservedSuspiciousSignature"] as? String) == signature {
return
}
let backup = self.readConfigFingerprint(
at: configURL.deletingLastPathComponent().appendingPathComponent("\(configURL.lastPathComponent).bak"))
let clobberedPath = self.persistClobberedSnapshot(
data: data,
configURL: configURL,
observedAt: observedAt)
self.logger.warning("config observe anomaly (\(suspicious.joined(separator: ", "))) at \(configURL.path)")
self.appendConfigObserveAudit([
"phase": "read",
"configPath": configURL.path,
"exists": true,
"valid": valid,
"hash": current["hash"] ?? NSNull(),
"bytes": current["bytes"] ?? NSNull(),
"mtimeMs": current["mtimeMs"] ?? NSNull(),
"ctimeMs": current["ctimeMs"] ?? NSNull(),
"hasMeta": current["hasMeta"] ?? false,
"gatewayMode": current["gatewayMode"] ?? NSNull(),
"suspicious": suspicious,
"lastKnownGoodHash": lastKnownGood?["hash"] ?? NSNull(),
"lastKnownGoodBytes": lastKnownGood?["bytes"] ?? NSNull(),
"lastKnownGoodMtimeMs": lastKnownGood?["mtimeMs"] ?? NSNull(),
"lastKnownGoodCtimeMs": lastKnownGood?["ctimeMs"] ?? NSNull(),
"lastKnownGoodGatewayMode": lastKnownGood?["gatewayMode"] ?? NSNull(),
"backupHash": backup?["hash"] ?? NSNull(),
"backupBytes": backup?["bytes"] ?? NSNull(),
"backupMtimeMs": backup?["mtimeMs"] ?? NSNull(),
"backupCtimeMs": backup?["ctimeMs"] ?? NSNull(),
"backupGatewayMode": backup?["gatewayMode"] ?? NSNull(),
"clobberedPath": clobberedPath ?? NSNull(),
])
var nextEntry = entry
nextEntry["lastObservedSuspiciousSignature"] = signature
state = self.setConfigHealthEntry(state: state, configPath: configURL.path, entry: nextEntry)
self.writeConfigHealthState(state)
}
private static func appendConfigWriteAudit(_ fields: [String: Any]) {
var record: [String: Any] = [
"ts": ISO8601DateFormatter().string(from: Date()),
@@ -351,4 +565,40 @@ enum OpenClawConfigFile {
// best-effort
}
}
private static func appendConfigObserveAudit(_ fields: [String: Any]) {
var record: [String: Any] = [
"ts": ISO8601DateFormatter().string(from: Date()),
"source": "macos-openclaw-config-file",
"event": "config.observe",
"pid": ProcessInfo.processInfo.processIdentifier,
"argv": Array(ProcessInfo.processInfo.arguments.prefix(8)),
]
for (key, value) in fields {
record[key] = value is NSNull ? NSNull() : value
}
guard JSONSerialization.isValidJSONObject(record),
let data = try? JSONSerialization.data(withJSONObject: record)
else {
return
}
var line = Data()
line.append(data)
line.append(0x0A)
let logURL = self.configAuditLogURL()
do {
try FileManager().createDirectory(
at: logURL.deletingLastPathComponent(),
withIntermediateDirectories: true)
if !FileManager().fileExists(atPath: logURL.path) {
FileManager().createFile(atPath: logURL.path, contents: nil)
}
let handle = try FileHandle(forWritingTo: logURL)
defer { try? handle.close() }
try handle.seekToEnd()
try handle.write(contentsOf: line)
} catch {
// best-effort
}
}
}

View File

@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.3.22</string>
<string>2026.3.25</string>
<key>CFBundleVersion</key>
<string>202603220</string>
<string>202603250</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@@ -95,7 +95,8 @@ struct SkillsSettings: View {
skillKey: skill.skillKey,
skillName: skill.name,
envKey: envKey,
isPrimary: isPrimary)
isPrimary: isPrimary,
homepage: skill.homepage)
})
}
if !self.model.skills.isEmpty, self.filteredSkills.isEmpty {
@@ -258,8 +259,15 @@ private struct SkillRow: View {
guard let raw = self.skill.homepage?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return nil
}
guard !raw.isEmpty else { return nil }
return URL(string: raw)
guard
!raw.isEmpty,
let url = URL(string: raw),
let scheme = url.scheme?.lowercased(),
scheme == "http" || scheme == "https"
else {
return nil
}
return url
}
private var enabledBinding: Binding<Bool> {
@@ -428,6 +436,7 @@ private struct EnvEditorState: Identifiable {
let skillName: String
let envKey: String
let isPrimary: Bool
let homepage: String?
var id: String {
"\(self.skillKey)::\(self.envKey)"
@@ -447,8 +456,15 @@ private struct EnvEditorView: View {
Text(self.subtitle)
.font(.subheadline)
.foregroundStyle(.secondary)
if let homepageUrl = self.homepageUrl {
Link("Get your key →", destination: homepageUrl)
.font(.caption)
}
SecureField(self.editor.envKey, text: self.$value)
.textFieldStyle(.roundedBorder)
Text("Saved to openclaw.json under skills.entries.\(self.editor.skillKey)")
.font(.caption2)
.foregroundStyle(.tertiary)
HStack {
Button("Cancel") { self.dismiss() }
Spacer()
@@ -464,6 +480,21 @@ private struct EnvEditorView: View {
.frame(width: 420)
}
private var homepageUrl: URL? {
guard let raw = self.editor.homepage?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return nil
}
guard
!raw.isEmpty,
let url = URL(string: raw),
let scheme = url.scheme?.lowercased(),
scheme == "http" || scheme == "https"
else {
return nil
}
return url
}
private var title: String {
self.editor.isPrimary ? "Set API Key" : "Set Environment Variable"
}
@@ -539,12 +570,12 @@ final class SkillsSettingsModel {
_ = try await GatewayConnection.shared.skillsUpdate(
skillKey: skillKey,
apiKey: value)
self.statusMessage = "Saved API key"
self.statusMessage = "Saved API key — stored in openclaw.json (skills.entries.\(skillKey))"
} else {
_ = try await GatewayConnection.shared.skillsUpdate(
skillKey: skillKey,
env: [envKey: value])
self.statusMessage = "Saved \(envKey)"
self.statusMessage = "Saved \(envKey) — stored in openclaw.json (skills.entries.\(skillKey).env)"
}
} catch {
self.statusMessage = error.localizedDescription
@@ -608,7 +639,8 @@ extension SkillsSettings {
skillKey: "test",
skillName: "Test Skill",
envKey: "API_KEY",
isPrimary: true),
isPrimary: true,
homepage: "https://example.com"),
onSave: { _ in })
_ = editor.body
}

View File

@@ -8,6 +8,11 @@ import Speech
actor TalkModeRuntime {
static let shared = TalkModeRuntime()
enum PlaybackPlan: Equatable {
case elevenLabsThenSystemVoice(apiKey: String, voiceId: String)
case systemVoiceOnly
}
private let logger = Logger(subsystem: "ai.openclaw", category: "talk.runtime")
private let ttsLogger = Logger(subsystem: "ai.openclaw", category: "talk.tts")
private static let defaultModelIdFallback = "eleven_v3"
@@ -451,17 +456,22 @@ actor TalkModeRuntime {
private func playAssistant(text: String) async {
guard let input = await self.preparePlaybackInput(text: text) else { return }
do {
if let apiKey = input.apiKey, !apiKey.isEmpty, let voiceId = input.voiceId {
switch Self.playbackPlan(apiKey: input.apiKey, voiceId: input.voiceId) {
case let .elevenLabsThenSystemVoice(apiKey, voiceId):
do {
try await self.playElevenLabs(input: input, apiKey: apiKey, voiceId: voiceId)
} else {
try await self.playSystemVoice(input: input)
} catch {
self.ttsLogger
.error(
"talk TTS failed: \(error.localizedDescription, privacy: .public); " +
"falling back to system voice")
do {
try await self.playSystemVoice(input: input)
} catch {
self.ttsLogger.error("talk system voice failed: \(error.localizedDescription, privacy: .public)")
}
}
} catch {
self.ttsLogger
.error(
"talk TTS failed: \(error.localizedDescription, privacy: .public); " +
"falling back to system voice")
case .systemVoiceOnly:
do {
try await self.playSystemVoice(input: input)
} catch {
@@ -475,6 +485,13 @@ actor TalkModeRuntime {
}
}
static func playbackPlan(apiKey: String?, voiceId: String?) -> PlaybackPlan {
guard let apiKey, !apiKey.isEmpty, let voiceId else {
return .systemVoiceOnly
}
return .elevenLabsThenSystemVoice(apiKey: apiKey, voiceId: voiceId)
}
private struct TalkPlaybackInput {
let generation: Int
let cleanedText: String
@@ -664,9 +681,12 @@ actor TalkModeRuntime {
await MainActor.run { TalkModeController.shared.updatePhase(.speaking) }
self.phase = .speaking
await TalkSystemSpeechSynthesizer.shared.stop()
// Use app locale as fallback when no explicit language is set (e.g. system voice without ElevenLabs directive).
let appLocale = await MainActor.run { AppStateStore.shared.voiceWakeLocaleID }
let ttsLanguage = input.language ?? appLocale
try await TalkSystemSpeechSynthesizer.shared.speak(
text: input.cleanedText,
language: input.language)
language: ttsLanguage)
self.ttsLogger.info("talk system voice done")
}

View File

@@ -126,6 +126,13 @@ struct MacGatewayChatTransport: OpenClawChatTransport {
timeoutMs: 10000)
}
func compactSession(sessionKey: String) async throws {
_ = try await GatewayConnection.shared.request(
method: "sessions.compact",
params: ["key": AnyCodable(sessionKey)],
timeoutMs: 10000)
}
func events() -> AsyncStream<OpenClawChatTransportEvent> {
AsyncStream { continuation in
let task = Task {

View File

@@ -2764,6 +2764,110 @@ public struct ToolsCatalogResult: Codable, Sendable {
}
}
public struct ToolsEffectiveParams: Codable, Sendable {
public let agentid: String?
public let sessionkey: String
public init(
agentid: String?,
sessionkey: String)
{
self.agentid = agentid
self.sessionkey = sessionkey
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case sessionkey = "sessionKey"
}
}
public struct ToolsEffectiveEntry: Codable, Sendable {
public let id: String
public let label: String
public let description: String
public let rawdescription: String
public let source: AnyCodable
public let pluginid: String?
public let channelid: String?
public init(
id: String,
label: String,
description: String,
rawdescription: String,
source: AnyCodable,
pluginid: String?,
channelid: String?)
{
self.id = id
self.label = label
self.description = description
self.rawdescription = rawdescription
self.source = source
self.pluginid = pluginid
self.channelid = channelid
}
private enum CodingKeys: String, CodingKey {
case id
case label
case description
case rawdescription = "rawDescription"
case source
case pluginid = "pluginId"
case channelid = "channelId"
}
}
public struct ToolsEffectiveGroup: Codable, Sendable {
public let id: AnyCodable
public let label: String
public let source: AnyCodable
public let tools: [ToolsEffectiveEntry]
public init(
id: AnyCodable,
label: String,
source: AnyCodable,
tools: [ToolsEffectiveEntry])
{
self.id = id
self.label = label
self.source = source
self.tools = tools
}
private enum CodingKeys: String, CodingKey {
case id
case label
case source
case tools
}
}
public struct ToolsEffectiveResult: Codable, Sendable {
public let agentid: String
public let profile: String
public let groups: [ToolsEffectiveGroup]
public init(
agentid: String,
profile: String,
groups: [ToolsEffectiveGroup])
{
self.agentid = agentid
self.profile = profile
self.groups = groups
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case profile
case groups
}
}
public struct SkillsBinsParams: Codable, Sendable {}
public struct SkillsBinsResult: Codable, Sendable {

View File

@@ -0,0 +1,12 @@
import Foundation
import Testing
@testable import OpenClaw
struct ExecApprovalCommandDisplaySanitizerTests {
@Test func `escapes invisible command spoofing characters`() {
let input = "date\u{200B}\u{3164}\u{FFA0}\u{115F}\u{1160}가"
#expect(
ExecApprovalCommandDisplaySanitizer.sanitize(input) ==
"date\\u{200B}\\u{3164}\\u{FFA0}\\u{115F}\\u{1160}가")
}
}

View File

@@ -135,4 +135,69 @@ struct OpenClawConfigFileTests {
#expect(auditRoot?["configPath"] as? String == configPath.path)
}
}
@MainActor
@Test
func `load dict audits suspicious out-of-band clobbers`() async throws {
let stateDir = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true)
let configPath = stateDir.appendingPathComponent("openclaw.json")
let auditPath = stateDir.appendingPathComponent("logs/config-audit.jsonl")
defer { try? FileManager().removeItem(at: stateDir) }
try await TestIsolation.withEnvValues([
"OPENCLAW_STATE_DIR": stateDir.path,
"OPENCLAW_CONFIG_PATH": configPath.path,
]) {
OpenClawConfigFile.saveDict([
"update": ["channel": "beta"],
"browser": ["enabled": true],
"gateway": ["mode": "local"],
"channels": [
"discord": [
"enabled": true,
"dmPolicy": "pairing",
],
],
])
_ = OpenClawConfigFile.loadDict()
let clobbered = """
{
"update": {
"channel": "beta"
}
}
"""
try clobbered.write(to: configPath, atomically: true, encoding: .utf8)
let loaded = OpenClawConfigFile.loadDict()
#expect((loaded["gateway"] as? [String: Any]) == nil)
let rawAudit = try String(contentsOf: auditPath, encoding: .utf8)
let lines = rawAudit
.split(whereSeparator: \.isNewline)
.map(String.init)
let observeLine = lines.reversed().first { $0.contains("\"event\":\"config.observe\"") }
#expect(observeLine != nil)
guard let observeLine else {
Issue.record("Missing config.observe audit line")
return
}
let auditRoot = try JSONSerialization.jsonObject(with: Data(observeLine.utf8)) as? [String: Any]
#expect(auditRoot?["source"] as? String == "macos-openclaw-config-file")
#expect(auditRoot?["configPath"] as? String == configPath.path)
let suspicious = auditRoot?["suspicious"] as? [String] ?? []
#expect(suspicious.contains("gateway-mode-missing-vs-last-good"))
#expect(suspicious.contains("update-channel-only-root"))
let clobberedPath = auditRoot?["clobberedPath"] as? String
#expect(clobberedPath != nil)
if let clobberedPath {
let preserved = try String(contentsOfFile: clobberedPath, encoding: .utf8)
#expect(preserved == clobbered)
}
}
}
}

View File

@@ -11,4 +11,13 @@ struct TalkModeRuntimeSpeechTests {
#expect(request.shouldReportPartialResults)
#expect(request.taskHint == .dictation)
}
@Test func `playback plan falls back only from elevenlabs`() {
#expect(
TalkModeRuntime.playbackPlan(apiKey: "key", voiceId: "voice")
== .elevenLabsThenSystemVoice(apiKey: "key", voiceId: "voice"))
#expect(TalkModeRuntime.playbackPlan(apiKey: nil, voiceId: "voice") == .systemVoiceOnly)
#expect(TalkModeRuntime.playbackPlan(apiKey: "key", voiceId: nil) == .systemVoiceOnly)
#expect(TalkModeRuntime.playbackPlan(apiKey: "", voiceId: "voice") == .systemVoiceOnly)
}
}

View File

@@ -28,6 +28,7 @@ public protocol OpenClawChatTransport: Sendable {
func setActiveSessionKey(_ sessionKey: String) async throws
func resetSession(sessionKey: String) async throws
func compactSession(sessionKey: String) async throws
}
extension OpenClawChatTransport {
@@ -40,6 +41,13 @@ extension OpenClawChatTransport {
userInfo: [NSLocalizedDescriptionKey: "sessions.reset not supported by this transport"])
}
public func compactSession(sessionKey _: String) async throws {
throw NSError(
domain: "OpenClawChatTransport",
code: 0,
userInfo: [NSLocalizedDescriptionKey: "sessions.compact not supported by this transport"])
}
public func abortRun(sessionKey _: String, runId _: String) async throws {
throw NSError(
domain: "OpenClawChatTransport",

View File

@@ -60,6 +60,9 @@ public final class OpenClawChatViewModel {
private var nextThinkingSelectionRequestID: UInt64 = 0
private var latestThinkingSelectionRequestIDsBySession: [String: UInt64] = [:]
private var latestThinkingLevelsBySession: [String: String] = [:]
private var isCompacting = false
private var lastCompactAt: Date?
private let compactCooldown: TimeInterval = 60
private var pendingToolCallsById: [String: OpenClawChatPendingToolCall] = [:] {
didSet {
@@ -465,6 +468,7 @@ public final class OpenClawChatViewModel {
}
private static let resetTriggers: Set<String> = ["/new", "/reset", "/clear"]
private static let compactTriggers: Set<String> = ["/compact"]
private func performSend() async {
guard !self.isSending else { return }
@@ -476,6 +480,11 @@ public final class OpenClawChatViewModel {
await self.performReset()
return
}
if Self.compactTriggers.contains(trimmed.lowercased()) {
self.input = ""
await self.performCompact()
return
}
let sessionKey = self.sessionKey
@@ -623,6 +632,42 @@ public final class OpenClawChatViewModel {
await self.bootstrap()
}
private func performCompact() async {
guard !self.isCompacting else { return }
guard !self.isSending, self.pendingRuns.isEmpty, !self.isAborting else {
self.errorText = "Wait for the current response before compacting the session."
return
}
if let lastCompactAt,
Date().timeIntervalSince(lastCompactAt) < self.compactCooldown
{
self.errorText = "Please wait before compacting this session again."
return
}
self.isCompacting = true
self.isLoading = true
self.errorText = nil
defer {
self.isLoading = false
self.isCompacting = false
}
do {
try await self.transport.compactSession(sessionKey: self.sessionKey)
} catch {
self.errorText = "Unable to compact the session. Please try again."
let nsError = error as NSError
chatUILogger.error(
"session compact failed domain=\(nsError.domain, privacy: .public) code=\(nsError.code, privacy: .public) details=\(String(describing: error), privacy: .private)"
)
return
}
self.lastCompactAt = Date()
await self.bootstrap()
}
private func performSelectThinkingLevel(_ level: String) async {
let next = Self.normalizedThinkingLevel(level) ?? "off"
guard next != self.thinkingLevel else { return }

View File

@@ -51,11 +51,11 @@ public final class TalkSystemSpeechSynthesizer: NSObject {
}
self.currentUtterance = utterance
let estimatedSeconds = max(3.0, min(180.0, Double(trimmed.count) * 0.08))
let watchdogTimeout = Self.watchdogTimeoutSeconds(text: trimmed, language: language ?? utterance.voice?.language)
self.watchdog?.cancel()
self.watchdog = Task { @MainActor [weak self] in
guard let self else { return }
try? await Task.sleep(nanoseconds: UInt64(estimatedSeconds * 1_000_000_000))
try? await Task.sleep(nanoseconds: UInt64(watchdogTimeout * 1_000_000_000))
if Task.isCancelled { return }
guard self.currentToken == token else { return }
if self.synth.isSpeaking {
@@ -63,7 +63,7 @@ public final class TalkSystemSpeechSynthesizer: NSObject {
}
self.finishCurrent(
with: NSError(domain: "TalkSystemSpeechSynthesizer", code: 408, userInfo: [
NSLocalizedDescriptionKey: "system TTS timed out after \(estimatedSeconds)s",
NSLocalizedDescriptionKey: "system TTS timed out after \(watchdogTimeout)s",
]))
}
@@ -83,6 +83,37 @@ public final class TalkSystemSpeechSynthesizer: NSObject {
}
}
static func watchdogTimeoutSeconds(text: String, language: String?) -> Double {
// Estimate speech duration per language, then apply 3x safety margin.
// The watchdog is a hang guard normal completion relies on didFinish.
//
// Speech rates based on Pellegrino et al. (2019) syllable-per-second data,
// adjusted for TTS synthesis (slower than natural speech):
// https://www.science.org/doi/10.1126/sciadv.aaw2594
// Japanese: 7.84 SPS -> ~0.20s/char (mixed kana/kanji avg ~1.5 mora/char)
// Korean: 5.96 SPS -> ~0.25s/char (1 char = 1 syllable)
// Chinese: 5.18 SPS -> ~0.28s/char (1 char = 1 syllable)
// English: 6.19 SPS -> ~0.08s/char (avg ~5 chars/syllable)
let normalizedLanguage = language?.lowercased() ?? "en"
let perCharSeconds: Double
let minSeconds: Double
if normalizedLanguage.hasPrefix("ko") {
perCharSeconds = 0.25
minSeconds = 10.0
} else if normalizedLanguage.hasPrefix("zh") {
perCharSeconds = 0.28
minSeconds = 10.0
} else if normalizedLanguage.hasPrefix("ja") {
perCharSeconds = 0.20
minSeconds = 10.0
} else {
perCharSeconds = 0.08
minSeconds = 3.0
}
let estimatedSeconds = max(minSeconds, min(300.0, Double(text.count) * perCharSeconds))
return estimatedSeconds * 3.0
}
private func matchesCurrentUtterance(_ utteranceID: ObjectIdentifier) -> Bool {
guard let currentUtterance = self.currentUtterance else { return false }
return ObjectIdentifier(currentUtterance) == utteranceID

View File

@@ -2764,6 +2764,110 @@ public struct ToolsCatalogResult: Codable, Sendable {
}
}
public struct ToolsEffectiveParams: Codable, Sendable {
public let agentid: String?
public let sessionkey: String
public init(
agentid: String?,
sessionkey: String)
{
self.agentid = agentid
self.sessionkey = sessionkey
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case sessionkey = "sessionKey"
}
}
public struct ToolsEffectiveEntry: Codable, Sendable {
public let id: String
public let label: String
public let description: String
public let rawdescription: String
public let source: AnyCodable
public let pluginid: String?
public let channelid: String?
public init(
id: String,
label: String,
description: String,
rawdescription: String,
source: AnyCodable,
pluginid: String?,
channelid: String?)
{
self.id = id
self.label = label
self.description = description
self.rawdescription = rawdescription
self.source = source
self.pluginid = pluginid
self.channelid = channelid
}
private enum CodingKeys: String, CodingKey {
case id
case label
case description
case rawdescription = "rawDescription"
case source
case pluginid = "pluginId"
case channelid = "channelId"
}
}
public struct ToolsEffectiveGroup: Codable, Sendable {
public let id: AnyCodable
public let label: String
public let source: AnyCodable
public let tools: [ToolsEffectiveEntry]
public init(
id: AnyCodable,
label: String,
source: AnyCodable,
tools: [ToolsEffectiveEntry])
{
self.id = id
self.label = label
self.source = source
self.tools = tools
}
private enum CodingKeys: String, CodingKey {
case id
case label
case source
case tools
}
}
public struct ToolsEffectiveResult: Codable, Sendable {
public let agentid: String
public let profile: String
public let groups: [ToolsEffectiveGroup]
public init(
agentid: String,
profile: String,
groups: [ToolsEffectiveGroup])
{
self.agentid = agentid
self.profile = profile
self.groups = groups
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case profile
case groups
}
}
public struct SkillsBinsParams: Codable, Sendable {}
public struct SkillsBinsResult: Codable, Sendable {

View File

@@ -84,6 +84,7 @@ private func makeViewModel(
sessionsResponses: [OpenClawChatSessionsListResponse] = [],
modelResponses: [[OpenClawChatModelChoice]] = [],
resetSessionHook: (@Sendable (String) async throws -> Void)? = nil,
compactSessionHook: (@Sendable (String) async throws -> Void)? = nil,
setSessionModelHook: (@Sendable (String?) async throws -> Void)? = nil,
setSessionThinkingHook: (@Sendable (String) async throws -> Void)? = nil,
initialThinkingLevel: String? = nil,
@@ -95,6 +96,7 @@ private func makeViewModel(
sessionsResponses: sessionsResponses,
modelResponses: modelResponses,
resetSessionHook: resetSessionHook,
compactSessionHook: compactSessionHook,
setSessionModelHook: setSessionModelHook,
setSessionThinkingHook: setSessionThinkingHook)
let vm = await MainActor.run {
@@ -219,11 +221,25 @@ private actor AsyncGate {
}
}
private actor AsyncCounter {
private var value: Int
init(_ initialValue: Int = 0) {
self.value = initialValue
}
func increment() -> Int {
self.value += 1
return self.value
}
}
private actor TestChatTransportState {
var historyCallCount: Int = 0
var sessionsCallCount: Int = 0
var modelsCallCount: Int = 0
var resetSessionKeys: [String] = []
var compactSessionKeys: [String] = []
var sentRunIds: [String] = []
var sentThinkingLevels: [String] = []
var abortedRunIds: [String] = []
@@ -237,6 +253,7 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
private let sessionsResponses: [OpenClawChatSessionsListResponse]
private let modelResponses: [[OpenClawChatModelChoice]]
private let resetSessionHook: (@Sendable (String) async throws -> Void)?
private let compactSessionHook: (@Sendable (String) async throws -> Void)?
private let setSessionModelHook: (@Sendable (String?) async throws -> Void)?
private let setSessionThinkingHook: (@Sendable (String) async throws -> Void)?
@@ -248,6 +265,7 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
sessionsResponses: [OpenClawChatSessionsListResponse] = [],
modelResponses: [[OpenClawChatModelChoice]] = [],
resetSessionHook: (@Sendable (String) async throws -> Void)? = nil,
compactSessionHook: (@Sendable (String) async throws -> Void)? = nil,
setSessionModelHook: (@Sendable (String?) async throws -> Void)? = nil,
setSessionThinkingHook: (@Sendable (String) async throws -> Void)? = nil)
{
@@ -255,6 +273,7 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
self.sessionsResponses = sessionsResponses
self.modelResponses = modelResponses
self.resetSessionHook = resetSessionHook
self.compactSessionHook = compactSessionHook
self.setSessionModelHook = setSessionModelHook
self.setSessionThinkingHook = setSessionThinkingHook
var cont: AsyncStream<OpenClawChatTransportEvent>.Continuation!
@@ -336,6 +355,13 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
}
}
func compactSession(sessionKey: String) async throws {
await self.state.compactSessionKeysAppend(sessionKey)
if let compactSessionHook = self.compactSessionHook {
try await compactSessionHook(sessionKey)
}
}
func setSessionThinking(sessionKey _: String, thinkingLevel: String) async throws {
await self.state.patchedThinkingLevelsAppend(thinkingLevel)
if let setSessionThinkingHook = self.setSessionThinkingHook {
@@ -375,6 +401,10 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
func resetSessionKeys() async -> [String] {
await self.state.resetSessionKeys
}
func compactSessionKeys() async -> [String] {
await self.state.compactSessionKeys
}
}
extension TestChatTransportState {
@@ -413,6 +443,10 @@ extension TestChatTransportState {
fileprivate func resetSessionKeysAppend(_ v: String) {
self.resetSessionKeys.append(v)
}
fileprivate func compactSessionKeysAppend(_ v: String) {
self.compactSessionKeys.append(v)
}
}
@Suite struct ChatViewModelTests {
@@ -915,6 +949,140 @@ extension TestChatTransportState {
#expect(await transport.lastSentRunId() == nil)
}
@Test func compactTriggerCompactsSessionAndReloadsHistory() async throws {
let before = historyPayload(
messages: [
chatTextMessage(role: "assistant", text: "before compact", timestamp: 1),
])
let after = historyPayload(
messages: [
chatTextMessage(role: "assistant", text: "after compact", timestamp: 2),
])
let (transport, vm) = await makeViewModel(historyResponses: [before, after])
try await loadAndWaitBootstrap(vm: vm)
try await waitUntil("initial history loaded") {
await MainActor.run { vm.messages.first?.content.first?.text == "before compact" }
}
await MainActor.run {
vm.input = "/compact"
vm.send()
}
try await waitUntil("compact called") {
await transport.compactSessionKeys() == ["main"]
}
try await waitUntil("history reloaded") {
await MainActor.run { vm.messages.first?.content.first?.text == "after compact" }
}
#expect(await transport.lastSentRunId() == nil)
}
@Test func compactTriggerShowsGenericErrorMessageOnFailure() async throws {
let history = historyPayload()
let (transport, vm) = await makeViewModel(
historyResponses: [history],
compactSessionHook: { _ in
throw NSError(
domain: "TestCompact",
code: 42,
userInfo: [NSLocalizedDescriptionKey: "backend details should not leak"])
})
try await loadAndWaitBootstrap(vm: vm)
await MainActor.run {
vm.input = "/compact"
vm.send()
}
try await waitUntil("compact attempted") {
await transport.compactSessionKeys() == ["main"]
}
#expect(await MainActor.run { vm.errorText } == "Unable to compact the session. Please try again.")
}
@Test func compactTriggerIgnoresConcurrentAndImmediateRepeatRequests() async throws {
let before = historyPayload(
messages: [
chatTextMessage(role: "assistant", text: "before compact", timestamp: 1),
])
let after = historyPayload(
messages: [
chatTextMessage(role: "assistant", text: "after compact", timestamp: 2),
])
let gate = AsyncGate()
let (transport, vm) = await makeViewModel(
historyResponses: [before, after],
compactSessionHook: { _ in
await gate.wait()
})
try await loadAndWaitBootstrap(vm: vm)
await MainActor.run {
vm.input = "/compact"
vm.send()
vm.input = "/compact"
vm.send()
}
try await waitUntil("single compact request issued") {
await transport.compactSessionKeys() == ["main"]
}
#expect(await MainActor.run { vm.errorText } == nil)
await gate.open()
try await waitUntil("history reloaded after compact") {
await MainActor.run { vm.messages.first?.content.first?.text == "after compact" }
}
await MainActor.run {
vm.input = "/compact"
vm.send()
}
try await Task.sleep(for: .milliseconds(50))
#expect(await transport.compactSessionKeys() == ["main"])
#expect(await MainActor.run { vm.errorText } == "Please wait before compacting this session again.")
}
@Test func compactTriggerAllowsImmediateRetryAfterFailure() async throws {
let history = historyPayload()
let attemptCount = AsyncCounter()
let (transport, vm) = await makeViewModel(
historyResponses: [history],
compactSessionHook: { _ in
let next = await attemptCount.increment()
if next == 1 {
throw NSError(
domain: "TestCompact",
code: 42,
userInfo: [NSLocalizedDescriptionKey: "temporary failure"])
}
})
try await loadAndWaitBootstrap(vm: vm)
await MainActor.run {
vm.input = "/compact"
vm.send()
}
try await waitUntil("first compact attempted") {
await transport.compactSessionKeys() == ["main"]
}
#expect(await MainActor.run { vm.errorText } == "Unable to compact the session. Please try again.")
await MainActor.run {
vm.input = "/compact"
vm.send()
}
try await waitUntil("second compact attempted") {
await transport.compactSessionKeys() == ["main", "main"]
}
#expect(await MainActor.run { vm.errorText } == nil)
}
@Test func bootstrapsModelSelectionFromSessionAndDefaults() async throws {
let now = Date().timeIntervalSince1970 * 1000
let history = historyPayload()

View File

@@ -0,0 +1,44 @@
import XCTest
@testable import OpenClawKit
final class TalkSystemSpeechSynthesizerTests: XCTestCase {
func testWatchdogTimeoutDefaultsToLatinProfile() {
let timeout = TalkSystemSpeechSynthesizer.watchdogTimeoutSeconds(
text: String(repeating: "a", count: 100),
language: nil)
XCTAssertEqual(timeout, 24.0, accuracy: 0.001)
}
func testWatchdogTimeoutUsesKoreanProfile() {
let timeout = TalkSystemSpeechSynthesizer.watchdogTimeoutSeconds(
text: String(repeating: "", count: 100),
language: "ko-KR")
XCTAssertEqual(timeout, 75.0, accuracy: 0.001)
}
func testWatchdogTimeoutUsesChineseProfile() {
let timeout = TalkSystemSpeechSynthesizer.watchdogTimeoutSeconds(
text: String(repeating: "", count: 100),
language: "zh-CN")
XCTAssertEqual(timeout, 84.0, accuracy: 0.001)
}
func testWatchdogTimeoutUsesJapaneseProfile() {
let timeout = TalkSystemSpeechSynthesizer.watchdogTimeoutSeconds(
text: String(repeating: "", count: 100),
language: "ja-JP")
XCTAssertEqual(timeout, 60.0, accuracy: 0.001)
}
func testWatchdogTimeoutClampsVeryLongUtterances() {
let timeout = TalkSystemSpeechSynthesizer.watchdogTimeoutSeconds(
text: String(repeating: "a", count: 10_000),
language: "en-US")
XCTAssertEqual(timeout, 900.0, accuracy: 0.001)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5605}
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5531}
{"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true}
{"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true}
{"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -669,6 +669,7 @@
{"recordType":"path","path":"auth.order.*.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"auth.profiles","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["access","auth","storage"],"label":"Auth Profiles","help":"Named auth profiles (provider + mode + optional email).","hasChildren":true}
{"recordType":"path","path":"auth.profiles.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"auth.profiles.*.displayName","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"auth.profiles.*.email","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"auth.profiles.*.mode","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"auth.profiles.*.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -745,6 +746,7 @@
{"recordType":"path","path":"channels.bluebubbles.accounts.*.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.accounts.*.dmPolicy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.accounts.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.accounts.*.enrichGroupParticipantsFromContacts","kind":"channel","type":"boolean","required":true,"defaultValue":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.accounts.*.groupAllowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.bluebubbles.accounts.*.groupAllowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.accounts.*.groupPolicy","kind":"channel","type":"string","required":false,"enumValues":["open","disabled","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -794,6 +796,7 @@
{"recordType":"path","path":"channels.bluebubbles.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.dmPolicy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":["access","channels","network"],"label":"BlueBubbles DM Policy","help":"Direct message access control (\"pairing\" recommended). \"open\" requires channels.bluebubbles.allowFrom=[\"*\"].","hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.enrichGroupParticipantsFromContacts","kind":"channel","type":"boolean","required":true,"defaultValue":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.groupAllowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.bluebubbles.groupAllowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.bluebubbles.groupPolicy","kind":"channel","type":"string","required":false,"enumValues":["open","disabled","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -916,6 +919,7 @@
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.allow","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.autoArchiveDuration","kind":"channel","type":["number","string"],"required":false,"enumValues":["60","1440","4320","10080"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.autoThread","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.autoThreadName","kind":"channel","type":"string","required":false,"enumValues":["message","generated"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.ignoreOtherMentions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.includeThreadStarter","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1025,47 +1029,8 @@
{"recordType":"path","path":"channels.discord.accounts.*.voice.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.auto","kind":"channel","type":"string","required":false,"enumValues":["off","always","inbound","tagged"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.lang","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.outputFormat","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.pitch","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.proxy","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.rate","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.saveSubtitles","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.voice","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.edge.volume","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.apiKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.apiKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.apiKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.apiKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.applyTextNormalization","kind":"channel","type":"string","required":false,"enumValues":["auto","on","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.languageCode","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.modelId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.seed","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.voiceId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.voiceSettings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.voiceSettings.similarityBoost","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.voiceSettings.speed","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.voiceSettings.stability","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.voiceSettings.style","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.elevenlabs.voiceSettings.useSpeakerBoost","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.maxTextLength","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.lang","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.outputFormat","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.pitch","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.proxy","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.rate","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.saveSubtitles","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.voice","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.microsoft.volume","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.mode","kind":"channel","type":"string","required":false,"enumValues":["final","all"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.modelOverrides","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.modelOverrides.allowModelId","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1076,18 +1041,16 @@
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.modelOverrides.allowVoice","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.modelOverrides.allowVoiceSettings","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.modelOverrides.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.apiKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.apiKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.apiKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.apiKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.instructions","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.model","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.speed","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.openai.voice","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.prefsPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.provider","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers.*.*","kind":"channel","type":["array","boolean","null","number","object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers.*.*.*","kind":"channel","type":[],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers.*.apiKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers.*.apiKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers.*.apiKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.providers.*.apiKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.summaryModel","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.voice.tts.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.ackReaction","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1182,6 +1145,7 @@
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.allow","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.autoArchiveDuration","kind":"channel","type":["number","string"],"required":false,"enumValues":["60","1440","4320","10080"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.autoThread","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.autoThreadName","kind":"channel","type":"string","required":false,"enumValues":["message","generated"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.ignoreOtherMentions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.includeThreadStarter","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1291,47 +1255,8 @@
{"recordType":"path","path":"channels.discord.voice.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Discord Voice Enabled","help":"Enable Discord voice channel conversations (default: true). Omit channels.discord.voice to keep voice support disabled for the account.","hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","media","network"],"label":"Discord Voice Text-to-Speech","help":"Optional TTS overrides for Discord voice playback (merged with messages.tts).","hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.auto","kind":"channel","type":"string","required":false,"enumValues":["off","always","inbound","tagged"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.edge.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.lang","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.outputFormat","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.pitch","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.proxy","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.rate","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.saveSubtitles","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.voice","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.edge.volume","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.apiKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.apiKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.apiKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.apiKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.applyTextNormalization","kind":"channel","type":"string","required":false,"enumValues":["auto","on","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.languageCode","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.modelId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.seed","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.voiceId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.voiceSettings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.voiceSettings.similarityBoost","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.voiceSettings.speed","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.voiceSettings.stability","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.voiceSettings.style","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.elevenlabs.voiceSettings.useSpeakerBoost","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.maxTextLength","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.lang","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.outputFormat","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.pitch","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.proxy","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.rate","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.saveSubtitles","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.voice","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.microsoft.volume","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.mode","kind":"channel","type":"string","required":false,"enumValues":["final","all"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.modelOverrides","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.modelOverrides.allowModelId","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1342,18 +1267,16 @@
{"recordType":"path","path":"channels.discord.voice.tts.modelOverrides.allowVoice","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.modelOverrides.allowVoiceSettings","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.modelOverrides.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.openai.apiKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.openai.apiKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai.apiKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai.apiKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai.instructions","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai.model","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai.speed","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.openai.voice","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.prefsPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.provider","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.providers","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.providers.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.providers.*.*","kind":"channel","type":["array","boolean","null","number","object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.providers.*.*.*","kind":"channel","type":[],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.providers.*.apiKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","media","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.discord.voice.tts.providers.*.apiKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.providers.*.apiKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.providers.*.apiKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.summaryModel","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.voice.tts.timeoutMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Feishu","help":"飞书/Lark enterprise messaging with doc/wiki/drive tools.","hasChildren":true}
@@ -1516,7 +1439,7 @@
{"recordType":"path","path":"channels.feishu.reactionNotifications","kind":"channel","type":"string","required":true,"enumValues":["off","own","all"],"defaultValue":"own","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.renderMode","kind":"channel","type":"string","required":false,"enumValues":["auto","raw","card"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.replyInThread","kind":"channel","type":"string","required":false,"enumValues":["disabled","enabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.requireMention","kind":"channel","type":"boolean","required":true,"defaultValue":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.resolveSenderNames","kind":"channel","type":"boolean","required":true,"defaultValue":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.streaming","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.textChunkLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2088,6 +2011,7 @@
{"recordType":"path","path":"channels.mattermost.accounts.*.actions.reactions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.accounts.*.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.blockStreaming","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.blockStreamingCoalesce","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -2136,6 +2060,7 @@
{"recordType":"path","path":"channels.mattermost.actions.reactions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Base URL","help":"Base URL for your Mattermost server (e.g., https://chat.example.com).","hasChildren":false}
{"recordType":"path","path":"channels.mattermost.blockStreaming","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.blockStreamingCoalesce","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -2181,7 +2106,7 @@
{"recordType":"path","path":"channels.mattermost.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Require Mention","help":"Require @mention in channels before responding (default: true).","hasChildren":false}
{"recordType":"path","path":"channels.mattermost.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.textChunkLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Microsoft Teams","help":"Bot Framework; enterprise support.","hasChildren":true}
{"recordType":"path","path":"channels.msteams","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Microsoft Teams","help":"Teams SDK; enterprise support.","hasChildren":true}
{"recordType":"path","path":"channels.msteams.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.appId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2205,9 +2130,13 @@
{"recordType":"path","path":"channels.msteams.dms.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.dms.*.historyLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.feedbackEnabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.feedbackReflection","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.feedbackReflectionCooldownMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.groupAllowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.groupAllowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.groupPolicy","kind":"channel","type":"string","required":true,"enumValues":["open","disabled","allowlist"],"defaultValue":"allowlist","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.groupWelcomeCard","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.healthMonitor","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.healthMonitor.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.heartbeat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -2222,6 +2151,8 @@
{"recordType":"path","path":"channels.msteams.mediaAuthAllowHosts","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.mediaAuthAllowHosts.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.promptStarters","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.promptStarters.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.replyStyle","kind":"channel","type":"string","required":false,"enumValues":["thread","top-level"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2269,11 +2200,13 @@
{"recordType":"path","path":"channels.msteams.webhook","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.webhook.path","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.webhook.port","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.welcomeCard","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Nextcloud Talk","help":"Self-hosted chat via Nextcloud Talk webhook bots.","hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2330,6 +2263,7 @@
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.webhookPublicUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2804,6 +2738,8 @@
{"recordType":"path","path":"channels.slack.webhookPath","kind":"channel","type":"string","required":true,"defaultValue":"/slack/events","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.synology-chat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Synology Chat","help":"Connect your Synology NAS Chat to OpenClaw with full agent capabilities.","hasChildren":true}
{"recordType":"path","path":"channels.synology-chat.*","kind":"channel","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.synology-chat.dangerouslyAllowInheritedWebhookPath","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.synology-chat.dangerouslyAllowNameMatching","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.telegram","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Telegram","help":"simplest way to get started — register a bot with @BotFather and get going.","hasChildren":true}
{"recordType":"path","path":"channels.telegram.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.telegram.accounts.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -3928,47 +3864,8 @@
{"recordType":"path","path":"messages.suppressToolErrors","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Suppress Tool Error Warnings","help":"When true, suppress ⚠️ tool-error warnings from being shown to the user. The agent already sees errors in context and can retry. Default: false.","hasChildren":false}
{"recordType":"path","path":"messages.tts","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["media"],"label":"Message Text-to-Speech","help":"Text-to-speech policy for reading agent replies aloud on supported voice or audio surfaces. Keep disabled unless voice playback is part of your operator/user workflow.","hasChildren":true}
{"recordType":"path","path":"messages.tts.auto","kind":"core","type":"string","required":false,"enumValues":["off","always","inbound","tagged"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"messages.tts.edge.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.lang","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.outputFormat","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.pitch","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.proxy","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.rate","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.saveSubtitles","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.timeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.voice","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.edge.volume","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"messages.tts.elevenlabs.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","media","security"],"hasChildren":true}
{"recordType":"path","path":"messages.tts.elevenlabs.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.applyTextNormalization","kind":"core","type":"string","required":false,"enumValues":["auto","on","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.languageCode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.modelId","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.seed","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.voiceId","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.voiceSettings","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"messages.tts.elevenlabs.voiceSettings.similarityBoost","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.voiceSettings.speed","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.voiceSettings.stability","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.voiceSettings.style","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.elevenlabs.voiceSettings.useSpeakerBoost","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.maxTextLength","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"messages.tts.microsoft.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.lang","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.outputFormat","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.pitch","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.proxy","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.rate","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.saveSubtitles","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.timeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.voice","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.microsoft.volume","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.mode","kind":"core","type":"string","required":false,"enumValues":["final","all"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.modelOverrides","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"messages.tts.modelOverrides.allowModelId","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -3979,18 +3876,16 @@
{"recordType":"path","path":"messages.tts.modelOverrides.allowVoice","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.modelOverrides.allowVoiceSettings","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.modelOverrides.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"messages.tts.openai.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","media","security"],"hasChildren":true}
{"recordType":"path","path":"messages.tts.openai.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai.baseUrl","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai.instructions","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai.speed","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.openai.voice","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.prefsPath","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.providers","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["media"],"label":"TTS Provider Settings","help":"Provider-specific TTS settings keyed by speech provider id. Use this instead of bundled provider-specific top-level keys so speech plugins stay decoupled from core config schema.","hasChildren":true}
{"recordType":"path","path":"messages.tts.providers.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["media"],"label":"TTS Provider Config","help":"Provider-specific TTS configuration for one speech provider id. Keep fields scoped to the plugin that owns that provider.","hasChildren":true}
{"recordType":"path","path":"messages.tts.providers.*.*","kind":"core","type":["array","boolean","null","number","object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"messages.tts.providers.*.*.*","kind":"core","type":[],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.providers.*.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","media","security"],"label":"TTS Provider API Key","help":"Provider API key used by that speech provider when its plugin requires authenticated TTS access.","hasChildren":true}
{"recordType":"path","path":"messages.tts.providers.*.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.providers.*.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.providers.*.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.summaryModel","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"messages.tts.timeoutMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"meta","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Metadata","help":"Metadata fields automatically maintained by OpenClaw to record write/version history for this config file. Keep these values system-managed and avoid manual edits unless debugging migration history.","hasChildren":true}
@@ -4058,7 +3953,7 @@
{"recordType":"path","path":"models.providers.*.models.*.reasoning","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"nodeHost","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Node Host","help":"Node host controls for features exposed from this gateway node to other nodes or clients. Keep defaults unless you intentionally proxy local capabilities across your node network.","hasChildren":true}
{"recordType":"path","path":"nodeHost.browserProxy","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Node Browser Proxy","help":"Groups browser-proxy settings for exposing local browser control through node routing. Enable only when remote node workflows need your local browser profiles.","hasChildren":true}
{"recordType":"path","path":"nodeHost.browserProxy.allowProfiles","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access","network","storage"],"label":"Node Browser Proxy Allowed Profiles","help":"Optional allowlist of browser profile names exposed through node proxy routing. Leave empty to expose all configured profiles, or use a tight list to enforce least-privilege profile access.","hasChildren":true}
{"recordType":"path","path":"nodeHost.browserProxy.allowProfiles","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access","network","storage"],"label":"Node Browser Proxy Allowed Profiles","help":"Optional allowlist of browser profile names exposed through node proxy routing. Leave empty to preserve the default full profile surface, including profile create/delete routes. When set, OpenClaw enforces least-privilege profile access and blocks persistent profile create/delete through the proxy.","hasChildren":true}
{"recordType":"path","path":"nodeHost.browserProxy.allowProfiles.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"nodeHost.browserProxy.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["network"],"label":"Node Browser Proxy Enabled","help":"Expose the local browser control server through node proxy routing so remote clients can use this host's browser capabilities. Keep disabled unless remote automation explicitly depends on it.","hasChildren":false}
{"recordType":"path","path":"plugins","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugins","help":"Plugin system controls for enabling extensions, constraining load scope, configuring entries, and tracking installs. Keep plugin policy explicit and least-privilege in production environments.","hasChildren":true}
@@ -4082,7 +3977,7 @@
{"recordType":"path","path":"plugins.entries.acpx.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACPX Runtime Config","help":"Plugin-defined config payload for acpx.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.acpx.config.command","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"acpx Command","help":"Optional path/command override for acpx (for example /home/user/repos/acpx/dist/cli.js). Leave unset to use plugin-local bundled acpx.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.acpx.config.cwd","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Default Working Directory","help":"Default cwd for ACP session operations when not set per session.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.acpx.config.expectedVersion","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Expected acpx Version","help":"Exact version to enforce (for example 0.1.16) or \"any\" to skip strict version matching.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.acpx.config.expectedVersion","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Expected acpx Version","help":"Exact version to enforce or \"any\" to skip strict version matching.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.acpx.config.mcpServers","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"MCP Servers","help":"Named MCP server definitions to inject into ACPX-backed session bootstrap. Each entry needs a command and can include args and env.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.acpx.config.mcpServers.*","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"plugins.entries.acpx.config.mcpServers.*.args","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -4141,6 +4036,15 @@
{"recordType":"path","path":"plugins.entries.brave.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.brave.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.brave.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.browser","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/browser-plugin","help":"OpenClaw browser tool plugin (plugin: browser)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.browser.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/browser-plugin Config","help":"Plugin-defined config payload for browser.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.browser.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/browser-plugin","hasChildren":false}
{"recordType":"path","path":"plugins.entries.browser.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.browser.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.browser.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.browser.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.browser.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.browser.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.byteplus","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/byteplus-provider","help":"OpenClaw BytePlus provider plugin (plugin: byteplus)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.byteplus.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/byteplus-provider Config","help":"Plugin-defined config payload for byteplus.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.byteplus.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/byteplus-provider","hasChildren":false}
@@ -4186,6 +4090,15 @@
{"recordType":"path","path":"plugins.entries.deepgram.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.deepgram.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.deepgram.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.deepseek","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/deepseek-provider","help":"OpenClaw DeepSeek provider plugin (plugin: deepseek)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.deepseek.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/deepseek-provider Config","help":"Plugin-defined config payload for deepseek.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.deepseek.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/deepseek-provider","hasChildren":false}
{"recordType":"path","path":"plugins.entries.deepseek.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.deepseek.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.deepseek.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.deepseek.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.deepseek.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.deepseek.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.device-pair","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Device Pairing","help":"Generate setup codes and approve device pairing requests. (plugin: device-pair)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.device-pair.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Device Pairing Config","help":"Plugin-defined config payload for device-pair.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.device-pair.config.publicUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Gateway URL","help":"Public WebSocket URL used for /pair setup codes (ws/wss or http/https).","hasChildren":false}
@@ -4245,6 +4158,18 @@
{"recordType":"path","path":"plugins.entries.discord.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.discord.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.discord.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.duckduckgo","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/duckduckgo-plugin","help":"OpenClaw DuckDuckGo plugin (plugin: duckduckgo)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.duckduckgo.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/duckduckgo-plugin Config","help":"Plugin-defined config payload for duckduckgo.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.duckduckgo.config.webSearch","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"plugins.entries.duckduckgo.config.webSearch.region","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"DuckDuckGo Region","help":"Optional DuckDuckGo region code such as us-en, uk-en, or de-de.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.duckduckgo.config.webSearch.safeSearch","kind":"plugin","type":"string","required":false,"enumValues":["strict","moderate","off"],"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"DuckDuckGo SafeSearch","help":"SafeSearch level for DuckDuckGo results.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.duckduckgo.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/duckduckgo-plugin","hasChildren":false}
{"recordType":"path","path":"plugins.entries.duckduckgo.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.duckduckgo.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.duckduckgo.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.duckduckgo.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.duckduckgo.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.duckduckgo.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.elevenlabs","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/elevenlabs-speech","help":"OpenClaw ElevenLabs speech plugin (plugin: elevenlabs)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.elevenlabs.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/elevenlabs-speech Config","help":"Plugin-defined config payload for elevenlabs.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.elevenlabs.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/elevenlabs-speech","hasChildren":false}
@@ -4459,6 +4384,15 @@
{"recordType":"path","path":"plugins.entries.memory-lancedb.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.memory-lancedb.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/microsoft-speech","help":"OpenClaw Microsoft speech plugin (plugin: microsoft)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.microsoft-foundry","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/microsoft-foundry","help":"OpenClaw Microsoft Foundry provider plugin (plugin: microsoft-foundry)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/microsoft-foundry Config","help":"Plugin-defined config payload for microsoft-foundry.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/microsoft-foundry","hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft-foundry.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/microsoft-speech Config","help":"Plugin-defined config payload for microsoft.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/microsoft-speech","hasChildren":false}
{"recordType":"path","path":"plugins.entries.microsoft.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
@@ -4649,15 +4583,6 @@
{"recordType":"path","path":"plugins.entries.qianfan.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.qianfan.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.qianfan.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"qwen-portal-auth","help":"Plugin entry for qwen-portal-auth.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"qwen-portal-auth Config","help":"Plugin-defined config payload for qwen-portal-auth.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable qwen-portal-auth","hasChildren":false}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.qwen-portal-auth.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.sglang","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/sglang-provider","help":"OpenClaw SGLang provider plugin (plugin: sglang)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.sglang.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/sglang-provider Config","help":"Plugin-defined config payload for sglang.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.sglang.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/sglang-provider","hasChildren":false}
@@ -5519,6 +5444,7 @@
{"recordType":"path","path":"tools.web.fetch.maxChars","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Fetch Max Chars","help":"Max characters returned by web_fetch (truncated).","hasChildren":false}
{"recordType":"path","path":"tools.web.fetch.maxCharsCap","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Fetch Hard Max Chars","help":"Hard cap for web_fetch maxChars (applies to config and tool calls).","hasChildren":false}
{"recordType":"path","path":"tools.web.fetch.maxRedirects","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","storage","tools"],"label":"Web Fetch Max Redirects","help":"Maximum redirects allowed for web_fetch (default: 3).","hasChildren":false}
{"recordType":"path","path":"tools.web.fetch.maxResponseBytes","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Fetch Max Download Size (bytes)","help":"Max download size before truncation.","hasChildren":false}
{"recordType":"path","path":"tools.web.fetch.readability","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Web Fetch Readability Extraction","help":"Use Readability to extract main content from HTML (fallbacks to basic HTML cleanup).","hasChildren":false}
{"recordType":"path","path":"tools.web.fetch.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Fetch Timeout (sec)","help":"Timeout in seconds for web_fetch requests.","hasChildren":false}
{"recordType":"path","path":"tools.web.fetch.userAgent","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Web Fetch User-Agent","help":"Override User-Agent header for web_fetch requests.","hasChildren":false}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,7 @@
{"category":"legacy","entrypoint":"index","importSpecifier":"openclaw/plugin-sdk","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/index.ts"}
{"declaration":"export function buildFalImageGenerationProvider(): ImageGenerationProvider;","entrypoint":"index","exportName":"buildFalImageGenerationProvider","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":190,"sourcePath":"extensions/fal/image-generation-provider.ts"}
{"declaration":"export function buildGoogleImageGenerationProvider(): ImageGenerationProvider;","entrypoint":"index","exportName":"buildGoogleImageGenerationProvider","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":95,"sourcePath":"extensions/google/image-generation-provider.ts"}
{"declaration":"export function buildOpenAIImageGenerationProvider(): ImageGenerationProvider;","entrypoint":"index","exportName":"buildOpenAIImageGenerationProvider","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":22,"sourcePath":"extensions/openai/image-generation-provider.ts"}
{"declaration":"export function delegateCompactionToRuntime(params: { sessionId: string; sessionKey?: string | undefined; sessionFile: string; tokenBudget?: number | undefined; force?: boolean | undefined; currentTokenCount?: number | undefined; compactionTarget?: \"budget\" | ... 1 more ... | undefined; customInstructions?: string | undefined; runtimeContext?: ContextEngineRuntimeContext | undefined; }): Promise<...>;","entrypoint":"index","exportName":"delegateCompactionToRuntime","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/context-engine/delegate.ts"}
{"declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"index","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":13,"sourcePath":"src/plugins/config-schema.ts"}
{"declaration":"export function onDiagnosticEvent(listener: (evt: DiagnosticEventPayload) => void): () => void;","entrypoint":"index","exportName":"onDiagnosticEvent","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":223,"sourcePath":"src/infra/diagnostic-events.ts"}
{"declaration":"export function onDiagnosticEvent(listener: (evt: DiagnosticEventPayload) => void): () => void;","entrypoint":"index","exportName":"onDiagnosticEvent","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":229,"sourcePath":"src/infra/diagnostic-events.ts"}
{"declaration":"export function registerContextEngine(id: string, factory: ContextEngineFactory): ContextEngineRegistrationResult;","entrypoint":"index","exportName":"registerContextEngine","importSpecifier":"openclaw/plugin-sdk","kind":"function","recordType":"export","sourceLine":377,"sourcePath":"src/context-engine/registry.ts"}
{"declaration":"export type AnyAgentTool = AnyAgentTool;","entrypoint":"index","exportName":"AnyAgentTool","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/agents/tools/common.ts"}
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"index","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -12,14 +9,14 @@
{"declaration":"export type ChannelAgentToolFactory = ChannelAgentToolFactory;","entrypoint":"index","exportName":"ChannelAgentToolFactory","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":23,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelCapabilities = ChannelCapabilities;","entrypoint":"index","exportName":"ChannelCapabilities","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":230,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelConfigSchema = ChannelConfigSchema;","entrypoint":"index","exportName":"ChannelConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":48,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"index","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":553,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"index","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"index","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":562,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"index","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":554,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"index","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"index","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":563,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"index","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelId = ChannelId;","entrypoint":"index","exportName":"ChannelId","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"index","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":516,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"index","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionName = \"send\" | \"broadcast\" | \"poll\" | \"poll-vote\" | \"react\" | \"reactions\" | \"read\" | \"edit\" | \"unsend\" | \"reply\" | \"sendWithEffect\" | \"renameGroup\" | \"setGroupIcon\" | \"addParticipant\" | \"removeParticipant\" | \"leaveGroup\" | \"sendAttachment\" | \"delete\" | \"pin\" | \"unpin\" | \"list-pins\" | \"permissions\" | \"thread-create\" | \"thread-list\" | \"thread-reply\" | \"search\" | \"sticker\" | \"sticker-search\" | \"member-info\" | \"role-info\" | \"emoji-list\" | \"emoji-upload\" | \"sticker-upload\" | \"role-add\" | \"role-remove\" | \"channel-info\" | \"channel-list\" | \"channel-create\" | \"channel-edit\" | \"channel-delete\" | \"channel-move\" | \"category-create\" | \"category-edit\" | \"category-delete\" | \"topic-create\" | \"topic-edit\" | \"voice-status\" | \"event-list\" | \"event-create\" | \"timeout\" | \"kick\" | \"ban\" | \"set-profile\" | \"set-presence\" | \"download-file\";","entrypoint":"index","exportName":"ChannelMessageActionName","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/channels/plugins/types.ts"}
{"declaration":"export type ChannelMessageActionName = \"send\" | \"broadcast\" | \"poll\" | \"poll-vote\" | \"react\" | \"reactions\" | \"read\" | \"edit\" | \"unsend\" | \"reply\" | \"sendWithEffect\" | \"renameGroup\" | \"setGroupIcon\" | \"addParticipant\" | \"removeParticipant\" | \"leaveGroup\" | \"sendAttachment\" | \"delete\" | \"pin\" | \"unpin\" | \"list-pins\" | \"permissions\" | \"thread-create\" | \"thread-list\" | \"thread-reply\" | \"search\" | \"sticker\" | \"sticker-search\" | \"member-info\" | \"role-info\" | \"emoji-list\" | \"emoji-upload\" | \"sticker-upload\" | \"role-add\" | \"role-remove\" | \"channel-info\" | \"channel-list\" | \"channel-create\" | \"channel-edit\" | \"channel-delete\" | \"channel-move\" | \"category-create\" | \"category-edit\" | \"category-delete\" | \"topic-create\" | \"topic-edit\" | \"voice-status\" | \"event-list\" | \"event-create\" | \"timeout\" | \"kick\" | \"ban\" | \"set-profile\" | \"set-presence\" | \"download-file\" | \"upload-file\";","entrypoint":"index","exportName":"ChannelMessageActionName","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/channels/plugins/types.ts"}
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"index","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":55,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"index","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":56,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSetupInput = ChannelSetupInput;","entrypoint":"index","exportName":"ChannelSetupInput","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":63,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -27,6 +24,8 @@
{"declaration":"export type ChannelSetupWizardAllowFromEntry = ChannelSetupWizardAllowFromEntry;","entrypoint":"index","exportName":"ChannelSetupWizardAllowFromEntry","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":154,"sourcePath":"src/channels/plugins/setup-wizard.ts"}
{"declaration":"export type ChannelStatusIssue = ChannelStatusIssue;","entrypoint":"index","exportName":"ChannelStatusIssue","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"index","exportName":"ClawdbotConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type CliBackendConfig = CliBackendConfig;","entrypoint":"index","exportName":"CliBackendConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/config/types.agent-defaults.ts"}
{"declaration":"export type CliBackendPlugin = CliBackendPlugin;","entrypoint":"index","exportName":"CliBackendPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1346,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type CompiledConfiguredBinding = CompiledConfiguredBinding;","entrypoint":"index","exportName":"CompiledConfiguredBinding","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":38,"sourcePath":"src/channels/plugins/binding-types.ts"}
{"declaration":"export type ConfiguredBindingConversation = ConversationRef;","entrypoint":"index","exportName":"ConfiguredBindingConversation","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/binding-types.ts"}
{"declaration":"export type ConfiguredBindingResolution = ConfiguredBindingResolution;","entrypoint":"index","exportName":"ConfiguredBindingResolution","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":49,"sourcePath":"src/channels/plugins/binding-types.ts"}
@@ -34,7 +33,7 @@
{"declaration":"export type ContextEngineInfo = ContextEngineInfo;","entrypoint":"index","exportName":"ContextEngineInfo","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":46,"sourcePath":"src/context-engine/types.ts"}
{"declaration":"export type ContextEngineMaintenanceResult = TranscriptRewriteResult;","entrypoint":"index","exportName":"ContextEngineMaintenanceResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":84,"sourcePath":"src/context-engine/types.ts"}
{"declaration":"export type ContextEngineRuntimeContext = ContextEngineRuntimeContext;","entrypoint":"index","exportName":"ContextEngineRuntimeContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":86,"sourcePath":"src/context-engine/types.ts"}
{"declaration":"export type DiagnosticEventPayload = DiagnosticEventPayload;","entrypoint":"index","exportName":"DiagnosticEventPayload","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":152,"sourcePath":"src/infra/diagnostic-events.ts"}
{"declaration":"export type DiagnosticEventPayload = DiagnosticEventPayload;","entrypoint":"index","exportName":"DiagnosticEventPayload","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":150,"sourcePath":"src/infra/diagnostic-events.ts"}
{"declaration":"export type GeneratedImageAsset = GeneratedImageAsset;","entrypoint":"index","exportName":"GeneratedImageAsset","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/image-generation/types.ts"}
{"declaration":"export type HookEntry = HookEntry;","entrypoint":"index","exportName":"HookEntry","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/hooks/types.ts"}
{"declaration":"export type ImageGenerationProvider = ImageGenerationProvider;","entrypoint":"index","exportName":"ImageGenerationProvider","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":66,"sourcePath":"src/image-generation/types.ts"}
@@ -42,21 +41,21 @@
{"declaration":"export type ImageGenerationResolution = ImageGenerationResolution;","entrypoint":"index","exportName":"ImageGenerationResolution","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/image-generation/types.ts"}
{"declaration":"export type ImageGenerationResult = ImageGenerationResult;","entrypoint":"index","exportName":"ImageGenerationResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":36,"sourcePath":"src/image-generation/types.ts"}
{"declaration":"export type ImageGenerationSourceImage = ImageGenerationSourceImage;","entrypoint":"index","exportName":"ImageGenerationSourceImage","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":14,"sourcePath":"src/image-generation/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"index","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":950,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"index","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":969,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"index","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"index","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1313,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"index","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":88,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"index","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":59,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"index","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1390,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"index","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":95,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"index","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":66,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"index","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"index","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":155,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"index","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":140,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"index","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":295,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"index","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":166,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"index","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":151,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"index","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":306,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ReplyPayload = ReplyPayload;","entrypoint":"index","exportName":"ReplyPayload","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/auto-reply/types.ts"}
{"declaration":"export type RuntimeEnv = RuntimeEnv;","entrypoint":"index","exportName":"RuntimeEnv","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/runtime.ts"}
{"declaration":"export type RuntimeLogger = RuntimeLogger;","entrypoint":"index","exportName":"RuntimeLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/plugins/runtime/types-core.ts"}
{"declaration":"export type RuntimeLogger = RuntimeLogger;","entrypoint":"index","exportName":"RuntimeLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/plugins/runtime/types-core.ts"}
{"declaration":"export type SecretInput = SecretInput;","entrypoint":"index","exportName":"SecretInput","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":16,"sourcePath":"src/config/types.secrets.ts"}
{"declaration":"export type SecretRef = SecretRef;","entrypoint":"index","exportName":"SecretRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":10,"sourcePath":"src/config/types.secrets.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"index","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":932,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"index","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":944,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type StatefulBindingTargetDescriptor = StatefulBindingTargetDescriptor;","entrypoint":"index","exportName":"StatefulBindingTargetDescriptor","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":17,"sourcePath":"src/channels/plugins/binding-types.ts"}
{"declaration":"export type StatefulBindingTargetDriver = StatefulBindingTargetDriver;","entrypoint":"index","exportName":"StatefulBindingTargetDriver","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":15,"sourcePath":"src/channels/plugins/stateful-target-drivers.ts"}
{"declaration":"export type StatefulBindingTargetReadyResult = StatefulBindingTargetReadyResult;","entrypoint":"index","exportName":"StatefulBindingTargetReadyResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/channels/plugins/stateful-target-drivers.ts"}
@@ -98,11 +97,13 @@
{"declaration":"export type BasicAllowlistResolutionEntry = BasicAllowlistResolutionEntry;","entrypoint":"allow-from","exportName":"BasicAllowlistResolutionEntry","importSpecifier":"openclaw/plugin-sdk/allow-from","kind":"type","recordType":"export","sourceLine":129,"sourcePath":"src/plugin-sdk/allow-from.ts"}
{"declaration":"export type CompiledAllowlist = CompiledAllowlist;","entrypoint":"allow-from","exportName":"CompiledAllowlist","importSpecifier":"openclaw/plugin-sdk/allow-from","kind":"type","recordType":"export","sourceLine":19,"sourcePath":"src/channels/allowlist-match.ts"}
{"category":"channel","entrypoint":"channel-actions","importSpecifier":"openclaw/plugin-sdk/channel-actions","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/channel-actions.ts"}
{"declaration":"export function createMessageToolButtonsSchema(): TSchema;","entrypoint":"channel-actions","exportName":"createMessageToolButtonsSchema","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":11,"sourcePath":"src/plugin-sdk/channel-actions.ts"}
{"declaration":"export function createMessageToolCardSchema(): TSchema;","entrypoint":"channel-actions","exportName":"createMessageToolCardSchema","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":27,"sourcePath":"src/plugin-sdk/channel-actions.ts"}
{"declaration":"export function createMessageToolButtonsSchema(): TSchema;","entrypoint":"channel-actions","exportName":"createMessageToolButtonsSchema","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":12,"sourcePath":"src/plugin-sdk/channel-actions.ts"}
{"declaration":"export function createMessageToolCardSchema(): TSchema;","entrypoint":"channel-actions","exportName":"createMessageToolCardSchema","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":30,"sourcePath":"src/plugin-sdk/channel-actions.ts"}
{"declaration":"export function createUnionActionGate<TAccount, TKey extends string>(accounts: readonly TAccount[], createGate: (account: TAccount) => OptionalDefaultGate<TKey>): OptionalDefaultGate<TKey>;","entrypoint":"channel-actions","exportName":"createUnionActionGate","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/actions/shared.ts"}
{"declaration":"export function listTokenSourcedAccounts<TAccount extends TokenSourcedAccount>(accounts: readonly TAccount[]): TAccount[];","entrypoint":"channel-actions","exportName":"listTokenSourcedAccounts","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":7,"sourcePath":"src/channels/plugins/actions/shared.ts"}
{"declaration":"export function optionalStringEnum<T extends readonly string[]>(values: T, options?: StringEnumOptions<T>): TOptional<TUnsafe<T[number]>>;","entrypoint":"channel-actions","exportName":"optionalStringEnum","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":31,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function resolveReactionMessageId(params: { args: Record<string, unknown>; toolContext?: ReactionToolContext | undefined; }): string | number | undefined;","entrypoint":"channel-actions","exportName":"resolveReactionMessageId","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":7,"sourcePath":"src/channels/plugins/actions/reaction-message-id.ts"}
{"declaration":"export function stringEnum<T extends readonly string[]>(values: T, options?: StringEnumOptions<T>): TUnsafe<T[number]>;","entrypoint":"channel-actions","exportName":"stringEnum","importSpecifier":"openclaw/plugin-sdk/channel-actions","kind":"function","recordType":"export","sourceLine":15,"sourcePath":"src/agents/schema/typebox.ts"}
{"category":"channel","entrypoint":"channel-config-schema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/channel-config-schema.ts"}
{"declaration":"export function buildCatchallMultiAccountChannelSchema<T extends ExtendableZodObject>(accountSchema: T): T;","entrypoint":"channel-config-schema","exportName":"buildCatchallMultiAccountChannelSchema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema","kind":"function","recordType":"export","sourceLine":26,"sourcePath":"src/channels/plugins/config-schema.ts"}
{"declaration":"export function buildChannelConfigSchema(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): ChannelConfigSchema;","entrypoint":"channel-config-schema","exportName":"buildChannelConfigSchema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema","kind":"function","recordType":"export","sourceLine":35,"sourcePath":"src/channels/plugins/config-schema.ts"}
@@ -113,15 +114,15 @@
{"declaration":"export const MarkdownConfigSchema: z.ZodOptional<z.ZodObject<{ tables: z.ZodOptional<z.ZodEnum<{ off: \"off\"; bullets: \"bullets\"; code: \"code\"; }>>; }, z.core.$strict>>;","entrypoint":"channel-config-schema","exportName":"MarkdownConfigSchema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema","kind":"const","recordType":"export","sourceLine":371,"sourcePath":"src/config/zod-schema.core.ts"}
{"declaration":"export const ToolPolicySchema: z.ZodOptional<z.ZodObject<{ allow: z.ZodOptional<z.ZodArray<z.ZodString>>; alsoAllow: z.ZodOptional<z.ZodArray<z.ZodString>>; deny: z.ZodOptional<z.ZodArray<z.ZodString>>; }, z.core.$strict>>;","entrypoint":"channel-config-schema","exportName":"ToolPolicySchema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema","kind":"const","recordType":"export","sourceLine":253,"sourcePath":"src/config/zod-schema.agent-runtime.ts"}
{"category":"channel","entrypoint":"channel-contract","importSpecifier":"openclaw/plugin-sdk/channel-contract","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/channel-contract.ts"}
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-contract","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-contract","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":564,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-contract","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-contract","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":565,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"channel-contract","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAgentTool = ChannelAgentTool;","entrypoint":"channel-contract","exportName":"ChannelAgentTool","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":18,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelGroupContext = ChannelGroupContext;","entrypoint":"channel-contract","exportName":"ChannelGroupContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":216,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"channel-contract","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":516,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"channel-contract","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionDiscoveryContext = ChannelMessageActionDiscoveryContext;","entrypoint":"channel-contract","exportName":"ChannelMessageActionDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":31,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionName = \"send\" | \"broadcast\" | \"poll\" | \"poll-vote\" | \"react\" | \"reactions\" | \"read\" | \"edit\" | \"unsend\" | \"reply\" | \"sendWithEffect\" | \"renameGroup\" | \"setGroupIcon\" | \"addParticipant\" | \"removeParticipant\" | \"leaveGroup\" | \"sendAttachment\" | \"delete\" | \"pin\" | \"unpin\" | \"list-pins\" | \"permissions\" | \"thread-create\" | \"thread-list\" | \"thread-reply\" | \"search\" | \"sticker\" | \"sticker-search\" | \"member-info\" | \"role-info\" | \"emoji-list\" | \"emoji-upload\" | \"sticker-upload\" | \"role-add\" | \"role-remove\" | \"channel-info\" | \"channel-list\" | \"channel-create\" | \"channel-edit\" | \"channel-delete\" | \"channel-move\" | \"category-create\" | \"category-edit\" | \"category-delete\" | \"topic-create\" | \"topic-edit\" | \"voice-status\" | \"event-list\" | \"event-create\" | \"timeout\" | \"kick\" | \"ban\" | \"set-profile\" | \"set-presence\" | \"download-file\";","entrypoint":"channel-contract","exportName":"ChannelMessageActionName","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/channels/plugins/types.ts"}
{"declaration":"export type ChannelMessageActionName = \"send\" | \"broadcast\" | \"poll\" | \"poll-vote\" | \"react\" | \"reactions\" | \"read\" | \"edit\" | \"unsend\" | \"reply\" | \"sendWithEffect\" | \"renameGroup\" | \"setGroupIcon\" | \"addParticipant\" | \"removeParticipant\" | \"leaveGroup\" | \"sendAttachment\" | \"delete\" | \"pin\" | \"unpin\" | \"list-pins\" | \"permissions\" | \"thread-create\" | \"thread-list\" | \"thread-reply\" | \"search\" | \"sticker\" | \"sticker-search\" | \"member-info\" | \"role-info\" | \"emoji-list\" | \"emoji-upload\" | \"sticker-upload\" | \"role-add\" | \"role-remove\" | \"channel-info\" | \"channel-list\" | \"channel-create\" | \"channel-edit\" | \"channel-delete\" | \"channel-move\" | \"category-create\" | \"category-edit\" | \"category-delete\" | \"topic-create\" | \"topic-edit\" | \"voice-status\" | \"event-list\" | \"event-create\" | \"timeout\" | \"kick\" | \"ban\" | \"set-profile\" | \"set-presence\" | \"download-file\" | \"upload-file\";","entrypoint":"channel-contract","exportName":"ChannelMessageActionName","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/channels/plugins/types.ts"}
{"declaration":"export type ChannelMessageToolDiscovery = ChannelMessageToolDiscovery;","entrypoint":"channel-contract","exportName":"ChannelMessageToolDiscovery","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":56,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageToolSchemaContribution = ChannelMessageToolSchemaContribution;","entrypoint":"channel-contract","exportName":"ChannelMessageToolSchemaContribution","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":51,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelStatusIssue = ChannelStatusIssue;","entrypoint":"channel-contract","exportName":"ChannelStatusIssue","importSpecifier":"openclaw/plugin-sdk/channel-contract","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -163,72 +164,72 @@
{"declaration":"export function resolvePollMaxSelections(optionCount: number, allowMultiselect: boolean | undefined): number;","entrypoint":"channel-runtime","exportName":"resolvePollMaxSelections","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":29,"sourcePath":"src/polls.ts"}
{"declaration":"export function resolveWhatsAppHeartbeatRecipients(cfg: OpenClawConfig, opts?: HeartbeatRecipientsOpts): HeartbeatRecipientsResult;","entrypoint":"channel-runtime","exportName":"resolveWhatsAppHeartbeatRecipients","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":48,"sourcePath":"src/channels/plugins/whatsapp-heartbeat.ts"}
{"declaration":"export function waitUntilAbort(signal?: AbortSignal | undefined, onAbort?: (() => void | Promise<void>) | undefined): Promise<void>;","entrypoint":"channel-runtime","exportName":"waitUntilAbort","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/plugin-sdk/channel-lifecycle.ts"}
{"declaration":"export const CHANNEL_MESSAGE_ACTION_NAMES: readonly [\"send\", \"broadcast\", \"poll\", \"poll-vote\", \"react\", \"reactions\", \"read\", \"edit\", \"unsend\", \"reply\", \"sendWithEffect\", \"renameGroup\", \"setGroupIcon\", \"addParticipant\", \"removeParticipant\", \"leaveGroup\", \"sendAttachment\", \"delete\", \"pin\", \"unpin\", \"list-pins\", \"permissions\", \"thread-create\", \"thread-list\", \"thread-reply\", \"search\", \"sticker\", \"sticker-search\", \"member-info\", \"role-info\", \"emoji-list\", \"emoji-upload\", \"sticker-upload\", \"role-add\", \"role-remove\", \"channel-info\", \"channel-list\", \"channel-create\", \"channel-edit\", \"channel-delete\", \"channel-move\", \"category-create\", \"category-edit\", \"category-delete\", \"topic-create\", \"topic-edit\", \"voice-status\", \"event-list\", \"event-create\", \"timeout\", \"kick\", \"ban\", \"set-profile\", \"set-presence\", \"set-profile\", \"download-file\"];","entrypoint":"channel-runtime","exportName":"CHANNEL_MESSAGE_ACTION_NAMES","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/message-action-names.ts"}
{"declaration":"export const CHANNEL_MESSAGE_ACTION_NAMES: readonly [\"send\", \"broadcast\", \"poll\", \"poll-vote\", \"react\", \"reactions\", \"read\", \"edit\", \"unsend\", \"reply\", \"sendWithEffect\", \"renameGroup\", \"setGroupIcon\", \"addParticipant\", \"removeParticipant\", \"leaveGroup\", \"sendAttachment\", \"delete\", \"pin\", \"unpin\", \"list-pins\", \"permissions\", \"thread-create\", \"thread-list\", \"thread-reply\", \"search\", \"sticker\", \"sticker-search\", \"member-info\", \"role-info\", \"emoji-list\", \"emoji-upload\", \"sticker-upload\", \"role-add\", \"role-remove\", \"channel-info\", \"channel-list\", \"channel-create\", \"channel-edit\", \"channel-delete\", \"channel-move\", \"category-create\", \"category-edit\", \"category-delete\", \"topic-create\", \"topic-edit\", \"voice-status\", \"event-list\", \"event-create\", \"timeout\", \"kick\", \"ban\", \"set-profile\", \"set-presence\", \"set-profile\", \"download-file\", \"upload-file\"];","entrypoint":"channel-runtime","exportName":"CHANNEL_MESSAGE_ACTION_NAMES","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/message-action-names.ts"}
{"declaration":"export const CHANNEL_MESSAGE_CAPABILITIES: readonly [\"interactive\", \"buttons\", \"cards\", \"components\", \"blocks\"];","entrypoint":"channel-runtime","exportName":"CHANNEL_MESSAGE_CAPABILITIES","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/message-capabilities.ts"}
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-runtime","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-runtime","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":564,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-runtime","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-runtime","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":565,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"channel-runtime","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAccountState = ChannelAccountState;","entrypoint":"channel-runtime","exportName":"ChannelAccountState","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":108,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAgentPromptAdapter = ChannelAgentPromptAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAgentPromptAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":463,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAgentTool = ChannelAgentTool;","entrypoint":"channel-runtime","exportName":"ChannelAgentTool","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":18,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAgentToolFactory = ChannelAgentToolFactory;","entrypoint":"channel-runtime","exportName":"ChannelAgentToolFactory","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":23,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelAllowlistAdapter = ChannelAllowlistAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAllowlistAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":497,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelAuthAdapter = ChannelAuthAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAuthAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":362,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelAllowlistAdapter = ChannelAllowlistAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAllowlistAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":498,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelAuthAdapter = ChannelAuthAdapter;","entrypoint":"channel-runtime","exportName":"ChannelAuthAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":363,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCapabilities = ChannelCapabilities;","entrypoint":"channel-runtime","exportName":"ChannelCapabilities","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":230,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelCapabilitiesDiagnostics = ChannelCapabilitiesDiagnostics;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDiagnostics","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCapabilitiesDisplayLine = ChannelCapabilitiesDisplayLine;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDisplayLine","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":42,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCapabilitiesDisplayTone = ChannelCapabilitiesDisplayTone;","entrypoint":"channel-runtime","exportName":"ChannelCapabilitiesDisplayTone","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":40,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandAdapter = ChannelCommandAdapter;","entrypoint":"channel-runtime","exportName":"ChannelCommandAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":444,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelCommandAdapter = ChannelCommandAdapter;","entrypoint":"channel-runtime","exportName":"ChannelCommandAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":445,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfigAdapter = ChannelConfigAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelConfigAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":91,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":553,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":562,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":406,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingConversationRef = ChannelConfiguredBindingConversationRef;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingConversationRef","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":554,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingMatch = ChannelConfiguredBindingMatch;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingMatch","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelConfiguredBindingProvider = ChannelConfiguredBindingProvider;","entrypoint":"channel-runtime","exportName":"ChannelConfiguredBindingProvider","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":563,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryAdapter = ChannelDirectoryAdapter;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":407,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelDirectoryEntry = ChannelDirectoryEntry;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryEntry","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":469,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelDirectoryEntryKind = ChannelDirectoryEntryKind;","entrypoint":"channel-runtime","exportName":"ChannelDirectoryEntryKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":467,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelElevatedAdapter = ChannelElevatedAdapter;","entrypoint":"channel-runtime","exportName":"ChannelElevatedAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":437,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelExecApprovalAdapter = ChannelExecApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":463,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelElevatedAdapter = ChannelElevatedAdapter;","entrypoint":"channel-runtime","exportName":"ChannelElevatedAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":438,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelExecApprovalAdapter = ChannelExecApprovalAdapter;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":464,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelExecApprovalForwardTarget = ChannelExecApprovalForwardTarget;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalForwardTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelExecApprovalInitiatingSurfaceState = ChannelExecApprovalInitiatingSurfaceState;","entrypoint":"channel-runtime","exportName":"ChannelExecApprovalInitiatingSurfaceState","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":27,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":346,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayAdapter = ChannelGatewayAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":347,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGroupAdapter = ChannelGroupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelGroupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGroupContext = ChannelGroupContext;","entrypoint":"channel-runtime","exportName":"ChannelGroupContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":216,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelHeartbeatAdapter = ChannelHeartbeatAdapter;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":372,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelHeartbeatAdapter = ChannelHeartbeatAdapter;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":373,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelHeartbeatDeps = ChannelHeartbeatDeps;","entrypoint":"channel-runtime","exportName":"ChannelHeartbeatDeps","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":116,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelId = ChannelId;","entrypoint":"channel-runtime","exportName":"ChannelId","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelLifecycleAdapter = ChannelLifecycleAdapter;","entrypoint":"channel-runtime","exportName":"ChannelLifecycleAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":449,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrStartResult = ChannelLoginWithQrStartResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrStartResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":317,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrWaitResult = ChannelLoginWithQrWaitResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrWaitResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":322,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":327,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutResult = ChannelLogoutResult;","entrypoint":"channel-runtime","exportName":"ChannelLogoutResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":311,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLifecycleAdapter = ChannelLifecycleAdapter;","entrypoint":"channel-runtime","exportName":"ChannelLifecycleAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":450,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrStartResult = ChannelLoginWithQrStartResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrStartResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":318,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLoginWithQrWaitResult = ChannelLoginWithQrWaitResult;","entrypoint":"channel-runtime","exportName":"ChannelLoginWithQrWaitResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":323,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutContext = ChannelLogoutContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelLogoutContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":328,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogoutResult = ChannelLogoutResult;","entrypoint":"channel-runtime","exportName":"ChannelLogoutResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":312,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelLogSink = ChannelLogSink;","entrypoint":"channel-runtime","exportName":"ChannelLogSink","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":209,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMentionAdapter = ChannelMentionAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMentionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":260,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionAdapter = ChannelMessageActionAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMessageActionAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":516,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"channel-runtime","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionDiscoveryContext = ChannelMessageActionDiscoveryContext;","entrypoint":"channel-runtime","exportName":"ChannelMessageActionDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":31,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageActionName = \"send\" | \"broadcast\" | \"poll\" | \"poll-vote\" | \"react\" | \"reactions\" | \"read\" | \"edit\" | \"unsend\" | \"reply\" | \"sendWithEffect\" | \"renameGroup\" | \"setGroupIcon\" | \"addParticipant\" | \"removeParticipant\" | \"leaveGroup\" | \"sendAttachment\" | \"delete\" | \"pin\" | \"unpin\" | \"list-pins\" | \"permissions\" | \"thread-create\" | \"thread-list\" | \"thread-reply\" | \"search\" | \"sticker\" | \"sticker-search\" | \"member-info\" | \"role-info\" | \"emoji-list\" | \"emoji-upload\" | \"sticker-upload\" | \"role-add\" | \"role-remove\" | \"channel-info\" | \"channel-list\" | \"channel-create\" | \"channel-edit\" | \"channel-delete\" | \"channel-move\" | \"category-create\" | \"category-edit\" | \"category-delete\" | \"topic-create\" | \"topic-edit\" | \"voice-status\" | \"event-list\" | \"event-create\" | \"timeout\" | \"kick\" | \"ban\" | \"set-profile\" | \"set-presence\" | \"download-file\";","entrypoint":"channel-runtime","exportName":"ChannelMessageActionName","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/channels/plugins/types.ts"}
{"declaration":"export type ChannelMessageActionName = \"send\" | \"broadcast\" | \"poll\" | \"poll-vote\" | \"react\" | \"reactions\" | \"read\" | \"edit\" | \"unsend\" | \"reply\" | \"sendWithEffect\" | \"renameGroup\" | \"setGroupIcon\" | \"addParticipant\" | \"removeParticipant\" | \"leaveGroup\" | \"sendAttachment\" | \"delete\" | \"pin\" | \"unpin\" | \"list-pins\" | \"permissions\" | \"thread-create\" | \"thread-list\" | \"thread-reply\" | \"search\" | \"sticker\" | \"sticker-search\" | \"member-info\" | \"role-info\" | \"emoji-list\" | \"emoji-upload\" | \"sticker-upload\" | \"role-add\" | \"role-remove\" | \"channel-info\" | \"channel-list\" | \"channel-create\" | \"channel-edit\" | \"channel-delete\" | \"channel-move\" | \"category-create\" | \"category-edit\" | \"category-delete\" | \"topic-create\" | \"topic-edit\" | \"voice-status\" | \"event-list\" | \"event-create\" | \"timeout\" | \"kick\" | \"ban\" | \"set-profile\" | \"set-presence\" | \"download-file\" | \"upload-file\";","entrypoint":"channel-runtime","exportName":"ChannelMessageActionName","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/channels/plugins/types.ts"}
{"declaration":"export type ChannelMessageCapability = \"interactive\" | \"buttons\" | \"cards\" | \"components\" | \"blocks\";","entrypoint":"channel-runtime","exportName":"ChannelMessageCapability","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/channels/plugins/message-capabilities.ts"}
{"declaration":"export type ChannelMessageToolDiscovery = ChannelMessageToolDiscovery;","entrypoint":"channel-runtime","exportName":"ChannelMessageToolDiscovery","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":56,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessageToolSchemaContribution = ChannelMessageToolSchemaContribution;","entrypoint":"channel-runtime","exportName":"ChannelMessageToolSchemaContribution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":51,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessagingAdapter = ChannelMessagingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelMessagingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":395,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMeta = ChannelMeta;","entrypoint":"channel-runtime","exportName":"ChannelMeta","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundAdapter = ChannelOutboundAdapter;","entrypoint":"channel-runtime","exportName":"ChannelOutboundAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":154,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundAdapter = ChannelOutboundAdapter;","entrypoint":"channel-runtime","exportName":"ChannelOutboundAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":155,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundContext = ChannelOutboundContext;","entrypoint":"channel-runtime","exportName":"ChannelOutboundContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":128,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelOutboundTargetMode = ChannelOutboundTargetMode;","entrypoint":"channel-runtime","exportName":"ChannelOutboundTargetMode","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":15,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelPairingAdapter = ChannelPairingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelPairingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":335,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelPairingAdapter = ChannelPairingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelPairingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":336,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":55,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type ChannelPollContext = ChannelPollContext;","entrypoint":"channel-runtime","exportName":"ChannelPollContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":547,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelPollResult = ChannelPollResult;","entrypoint":"channel-runtime","exportName":"ChannelPollResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":538,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelResolveKind = ChannelResolveKind;","entrypoint":"channel-runtime","exportName":"ChannelResolveKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":417,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolverAdapter = ChannelResolverAdapter;","entrypoint":"channel-runtime","exportName":"ChannelResolverAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":427,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolveResult = ChannelResolveResult;","entrypoint":"channel-runtime","exportName":"ChannelResolveResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":419,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":575,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolveKind = ChannelResolveKind;","entrypoint":"channel-runtime","exportName":"ChannelResolveKind","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":418,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolverAdapter = ChannelResolverAdapter;","entrypoint":"channel-runtime","exportName":"ChannelResolverAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":428,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelResolveResult = ChannelResolveResult;","entrypoint":"channel-runtime","exportName":"ChannelResolveResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":420,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSecurityAdapter = ChannelSecurityAdapter<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":576,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSecurityContext = ChannelSecurityContext<ResolvedAccount>;","entrypoint":"channel-runtime","exportName":"ChannelSecurityContext","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":254,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelSecurityDmPolicy = ChannelSecurityDmPolicy;","entrypoint":"channel-runtime","exportName":"ChannelSecurityDmPolicy","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":245,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelSetupAdapter = ChannelSetupAdapter;","entrypoint":"channel-runtime","exportName":"ChannelSetupAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":56,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelSetupInput = ChannelSetupInput;","entrypoint":"channel-runtime","exportName":"ChannelSetupInput","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":63,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":184,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelStatusAdapter = ChannelStatusAdapter<ResolvedAccount, Probe, Audit>;","entrypoint":"channel-runtime","exportName":"ChannelStatusAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":185,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelStatusIssue = ChannelStatusIssue;","entrypoint":"channel-runtime","exportName":"ChannelStatusIssue","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelStreamingAdapter = ChannelStreamingAdapter;","entrypoint":"channel-runtime","exportName":"ChannelStreamingAdapter","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":279,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelStructuredComponents = ChannelStructuredComponents;","entrypoint":"channel-runtime","exportName":"ChannelStructuredComponents","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":288,"sourcePath":"src/channels/plugins/types.core.ts"}
@@ -258,12 +259,12 @@
{"declaration":"export type ChannelSetupWizard = ChannelSetupWizard;","entrypoint":"channel-setup","exportName":"ChannelSetupWizard","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"type","recordType":"export","sourceLine":247,"sourcePath":"src/channels/plugins/setup-wizard.ts"}
{"declaration":"export type OptionalChannelSetupSurface = OptionalChannelSetupSurface;","entrypoint":"channel-setup","exportName":"OptionalChannelSetupSurface","importSpecifier":"openclaw/plugin-sdk/channel-setup","kind":"type","recordType":"export","sourceLine":29,"sourcePath":"src/plugin-sdk/channel-setup.ts"}
{"category":"channel","entrypoint":"command-auth","importSpecifier":"openclaw/plugin-sdk/command-auth","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/command-auth.ts"}
{"declaration":"export function buildCommandsMessage(cfg?: OpenClawConfig | undefined, skillCommands?: SkillCommandSpec[] | undefined, options?: CommandsMessageOptions | undefined): string;","entrypoint":"command-auth","exportName":"buildCommandsMessage","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":847,"sourcePath":"src/auto-reply/status.ts"}
{"declaration":"export function buildCommandsMessagePaginated(cfg?: OpenClawConfig | undefined, skillCommands?: SkillCommandSpec[] | undefined, options?: CommandsMessageOptions | undefined): CommandsMessageResult;","entrypoint":"command-auth","exportName":"buildCommandsMessagePaginated","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":856,"sourcePath":"src/auto-reply/status.ts"}
{"declaration":"export function buildCommandsPaginationKeyboard(currentPage: number, totalPages: number, agentId?: string | undefined): { text: string; callback_data: string; }[][];","entrypoint":"command-auth","exportName":"buildCommandsPaginationKeyboard","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":89,"sourcePath":"src/auto-reply/reply/commands-info.ts"}
{"declaration":"export function buildCommandsMessage(cfg?: OpenClawConfig | undefined, skillCommands?: SkillCommandSpec[] | undefined, options?: CommandsMessageOptions | undefined): string;","entrypoint":"command-auth","exportName":"buildCommandsMessage","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":1049,"sourcePath":"src/auto-reply/status.ts"}
{"declaration":"export function buildCommandsMessagePaginated(cfg?: OpenClawConfig | undefined, skillCommands?: SkillCommandSpec[] | undefined, options?: CommandsMessageOptions | undefined): CommandsMessageResult;","entrypoint":"command-auth","exportName":"buildCommandsMessagePaginated","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":1058,"sourcePath":"src/auto-reply/status.ts"}
{"declaration":"export function buildCommandsPaginationKeyboard(currentPage: number, totalPages: number, agentId?: string | undefined): { text: string; callback_data: string; }[][];","entrypoint":"command-auth","exportName":"buildCommandsPaginationKeyboard","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":175,"sourcePath":"src/auto-reply/reply/commands-info.ts"}
{"declaration":"export function buildCommandText(commandName: string, args?: string | undefined): string;","entrypoint":"command-auth","exportName":"buildCommandText","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":199,"sourcePath":"src/auto-reply/commands-registry.ts"}
{"declaration":"export function buildCommandTextFromArgs(command: ChatCommandDefinition, args?: CommandArgs | undefined): string;","entrypoint":"command-auth","exportName":"buildCommandTextFromArgs","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":291,"sourcePath":"src/auto-reply/commands-registry.ts"}
{"declaration":"export function buildHelpMessage(cfg?: OpenClawConfig | undefined): string;","entrypoint":"command-auth","exportName":"buildHelpMessage","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":727,"sourcePath":"src/auto-reply/status.ts"}
{"declaration":"export function buildHelpMessage(cfg?: OpenClawConfig | undefined): string;","entrypoint":"command-auth","exportName":"buildHelpMessage","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":844,"sourcePath":"src/auto-reply/status.ts"}
{"declaration":"export function buildModelsProviderData(cfg: OpenClawConfig, agentId?: string | undefined): Promise<ModelsProviderData>;","entrypoint":"command-auth","exportName":"buildModelsProviderData","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":37,"sourcePath":"src/auto-reply/reply/commands-models.ts"}
{"declaration":"export function createPreCryptoDirectDmAuthorizer(params: { resolveAccess: (senderId: string) => Promise<ResolvedInboundDirectDmAccess | Pick<ResolvedInboundDirectDmAccess, \"access\">>; issuePairingChallenge?: ((params: { ...; }) => Promise<...>) | undefined; onBlocked?: ((params: { ...; }) => void) | undefined; }): (input: { ...; }) => Promise<...>;","entrypoint":"command-auth","exportName":"createPreCryptoDirectDmAuthorizer","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":105,"sourcePath":"src/plugin-sdk/direct-dm.ts"}
{"declaration":"export function findCommandByNativeName(name: string, provider?: string | undefined): ChatCommandDefinition | undefined;","entrypoint":"command-auth","exportName":"findCommandByNativeName","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":187,"sourcePath":"src/auto-reply/commands-registry.ts"}
@@ -287,7 +288,7 @@
{"declaration":"export function parseCommandArgs(command: ChatCommandDefinition, raw?: string | undefined): CommandArgs | undefined;","entrypoint":"command-auth","exportName":"parseCommandArgs","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":254,"sourcePath":"src/auto-reply/commands-registry.ts"}
{"declaration":"export function resolveCommandArgChoices(params: { command: ChatCommandDefinition; arg: CommandArgDefinition; cfg?: OpenClawConfig | undefined; provider?: string | undefined; model?: string | undefined; }): ResolvedCommandArgChoice[];","entrypoint":"command-auth","exportName":"resolveCommandArgChoices","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":316,"sourcePath":"src/auto-reply/commands-registry.ts"}
{"declaration":"export function resolveCommandArgMenu(params: { command: ChatCommandDefinition; args?: CommandArgs | undefined; cfg?: OpenClawConfig | undefined; }): { arg: CommandArgDefinition; choices: ResolvedCommandArgChoice[]; title?: string | undefined; } | null;","entrypoint":"command-auth","exportName":"resolveCommandArgMenu","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":346,"sourcePath":"src/auto-reply/commands-registry.ts"}
{"declaration":"export function resolveCommandAuthorization(params: { ctx: MsgContext; cfg: OpenClawConfig; commandAuthorized: boolean; }): CommandAuthorization;","entrypoint":"command-auth","exportName":"resolveCommandAuthorization","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":303,"sourcePath":"src/auto-reply/command-auth.ts"}
{"declaration":"export function resolveCommandAuthorization(params: { ctx: MsgContext; cfg: OpenClawConfig; commandAuthorized: boolean; }): CommandAuthorization;","entrypoint":"command-auth","exportName":"resolveCommandAuthorization","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":461,"sourcePath":"src/auto-reply/command-auth.ts"}
{"declaration":"export function resolveCommandAuthorizedFromAuthorizers(params: { useAccessGroups: boolean; authorizers: CommandAuthorizer[]; modeWhenAccessGroupsOff?: CommandGatingModeWhenAccessGroupsOff | undefined; }): boolean;","entrypoint":"command-auth","exportName":"resolveCommandAuthorizedFromAuthorizers","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":8,"sourcePath":"src/channels/command-gating.ts"}
{"declaration":"export function resolveControlCommandGate(params: { useAccessGroups: boolean; authorizers: CommandAuthorizer[]; allowTextCommands: boolean; hasControlCommand: boolean; modeWhenAccessGroupsOff?: CommandGatingModeWhenAccessGroupsOff | undefined; }): { ...; };","entrypoint":"command-auth","exportName":"resolveControlCommandGate","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":31,"sourcePath":"src/channels/command-gating.ts"}
{"declaration":"export function resolveDirectDmAuthorizationOutcome(params: { isGroup: boolean; dmPolicy: string; senderAllowedForCommands: boolean; }): \"disabled\" | \"unauthorized\" | \"allowed\";","entrypoint":"command-auth","exportName":"resolveDirectDmAuthorizationOutcome","importSpecifier":"openclaw/plugin-sdk/command-auth","kind":"function","recordType":"export","sourceLine":120,"sourcePath":"src/plugin-sdk/command-auth.ts"}
@@ -330,37 +331,38 @@
{"declaration":"export function applyAccountNameToChannelSection(params: { cfg: OpenClawConfig; channelKey: string; accountId: string; name?: string | undefined; alwaysUseAccounts?: boolean | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"applyAccountNameToChannelSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":33,"sourcePath":"src/channels/plugins/setup-helpers.ts"}
{"declaration":"export function buildAgentSessionKey(params: { agentId: string; channel: string; accountId?: string | null | undefined; peer?: RoutePeer | null | undefined; dmScope?: \"main\" | \"per-peer\" | \"per-channel-peer\" | \"per-account-channel-peer\" | undefined; identityLinks?: Record<...> | undefined; }): string;","entrypoint":"core","exportName":"buildAgentSessionKey","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":91,"sourcePath":"src/routing/resolve-route.ts"}
{"declaration":"export function buildChannelConfigSchema(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): ChannelConfigSchema;","entrypoint":"core","exportName":"buildChannelConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":35,"sourcePath":"src/channels/plugins/config-schema.ts"}
{"declaration":"export function buildChannelOutboundSessionRoute(params: { cfg: OpenClawConfig; agentId: string; channel: string; accountId?: string | null | undefined; peer: { kind: \"direct\" | \"group\" | \"channel\"; id: string; }; chatType: \"direct\" | \"group\" | \"channel\"; from: string; to: string; threadId?: string | ... 1 more ... | undefined; }): ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"buildChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":162,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function channelTargetSchema(options?: { description?: string | undefined; } | undefined): TString;","entrypoint":"core","exportName":"channelTargetSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":33,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function channelTargetsSchema(options?: { description?: string | undefined; } | undefined): TArray<TString>;","entrypoint":"core","exportName":"channelTargetsSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":39,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function buildChannelOutboundSessionRoute(params: { cfg: OpenClawConfig; agentId: string; channel: string; accountId?: string | null | undefined; peer: { kind: \"direct\" | \"group\" | \"channel\"; id: string; }; chatType: \"direct\" | \"group\" | \"channel\"; from: string; to: string; threadId?: string | ... 1 more ... | undefined; }): ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"buildChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":161,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function channelTargetSchema(options?: { description?: string | undefined; } | undefined): TString;","entrypoint":"core","exportName":"channelTargetSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function channelTargetsSchema(options?: { description?: string | undefined; } | undefined): TArray<TString>;","entrypoint":"core","exportName":"channelTargetsSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":44,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function clearAccountEntryFields<TAccountEntry extends object>(params: { accounts?: Record<string, TAccountEntry> | undefined; accountId: string; fields: string[]; isValueSet?: ((value: unknown) => boolean) | undefined; markClearedOnFieldPresence?: boolean | undefined; }): { ...; };","entrypoint":"core","exportName":"clearAccountEntryFields","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function createChannelPluginBase<TResolvedAccount>(params: CreateChannelPluginBaseOptions<TResolvedAccount>): CreatedChannelPluginBase<TResolvedAccount>;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":436,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChatChannelPlugin<TResolvedAccount extends { accountId?: string | null; }, Probe = unknown, Audit = unknown>(params: { base: ChatChannelPluginBase<TResolvedAccount, Probe, Audit>; security?: ChannelSecurityAdapter<TResolvedAccount> | ChatChannelSecurityOptions<...> | undefined; pairing?: ChannelPairingAdapter | ... 1 more ... | undefined; threading?: ChannelThreadingAdapter | ... 1 more ... | undefined; outbound?: ChannelOutboundAdapter | ... 1 more ... | undefined; }): ChannelPlugin<...>;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":413,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":245,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":88,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":276,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChannelPluginBase<TResolvedAccount>(params: CreateChannelPluginBaseOptions<TResolvedAccount>): CreatedChannelPluginBase<TResolvedAccount>;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":435,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChatChannelPlugin<TResolvedAccount extends { accountId?: string | null; }, Probe = unknown, Audit = unknown>(params: { base: ChatChannelPluginBase<TResolvedAccount, Probe, Audit>; security?: ChannelSecurityAdapter<TResolvedAccount> | ChatChannelSecurityOptions<...> | undefined; pairing?: ChannelPairingAdapter | ... 1 more ... | undefined; threading?: ChannelThreadingAdapter | ... 1 more ... | undefined; outbound?: ChannelOutboundAdapter | ... 1 more ... | undefined; }): ChannelPlugin<...>;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":412,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":244,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":90,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":275,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function delegateCompactionToRuntime(params: { sessionId: string; sessionKey?: string | undefined; sessionFile: string; tokenBudget?: number | undefined; force?: boolean | undefined; currentTokenCount?: number | undefined; compactionTarget?: \"budget\" | ... 1 more ... | undefined; customInstructions?: string | undefined; runtimeContext?: ContextEngineRuntimeContext | undefined; }): Promise<...>;","entrypoint":"core","exportName":"delegateCompactionToRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/context-engine/delegate.ts"}
{"declaration":"export function deleteAccountFromConfigSection(params: { cfg: OpenClawConfig; sectionKey: string; accountId: string; clearBaseFields?: string[] | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"deleteAccountFromConfigSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":60,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":13,"sourcePath":"src/plugins/config-schema.ts"}
{"declaration":"export function enqueueKeyedTask<T>(params: { tails: Map<string, Promise<void>>; key: string; task: () => Promise<T>; hooks?: KeyedAsyncQueueHooks | undefined; }): Promise<...>;","entrypoint":"core","exportName":"enqueueKeyedTask","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":7,"sourcePath":"src/plugin-sdk/keyed-async-queue.ts"}
{"declaration":"export function formatPairingApproveHint(channelId: string): string;","entrypoint":"core","exportName":"formatPairingApproveHint","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":17,"sourcePath":"src/channels/plugins/helpers.ts"}
{"declaration":"export function getChatChannelMeta(id: \"telegram\" | \"whatsapp\" | \"discord\" | \"irc\" | \"googlechat\" | \"slack\" | \"signal\" | \"imessage\" | \"line\"): ChannelMeta;","entrypoint":"core","exportName":"getChatChannelMeta","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":158,"sourcePath":"src/channels/registry.ts"}
{"declaration":"export function getChatChannelMeta(id: \"telegram\" | \"whatsapp\" | \"discord\" | \"irc\" | \"googlechat\" | \"slack\" | \"signal\" | \"imessage\" | \"line\"): ChannelMeta;","entrypoint":"core","exportName":"getChatChannelMeta","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":155,"sourcePath":"src/channels/registry.ts"}
{"declaration":"export function isSecretRef(value: unknown): value is SecretRef;","entrypoint":"core","exportName":"isSecretRef","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":34,"sourcePath":"src/config/types.secrets.ts"}
{"declaration":"export function loadSecretFileSync(filePath: string, label: string, options?: SecretFileReadOptions): SecretFileReadResult;","entrypoint":"core","exportName":"loadSecretFileSync","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":29,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export function migrateBaseNameToDefaultAccount(params: { cfg: OpenClawConfig; channelKey: string; alwaysUseAccounts?: boolean | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"migrateBaseNameToDefaultAccount","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":92,"sourcePath":"src/channels/plugins/setup-helpers.ts"}
{"declaration":"export function normalizeAccountId(value: string | null | undefined): string;","entrypoint":"core","exportName":"normalizeAccountId","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":34,"sourcePath":"src/routing/account-id.ts"}
{"declaration":"export function normalizeAtHashSlug(raw?: string | null | undefined): string;","entrypoint":"core","exportName":"normalizeAtHashSlug","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":19,"sourcePath":"src/shared/string-normalization.ts"}
{"declaration":"export function normalizeHyphenSlug(raw?: string | null | undefined): string;","entrypoint":"core","exportName":"normalizeHyphenSlug","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":9,"sourcePath":"src/shared/string-normalization.ts"}
{"declaration":"export function optionalStringEnum<T extends readonly string[]>(values: T, options?: StringEnumOptions<T>): TOptional<TUnsafe<T[number]>>;","entrypoint":"core","exportName":"optionalStringEnum","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":26,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function optionalStringEnum<T extends readonly string[]>(values: T, options?: StringEnumOptions<T>): TOptional<TUnsafe<T[number]>>;","entrypoint":"core","exportName":"optionalStringEnum","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":31,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function parseOptionalDelimitedEntries(value?: string | undefined): string[] | undefined;","entrypoint":"core","exportName":"parseOptionalDelimitedEntries","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":23,"sourcePath":"src/channels/plugins/helpers.ts"}
{"declaration":"export function readSecretFileSync(filePath: string, label: string, options?: SecretFileReadOptions): string;","entrypoint":"core","exportName":"readSecretFileSync","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":118,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export function resolveGatewayBindUrl(params: { bind?: string | undefined; customBindHost?: string | undefined; scheme: \"ws\" | \"wss\"; port: number; pickTailnetHost: () => string | null; pickLanHost: () => string | null; }): GatewayBindUrlResult;","entrypoint":"core","exportName":"resolveGatewayBindUrl","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":11,"sourcePath":"src/shared/gateway-bind-url.ts"}
{"declaration":"export function resolveGatewayPort(cfg?: OpenClawConfig | undefined, env?: ProcessEnv): number;","entrypoint":"core","exportName":"resolveGatewayPort","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":285,"sourcePath":"src/config/paths.ts"}
{"declaration":"export function resolveTailnetHostWithRunner(runCommandWithTimeout?: TailscaleStatusCommandRunner | undefined): Promise<string | null>;","entrypoint":"core","exportName":"resolveTailnetHostWithRunner","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":43,"sourcePath":"src/shared/tailscale-status.ts"}
{"declaration":"export function resolveThreadSessionKeys(params: { baseSessionKey: string; threadId?: string | null | undefined; parentSessionKey?: string | undefined; useSuffix?: boolean | undefined; normalizeThreadId?: ((threadId: string) => string) | undefined; }): { ...; };","entrypoint":"core","exportName":"resolveThreadSessionKeys","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":234,"sourcePath":"src/routing/session-key.ts"}
{"declaration":"export function setAccountEnabledInConfigSection(params: { cfg: OpenClawConfig; sectionKey: string; accountId: string; enabled: boolean; allowTopLevel?: boolean | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"setAccountEnabledInConfigSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function stringEnum<T extends readonly string[]>(values: T, options?: StringEnumOptions<T>): TUnsafe<T[number]>;","entrypoint":"core","exportName":"stringEnum","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":15,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function stripChannelTargetPrefix(raw: string, ...providers: string[]): string;","entrypoint":"core","exportName":"stripChannelTargetPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":142,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripTargetKindPrefix(raw: string): string;","entrypoint":"core","exportName":"stripTargetKindPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":154,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripChannelTargetPrefix(raw: string, ...providers: string[]): string;","entrypoint":"core","exportName":"stripChannelTargetPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":141,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripTargetKindPrefix(raw: string): string;","entrypoint":"core","exportName":"stripTargetKindPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":153,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function tryReadSecretFileSync(filePath: string | undefined, label: string, options?: SecretFileReadOptions): string | undefined;","entrypoint":"core","exportName":"tryReadSecretFileSync","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":130,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export const DEFAULT_ACCOUNT_ID: \"default\";","entrypoint":"core","exportName":"DEFAULT_ACCOUNT_ID","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export","sourceLine":3,"sourcePath":"src/routing/account-id.ts"}
{"declaration":"export const DEFAULT_SECRET_FILE_MAX_BYTES: number;","entrypoint":"core","exportName":"DEFAULT_SECRET_FILE_MAX_BYTES","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export","sourceLine":5,"sourcePath":"src/infra/secret-file.ts"}
@@ -368,105 +370,107 @@
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"core","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessagingAdapter = ChannelMessagingAdapter;","entrypoint":"core","exportName":"ChannelMessagingAdapter","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":395,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundSessionRoute = ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"ChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":309,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"src/channels/plugins/types.core\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":137,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"src/channels/plugins/types.core\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":136,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"core","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":55,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type GatewayBindUrlResult = GatewayBindUrlResult;","entrypoint":"core","exportName":"GatewayBindUrlResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/shared/gateway-bind-url.ts"}
{"declaration":"export type GatewayRequestHandlerOptions = GatewayRequestHandlerOptions;","entrypoint":"core","exportName":"GatewayRequestHandlerOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":112,"sourcePath":"src/gateway/server-methods/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"core","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":950,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"core","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":969,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"core","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1313,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"core","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1067,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":88,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1295,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"core","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1284,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"core","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1276,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"core","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":103,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"core","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":120,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"core","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":965,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"core","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1096,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"core","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":59,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1390,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"core","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1108,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":95,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1372,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"core","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1339,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"core","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1331,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"core","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":110,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"core","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":131,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"core","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":984,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"core","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1145,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"core","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":66,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"core","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"core","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":571,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"core","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":155,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"core","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":446,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"core","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":233,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"core","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":217,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"core","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":140,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"core","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":499,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":515,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":524,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"core","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":487,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":254,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"core","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":277,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":548,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":587,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"core","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":427,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"core","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"core","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":338,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"core","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":374,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":329,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"core","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":460,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"core","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":353,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"core","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":414,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":312,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"core","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":395,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"core","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":295,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":536,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"core","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":582,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"core","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":166,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"core","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":457,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"core","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":244,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"core","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":228,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"core","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":151,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"core","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":510,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":526,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":535,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"core","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":498,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":265,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"core","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":288,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":598,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"core","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":438,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"core","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":569,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"core","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":349,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"core","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":385,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":340,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"core","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":471,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"core","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":364,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"core","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":425,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":323,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"core","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":406,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"core","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":306,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":547,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderUsageSnapshot = ProviderUsageSnapshot;","entrypoint":"core","exportName":"ProviderUsageSnapshot","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/infra/provider-usage.types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"core","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":477,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"core","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":488,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type RoutePeer = RoutePeer;","entrypoint":"core","exportName":"RoutePeer","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":21,"sourcePath":"src/routing/resolve-route.ts"}
{"declaration":"export type RoutePeerKind = ChatType;","entrypoint":"core","exportName":"RoutePeerKind","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":19,"sourcePath":"src/routing/resolve-route.ts"}
{"declaration":"export type SecretFileReadOptions = SecretFileReadOptions;","entrypoint":"core","exportName":"SecretFileReadOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export type SecretFileReadResult = SecretFileReadResult;","entrypoint":"core","exportName":"SecretFileReadResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"core","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":932,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"core","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":944,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type TailscaleStatusCommandResult = TailscaleStatusCommandResult;","entrypoint":"core","exportName":"TailscaleStatusCommandResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/shared/tailscale-status.ts"}
{"declaration":"export type TailscaleStatusCommandRunner = TailscaleStatusCommandRunner;","entrypoint":"core","exportName":"TailscaleStatusCommandRunner","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":6,"sourcePath":"src/shared/tailscale-status.ts"}
{"declaration":"export type UsageProviderId = UsageProviderId;","entrypoint":"core","exportName":"UsageProviderId","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":20,"sourcePath":"src/infra/provider-usage.types.ts"}
{"declaration":"export type UsageWindow = UsageWindow;","entrypoint":"core","exportName":"UsageWindow","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/infra/provider-usage.types.ts"}
{"declaration":"export class KeyedAsyncQueue","entrypoint":"core","exportName":"KeyedAsyncQueue","importSpecifier":"openclaw/plugin-sdk/core","kind":"class","recordType":"export","sourceLine":34,"sourcePath":"src/plugin-sdk/keyed-async-queue.ts"}
{"category":"core","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"plugin-entry","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":88,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"plugin-entry","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":90,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":13,"sourcePath":"src/plugins/config-schema.ts"}
{"declaration":"export type AnyAgentTool = AnyAgentTool;","entrypoint":"plugin-entry","exportName":"AnyAgentTool","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/agents/tools/common.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"plugin-entry","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":950,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"plugin-entry","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":969,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"plugin-entry","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1313,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1067,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":88,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1295,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"plugin-entry","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1284,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1276,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"plugin-entry","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":965,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"plugin-entry","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1096,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"plugin-entry","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":59,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":571,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":155,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":446,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":233,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":217,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"plugin-entry","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":140,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":499,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":515,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":524,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"plugin-entry","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":487,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":254,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"plugin-entry","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":277,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":548,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":587,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"plugin-entry","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":427,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":558,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":338,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"plugin-entry","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":374,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":329,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":460,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":353,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"plugin-entry","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":414,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":312,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":395,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"plugin-entry","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":295,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":536,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"plugin-entry","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":477,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"plugin-entry","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":932,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1390,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1108,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":95,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1372,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"plugin-entry","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1339,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1331,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":110,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":131,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"plugin-entry","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":984,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"plugin-entry","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1145,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"plugin-entry","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":66,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":582,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":166,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":457,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":244,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":228,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"plugin-entry","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":151,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":510,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":526,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":535,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"plugin-entry","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":498,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":265,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"plugin-entry","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":288,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":559,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":598,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"plugin-entry","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":438,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":569,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":349,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"plugin-entry","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":385,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":340,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":471,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":364,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"plugin-entry","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":425,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":323,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":406,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"plugin-entry","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":306,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":547,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"plugin-entry","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":488,"sourcePath":"src/plugins/types.ts"}
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"plugin-entry","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":944,"sourcePath":"src/plugins/types.ts"}
{"category":"provider","entrypoint":"provider-onboard","importSpecifier":"openclaw/plugin-sdk/provider-onboard","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/provider-onboard.ts"}
{"declaration":"export function applyAgentDefaultModelPrimary(cfg: OpenClawConfig, primary: string): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyAgentDefaultModelPrimary","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":76,"sourcePath":"src/plugins/provider-onboarding-config.ts"}
{"declaration":"export function applyCloudflareAiGatewayConfig(cfg: OpenClawConfig, params?: { accountId?: string | undefined; gatewayId?: string | undefined; } | undefined): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyCloudflareAiGatewayConfig","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":85,"sourcePath":"extensions/cloudflare-ai-gateway/onboard.ts"}
@@ -536,7 +540,7 @@
{"declaration":"export function removeAckReactionAfterReply(params: { removeAfterReply: boolean; ackReactionPromise: Promise<boolean> | null; ackReactionValue: string | null; remove: () => Promise<void>; onError?: ((err: unknown) => void) | undefined; }): void;","entrypoint":"testing","exportName":"removeAckReactionAfterReply","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":81,"sourcePath":"src/channels/ack-reactions.ts"}
{"declaration":"export function shouldAckReaction(params: AckReactionGateParams): boolean;","entrypoint":"testing","exportName":"shouldAckReaction","importSpecifier":"openclaw/plugin-sdk/testing","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/channels/ack-reactions.ts"}
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"testing","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":144,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type ChannelGatewayContext = ChannelGatewayContext<ResolvedAccount>;","entrypoint":"testing","exportName":"ChannelGatewayContext","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/channels/plugins/types.adapters.ts"}
{"declaration":"export type MockFn = MockFn<T>;","entrypoint":"testing","exportName":"MockFn","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":5,"sourcePath":"src/test-utils/vitest-mock-fn.ts"}
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"testing","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"testing","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/testing","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
@@ -559,6 +563,7 @@
{"declaration":"export function registerWebhookTarget<T extends { path: string; }>(targetsByPath: Map<string, T[]>, target: T, opts?: RegisterWebhookTargetOptions<T> | undefined): RegisteredWebhookTarget<T>;","entrypoint":"webhook-ingress","exportName":"registerWebhookTarget","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export","sourceLine":61,"sourcePath":"src/plugin-sdk/webhook-targets.ts"}
{"declaration":"export function registerWebhookTargetWithPluginRoute<T extends { path: string; }>(params: { targetsByPath: Map<string, T[]>; target: T; route: RegisterWebhookPluginRouteOptions; onLastPathTargetRemoved?: ((params: { ...; }) => void) | undefined; }): RegisteredWebhookTarget<...>;","entrypoint":"webhook-ingress","exportName":"registerWebhookTargetWithPluginRoute","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export","sourceLine":30,"sourcePath":"src/plugin-sdk/webhook-targets.ts"}
{"declaration":"export function requestBodyErrorToText(code: RequestBodyLimitErrorCode): string;","entrypoint":"webhook-ingress","exportName":"requestBodyErrorToText","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export","sourceLine":60,"sourcePath":"src/infra/http-body.ts"}
{"declaration":"export function resolveRequestClientIp(req?: IncomingMessage | undefined, trustedProxies?: string[] | undefined, allowRealIpFallback?: boolean): string | undefined;","entrypoint":"webhook-ingress","exportName":"resolveRequestClientIp","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export","sourceLine":186,"sourcePath":"src/gateway/net.ts"}
{"declaration":"export function resolveSingleWebhookTarget<T>(targets: readonly T[], isMatch: (target: T) => boolean): WebhookTargetMatchResult<T>;","entrypoint":"webhook-ingress","exportName":"resolveSingleWebhookTarget","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export","sourceLine":193,"sourcePath":"src/plugin-sdk/webhook-targets.ts"}
{"declaration":"export function resolveSingleWebhookTargetAsync<T>(targets: readonly T[], isMatch: (target: T) => Promise<boolean>): Promise<WebhookTargetMatchResult<T>>;","entrypoint":"webhook-ingress","exportName":"resolveSingleWebhookTargetAsync","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export","sourceLine":212,"sourcePath":"src/plugin-sdk/webhook-targets.ts"}
{"declaration":"export function resolveWebhookPath(params: { webhookPath?: string | undefined; webhookUrl?: string | undefined; defaultPath?: string | null | undefined; }): string | null;","entrypoint":"webhook-ingress","exportName":"resolveWebhookPath","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export","sourceLine":15,"sourcePath":"src/plugin-sdk/webhook-path.ts"}

View File

@@ -363,7 +363,7 @@ Recurring job in a custom persistent session:
Notes:
- `schedule.kind`: `at` (`at`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`).
- `schedule.at` accepts ISO 8601 (timezone optional; treated as UTC when omitted).
- `schedule.at` accepts ISO 8601. Tool/API values without a timezone are treated as UTC; the CLI also accepts `openclaw cron add|edit --at "<offset-less-iso>" --tz <iana>` for local wall-clock one-shots.
- `everyMs` is milliseconds.
- `sessionTarget`: `"main"`, `"isolated"`, `"current"`, or `"session:<custom-id>"`.
- `"current"` is resolved to `"session:<sessionKey>"` at creation time.

View File

@@ -275,6 +275,68 @@ Triggered when the gateway starts:
- **`gateway:startup`**: After channels start and hooks are loaded
### Session Patch Events
Triggered when session properties are modified:
- **`session:patch`**: When a session is updated
#### Session Event Context
Session events include rich context about the session and changes:
```typescript
{
sessionEntry: SessionEntry, // The complete updated session entry
patch: { // The patch object (only changed fields)
// Session identity & labeling
label?: string | null, // Human-readable session label
// AI model configuration
model?: string | null, // Model override (e.g., "claude-opus-4-5")
thinkingLevel?: string | null, // Thinking level ("off"|"low"|"med"|"high")
verboseLevel?: string | null, // Verbose output level
reasoningLevel?: string | null, // Reasoning mode override
elevatedLevel?: string | null, // Elevated mode override
responseUsage?: "off" | "tokens" | "full" | null, // Usage display mode
// Tool execution settings
execHost?: string | null, // Exec host (sandbox|gateway|node)
execSecurity?: string | null, // Security mode (deny|allowlist|full)
execAsk?: string | null, // Approval mode (off|on-miss|always)
execNode?: string | null, // Node ID for host=node
// Subagent coordination
spawnedBy?: string | null, // Parent session key (for subagents)
spawnDepth?: number | null, // Nesting depth (0 = root)
// Communication policies
sendPolicy?: "allow" | "deny" | null, // Message send policy
groupActivation?: "mention" | "always" | null, // Group chat activation
},
cfg: OpenClawConfig // Current gateway config
}
```
**Security note:** Only privileged clients (including the Control UI) can trigger `session:patch` events. Standard WebChat clients are blocked from patching sessions (see PR #20800), so the hook will not fire from those connections.
See `SessionsPatchParamsSchema` in `src/gateway/protocol/schema/sessions.ts` for the complete type definition.
#### Example: Session Patch Logger Hook
```typescript
const handler = async (event) => {
if (event.type !== "session" || event.action !== "patch") {
return;
}
const { patch } = event.context;
console.log(`[session-patch] Session updated: ${event.sessionKey}`);
console.log(`[session-patch] Changes:`, patch);
};
export default handler;
```
### Message Events
Triggered when messages are received or sent:

View File

@@ -107,7 +107,7 @@ Quick rules:
- `Config path not found: agents.defaults.userTimezone` means the key is unset; heartbeat falls back to host timezone (or `activeHours.timezone` if set).
- Cron without `--tz` uses gateway host timezone.
- Heartbeat `activeHours` uses configured timezone resolution (`user`, `local`, or explicit IANA tz).
- ISO timestamps without timezone are treated as UTC for cron `at` schedules.
- Cron `at` schedules treat ISO timestamps without timezone as UTC unless you used CLI `--at "<offset-less-iso>" --tz <iana>`.
Common signatures:

View File

@@ -86,7 +86,7 @@ Payload:
- `deliver` optional (boolean): If `true`, the agent's response will be sent to the messaging channel. Defaults to `true`. Responses that are only heartbeat acknowledgments are automatically skipped.
- `channel` optional (string): The messaging channel for delivery. Core channels: `last`, `whatsapp`, `telegram`, `discord`, `slack`, `signal`, `imessage`, `irc`, `googlechat`, `line`. Extension channels (plugins): `msteams`, `mattermost`, and others. Defaults to `last`.
- `to` optional (string): The recipient identifier for the channel (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack/Mattermost (plugin), conversation ID for Microsoft Teams). Defaults to the last recipient in the main session.
- `model` optional (string): Model override (e.g., `anthropic/claude-3-5-sonnet` or an alias). Must be in the allowed model list if restricted.
- `model` optional (string): Model override (e.g., `anthropic/claude-sonnet-4-6` or an alias). Must be in the allowed model list if restricted.
- `thinking` optional (string): Thinking level override (e.g., `low`, `medium`, `high`).
- `timeoutSeconds` optional (number): Maximum duration for the agent run in seconds.

View File

@@ -162,6 +162,25 @@ Groups:
- `channels.bluebubbles.groupPolicy = open | allowlist | disabled` (default: `allowlist`).
- `channels.bluebubbles.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
### Contact name enrichment (macOS, optional)
BlueBubbles group webhooks often only include raw participant addresses. If you want `GroupMembers` context to show local contact names instead, you can opt in to local Contacts enrichment on macOS:
- `channels.bluebubbles.enrichGroupParticipantsFromContacts = true` enables the lookup. Default: `false`.
- Lookups run only after group access, command authorization, and mention gating have allowed the message through.
- Only unnamed phone participants are enriched.
- Raw phone numbers remain as the fallback when no local match is found.
```json5
{
channels: {
bluebubbles: {
enrichGroupParticipantsFromContacts: true,
},
},
}
```
### Mention gating (groups)
BlueBubbles supports mention gating for group chats, matching iMessage/WhatsApp behavior:
@@ -300,6 +319,7 @@ Provider options:
- `channels.bluebubbles.allowFrom`: DM allowlist (handles, emails, E.164 numbers, `chat_id:*`, `chat_guid:*`).
- `channels.bluebubbles.groupPolicy`: `open | allowlist | disabled` (default: `allowlist`).
- `channels.bluebubbles.groupAllowFrom`: Group sender allowlist.
- `channels.bluebubbles.enrichGroupParticipantsFromContacts`: On macOS, optionally enrich unnamed group participants from local Contacts after gating passes. Default: `false`.
- `channels.bluebubbles.groups`: Per-group config (`requireMention`, etc.).
- `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`).
- `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `false`; required for streaming replies).

View File

@@ -185,7 +185,7 @@ Edit `~/.openclaw/openclaw.json`:
main: {
appId: "cli_xxx",
appSecret: "xxx",
botName: "My AI assistant",
name: "My AI assistant",
},
},
},
@@ -316,41 +316,43 @@ After approval, you can chat normally.
**1. Group policy** (`channels.feishu.groupPolicy`):
- `"open"` = allow everyone in groups (default)
- `"open"` = allow everyone in groups
- `"allowlist"` = only allow `groupAllowFrom`
- `"disabled"` = disable group messages
**2. Mention requirement** (`channels.feishu.groups.<chat_id>.requireMention`):
Default: `allowlist`
- `true` = require @mention (default)
- `false` = respond without mentions
**2. Mention requirement** (`channels.feishu.requireMention`, overridable via `channels.feishu.groups.<chat_id>.requireMention`):
- explicit `true` = require @mention
- explicit `false` = respond without mentions
- when unset and `groupPolicy: "open"` = default to `false`
- when unset and `groupPolicy` is not `"open"` = default to `true`
---
## Group configuration examples
### Allow all groups, require @mention (default)
### Allow all groups, no @mention required (default for open groups)
```json5
{
channels: {
feishu: {
groupPolicy: "open",
// Default requireMention: true
},
},
}
```
### Allow all groups, no @mention required
### Allow all groups, but still require @mention
```json5
{
channels: {
feishu: {
groups: {
oc_xxx: { requireMention: false },
},
groupPolicy: "open",
requireMention: true,
},
},
}
@@ -494,12 +496,12 @@ openclaw pairing list feishu
main: {
appId: "cli_xxx",
appSecret: "xxx",
botName: "Primary bot",
name: "Primary bot",
},
backup: {
appId: "cli_yyy",
appSecret: "yyy",
botName: "Backup bot",
name: "Backup bot",
enabled: false,
},
},
@@ -680,9 +682,10 @@ Key options:
| `channels.feishu.accounts.<id>.domain` | Per-account API domain override | `feishu` |
| `channels.feishu.dmPolicy` | DM policy | `pairing` |
| `channels.feishu.allowFrom` | DM allowlist (open_id list) | - |
| `channels.feishu.groupPolicy` | Group policy | `open` |
| `channels.feishu.groupPolicy` | Group policy | `allowlist` |
| `channels.feishu.groupAllowFrom` | Group allowlist | - |
| `channels.feishu.groups.<chat_id>.requireMention` | Require @mention | `true` |
| `channels.feishu.requireMention` | Default require @mention | conditional |
| `channels.feishu.groups.<chat_id>.requireMention` | Per-group require @mention override | inherited |
| `channels.feishu.groups.<chat_id>.enabled` | Enable group | `true` |
| `channels.feishu.textChunkLimit` | Message chunk size | `2000` |
| `channels.feishu.mediaMaxMb` | Media size limit | `30` |

View File

@@ -366,6 +366,10 @@ Group inbound payloads set:
- `WasMentioned` (mention gating result)
- Telegram forum topics also include `MessageThreadId` and `IsForum`.
Channel specific notes:
- BlueBubbles can optionally enrich unnamed macOS group participants from the local Contacts database before populating `GroupMembers`. This is off by default and only runs after normal group gating passes.
The agent system prompt includes a group intro on the first turn of a new group session. It reminds the model to respond like a human, avoid Markdown tables, and avoid typing literal `\n` sequences.
## iMessage specifics

View File

@@ -26,12 +26,14 @@ Text is supported everywhere; media and reactions vary by channel.
- [Nextcloud Talk](/channels/nextcloud-talk) — Self-hosted chat via Nextcloud Talk (plugin, installed separately).
- [Nostr](/channels/nostr) — Decentralized DMs via NIP-04 (plugin, installed separately).
- [Signal](/channels/signal) — signal-cli; privacy-focused.
- [Synology Chat](/channels/synology-chat) — Synology NAS Chat via outgoing+incoming webhooks (plugin, installed separately).
- [Slack](/channels/slack) — Bolt SDK; workspace apps.
- [Synology Chat](/channels/synology-chat) — Synology NAS Chat via outgoing+incoming webhooks (plugin, installed separately).
- [Telegram](/channels/telegram) — Bot API via grammY; supports groups.
- [Tlon](/channels/tlon) — Urbit-based messenger (plugin, installed separately).
- [Twitch](/channels/twitch) — Twitch chat via IRC connection (plugin, installed separately).
- [Voice Call](/plugins/voice-call) — Telephony via Plivo or Twilio (plugin, installed separately).
- [WebChat](/web/webchat) — Gateway WebChat UI over WebSocket.
- [WeChat](https://www.npmjs.com/package/@tencent-weixin/openclaw-weixin) — Tencent iLink Bot plugin via QR login; private chats only.
- [WhatsApp](/channels/whatsapp) — Most popular; uses Baileys and requires QR pairing.
- [Zalo](/channels/zalo) — Zalo Bot API; Vietnam's popular messenger (plugin, installed separately).
- [Zalo Personal](/channels/zalouser) — Zalo personal account via QR login (plugin, installed separately).

View File

@@ -74,7 +74,7 @@ If you see logs like:
Example (allow anyone in `#tuirc-dev` to talk to the bot):
```json55
```json5
{
channels: {
irc: {
@@ -95,7 +95,7 @@ That means you may see logs like `drop channel … (missing-mention)` unless the
To make the bot reply in an IRC channel **without needing a mention**, disable mention gating for that channel:
```json55
```json5
{
channels: {
irc: {
@@ -113,7 +113,7 @@ To make the bot reply in an IRC channel **without needing a mention**, disable m
Or to allow **all** IRC channels (no per-channel allowlist) and still reply without mentions:
```json55
```json5
{
channels: {
irc: {
@@ -133,7 +133,7 @@ To reduce risk, restrict tools for that channel.
### Same tools for everyone in the channel
```json55
```json5
{
channels: {
irc: {
@@ -154,7 +154,7 @@ To reduce risk, restrict tools for that channel.
Use `toolsBySender` to apply a stricter policy to `"*"` and a looser one to your nick:
```json55
```json5
{
channels: {
irc: {

View File

@@ -36,7 +36,7 @@ openclaw pairing list telegram
openclaw pairing approve telegram <CODE>
```
Supported channels: `bluebubbles`, `discord`, `feishu`, `googlechat`, `imessage`, `irc`, `line`, `matrix`, `mattermost`, `msteams`, `nextcloud-talk`, `nostr`, `signal`, `slack`, `synology-chat`, `telegram`, `twitch`, `whatsapp`, `zalo`, `zalouser`.
Supported channels: `bluebubbles`, `discord`, `feishu`, `googlechat`, `imessage`, `irc`, `line`, `matrix`, `mattermost`, `msteams`, `nextcloud-talk`, `nostr`, `openclaw-weixin`, `signal`, `slack`, `synology-chat`, `telegram`, `twitch`, `whatsapp`, `zalo`, `zalouser`.
### Where the state lives

View File

@@ -344,6 +344,8 @@ Available action groups in current Slack tooling:
| memberInfo | enabled |
| emojiList | enabled |
Current Slack message actions include `send`, `upload-file`, `download-file`, `read`, `edit`, `delete`, `pin`, `unpin`, `list-pins`, `member-info`, and `emoji-list`.
## Events and operational behavior
- Message edits/deletes/thread broadcasts are mapped into system events.

View File

@@ -79,6 +79,7 @@ Config values override env vars.
- In `allowlist` mode, an empty `allowedUserIds` list is treated as misconfiguration and the webhook route will not start (use `dmPolicy: "open"` for allow-all).
- `dmPolicy: "open"` allows any sender.
- `dmPolicy: "disabled"` blocks DMs.
- Reply recipient binding stays on stable numeric `user_id` by default. `channels.synology-chat.dangerouslyAllowNameMatching: true` is break-glass compatibility mode that re-enables mutable username/nickname lookup for reply delivery.
- Pairing approvals work with:
- `openclaw pairing list synology-chat`
- `openclaw pairing approve synology-chat <CODE>`
@@ -102,6 +103,11 @@ Multiple Synology Chat accounts are supported under `channels.synology-chat.acco
Each account can override token, incoming URL, webhook path, DM policy, and limits.
Direct-message sessions are isolated per account and user, so the same numeric `user_id`
on two different Synology accounts does not share transcript state.
Give each enabled account a distinct `webhookPath`. OpenClaw now rejects duplicate exact paths
and refuses to start named accounts that only inherit a shared webhook path in multi-account setups.
If you intentionally need legacy inheritance for a named account, set
`dangerouslyAllowInheritedWebhookPath: true` on that account or at `channels.synology-chat`,
but duplicate exact paths are still rejected fail-closed. Prefer explicit per-account paths.
```json5
{
@@ -132,3 +138,5 @@ on two different Synology accounts does not share transcript state.
- Keep `allowInsecureSsl: false` unless you explicitly trust a self-signed local NAS cert.
- Inbound webhook requests are token-verified and rate-limited per sender.
- Prefer `dmPolicy: "allowlist"` for production.
- Keep `dangerouslyAllowNameMatching` off unless you explicitly need legacy username-based reply delivery.
- Keep `dangerouslyAllowInheritedWebhookPath` off unless you explicitly accept shared-path routing risk in a multi-account setup.

View File

@@ -12,20 +12,21 @@ The CI runs on every push to `main` and every pull request. It uses smart scopin
## Job Overview
| Job | Purpose | When it runs |
| ----------------- | ------------------------------------------------------- | ---------------------------------- |
| `docs-scope` | Detect docs-only changes | Always |
| `changed-scope` | Detect which areas changed (node/macos/android/windows) | Non-doc changes |
| `check` | TypeScript types, lint, format | Non-docs, node changes |
| `check-docs` | Markdown lint + broken link check | Docs changed |
| `secrets` | Detect leaked secrets | Always |
| `build-artifacts` | Build dist once, share with `release-check` | Pushes to `main`, node changes |
| `release-check` | Validate npm pack contents | Pushes to `main` after build |
| `checks` | Node tests + protocol check on PRs; Bun compat on push | Non-docs, node changes |
| `compat-node22` | Minimum supported Node runtime compatibility | Pushes to `main`, node changes |
| `checks-windows` | Windows-specific tests | Non-docs, windows-relevant changes |
| `macos` | Swift lint/build/test + TS tests | PRs with macos changes |
| `android` | Gradle build + tests | Non-docs, android changes |
| Job | Purpose | When it runs |
| ----------------- | ------------------------------------------------------------------------- | ------------------------------------------------ |
| `preflight` | Docs scope, change scope, key scan, workflow audit, prod dependency audit | Always; node-based audit only on non-doc changes |
| `docs-scope` | Detect docs-only changes | Always |
| `changed-scope` | Detect which areas changed (node/macos/android/windows) | Non-doc changes |
| `check` | TypeScript types, lint, format | Non-docs, node changes |
| `check-docs` | Markdown lint + broken link check | Docs changed |
| `secrets` | Detect leaked secrets | Always |
| `build-artifacts` | Build dist once, share with `release-check` | Pushes to `main`, node changes |
| `release-check` | Validate npm pack contents | Pushes to `main` after build |
| `checks` | Node tests + protocol check on PRs; Bun compat on push | Non-docs, node changes |
| `compat-node22` | Minimum supported Node runtime compatibility | Pushes to `main`, node changes |
| `checks-windows` | Windows-specific tests | Non-docs, windows-relevant changes |
| `macos` | Swift lint/build/test + TS tests | PRs with macos changes |
| `android` | Gradle build + tests | Non-docs, android changes |
## Fail-Fast Order

View File

@@ -1,5 +1,5 @@
---
summary: "CLI reference for `openclaw config` (get/set/unset/file/validate)"
summary: "CLI reference for `openclaw config` (get/set/unset/file/schema/validate)"
read_when:
- You want to read or edit config non-interactively
title: "config"
@@ -7,7 +7,7 @@ title: "config"
# `openclaw config`
Config helpers for non-interactive edits in `openclaw.json`: get/set/unset/validate
Config helpers for non-interactive edits in `openclaw.json`: get/set/unset/file/schema/validate
values by path and print the active config file. Run without a subcommand to
open the configure wizard (same as `openclaw configure`).
@@ -15,6 +15,7 @@ open the configure wizard (same as `openclaw configure`).
```bash
openclaw config file
openclaw config schema
openclaw config get browser.executablePath
openclaw config set browser.executablePath "/usr/bin/google-chrome"
openclaw config set agents.defaults.heartbeat.every "2h"
@@ -27,7 +28,21 @@ openclaw config validate
openclaw config validate --json
```
## Paths
### `config schema`
Print the generated JSON schema for `openclaw.json` to stdout as plain text.
```bash
openclaw config schema
```
Pipe it into a file when you want to inspect or validate it with other tools:
```bash
openclaw config schema > openclaw.schema.json
```
### Paths
Paths use dot or bracket notation:

View File

@@ -21,6 +21,9 @@ output internal. `--deliver` remains as a deprecated alias for `--announce`.
Note: one-shot (`--at`) jobs delete after success by default. Use `--keep-after-run` to keep them.
Note: for one-shot CLI jobs, offset-less `--at` datetimes are treated as UTC unless you also pass
`--tz <iana>`, which interprets that local wall-clock time in the given timezone.
Note: recurring jobs now use exponential retry backoff after consecutive errors (30s → 1m → 5m → 15m → 60m), then return to normal schedule after the next successful run.
Note: `openclaw cron run` now returns as soon as the manual run is queued for execution. Successful responses include `{ ok: true, enqueued: true, runId }`; use `openclaw cron runs --id <job-id>` to follow the eventual outcome.

View File

@@ -55,7 +55,8 @@ Notes:
- `--reset`: reset dev config + credentials + sessions + workspace (requires `--dev`).
- `--force`: kill any existing listener on the selected port before starting.
- `--verbose`: verbose logs.
- `--claude-cli-logs`: only show claude-cli logs in the console (and enable its stdout/stderr).
- `--cli-backend-logs`: only show CLI backend logs in the console (and enable stdout/stderr).
- `--claude-cli-logs`: deprecated alias for `--cli-backend-logs`.
- `--ws-log <auto|full|compact>`: websocket log style (default `auto`).
- `--compact`: alias for `--ws-log compact`.
- `--raw-stream`: log raw model stream events to jsonl.
@@ -193,7 +194,7 @@ Notes:
- When token auth requires a token and `gateway.auth.token` is SecretRef-managed, `gateway install` validates that the SecretRef is resolvable but does not persist the resolved token into service environment metadata.
- If token auth requires a token and the configured token SecretRef is unresolved, install fails closed instead of persisting fallback plaintext.
- For password auth on `gateway run`, prefer `OPENCLAW_GATEWAY_PASSWORD`, `--password-file`, or a SecretRef-backed `gateway.auth.password` over inline `--password`.
- In inferred auth mode, shell-only `OPENCLAW_GATEWAY_PASSWORD`/`CLAWDBOT_GATEWAY_PASSWORD` does not relax install token requirements; use durable config (`gateway.auth.password` or config `env`) when installing a managed service.
- In inferred auth mode, shell-only `OPENCLAW_GATEWAY_PASSWORD` does not relax install token requirements; use durable config (`gateway.auth.password` or config `env`) when installing a managed service.
- If both `gateway.auth.token` and `gateway.auth.password` are configured and `gateway.auth.mode` is unset, install is blocked until mode is set explicitly.
- Lifecycle commands accept `--json` for scripting.

View File

@@ -113,7 +113,9 @@ openclaw [--dev] [--profile <name>] <command>
audit
secrets
reload
migrate
audit
configure
apply
reset
uninstall
update
@@ -132,16 +134,21 @@ openclaw [--dev] [--profile <name>] <command>
check
plugins
list
info
inspect
install
uninstall
update
enable
disable
doctor
marketplace list
memory
status
index
search
message
send
broadcast
agent
agents
list
@@ -389,7 +396,7 @@ Interactive configuration wizard (models, channels, skills, gateway).
### `config`
Non-interactive config helpers (get/set/unset/file/validate). Running `openclaw config` with no
Non-interactive config helpers (get/set/unset/file/schema/validate). Running `openclaw config` with no
subcommand launches the wizard.
Subcommands:
@@ -406,6 +413,7 @@ Subcommands:
- `config set --strict-json`: require JSON5 parsing for path/value input. `--json` remains a legacy alias for strict parsing outside dry-run output mode.
- `config unset <path>`: remove a value.
- `config file`: print the active config file path.
- `config schema`: print the generated JSON schema for `openclaw.json`.
- `config validate`: validate the current config against the schema without starting the gateway.
- `config validate --json`: emit machine-readable JSON output.
@@ -772,7 +780,8 @@ Options:
- `--reset` (reset dev config + credentials + sessions + workspace)
- `--force` (kill existing listener on port)
- `--verbose`
- `--claude-cli-logs`
- `--cli-backend-logs`
- `--claude-cli-logs` (deprecated alias)
- `--ws-log <auto|full|compact>`
- `--compact` (alias for `--ws-log compact`)
- `--raw-stream`
@@ -863,6 +872,13 @@ Policy note: this is technical compatibility. Anthropic has blocked some
subscription usage outside Claude Code in the past; verify current Anthropic
terms before relying on setup-token in production.
Anthropic Claude CLI migration:
```bash
openclaw models auth login --provider anthropic --method cli --set-default
openclaw onboard --auth-choice anthropic-cli
```
### `models` (root)
`openclaw models` is an alias for `models status`.
@@ -1035,7 +1051,7 @@ Subcommands:
Auth notes:
- `node` resolves gateway auth from env/config (no `--token`/`--password` flags): `OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`, then `gateway.auth.*`. In local mode, node host intentionally ignores `gateway.remote.*`; in `gateway.mode=remote`, `gateway.remote.*` participates per remote precedence rules.
- Legacy `CLAWDBOT_GATEWAY_*` env vars are intentionally ignored for node-host auth resolution.
- Node-host auth resolution only honors `OPENCLAW_GATEWAY_*` env vars.
## Nodes

View File

@@ -38,7 +38,7 @@ Notes:
- `models set <model-or-alias>` accepts `provider/model` or an alias.
- Model refs are parsed by splitting on the **first** `/`. If the model ID includes `/` (OpenRouter-style), include the provider prefix (example: `openrouter/moonshotai/kimi-k2`).
- If you omit the provider, OpenClaw treats the input as an alias or a model for the **default provider** (only works when there is no `/` in the model ID).
- `models status` may show `marker(<value>)` in auth output for non-secret placeholders (for example `OPENAI_API_KEY`, `secretref-managed`, `minimax-oauth`, `qwen-oauth`, `ollama-local`) instead of masking them as secrets.
- `models status` may show `marker(<value>)` in auth output for non-secret placeholders (for example `OPENAI_API_KEY`, `secretref-managed`, `minimax-oauth`, `oauth:chutes`, `ollama-local`) instead of masking them as secrets.
### `models status`
@@ -74,8 +74,17 @@ openclaw models auth paste-token
`models auth login` runs a provider plugins auth flow (OAuth/API key). Use
`openclaw plugins list` to see which providers are installed.
Examples:
```bash
openclaw models auth login --provider anthropic --method cli --set-default
openclaw models auth login --provider openai-codex --set-default
```
Notes:
- `login --provider anthropic --method cli --set-default` reuses a local Claude
CLI login and rewrites the main Anthropic default-model path to `claude-cli/...`.
- `setup-token` prompts for a setup-token value (generate it with `claude setup-token` on any machine).
- `paste-token` accepts a token string generated elsewhere or from automation.
- Anthropic policy note: setup-token support is technical compatibility. Anthropic has blocked some subscription usage outside Claude Code in the past, so verify current terms before using it broadly.

View File

@@ -31,6 +31,11 @@ Node hosts automatically advertise a browser proxy if `browser.enabled` is not
disabled on the node. This lets the agent use browser automation on that node
without extra configuration.
By default, the proxy exposes the node's normal browser profile surface. If you
set `nodeHost.browserProxy.allowProfiles`, the proxy becomes restrictive:
non-allowlisted profile targeting is rejected, and persistent profile
create/delete routes are blocked through the proxy.
Disable it on the node if needed:
```json5
@@ -67,7 +72,7 @@ Options:
- In local mode, node host intentionally does not inherit `gateway.remote.token` / `gateway.remote.password`.
- If `gateway.auth.token` / `gateway.auth.password` is explicitly configured via SecretRef and unresolved, node auth resolution fails closed (no remote fallback masking).
- In `gateway.mode=remote`, remote client fields (`gateway.remote.token` / `gateway.remote.password`) are also eligible per remote precedence rules.
- Legacy `CLAWDBOT_GATEWAY_*` env vars are ignored for node host auth resolution.
- Node host auth resolution only honors `OPENCLAW_GATEWAY_*` env vars.
## Service (background)

View File

@@ -120,6 +120,11 @@ Marketplace sources can be:
- a GitHub repo shorthand such as `owner/repo`
- a git URL
For remote marketplaces loaded from GitHub or git, plugin entries must stay
inside the cloned marketplace repo. OpenClaw accepts relative path sources from
that repo and rejects external git, GitHub, URL/archive, and absolute-path
plugin sources from remote manifests.
For local paths and archives, OpenClaw auto-detects:
- native OpenClaw plugins (`openclaw.plugin.json`)

View File

@@ -92,6 +92,13 @@ These run inside the agent loop or gateway pipeline:
- **`session_start` / `session_end`**: session lifecycle boundaries.
- **`gateway_start` / `gateway_stop`**: gateway lifecycle events.
Hook decision rules for outbound/tool guards:
- `before_tool_call`: `{ block: true }` is terminal and stops lower-priority handlers.
- `before_tool_call`: `{ block: false }` is a no-op and does not clear a prior block.
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
- `message_sending`: `{ cancel: false }` is a no-op and does not clear a prior cancel.
See [Plugin hooks](/plugins/architecture#provider-runtime-hooks) for the hook API and registration details.
## Streaming + partial replies

View File

@@ -56,6 +56,9 @@ When a session is **close to auto-compaction**, OpenClaw triggers a **silent,
agentic turn** that reminds the model to write durable memory **before** the
context is compacted. The default prompts explicitly say the model _may reply_,
but usually `NO_REPLY` is the correct response so the user never sees this turn.
The active memory plugin owns the prompt/path policy for that flush; the
default `memory-core` plugin writes to the canonical daily file under
`memory/YYYY-MM-DD.md`.
This is controlled by `agents.defaults.compaction.memoryFlush`:

View File

@@ -108,7 +108,6 @@ Current bundled examples:
- `byteplus`, `cloudflare-ai-gateway`, `huggingface`, `kimi-coding`,
`modelstudio`, `nvidia`, `qianfan`, `synthetic`, `together`, `venice`,
`vercel-ai-gateway`, and `volcengine`: plugin-owned catalogs only
- `qwen-portal`: plugin-owned catalog, OAuth login, and OAuth refresh
- `minimax` and `xiaomi`: plugin-owned catalogs plus usage auth/snapshot logic
The bundled `openai` plugin now owns both provider ids: `openai` and
@@ -348,22 +347,6 @@ Kimi Coding uses Moonshot AI's Anthropic-compatible endpoint:
}
```
### Qwen OAuth (free tier)
Qwen provides OAuth access to Qwen Coder + Vision via a device-code flow.
The bundled provider plugin is enabled by default, so just log in:
```bash
openclaw models auth login --provider qwen-portal --set-default
```
Model refs:
- `qwen-portal/coder-model`
- `qwen-portal/vision-model`
See [/providers/qwen](/providers/qwen) for setup details and notes.
### Volcano Engine (Doubao)
Volcano Engine (火山引擎) provides access to Doubao and other models in China.

View File

@@ -10,7 +10,7 @@ title: "OAuth"
# OAuth
OpenClaw supports “subscription auth” via OAuth for providers that offer it (notably **OpenAI Codex (ChatGPT OAuth)**). For Anthropic subscriptions, use the **setup-token** flow. Anthropic subscription use outside Claude Code has been restricted for some users in the past, so treat it as a user-choice risk and verify current Anthropic policy yourself. OpenAI Codex OAuth is explicitly supported for use in external tools like OpenClaw. This page explains:
OpenClaw supports “subscription auth” via OAuth for providers that offer it (notably **OpenAI Codex (ChatGPT OAuth)**). For Anthropic subscriptions, you can either use the **setup-token** flow or reuse a local **Claude CLI** login on the gateway host. Anthropic subscription use outside Claude Code has been restricted for some users in the past, so treat it as a user-choice risk and verify current Anthropic policy yourself. OpenAI Codex OAuth is explicitly supported for use in external tools like OpenClaw. This page explains:
For Anthropic in production, API key auth is the safer recommended path over subscription setup-token auth.
@@ -80,19 +80,48 @@ Verify:
openclaw models status
```
## Anthropic Claude CLI migration
If Claude CLI is already installed and signed in on the gateway host, you can
switch Anthropic model selection over to the local CLI backend:
```bash
openclaw models auth login --provider anthropic --method cli --set-default
```
Onboarding shortcut:
```bash
openclaw onboard --auth-choice anthropic-cli
```
This keeps existing Anthropic auth profiles for rollback, but rewrites the main
default-model path from `anthropic/...` to `claude-cli/...`.
## OAuth exchange (how login works)
OpenClaws interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands.
### Anthropic setup-token
### Anthropic setup-token / Claude CLI
Flow shape:
Setup-token path:
1. run `claude setup-token`
2. paste the token into OpenClaw
3. store as a token auth profile (no refresh)
The wizard path is `openclaw onboard` → auth choice `setup-token` (Anthropic).
Claude CLI path:
1. sign in with `claude auth login` on the gateway host
2. run `openclaw models auth login --provider anthropic --method cli --set-default`
3. store no new auth profile; switch model selection to `claude-cli/...`
Wizard paths:
- `openclaw onboard` → auth choice `anthropic-cli`
- `openclaw onboard` → auth choice `setup-token` (Anthropic)
### OpenAI Codex (ChatGPT OAuth)

View File

@@ -162,13 +162,20 @@ Enforcement points:
## sessions_spawn
Spawn a sub-agent run in an isolated session and announce the result back to the requester chat channel.
Spawn an isolated delegated session.
- Default runtime: OpenClaw sub-agent (`runtime: "subagent"`).
- ACP harness sessions use `runtime: "acp"` and follow ACP-specific targeting/policy rules.
- This section focuses on sub-agent behavior unless noted otherwise. For ACP-specific behavior, see [ACP Agents](/tools/acp-agents).
Parameters:
- `task` (required)
- `runtime?` (`subagent|acp`; defaults to `subagent`)
- `label?` (optional; used for logs/UI)
- `agentId?` (optional; spawn under another agent id if allowed)
- `agentId?` (optional)
- `runtime: "subagent"`: target another OpenClaw agent id if allowed by `subagents.allowAgents`
- `runtime: "acp"`: target an ACP harness id if allowed by `acp.allowedAgents`
- `model?` (optional; overrides the sub-agent model; invalid values error)
- `thinking?` (optional; overrides thinking level for the sub-agent run)
- `runTimeoutSeconds?` (defaults to `agents.defaults.subagents.runTimeoutSeconds` when set, otherwise `0`; when set, aborts the sub-agent run after N seconds)
@@ -181,12 +188,14 @@ Parameters:
Allowlist:
- `agents.list[].subagents.allowAgents`: list of agent ids allowed via `agentId` (`["*"]` to allow any). Default: only the requester agent.
- `runtime: "subagent"`: `agents.list[].subagents.allowAgents` controls which OpenClaw agent ids are allowed via `agentId` (`["*"]` to allow any). Default: only the requester agent.
- `runtime: "acp"`: `acp.allowedAgents` controls which ACP harness ids are allowed. This is a separate policy from `subagents.allowAgents`.
- Sandbox inheritance guard: if the requester session is sandboxed, `sessions_spawn` rejects targets that would run unsandboxed.
Discovery:
- Use `agents_list` to discover which agent ids are allowed for `sessions_spawn`.
- Use `agents_list` to discover allowed targets for `runtime: "subagent"`.
- For `runtime: "acp"`, use configured ACP harness ids and `acp.allowedAgents`; `agents_list` does not list ACP harness targets.
Behavior:

View File

@@ -52,6 +52,10 @@
]
},
"redirects": [
{
"source": "/providers/modelstudio",
"destination": "/providers/qwen_modelstudio"
},
{
"source": "/platforms/oracle",
"destination": "/install/oracle"
@@ -950,6 +954,7 @@
"pages": [
"install/updating",
"install/migrating",
"install/migrating-matrix",
"install/uninstall",
"install/development-channels"
]
@@ -979,8 +984,8 @@
"channels/nextcloud-talk",
"channels/nostr",
"channels/signal",
"channels/synology-chat",
"channels/slack",
"channels/synology-chat",
"channels/telegram",
"channels/tlon",
"channels/twitch",
@@ -1180,34 +1185,35 @@
"pages": [
"providers/anthropic",
"providers/bedrock",
"providers/cloudflare-ai-gateway",
"providers/claude-max-api-proxy",
"providers/cloudflare-ai-gateway",
"providers/deepgram",
"providers/deepseek",
"providers/github-copilot",
"providers/glm",
"providers/google",
"providers/groq",
"providers/huggingface",
"providers/kilocode",
"providers/litellm",
"providers/glm",
"providers/minimax",
"providers/modelstudio",
"providers/moonshot",
"providers/mistral",
"providers/moonshot",
"providers/nvidia",
"providers/ollama",
"providers/openai",
"providers/opencode-go",
"providers/opencode",
"providers/opencode-go",
"providers/openrouter",
"providers/perplexity-provider",
"providers/qianfan",
"providers/qwen_modelstudio",
"providers/qwen",
"providers/sglang",
"providers/synthetic",
"providers/together",
"providers/vercel-ai-gateway",
"providers/venice",
"providers/vercel-ai-gateway",
"providers/vllm",
"providers/volcengine",
"providers/xai",
@@ -1744,7 +1750,6 @@
{
"group": "扩展",
"pages": [
"zh-CN/plugins/architecture",
"zh-CN/plugins/voice-call",
"zh-CN/plugins/zalouser",
"zh-CN/plugins/manifest",

View File

@@ -113,6 +113,26 @@ Optional ops scripts (systemd/Termux) are documented here:
> `claude setup-token` requires an interactive TTY.
## Anthropic: Claude CLI migration
If Claude CLI is already installed and signed in on the gateway host, you can
switch an existing Anthropic setup over to the CLI backend instead of pasting a
setup-token:
```bash
openclaw models auth login --provider anthropic --method cli --set-default
```
This keeps your existing Anthropic auth profiles for rollback, but changes the
default model selection to `claude-cli/...` and adds matching Claude CLI
allowlist entries under `agents.defaults.models`.
Onboarding shortcut:
```bash
openclaw onboard --auth-choice anthropic-cli
```
## Checking model auth status
```bash

View File

@@ -22,13 +22,14 @@ want “always works” text responses without relying on external APIs.
## Beginner-friendly quick start
You can use Claude Code CLI **without any config** (OpenClaw ships a built-in default):
You can use Claude Code CLI **without any config** (the bundled Anthropic plugin
registers a default backend):
```bash
openclaw agent --message "hi" --model claude-cli/opus-4.6
```
Codex CLI also works out of the box:
Codex CLI also works out of the box (via the bundled OpenAI plugin):
```bash
openclaw agent --message "hi" --model codex-cli/gpt-5.4
@@ -53,6 +54,11 @@ command path:
Thats it. No keys, no extra auth config needed beyond the CLI itself.
If you use a bundled CLI backend as the **primary message provider** on a
gateway host, OpenClaw now auto-loads the owning bundled plugin when your config
explicitly references that backend in a model ref or under
`agents.defaults.cliBackends`.
## Using it as a fallback
Add a CLI backend to your fallback list so it only runs when primary models fail:
@@ -180,9 +186,9 @@ Input modes:
- `input: "stdin"` sends the prompt via stdin.
- If the prompt is very long and `maxPromptArgChars` is set, stdin is used.
## Defaults (built-in)
## Defaults (plugin-owned)
OpenClaw ships a default for `claude-cli`:
The bundled Anthropic plugin registers a default for `claude-cli`:
- `command: "claude"`
- `args: ["-p", "--output-format", "json", "--permission-mode", "bypassPermissions"]`
@@ -193,19 +199,38 @@ OpenClaw ships a default for `claude-cli`:
- `systemPromptWhen: "first"`
- `sessionMode: "always"`
OpenClaw also ships a default for `codex-cli`:
The bundled OpenAI plugin also registers a default for `codex-cli`:
- `command: "codex"`
- `args: ["exec","--json","--color","never","--sandbox","read-only","--skip-git-repo-check"]`
- `resumeArgs: ["exec","resume","{sessionId}","--color","never","--sandbox","read-only","--skip-git-repo-check"]`
- `args: ["exec","--json","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]`
- `resumeArgs: ["exec","resume","{sessionId}","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]`
- `output: "jsonl"`
- `resumeOutput: "text"`
- `modelArg: "--model"`
- `imageArg: "--image"`
- `sessionMode: "existing"`
The bundled Google plugin also registers a default for `google-gemini-cli`:
- `command: "gemini"`
- `args: ["--prompt", "--output-format", "json"]`
- `resumeArgs: ["--resume", "{sessionId}", "--prompt", "--output-format", "json"]`
- `modelArg: "--model"`
- `sessionMode: "existing"`
- `sessionIdFields: ["session_id", "sessionId"]`
Override only if needed (common: absolute `command` path).
## Plugin-owned defaults
CLI backend defaults are now part of the plugin surface:
- Plugins register them with `api.registerCliBackend(...)`.
- The backend `id` becomes the provider prefix in model refs.
- User config in `agents.defaults.cliBackends.<id>` still overrides the plugin default.
- Backend-specific config cleanup stays plugin-owned through the optional
`normalizeConfig` hook.
## Limitations
- **No OpenClaw tools** (the CLI backend never receives tool calls). Some CLIs

View File

@@ -70,11 +70,35 @@ Default mode is `gateway.reload.mode="hybrid"`.
- One always-on process for routing, control plane, and channel connections.
- Single multiplexed port for:
- WebSocket control/RPC
- HTTP APIs (OpenAI-compatible, Responses, tools invoke)
- HTTP APIs, OpenAI compatible (`/v1/models`, `/v1/embeddings`, `/v1/chat/completions`, `/v1/responses`, `/tools/invoke`)
- Control UI and hooks
- Default bind mode: `loopback`.
- Auth is required by default (`gateway.auth.token` / `gateway.auth.password`, or `OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`).
## OpenAI-compatible endpoints
OpenClaws highest-leverage compatibility surface is now:
- `GET /v1/models`
- `GET /v1/models/{id}`
- `POST /v1/embeddings`
- `POST /v1/chat/completions`
- `POST /v1/responses`
Why this set matters:
- Most Open WebUI, LobeChat, and LibreChat integrations probe `/v1/models` first.
- Many RAG and memory pipelines expect `/v1/embeddings`.
- Agent-native clients increasingly prefer `/v1/responses`.
Planning note:
- `/v1/models` is agent-first: it returns `openclaw`, `openclaw/default`, and `openclaw/<agentId>`.
- `openclaw/default` is the stable alias that always maps to the configured default agent.
- Use `x-openclaw-model` when you want a backend provider/model override; otherwise the selected agent's normal model and embedding setup stays in control.
All of these run on the main Gateway port and use the same trusted operator auth boundary as the rest of the Gateway HTTP API.
### Port and bind precedence
| Setting | Resolution order |

View File

@@ -14,6 +14,13 @@ This endpoint is **disabled by default**. Enable it in config first.
- `POST /v1/chat/completions`
- Same port as the Gateway (WS + HTTP multiplex): `http://<gateway-host>:<port>/v1/chat/completions`
When the Gateways OpenAI-compatible HTTP surface is enabled, it also serves:
- `GET /v1/models`
- `GET /v1/models/{id}`
- `POST /v1/embeddings`
- `POST /v1/responses`
Under the hood, requests are executed as a normal Gateway agent run (same codepath as `openclaw agent`), so routing/permissions/config match your Gateway.
## Authentication
@@ -41,20 +48,25 @@ Treat this endpoint as a **full operator-access** surface for the gateway instan
See [Security](/gateway/security) and [Remote access](/gateway/remote).
## Choosing an agent
## Agent-first model contract
No custom headers required: encode the agent id in the OpenAI `model` field:
OpenClaw treats the OpenAI `model` field as an **agent target**, not a raw provider model id.
- `model: "openclaw:<agentId>"` (example: `"openclaw:main"`, `"openclaw:beta"`)
- `model: "agent:<agentId>"` (alias)
- `model: "openclaw"` routes to the configured default agent.
- `model: "openclaw/default"` also routes to the configured default agent.
- `model: "openclaw/<agentId>"` routes to a specific agent.
Or target a specific OpenClaw agent by header:
Optional request headers:
- `x-openclaw-agent-id: <agentId>` (default: `main`)
- `x-openclaw-model: <provider/model-or-bare-id>` overrides the backend model for the selected agent.
- `x-openclaw-agent-id: <agentId>` remains supported as a compatibility override.
- `x-openclaw-session-key: <sessionKey>` fully controls session routing.
- `x-openclaw-message-channel: <channel>` sets the synthetic ingress channel context for channel-aware prompts and policies.
Advanced:
Compatibility aliases still accepted:
- `x-openclaw-session-key: <sessionKey>` to fully control session routing.
- `model: "openclaw:<agentId>"`
- `model: "agent:<agentId>"`
## Enabling the endpoint
@@ -94,6 +106,57 @@ By default the endpoint is **stateless per request** (a new session key is gener
If the request includes an OpenAI `user` string, the Gateway derives a stable session key from it, so repeated calls can share an agent session.
## Why this surface matters
This is the highest-leverage compatibility set for self-hosted frontends and tooling:
- Most Open WebUI, LobeChat, and LibreChat setups expect `/v1/models`.
- Many RAG systems expect `/v1/embeddings`.
- Existing OpenAI chat clients can usually start with `/v1/chat/completions`.
- More agent-native clients increasingly prefer `/v1/responses`.
## Model list and agent routing
<AccordionGroup>
<Accordion title="What does `/v1/models` return?">
An OpenClaw agent-target list.
The returned ids are `openclaw`, `openclaw/default`, and `openclaw/<agentId>` entries.
Use them directly as OpenAI `model` values.
</Accordion>
<Accordion title="Does `/v1/models` list agents or sub-agents?">
It lists top-level agent targets, not backend provider models and not sub-agents.
Sub-agents remain internal execution topology. They do not appear as pseudo-models.
</Accordion>
<Accordion title="Why is `openclaw/default` included?">
`openclaw/default` is the stable alias for the configured default agent.
That means clients can keep using one predictable id even if the real default agent id changes between environments.
</Accordion>
<Accordion title="How do I override the backend model?">
Use `x-openclaw-model`.
Examples:
`x-openclaw-model: openai/gpt-5.4`
`x-openclaw-model: gpt-5.4`
If you omit it, the selected agent runs with its normal configured model choice.
</Accordion>
<Accordion title="How do embeddings fit this contract?">
`/v1/embeddings` uses the same agent-target `model` ids.
Use `model: "openclaw/default"` or `model: "openclaw/<agentId>"`.
When you need a specific embedding model, send it in `x-openclaw-model`.
Without that header, the request passes through to the selected agent's normal embedding setup.
</Accordion>
</AccordionGroup>
## Streaming (SSE)
Set `stream: true` to receive Server-Sent Events (SSE):
@@ -102,6 +165,30 @@ Set `stream: true` to receive Server-Sent Events (SSE):
- Each event line is `data: <json>`
- Stream ends with `data: [DONE]`
## Open WebUI quick setup
For a basic Open WebUI connection:
- Base URL: `http://127.0.0.1:18789/v1`
- Docker on macOS base URL: `http://host.docker.internal:18789/v1`
- API key: your Gateway bearer token
- Model: `openclaw/default`
Expected behavior:
- `GET /v1/models` should list `openclaw/default`
- Open WebUI should use `openclaw/default` as the chat model id
- If you want a specific backend provider/model for that agent, set the agent's normal default model or send `x-openclaw-model`
Quick smoke:
```bash
curl -sS http://127.0.0.1:18789/v1/models \
-H 'Authorization: Bearer YOUR_TOKEN'
```
If that returns `openclaw/default`, most Open WebUI setups can connect with the same base URL and token.
## Examples
Non-streaming:
@@ -110,9 +197,8 @@ Non-streaming:
curl -sS http://127.0.0.1:18789/v1/chat/completions \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-H 'x-openclaw-agent-id: main' \
-d '{
"model": "openclaw",
"model": "openclaw/default",
"messages": [{"role":"user","content":"hi"}]
}'
```
@@ -123,10 +209,44 @@ Streaming:
curl -N http://127.0.0.1:18789/v1/chat/completions \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-H 'x-openclaw-agent-id: main' \
-H 'x-openclaw-model: openai/gpt-5.4' \
-d '{
"model": "openclaw",
"model": "openclaw/research",
"stream": true,
"messages": [{"role":"user","content":"hi"}]
}'
```
List models:
```bash
curl -sS http://127.0.0.1:18789/v1/models \
-H 'Authorization: Bearer YOUR_TOKEN'
```
Fetch one model:
```bash
curl -sS http://127.0.0.1:18789/v1/models/openclaw%2Fdefault \
-H 'Authorization: Bearer YOUR_TOKEN'
```
Create embeddings:
```bash
curl -sS http://127.0.0.1:18789/v1/embeddings \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-H 'x-openclaw-model: openai/text-embedding-3-small' \
-d '{
"model": "openclaw/default",
"input": ["alpha", "beta"]
}'
```
Notes:
- `/v1/models` returns OpenClaw agent targets, not raw provider catalogs.
- `openclaw/default` is always present so one stable id works across environments.
- Backend provider/model overrides belong in `x-openclaw-model`, not the OpenAI `model` field.
- `/v1/embeddings` supports `input` as a string or array of strings.

View File

@@ -24,11 +24,22 @@ Operational behavior matches [OpenAI Chat Completions](/gateway/openai-http-api)
- use `Authorization: Bearer <token>` with the normal Gateway auth config
- treat the endpoint as full operator access for the gateway instance
- select agents with `model: "openclaw:<agentId>"`, `model: "agent:<agentId>"`, or `x-openclaw-agent-id`
- select agents with `model: "openclaw"`, `model: "openclaw/default"`, `model: "openclaw/<agentId>"`, or `x-openclaw-agent-id`
- use `x-openclaw-model` when you want to override the selected agent's backend model
- use `x-openclaw-session-key` for explicit session routing
- use `x-openclaw-message-channel` when you want a non-default synthetic ingress channel context
Enable or disable this endpoint with `gateway.http.endpoints.responses.enabled`.
The same compatibility surface also includes:
- `GET /v1/models`
- `GET /v1/models/{id}`
- `POST /v1/embeddings`
- `POST /v1/chat/completions`
For the canonical explanation of how agent-target models, `openclaw/default`, embeddings pass-through, and backend model overrides fit together, see [OpenAI Chat Completions](/gateway/openai-http-api#agent-first-model-contract) and [Model list and agent routing](/gateway/openai-http-api#model-list-and-agent-routing).
## Session behavior
By default the endpoint is **stateless per request** (a new session key is generated each call).
@@ -54,9 +65,12 @@ Accepted but **currently ignored**:
- `reasoning`
- `metadata`
- `store`
- `previous_response_id`
- `truncation`
Supported:
- `previous_response_id`: OpenClaw reuses the earlier response session when the request stays within the same agent/user/requested-session scope.
## Items (input)
### `message`

View File

@@ -181,6 +181,13 @@ The Gateway treats these as **claims** and enforces server-side allowlists.
- `source`: `core` or `plugin`
- `pluginId`: plugin owner when `source="plugin"`
- `optional`: whether a plugin tool is optional
- Operators may call `tools.effective` (`operator.read`) to fetch the runtime-effective tool
inventory for a session.
- `sessionKey` is required.
- The gateway derives trusted runtime context from the session server-side instead of accepting
caller-supplied auth or delivery context.
- The response is session-scoped and reflects what the active conversation can use right now,
including core, plugin, and channel tools.
## Exec approvals

View File

@@ -117,7 +117,7 @@ Gateway credential resolution follows one shared contract across call/probe/stat
- password: `OPENCLAW_GATEWAY_PASSWORD` -> `gateway.remote.password` -> `gateway.auth.password`
- Node-host local-mode exception: `gateway.remote.token` / `gateway.remote.password` are ignored.
- Remote probe/status token checks are strict by default: they use `gateway.remote.token` only (no local token fallback) when targeting remote mode.
- Legacy `CLAWDBOT_GATEWAY_*` env vars are only used by compatibility call paths; probe/status/auth resolution uses `OPENCLAW_GATEWAY_*` only.
- Gateway env overrides use `OPENCLAW_GATEWAY_*` only.
## Chat UI over SSH

View File

@@ -51,7 +51,7 @@ Examples of inactive surfaces:
- In local mode without those remote surfaces:
- `gateway.remote.token` is active when token auth can win and no env/auth token is configured.
- `gateway.remote.password` is active only when password auth can win and no env/auth password is configured.
- `gateway.auth.token` SecretRef is inactive for startup auth resolution when `OPENCLAW_GATEWAY_TOKEN` (or `CLAWDBOT_GATEWAY_TOKEN`) is set, because env token input wins for that runtime.
- `gateway.auth.token` SecretRef is inactive for startup auth resolution when `OPENCLAW_GATEWAY_TOKEN` is set, because env token input wins for that runtime.
## Gateway auth surface diagnostics

View File

@@ -313,6 +313,8 @@ schema:
- `channels.googlechat.dangerouslyAllowNameMatching`
- `channels.googlechat.accounts.<accountId>.dangerouslyAllowNameMatching`
- `channels.msteams.dangerouslyAllowNameMatching`
- `channels.synology-chat.dangerouslyAllowNameMatching` (extension channel)
- `channels.synology-chat.accounts.<accountId>.dangerouslyAllowNameMatching` (extension channel)
- `channels.zalouser.dangerouslyAllowNameMatching` (extension channel)
- `channels.irc.dangerouslyAllowNameMatching` (extension channel)
- `channels.irc.accounts.<accountId>.dangerouslyAllowNameMatching` (extension channel)

View File

@@ -546,8 +546,8 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
</Accordion>
<Accordion title="Can I use Claude Max subscription without an API key?">
Yes. You can authenticate with a **setup-token**
instead of an API key. This is the subscription path.
Yes. You can either use a **setup-token** or reuse a local **Claude CLI**
login on the gateway host.
Claude Pro/Max subscriptions **do not include an API key**, so this is the
technical path for subscription accounts. But this is your decision: Anthropic
@@ -572,7 +572,12 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
</Accordion>
<Accordion title="Do you support Claude subscription auth (Claude Pro or Max)?">
Yes - via **setup-token**. OpenClaw no longer reuses Claude Code CLI OAuth tokens; use a setup-token or an Anthropic API key. Generate the token anywhere and paste it on the gateway host. See [Anthropic](/providers/anthropic) and [OAuth](/concepts/oauth).
Yes. You can either:
- use a **setup-token**
- reuse a local **Claude CLI** login on the gateway host with `openclaw models auth login --provider anthropic --method cli --set-default`
Setup-token is still supported. Claude CLI migration is simpler when the gateway host already runs Claude Code. See [Anthropic](/providers/anthropic) and [OAuth](/concepts/oauth).
Important: this is technical compatibility, not a policy guarantee. Anthropic
has blocked some subscription usage outside Claude Code in the past.
@@ -2146,17 +2151,15 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
<Accordion title='Why do I see "Unknown model: minimax/MiniMax-M2.7"?'>
This means the **provider isn't configured** (no MiniMax provider config or auth
profile was found), so the model can't be resolved. A fix for this detection is
in **2026.1.12** (unreleased at the time of writing).
profile was found), so the model can't be resolved.
Fix checklist:
1. Upgrade to **2026.1.12** (or run from source `main`), then restart the gateway.
1. Upgrade to a current OpenClaw release (or run from source `main`), then restart the gateway.
2. Make sure MiniMax is configured (wizard or JSON), or that a MiniMax API key
exists in env/auth profiles so the provider can be injected.
3. Use the exact model id (case-sensitive): `minimax/MiniMax-M2.7`,
`minimax/MiniMax-M2.7-highspeed`, `minimax/MiniMax-M2.5`, or
`minimax/MiniMax-M2.5-highspeed`.
3. Use the exact model id (case-sensitive): `minimax/MiniMax-M2.7` or
`minimax/MiniMax-M2.7-highspeed`.
4. Run:
```bash

View File

@@ -23,6 +23,7 @@ This doc is a “how we test” guide:
Most days:
- Full gate (expected before push): `pnpm build && pnpm check && pnpm test`
- Faster local full-suite run on a roomy machine: `pnpm test:max`
When you touch tests or want extra confidence:
@@ -54,11 +55,19 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
- Should be fast and stable
- Scheduler note:
- `pnpm test` now keeps a small checked-in behavioral manifest for true pool/isolation overrides and a separate timing snapshot for the slowest unit files.
- Shared unit coverage now defaults to `threads`, while the manifest keeps the measured fork-only exceptions and heavy singleton lanes explicit.
- The extension suite (`vitest.extensions.config.ts`) also now defaults to `threads`; the March 22, 2026 direct full-suite control run passed clean without extension-specific fork exceptions.
- The channel suite (`vitest.channels.config.ts`) now also defaults to `threads`; the March 22, 2026 direct full-suite control run passed clean without channel-specific fork exceptions.
- Extension-only local runs now also use a checked-in extensions timing snapshot plus a slightly coarser shared batch target on high-memory hosts, so the shared extensions lane avoids spawning an extra batch when two measured shared runs are enough.
- High-memory local extension shared batches also run with a slightly higher worker cap than before, which shortened the two remaining shared extension batches without changing the isolated extension lanes.
- High-memory local channel runs now reuse the checked-in channel timing snapshot to split the shared channels lane into a few measured batches instead of one long shared worker.
- High-memory local channel shared batches also run with a slightly lower worker cap than shared unit batches, which helped targeted channel reruns avoid CPU oversubscription once isolated channel lanes are already in flight.
- Targeted local channel reruns now start splitting shared channel work a bit earlier, which keeps medium-sized targeted reruns from leaving one oversized shared channel batch on the critical path.
- Targeted local unit reruns also split medium-sized shared unit selections into measured batches, which helps large focused reruns overlap instead of waiting behind one long shared unit lane.
- High-memory local multi-surface runs also use slightly coarser shared `unit-fast` batches so the mixed planner spends less time spinning up extra shared unit workers before the later surfaces can overlap.
- Shared unit, extension, channel, and gateway runs all stay on Vitest `forks`.
- The wrapper keeps measured fork-isolated exceptions and heavy singleton lanes explicit in `test/fixtures/test-parallel.behavior.json`.
- The wrapper peels the heaviest measured files into dedicated lanes instead of relying on a growing hand-maintained exclusion list.
- Refresh the timing snapshot with `pnpm test:perf:update-timings` after major suite shape changes.
- For surface-only local runs, unit, extension, and channel shared lanes can overlap their isolated hotspots instead of waiting behind one serial prefix.
- For multi-surface local runs, the wrapper keeps the shared surface phases ordered, but batches inside the same shared phase now fan out together, deferred isolated work can overlap the next shared phase, and spare `unit-fast` headroom now starts that deferred work earlier instead of leaving those slots idle.
- Refresh the timing snapshots with `pnpm test:perf:update-timings` and `pnpm test:perf:update-timings:extensions` after major suite shape changes.
- Embedded runner note:
- When you change message-tool discovery inputs or compaction runtime context,
keep both levels of coverage.
@@ -72,17 +81,19 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
sufficient substitute for those integration paths.
- Pool note:
- Base Vitest config still defaults to `forks`.
- Unit wrapper lanes default to `threads`, with explicit manifest fork-only exceptions.
- Extension scoped config defaults to `threads`.
- Channel scoped config defaults to `threads`.
- Unit, channel, extension, and gateway wrapper lanes all default to `forks`.
- Unit, channel, and extension configs default to `isolate: false` for faster file startup.
- `pnpm test` also passes `--isolate=false` at the wrapper level.
- Opt back into Vitest file isolation with `OPENCLAW_TEST_ISOLATE=1 pnpm test`.
- `OPENCLAW_TEST_NO_ISOLATE=0` or `OPENCLAW_TEST_NO_ISOLATE=false` also force isolated runs.
- Fast-local iteration note:
- `pnpm test:changed` runs the wrapper with `--changed origin/main`.
- `pnpm test:changed:max` keeps the same changed-file filter but uses the wrapper's aggressive local planner profile.
- `pnpm test:max` exposes that same planner profile for a full local run.
- On supported local Node versions, including Node 25, the normal profile can use top-level lane parallelism. `pnpm test:max` still pushes the planner harder when you want a more aggressive local run.
- The base Vitest config marks the wrapper manifests/config files as `forceRerunTriggers` so changed-mode reruns stay correct when scheduler inputs change.
- Use `OPENCLAW_VITEST_FS_MODULE_CACHE=1` for repeated local reruns on a stable branch when transform cost dominates.
- Vitest's filesystem module cache is now enabled by default for Node-side test reruns.
- Opt out with `OPENCLAW_VITEST_FS_MODULE_CACHE=0` or `OPENCLAW_VITEST_FS_MODULE_CACHE=false` if you suspect stale transform cache behavior.
- Perf-debug note:
- `pnpm test:perf:imports` enables Vitest import-duration reporting plus import-breakdown output.
- `pnpm test:perf:imports:changed` scopes the same profiling view to files changed since `origin/main`.
@@ -190,7 +201,7 @@ Live tests are split into two layers so we can isolate failures:
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
- Set `OPENCLAW_LIVE_MODELS=modern` (or `all`, alias for modern) to actually run this suite; otherwise it skips to keep `pnpm test:live` focused on gateway smoke
- How to select models:
- `OPENCLAW_LIVE_MODELS=modern` to run the modern allowlist (Opus/Sonnet/Haiku 4.5, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.5, Grok 4)
- `OPENCLAW_LIVE_MODELS=modern` to run the modern allowlist (Opus/Sonnet/Haiku 4.5, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4)
- `OPENCLAW_LIVE_MODELS=all` is an alias for the modern allowlist
- or `OPENCLAW_LIVE_MODELS="openai/gpt-5.2,anthropic/claude-opus-4-6,..."` (comma allowlist)
- How to select providers:
@@ -221,7 +232,7 @@ Live tests are split into two layers so we can isolate failures:
- How to enable:
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
- How to select models:
- Default: modern allowlist (Opus/Sonnet/Haiku 4.5, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.5, Grok 4)
- Default: modern allowlist (Opus/Sonnet/Haiku 4.5, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4)
- `OPENCLAW_LIVE_GATEWAY_MODELS=all` is an alias for the modern allowlist
- Or set `OPENCLAW_LIVE_GATEWAY_MODELS="provider/model"` (or comma list) to narrow
- How to select providers (avoid “OpenRouter everything”):
@@ -294,6 +305,19 @@ OPENCLAW_LIVE_CLI_BACKEND=1 \
pnpm test:live src/gateway/gateway-cli-backend.live.test.ts
```
Docker recipe:
```bash
pnpm test:docker:live-cli-backend
```
Notes:
- The Docker runner lives at `scripts/test-live-cli-backend-docker.sh`.
- It runs the live CLI-backend smoke inside the repo Docker image as the non-root `node` user, because Claude CLI rejects `bypassPermissions` when invoked as root.
- For `claude-cli`, it installs the Linux `@anthropic-ai/claude-code` package into a cached writable prefix at `OPENCLAW_DOCKER_CLI_TOOLS_DIR` (default: `~/.cache/openclaw/docker-cli-tools`).
- It copies `~/.claude` into the container when available, but on machines where Claude auth is backed by `ANTHROPIC_API_KEY`, it also preserves `ANTHROPIC_API_KEY` / `ANTHROPIC_API_KEY_OLD` for the child Claude CLI via `OPENCLAW_LIVE_CLI_BACKEND_PRESERVE_ENV`.
### Recommended live recipes
Narrow, explicit allowlists are fastest and least flaky:
@@ -305,7 +329,7 @@ Narrow, explicit allowlists are fastest and least flaky:
- `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.2" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
- Tool calling across several providers:
- `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.2,anthropic/claude-opus-4-6,google/gemini-3-flash-preview,zai/glm-4.7,minimax/minimax-m2.5" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
- `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.2,anthropic/claude-opus-4-6,google/gemini-3-flash-preview,zai/glm-4.7,minimax/MiniMax-M2.7" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
- Google focus (Gemini API key + Antigravity):
- Gemini (API key): `OPENCLAW_LIVE_GATEWAY_MODELS="google/gemini-3-flash-preview" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
@@ -334,10 +358,10 @@ This is the “common models” run we expect to keep working:
- Google (Gemini API): `google/gemini-3.1-pro-preview` and `google/gemini-3-flash-preview` (avoid older Gemini 2.x models)
- Google (Antigravity): `google-antigravity/claude-opus-4-6-thinking` and `google-antigravity/gemini-3-flash`
- Z.AI (GLM): `zai/glm-4.7`
- MiniMax: `minimax/minimax-m2.5`
- MiniMax: `minimax/MiniMax-M2.7`
Run gateway smoke with tools + image:
`OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.2,openai-codex/gpt-5.4,anthropic/claude-opus-4-6,google/gemini-3.1-pro-preview,google/gemini-3-flash-preview,google-antigravity/claude-opus-4-6-thinking,google-antigravity/gemini-3-flash,zai/glm-4.7,minimax/minimax-m2.5" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
`OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.2,openai-codex/gpt-5.4,anthropic/claude-opus-4-6,google/gemini-3.1-pro-preview,google/gemini-3-flash-preview,google-antigravity/claude-opus-4-6-thinking,google-antigravity/gemini-3-flash,zai/glm-4.7,minimax/MiniMax-M2.7" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
### Baseline: tool calling (Read + optional Exec)
@@ -347,7 +371,7 @@ Pick at least one per provider family:
- Anthropic: `anthropic/claude-opus-4-6` (or `anthropic/claude-sonnet-4-6`)
- Google: `google/gemini-3-flash-preview` (or `google/gemini-3.1-pro-preview`)
- Z.AI (GLM): `zai/glm-4.7`
- MiniMax: `minimax/minimax-m2.5`
- MiniMax: `minimax/MiniMax-M2.7`
Optional additional coverage (nice to have):
@@ -423,20 +447,40 @@ If you want to rely on env keys (e.g. exported in your `~/.profile`), run local
## Docker runners (optional "works in Linux" checks)
These run `pnpm test:live` inside the repo Docker image, mounting your local config dir and workspace (and sourcing `~/.profile` if mounted). They also bind-mount CLI auth homes like `~/.codex`, `~/.claude`, `~/.qwen`, and `~/.minimax` when present, then copy them into the container home before the run so external-CLI OAuth can refresh tokens without mutating the host auth store:
These Docker runners split into two buckets:
- Live-model runners: `test:docker:live-models` and `test:docker:live-gateway` run `pnpm test:live` inside the repo Docker image, mounting your local config dir and workspace (and sourcing `~/.profile` if mounted).
- Container smoke runners: `test:docker:openwebui`, `test:docker:onboard`, `test:docker:gateway-network`, and `test:docker:plugins` boot one or more real containers and verify higher-level integration paths.
The live-model Docker runners also bind-mount only the needed CLI auth homes (or all supported ones when the run is not narrowed), then copy them into the container home before the run so external-CLI OAuth can refresh tokens without mutating the host auth store:
- Direct models: `pnpm test:docker:live-models` (script: `scripts/test-live-models-docker.sh`)
- CLI backend smoke: `pnpm test:docker:live-cli-backend` (script: `scripts/test-live-cli-backend-docker.sh`)
- Gateway + dev agent: `pnpm test:docker:live-gateway` (script: `scripts/test-live-gateway-models-docker.sh`)
- Open WebUI live smoke: `pnpm test:docker:openwebui` (script: `scripts/e2e/openwebui-docker.sh`)
- Onboarding wizard (TTY, full scaffolding): `pnpm test:docker:onboard` (script: `scripts/e2e/onboard-docker.sh`)
- Gateway networking (two containers, WS auth + health): `pnpm test:docker:gateway-network` (script: `scripts/e2e/gateway-network-docker.sh`)
- Plugins (custom extension load + registry smoke): `pnpm test:docker:plugins` (script: `scripts/e2e/plugins-docker.sh`)
- Plugins (install smoke + `/plugin` alias + Claude-bundle restart semantics): `pnpm test:docker:plugins` (script: `scripts/e2e/plugins-docker.sh`)
The live-model Docker runners also bind-mount the current checkout read-only and
stage it into a temporary workdir inside the container. This keeps the runtime
image slim while still running Vitest against your exact local source/config.
They also set `OPENCLAW_SKIP_CHANNELS=1` so gateway live probes do not start
real Telegram/Discord/etc. channel workers inside the container.
`test:docker:live-models` still runs `pnpm test:live`, so pass through
`OPENCLAW_LIVE_GATEWAY_*` as well when you need to narrow or exclude gateway
live coverage from that Docker lane.
`test:docker:openwebui` is a higher-level compatibility smoke: it starts an
OpenClaw gateway container with the OpenAI-compatible HTTP endpoints enabled,
starts a pinned Open WebUI container against that gateway, signs in through
Open WebUI, verifies `/api/models` exposes `openclaw/default`, then sends a
real chat request through Open WebUI's `/api/chat/completions` proxy.
The first run can be noticeably slower because Docker may need to pull the
Open WebUI image and Open WebUI may need to finish its own cold-start setup.
This lane expects a usable live model key, and `OPENCLAW_PROFILE_FILE`
(`~/.profile` by default) is the primary way to provide it in Dockerized runs.
Successful runs print a small JSON payload like `{ "ok": true, "model":
"openclaw/default", ... }`.
Manual ACP plain-language thread smoke (not CI):
@@ -448,10 +492,17 @@ Useful env vars:
- `OPENCLAW_CONFIG_DIR=...` (default: `~/.openclaw`) mounted to `/home/node/.openclaw`
- `OPENCLAW_WORKSPACE_DIR=...` (default: `~/.openclaw/workspace`) mounted to `/home/node/.openclaw/workspace`
- `OPENCLAW_PROFILE_FILE=...` (default: `~/.profile`) mounted to `/home/node/.profile` and sourced before running tests
- External CLI auth dirs under `$HOME` (`.codex`, `.claude`, `.qwen`, `.minimax`) are mounted read-only under `/host-auth/...`, then copied into `/home/node/...` before tests start
- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...` (default: `~/.cache/openclaw/docker-cli-tools`) mounted to `/home/node/.npm-global` for cached CLI installs inside Docker
- External CLI auth dirs under `$HOME` are mounted read-only under `/host-auth/...`, then copied into `/home/node/...` before tests start
- Default: mount all supported dirs (`.codex`, `.claude`, `.minimax`)
- Narrowed provider runs mount only the needed dirs inferred from `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS`
- Override manually with `OPENCLAW_DOCKER_AUTH_DIRS=all`, `OPENCLAW_DOCKER_AUTH_DIRS=none`, or a comma list like `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex`
- `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...` to narrow the run
- `OPENCLAW_LIVE_GATEWAY_PROVIDERS=...` / `OPENCLAW_LIVE_PROVIDERS=...` to filter providers in-container
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` to ensure creds come from the profile store (not env)
- `OPENCLAW_OPENWEBUI_MODEL=...` to choose the model exposed by the gateway for the Open WebUI smoke
- `OPENCLAW_OPENWEBUI_PROMPT=...` to override the nonce-check prompt used by the Open WebUI smoke
- `OPENWEBUI_IMAGE=...` to override the pinned Open WebUI image tag
## Docs sanity

View File

@@ -54,7 +54,7 @@ OpenClaw is a **self-hosted gateway** that connects your favorite chat apps —
- **Agent-native**: built for coding agents with tool use, sessions, memory, and multi-agent routing
- **Open source**: MIT licensed, community-driven
**What do you need?** Node 24 (recommended), or Node 22 LTS (`22.16+`) for compatibility, an API key from your chosen provider, and 5 minutes. For best quality and security, use the strongest latest-generation model available.
**What do you need?** Node 24 (recommended), or Node 22 LTS (`22.14+`) for compatibility, an API key from your chosen provider, and 5 minutes. For best quality and security, use the strongest latest-generation model available.
## How it works

View File

@@ -48,7 +48,7 @@ The Ansible playbook installs and configures:
1. **Tailscale** -- mesh VPN for secure remote access
2. **UFW firewall** -- SSH + Tailscale ports only
3. **Docker CE + Compose V2** -- for agent sandboxes
4. **Node.js 24 + pnpm** -- runtime dependencies (Node 22 LTS, currently `22.16+`, remains supported)
4. **Node.js 24 + pnpm** -- runtime dependencies (Node 22 LTS, currently `22.14+`, remains supported)
5. **OpenClaw** -- host-based, not containerized
6. **Systemd service** -- auto-start with security hardening

View File

@@ -41,7 +41,7 @@ Bun is an optional local runtime for running TypeScript directly (`bun run ...`,
Bun blocks dependency lifecycle scripts unless explicitly trusted. For this repo, the commonly blocked scripts are not required:
- `@whiskeysockets/baileys` `preinstall` -- checks Node major >= 20 (OpenClaw defaults to Node 24 and still supports Node 22 LTS, currently `22.16+`)
- `@whiskeysockets/baileys` `preinstall` -- checks Node major >= 20 (OpenClaw defaults to Node 24 and still supports Node 22 LTS, currently `22.14+`)
- `protobufjs` `postinstall` -- emits warnings about incompatible version schemes (no build artifacts)
If you hit a runtime issue that requires these scripts, trust them explicitly:

View File

@@ -55,6 +55,10 @@ Docker is **optional**. Use it only if you want a containerized gateway or to va
- generate a gateway token and write it to `.env`
- start the gateway via Docker Compose
During setup, pre-start onboarding and config writes run through
`openclaw-gateway` directly. `openclaw-cli` is for commands you run after
the gateway container already exists.
</Step>
<Step title="Open the Control UI">
@@ -94,7 +98,15 @@ If you prefer to run each step yourself instead of using the setup script:
```bash
docker build -t openclaw:local -f Dockerfile .
docker compose run --rm openclaw-cli onboard
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
dist/index.js onboard --mode local --no-install-daemon
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
dist/index.js config set gateway.mode local
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
dist/index.js config set gateway.bind lan
docker compose run --rm --no-deps --entrypoint node openclaw-gateway \
dist/index.js config set gateway.controlUi.allowedOrigins \
'["http://localhost:18789","http://127.0.0.1:18789"]' --strict-json
docker compose up -d openclaw-gateway
```
@@ -104,6 +116,13 @@ or `OPENCLAW_HOME_VOLUME`, the setup script writes `docker-compose.extra.yml`;
include it with `-f docker-compose.yml -f docker-compose.extra.yml`.
</Note>
<Note>
Because `openclaw-cli` shares `openclaw-gateway`'s network namespace, it is a
post-start tool. Before `docker compose up -d openclaw-gateway`, run onboarding
and setup-time config writes through `openclaw-gateway` with
`--no-deps --entrypoint node`.
</Note>
### Environment variables
The setup script accepts these optional environment variables:

View File

@@ -45,7 +45,7 @@ For all flags and CI/automation options, see [Installer internals](/install/inst
## System requirements
- **Node 24** (recommended) or Node 22.16+ — the installer script handles this automatically
- **Node 24** (recommended) or Node 22.14+ — the installer script handles this automatically
- **macOS, Linux, or Windows** — both native Windows and WSL2 are supported; WSL2 is more stable. See [Windows](/platforms/windows).
- `pnpm` is only needed if you build from source

View File

@@ -71,7 +71,7 @@ Recommended for most interactive installs on macOS/Linux/WSL.
Supports macOS and Linux (including WSL). If macOS is detected, installs Homebrew if missing.
</Step>
<Step title="Ensure Node.js 24 by default">
Checks Node version and installs Node 24 if needed (Homebrew on macOS, NodeSource setup scripts on Linux apt/dnf/yum). OpenClaw still supports Node 22 LTS, currently `22.16+`, for compatibility.
Checks Node version and installs Node 24 if needed (Homebrew on macOS, NodeSource setup scripts on Linux apt/dnf/yum). OpenClaw still supports Node 22 LTS, currently `22.14+`, for compatibility.
</Step>
<Step title="Ensure Git">
Installs Git if missing.
@@ -257,7 +257,7 @@ Designed for environments where you want everything under a local prefix (defaul
Requires PowerShell 5+.
</Step>
<Step title="Ensure Node.js 24 by default">
If missing, attempts install via winget, then Chocolatey, then Scoop. Node 22 LTS, currently `22.16+`, remains supported for compatibility.
If missing, attempts install via winget, then Chocolatey, then Scoop. Node 22 LTS, currently `22.14+`, remains supported for compatibility.
</Step>
<Step title="Install OpenClaw">
- `npm` method (default): global npm install using selected `-Tag`

View File

@@ -9,7 +9,7 @@ read_when:
# Node.js
OpenClaw requires **Node 22.16 or newer**. **Node 24 is the default and recommended runtime** for installs, CI, and release workflows. Node 22 remains supported via the active LTS line. The [installer script](/install#alternative-install-methods) will detect and install Node automatically — this page is for when you want to set up Node yourself and make sure everything is wired up correctly (versions, PATH, global installs).
OpenClaw requires **Node 22.14 or newer**. **Node 24 is the default and recommended runtime** for installs, CI, and release workflows. Node 22 remains supported via the active LTS line. The [installer script](/install#alternative-install-methods) will detect and install Node automatically — this page is for when you want to set up Node yourself and make sure everything is wired up correctly (versions, PATH, global installs).
## Check your version
@@ -17,7 +17,7 @@ OpenClaw requires **Node 22.16 or newer**. **Node 24 is the default and recommen
node -v
```
If this prints `v24.x.x` or higher, you're on the recommended default. If it prints `v22.16.x` or higher, you're on the supported Node 22 LTS path, but we still recommend upgrading to Node 24 when convenient. If Node isn't installed or the version is too old, pick an install method below.
If this prints `v24.x.x` or higher, you're on the recommended default. If it prints `v22.14.x` or higher, you're on the supported Node 22 LTS path, but we still recommend upgrading to Node 24 when convenient. If Node isn't installed or the version is too old, pick an install method below.
## Install Node

View File

@@ -100,7 +100,7 @@ Notes:
- In local mode, node host intentionally ignores `gateway.remote.token` / `gateway.remote.password`.
- In remote mode, `gateway.remote.token` / `gateway.remote.password` are eligible per remote precedence rules.
- If active local `gateway.auth.*` SecretRefs are configured but unresolved, node-host auth fails closed.
- Legacy `CLAWDBOT_GATEWAY_*` env vars are intentionally ignored by node-host auth resolution.
- Node-host auth resolution only honors `OPENCLAW_GATEWAY_*` env vars.
### Start a node host (service)

View File

@@ -15,7 +15,7 @@ Native Linux companion apps are planned. Contributions are welcome if you want t
## Beginner quick path (VPS)
1. Install Node 24 (recommended; Node 22 LTS, currently `22.16+`, still works for compatibility)
1. Install Node 24 (recommended; Node 22 LTS, currently `22.14+`, still works for compatibility)
2. `npm i -g openclaw@latest`
3. `openclaw onboard --install-daemon`
4. From your laptop: `ssh -N -L 18789:127.0.0.1:18789 <user>@<host>`

Some files were not shown because too many files have changed in this diff Show More