Compare commits

...

104 Commits

Author SHA1 Message Date
huntharo
bb77ee5f0c xAI: clarify search tool auth fallback 2026-03-29 13:36:24 -04:00
huntharo
ad6ac51fc8 xAI: reuse auth profiles for search tools 2026-03-29 13:36:24 -04:00
huntharo
1dc3d7a7e1 configure: reuse stored provider api keys 2026-03-29 13:36:24 -04:00
huntharo
ed17f0ab5f xAI: reduce auth trace log spam 2026-03-29 13:36:24 -04:00
huntharo
d3c0618525 configure: preserve chosen provider model 2026-03-29 13:36:24 -04:00
Tyler Yust
798e5f9501 plugin-sdk: fix provider setup import cycles 2026-03-29 09:59:52 -07:00
Peter Steinberger
56640a6725 fix(plugin-sdk): break vllm setup recursion 2026-03-29 17:55:09 +01:00
Keith Elliott
2d2e386b94 fix(matrix): resolve crypto bootstrap failure and multi-extension idHint warning (#53298)
Merged via squash.

Prepared head SHA: 6f5813ffff
Co-authored-by: keithce <2086282+keithce@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 12:38:10 -04:00
Peter Steinberger
ba7911bd16 ci: extend long-running test lane timeouts 2026-03-29 16:48:50 +01:00
Peter Steinberger
354bc01f29 ci: raise test workflow timeouts 2026-03-29 16:00:51 +01:00
Peter Steinberger
637b4c8193 refactor: move remaining provider policy into plugins 2026-03-29 23:05:58 +09:00
Peter Steinberger
edc58a6864 refactor: generalize provider transport hooks 2026-03-29 23:05:58 +09:00
Peter Steinberger
8109195ad8 fix(plugin-sdk): avoid recursive bundled facade loads 2026-03-29 15:00:25 +01:00
Peter Steinberger
24d16c39ad refactor(plugin-sdk): remove source alias residue 2026-03-29 14:53:03 +01:00
Peter Steinberger
e6116769b4 build(plugin-sdk): verify dist facade exports 2026-03-29 14:53:03 +01:00
Peter Steinberger
2c9bc0bb78 chore(deps): bump workspace dependencies 2026-03-29 14:41:58 +01:00
Peter Steinberger
2dd29db464 fix: ease bundled browser plugin recovery 2026-03-29 22:39:30 +09:00
Peter Steinberger
f1af7d66d2 chore: bump version to 2026.3.29 2026-03-29 14:33:12 +01:00
Thomas M
0a01386756 fix: canonicalize session keys at write time (#30654) (thanks @thomasxm)
* fix: canonicalize session keys at write time to prevent orphaned sessions (#29683)

resolveSessionKey() uses hardcoded DEFAULT_AGENT_ID="main", but all read
paths canonicalize via cfg. When the configured default agent differs
(e.g. "ops" with mainKey "work"), writes produce "agent:main:main" while
reads look up "agent:ops:work", orphaning transcripts on every restart.

Fix all three write-path call sites by wrapping with
canonicalizeMainSessionAlias:
- initSessionState (auto-reply/reply/session.ts)
- runWebHeartbeatOnce (web/auto-reply/heartbeat-runner.ts)
- resolveCronAgentSessionKey (cron/isolated-agent/session-key.ts)

Add startup migration (migrateOrphanedSessionKeys) to rename existing
orphaned keys to canonical form, merging by most-recent updatedAt.

* fix: address review — track agent IDs in migration map, align snapshot key

P1: migrateOrphanedSessionKeys now tracks agentId alongside each store
path in a Map instead of inferring from the filesystem path. This
correctly handles custom session.store templates outside the default
agents/<id>/ layout.

P2: Pass the already-canonicalized sessionKey to getSessionSnapshot so
the heartbeat snapshot reads/restores use the same key as the write path.

* fix: log migration results at all early return points

migrateOrphanedSessionKeys runs before detectLegacyStateMigrations, so
it can canonicalize legacy keys (e.g. "main" → "agent:main:main") before
the legacy detector sees them. This caused the early return path to skip
logging, breaking doctor-state-migrations tests that assert log.info was
called.

Extract logMigrationResults helper and call it at every return point.

* fix: handle shared stores and ~ expansion in migration

P1: When session.store has no {agentId}, all agents resolve to the same
file. Track all agentIds per store path (Map<path, Set<id>>) and run
canonicalization once per agent. Skip cross-agent "agent:main:*"
remapping when "main" is a legitimate configured agent sharing the store,
to avoid merging its data into another agent's namespace.

P2: Use expandHomePrefix (environment-aware ~ resolution) instead of
os.homedir() in resolveStorePathFromTemplate, matching the runtime
resolveStorePath behavior for OPENCLAW_HOME/HOME overrides.

* fix: narrow cross-agent remap to provable orphan aliases only

Only remap agent:main:* keys where the suffix is a main session alias
("main" or the configured mainKey). Other agent:main:* keys — hooks,
subagents, cron sessions, per-sender keys — may be intentional
cross-agent references and must not be silently moved into another
agent's namespace.

* fix: run orphan-key session migration at gateway startup (#29683)

* fix: canonicalize cross-agent legacy main aliases in session keys (#29683)

* fix: guard shared-store migration against cross-agent legacy alias remap (#29683)

* refactor: split session-key migration out of pr 30654

---------

Co-authored-by: Your Name <your_email@example.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 18:59:25 +05:30
Peter Steinberger
e0f0a1aa1f docs: clarify browser allowlist troubleshooting 2026-03-29 22:19:22 +09:00
wangchunyue
2c8c4e45f1 fix: preserve fallback prompt on model fallback for new sessions (#55632) (thanks @openperf)
* fix(agents): preserve original task prompt on model fallback for new sessions

* fix(agents): use dynamic transcript check for sessionHasHistory on fallback retry

Address Greptile review feedback: replace the static !isNewSession flag
with a dynamic sessionFileHasContent() check that reads the on-disk
transcript before each fallback retry. This correctly handles the edge
case where the primary model completes at least one assistant-response
turn (flushing the user message to disk) before failing - the fallback
now sends the recovery prompt instead of duplicating the original body.

The !isNewSession short-circuit is kept as a fast path so existing
sessions skip the file read entirely.

* fix(agents): address security vulnerabilities in session fallback logic

Fixes three medium-severity security issues identified by Aisle Security Analysis on PR #55632:
- CWE-400: Unbounded session transcript read in sessionFileHasContent()
- CWE-400: Symlink-following in sessionFileHasContent()
- CWE-201: Sensitive prompt replay to a different fallback provider

* fix(agents): use JSONL parsing for session history detection (CWE-703)

Replace bounded byte-prefix substring matching in sessionFileHasContent()
with line-by-line JSONL record parsing. The previous approach could miss
an assistant message when the preceding user content exceeded the 256KB
read limit, causing a false negative that blocks cross-provider fallback
entirely.

* fix(agents): preserve fallback prompt across providers

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 18:35:45 +05:30
wangchunyue
fc3f6fa51f fix: preserve node exec cwd on remote hosts (#50961) (thanks @openperf)
* fix(gateway): skip local workdir resolution for remote node execution

* chore: add inline comment for non-obvious node workdir skip

* fix: preserve node exec cwd on remote hosts (#50961) (thanks @openperf)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 17:46:49 +05:30
Tak Hoffman
5f85c4e69f Docs: add runtime semantics guidance to AGENTS 2026-03-29 06:57:56 -05:00
Ayaan Zaidi
ee701d6bad build: bump Android version to 2026.3.29 2026-03-29 17:15:23 +05:30
Mariano
92d0b3a557 Gateway: abstract task registry storage (#56927)
Merged via squash.

Prepared head SHA: 8db9b860e8
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-29 12:58:06 +02:00
Mariano
17c36b5093 Gateway: track background task lifecycle (#52518)
Merged via squash.

Prepared head SHA: 7c4554204e
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-29 12:48:02 +02:00
Peter Steinberger
270d0c5158 fix: avoid telegram plugin self-recursive sdk imports 2026-03-29 11:32:29 +01:00
Luke
88ca0b2c3f fix(status): handle node-only hosts on current main (#56718)
* Status: handle node-only hosts

* Status: address follow-up review nits

* Changelog: note node-only status fix

* Status: lazy-load node-only helper
2026-03-29 21:12:08 +11:00
nanakotsai
571da81a35 fix: keep openai-codex on HTTP responses transport 2026-03-29 15:04:38 +05:30
Vincent Koc
e06069c8c2 fix(sandbox): add CJK fonts to browser image (#56905) 2026-03-29 18:21:51 +09:00
助爪
443295448c Track ACP sessions_spawn runs and emit ACP lifecycle events (#40885)
* Fix ACP sessions_spawn lifecycle tracking

* fix(tests): resolve leftover merge markers in sessions spawn lifecycle test

* fix(agents): clarify acp spawn cleanup semantics

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-29 18:20:10 +09:00
Vincent Koc
a7a89fb680 fix(ci): retry actionlint release downloads 2026-03-29 18:11:38 +09:00
xingjie zhou
81193336d0 fix(acpx): read ACPX_PINNED_VERSION from package.json instead of hard… (#49089)
* fix(acpx): read ACPX_PINNED_VERSION from package.json instead of hardcoding

The hardcoded ACPX_PINNED_VERSION ("0.1.16") falls out of sync with the bundled acpx version in package.json every release, causing ACP runtime to be marked unavailable due to version mismatch (see #43997).

* Validate and sanitize ACPX version retrieval

Add validation for acpx version from package.json
2026-03-29 18:05:55 +09:00
Frank Yang
5adc50ce6b docs(changelog): add slack status reactions entry 2026-03-29 16:57:47 +08:00
Vincent Koc
7c50138f62 fix(plugins): keep built cli metadata scans lightweight 2026-03-29 17:55:17 +09:00
Hsiao A
cea7162490 feat(slack): status reaction lifecycle for tool/thinking progress indicators (#56430)
Merged via squash.

Prepared head SHA: 1ba5df3e3b
Co-authored-by: hsiaoa <70124331+hsiaoa@users.noreply.github.com>
Co-authored-by: frankekn <4488090+frankekn@users.noreply.github.com>
Reviewed-by: @frankekn
2026-03-29 16:49:53 +08:00
Vincent Koc
e28fdb08b8 docs: add LINE ACP support and plugin requireApproval hook docs
- LINE: document ACP conversation binding support (#45826)
- Plugins: document requireApproval in before_tool_call hook semantics (#55339)
2026-03-29 17:45:26 +09:00
Vincent Koc
2899ce5198 Update CHANGELOG.md 2026-03-29 17:42:04 +09:00
Vincent Koc
af694def5b fix(agents): fail closed on silent turns (#52593)
* fix(agents): fail closed on silent turns

* fix(agents): suppress all silent turn emissions

* fix(agents): pass silent turns into embedded subscribe
2026-03-29 17:40:20 +09:00
Vincent Koc
f897aba69a docs: add missing feature docs for Matrix E2EE thumbnails, LINE media, and CJK memory
- Matrix: note encrypted thumbnail behavior in E2EE rooms (#54711)
- LINE: add outbound media section for image/video/audio sends (#45826)
- Memory: document CJK trigram tokenization and chunk sizing
2026-03-29 17:26:02 +09:00
Vincent Koc
3aac43e30b docs: remove stale MiniMax M2.5 refs and add image generation docs
After the M2.7-only catalog trim (#54487), update 10 docs files:
- Replace removed M2.5/VL-01 model references across FAQ, wizard,
  config reference, local-models, and provider pages
- Make local-models guide model-agnostic (generic LM Studio placeholder)
- Add image-01 generation section to minimax.md
- Leave third-party catalogs (Synthetic, Venice) unchanged
2026-03-29 17:26:02 +09:00
Vincent Koc
57882f0351 fix(web-search): localize shared search cache (#54040)
* fix(web-search): localize shared search cache

* docs(changelog): note localized web search cache

* test(web-search): assert module-local cache behavior

* Update CHANGELOG.md
2026-03-29 17:25:07 +09:00
Vignesh Natarajan
4d54376483 Tests: stabilize shard-2 queue and channel state 2026-03-29 01:12:58 -07:00
Vignesh Natarajan
9c185faba9 Agents: cover subagent memory tool policy 2026-03-29 01:12:58 -07:00
factnest365-ops
6c85c82ba3 fix: allow memory_search and memory_get in sub-agent sessions
Remove memory_search and memory_get from SUBAGENT_TOOL_DENY_ALWAYS.

These are read-only tools with no side effects that are essential for
multi-agent setups relying on shared memory for context retrieval.

Rationale:
- Read-only tools (memory_search, memory_get) have no side effects and
  cannot modify state, send messages, or affect external systems
- Other read-only tools (read, web_search, web_fetch) are already
  available to sub-agents by default
- Multi-agent deployments with shared knowledge depend on memory tools
  for context retrieval
- The workaround (tools.subagents.tools.alsoAllow) works but requires
  manual configuration that contradicts memorySearch.enabled: true

Fixes #55385
2026-03-29 01:12:58 -07:00
Peter Steinberger
341e617c84 docs(plugins): refresh bundled plugin runtime docs 2026-03-29 09:10:39 +01:00
Peter Steinberger
caeeecf399 refactor(test): centralize bundled channel test roots 2026-03-29 09:10:39 +01:00
Peter Steinberger
8e0ab35b0e refactor(plugins): decouple bundled plugin runtime loading 2026-03-29 09:10:38 +01:00
Vincent Koc
1738d540f4 fix(gateway/auth): local trusted-proxy fallback to require token auth (#54536)
* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

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

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-29 17:05:00 +09:00
Vincent Koc
9777781001 fix(subagents): preserve requester agent for inline announces (#55998)
* fix(subagents): preserve requester agent on inline announce

* docs(changelog): remove maintainer follow-up entry
2026-03-29 17:00:05 +09:00
Vincent Koc
d6a4ec6a3d fix(telegram): sanitize invalid stream-order errors (#55999)
* fix(telegram): sanitize invalid stream order errors

* docs(changelog): remove maintainer follow-up entry
2026-03-29 16:59:15 +09:00
Vincent Koc
aec58d4cde fix(agents): repair btw reasoning and oauth snapshot refresh (#56001)
* fix(agents): repair btw reasoning and oauth snapshot refresh

* Update CHANGELOG.md

* test(agents): strengthen btw reasoning assertion
2026-03-29 16:58:49 +09:00
Peter Steinberger
f4d60478c9 test: reset plugin runtime state in optional tools tests 2026-03-29 08:52:12 +01:00
Ted Li
ebb919e311 fix: prefer transcript model in sessions list (#55628) (thanks @MonkeyLeeT)
* gateway: prefer transcript model in sessions list

* gateway: keep live subagent model in session rows

* gateway: prefer selected model until runtime refresh

* gateway: simplify session model identity selection

* gateway: avoid transcript model fallback on cost-only reads
2026-03-29 13:20:55 +05:30
Vignesh Natarajan
08b5206b19 chore(test): harden channel plugin registry against malformed runtime state 2026-03-29 00:47:39 -07:00
Vignesh Natarajan
8bdb518bde Memory/LanceDB: fix bundled runtime manifest lookup (#56623) 2026-03-29 00:37:46 -07:00
Peter Steinberger
c48e0f8e6a style: normalize import order and formatting 2026-03-29 16:33:22 +09:00
Peter Steinberger
04c976b43d refactor(markdown): share render-aware chunking 2026-03-29 16:33:22 +09:00
Peter Steinberger
c664b67796 fix: apply Mistral compat across proxy transports 2026-03-29 16:32:31 +09:00
Ayaan Zaidi
4a5885df3a fix(imessage): try all inbound echo ids 2026-03-29 13:00:01 +05:30
GodsBoy
bc9c074b2c fix(channels): use pinned channel registry for outbound adapter resolution
loadChannelOutboundAdapter (via createChannelRegistryLoader) was reading
from getActivePluginRegistry() — the unpinned active registry that gets
replaced whenever loadOpenClawPlugins() runs (config schema reads, plugin
status queries, tool listings, etc.).

After replacement, the active registry may omit channel entries or carry
them in setup mode without outbound adapters, causing:

  Outbound not configured for channel: telegram

The channel inbound path already uses the pinned registry
(getActivePluginChannelRegistry) which is frozen at gateway startup and
survives all subsequent registry replacements. This commit aligns the
outbound path to use the same pinned surface.

Adds a regression test that pins a registry with a telegram outbound
adapter, replaces the active registry with an empty one, then asserts
loadChannelOutboundAdapter still resolves the adapter.

Fixes #54745
Fixes #54013
2026-03-29 12:54:14 +05:30
Rohan Marr
b29e180ef4 fix: prevent self-chat dedupe false positives (#55359) (thanks @rmarr)
* fix(imessage): prevent self-chat dedupe false positives (#47830)

Move echo cache remember() to post-send only, add early return when
inbound message ID doesn't match cached IDs (prevents text-based
false positives in self-chat), and reduce text TTL from 5s to 3s.

Three targeted changes to fix silent user message loss in self-chat:

1. deliver.ts: Remove pre-send remember() call — cache only reflects
   successfully-delivered content, not pre-send full text.

2. echo-cache.ts: Skip text fallback when inbound has a valid message ID
   that doesn't match any cached outbound ID. In self-chat, sender == target
   so scopes collide; a user message with a fresh ID but matching text was
   incorrectly dropped as an echo.

3. echo-cache.ts: Reduce text TTL from 5000ms to 3000ms — agent echoes
   arrive within 1-2s, 5s was too wide.

Adds self-chat-dedupe.test.ts (7 tests) + updates deliver.test.ts.
BlueBubbles uses a different cache pattern — no changes needed there.

Closes #47830

* review(imessage): strip debug logs, bump echo TTL to 4s (#47830)

Bruce Phase 4 review changes:
- Remove all [IMSG-DEBUG] console.error calls from inbound-processing.ts
  and monitor-provider.ts (23 lines, left over from Phase 2 debug deploy)
- Bump SENT_MESSAGE_TEXT_TTL_MS from 3s to 4s in echo-cache.ts to give
  ~2s margin above the observed 2.2s echo arrival time under load
- Update TTL tests to reflect 4s TTL (expired at 5s, live at 3s)

* fix(imessage): add dedupe comments and canary/compat/TTL tests

* fix(imessage): address review feedback on echo cache, shadowing, and test IDs

* refactor(imessage): hoist inboundMessageId to eliminate duplicate computation (#47830)

* fix(imessage): unify self-chat echo matching

* fix: use inbound guid for self-chat echo matching (#55359) (thanks @rmarr)

---------

Co-authored-by: Rohan Marr <rmarr@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 12:51:17 +05:30
Vignesh Natarajan
1a0c3bf400 Memory: fix FTS-only branch compile on rebased main 2026-03-29 00:09:33 -07:00
Vignesh Natarajan
598f539be5 Memory: keep FTS-only indexing on reindex (#42714) 2026-03-29 00:06:49 -07:00
opriz
41c30f0c59 fix: populate FTS-only memory search without provider (#56473) (thanks @opriz)
* fix(memory): build FTS index when no embedding provider is available

* fix(memory): trigger full reindex on provider→FTS-only transition

* fix(memory): return FTS-only keyword hits at default threshold

* fix: keep FTS-only memory hits at default threshold (#56473) (thanks @opriz)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 12:22:35 +05:30
Vignesh Natarajan
4b137da582 Test: harden command queue test isolation (CI chore) 2026-03-28 23:43:03 -07:00
Vignesh Natarajan
e816d0968a Status: suppress false dual-stack loopback port warning (#53398) 2026-03-28 23:25:02 -07:00
Gustavo Madeira Santana
c7330eb716 Docs: audit Matrix CLI and setup docs 2026-03-29 01:48:18 -04:00
Gustavo Madeira Santana
efa4e3d83e Docs: audit Matrix channel docs 2026-03-29 01:48:14 -04:00
gfzhx
d458e1d05c fix(discord): do not bypass requireMention for configuredBinding channels 2026-03-29 11:17:15 +05:30
Jakub Rusz
7e7e45c2f3 feat(matrix): add draft streaming (edit-in-place partial replies) (#56387)
Merged via squash.

Prepared head SHA: 53e566bf30
Co-authored-by: jrusz <55534579+jrusz@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 01:43:02 -04:00
wangchunyue
dd61171f5b fix: prevent Telegram polling watchdog from dropping replies (#56343) (thanks @openperf)
* fix(telegram): prevent polling watchdog from aborting in-flight message delivery

The polling-stall watchdog only tracked getUpdates timestamps to detect
network stalls. When the agent takes >90s to process a message (common
with local/large models), getUpdates naturally pauses, and the watchdog
misidentifies this as a stall. It then calls fetchAbortController.abort(),
which cancels all in-flight Telegram API requests — including the
sendMessage call delivering the agent's reply. The message is silently
lost with no retry.

Track a separate lastApiActivityAt timestamp that is updated whenever
any Telegram API call (sendMessage, sendChatAction, etc.) completes
successfully. The watchdog now only triggers when both getUpdates AND
all other API activity have been silent beyond the threshold, proving
the network is genuinely stalled rather than just busy processing.

Update existing stall test to account for the new timestamp, and add a
regression test verifying that recent sendMessage activity suppresses
the watchdog.

Fixes #56065
Related: #53374, #54708

* fix(telegram): guard watchdog against in-flight API calls

* fix(telegram): bound watchdog API liveness

* fix: track newest watchdog API activity (#56343) (thanks @openperf)

* fix: note Telegram watchdog delivery fix (#56343) (thanks @openperf)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 11:11:28 +05:30
Shen Yiming
eee8e9679e fix(clawhub): sanitize archive temp filenames (openclaw#56779)
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-29 00:38:03 -05:00
Ayaan Zaidi
7a16a48198 fix: keep telegram plugin callbacks explicit 2026-03-29 10:56:36 +05:30
scoootscooob
5d81b64343 fix(exec): fail closed when sandbox is unavailable and harden deny followups (#56800)
* fix(exec): fail closed when sandbox is unavailable and harden deny followups

* docs(changelog): note exec fail-closed fix
2026-03-28 22:20:49 -07:00
Ayaan Zaidi
d5e59621a7 fix: keep telegram plugin fallback explicit (#45911) (thanks @suboss87) 2026-03-29 10:45:57 +05:30
Ayaan Zaidi
1791c7c304 fix: unify telegram exec approval auth 2026-03-29 10:45:57 +05:30
Subash Natarajan
aee7992629 fix(telegram): accept approval callbacks from forwarding target recipients
When approvals.exec.targets routes to a Telegram DM, the recipient
receives inline approval buttons but may not have explicit
channels.telegram.execApprovals configured. This adds a fallback
isTelegramExecApprovalTargetRecipient check so those DM recipients
can act on the buttons they were sent.

Includes accountId scoping for multi-bot deployments and 9 new tests.
2026-03-29 10:45:57 +05:30
Ayaan Zaidi
3a43401924 fix(ui): keep explicit ended steer targets 2026-03-29 10:27:07 +05:30
fuller-stack-dev
83808fe494 fix: wire control-ui steer and redirect (#54625) (thanks @fuller-stack-dev)
* feat(ui): wire /steer slash command to sessions.steer RPC

* feat(ui): wire /steer (soft inject) and /redirect (hard restart) slash commands

* test: use generic subagent names in steer/redirect tests

* fix(ui): exempt steer/redirect from busy-queue and guard sessions.list failures

* fix(ui): skip 'all' wildcard in steer/redirect target resolution

* test: register slash-command-executor test in vitest config

* fix(ui): restrict steer target to subagent keys and active sessions

Address two review issues in resolveSteerTarget:

P2: Replace resolveKillTargets with a dedicated resolveSteerSubagent
that matches only on subagent key suffix or label, not agent id.
This prevents false-positive targeting when the first word collides
with an agent id (e.g. "/steer main refine plan").

P1: Filter out ended sessions (endedAt set) so stale subagents with
reused names are not targeted.

* fix(ui): use shared generateUUID for steer idempotency key

* fix: restore telegram test to upstream state (merge artifact)

* fix(ui): track redirected run so Abort works and concurrent sends are blocked

* fix(ui): skip run tracking when /redirect targets a subagent session

* fix(ui): block idle steer runs

* fix(ui): dedupe steer slash command

* fix(ui): show pending steer state

* fix: wire control-ui steer and redirect (#54625) (thanks @fuller-stack-dev)

* fix: tighten steer target resolution (#54625) (thanks @fuller-stack-dev)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 10:15:58 +05:30
Ayaan Zaidi
e3faa99c6a fix(plugins): preserve gateway-bindable registry reuse
# Conflicts:
#	src/agents/runtime-plugins.test.ts
#	src/agents/runtime-plugins.ts
#	src/plugins/loader.ts
#	src/plugins/tools.ts
2026-03-29 09:59:06 +05:30
Vignesh Natarajan
bb9394e123 Changelog: note Control UI agents metadata/file fix 2026-03-28 21:22:59 -07:00
Vignesh Natarajan
5a0bd9036c Chore: regenerate protocol Swift agent summary models 2026-03-28 21:17:47 -07:00
Vignesh Natarajan
384a590e54 Agents UI: fix effective model and file hydration 2026-03-28 21:10:39 -07:00
Dewaldt Huysamen
27188fa39f fix: scope subagent registry reuse to tool loading (#56240) (thanks @GodsBoy)
* fix(plugins): reuse active registry for sub-agent tool resolution

* test(plugins): harden resolveRuntimePluginRegistry with per-field, caller-shape, and cold-start tests

Add 11 regression tests covering:
- R1: Per-field isolation (coreGatewayHandlers, includeSetupOnlyChannelPlugins,
  preferSetupRuntimeForChannelPlugins each independently prevent fallback;
  empty onlyPluginIds[] treated as non-gateway-scoped)
- R2: Caller-shape regression (tools.ts, memory-runtime.ts,
  channel-resolution.ts shapes fall back; web-search-providers.runtime.ts
  with onlyPluginIds does not)
- R3: Cold-start path (null active registry falls through to loadOpenClawPlugins)

Add debug logging to resolveRuntimePluginRegistry recording which exit path
was taken (no-options, cache-key-match, non-gateway-scoped fallback, fresh load).

* refactor: simplify plugin registry resolution tests and trim happy-path debug logs

* fix(plugins): address review comments on registry fallback

- Fix cold-start test assertion: loadOpenClawPlugins always activates
  the registry (shouldActivate defaults to true), so getActivePluginRegistry()
  is not null after the call. Updated assertion to match actual behavior.

- Add safety comment documenting why the non-gateway-scoped fallback is
  safe despite cache-key mismatch: single-gateway-per-process model means
  sub-agents share workspaceDir, config, and env with the gateway.

* test(plugins): restructure per-field isolation tests to avoid load timeouts

Test isGatewayScopedLoad directly instead of going through the full
resolveRuntimePluginRegistry path which triggers expensive plugin
discovery. This fixes the includeSetupOnlyChannelPlugins test timing
out in CI while providing more precise coverage of the predicate.

* fix(plugins): expand safety comment to address startup-scoped registry concern

* fix(plugins): scope subagent registry reuse to tool loading

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 09:36:09 +05:30
Vignesh Natarajan
6883f688e8 Docker setup: force BuildKit for local builds 2026-03-28 20:46:35 -07:00
yuanchao
ec7f19e2ef fix(kimi): preserve valid Anthropic-compatible toolCall arguments in malformed-args repair path (openclaw#54491)
Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <7549002+yuanaichi@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 22:37:50 -05:00
Vignesh Natarajan
a2e4707cfe fix(agents): recover prefixed malformed tool-call JSON 2026-03-28 20:22:22 -07:00
Vignesh Natarajan
64da916590 fix(tui): stop hijacking j/k in model search 2026-03-28 19:48:00 -07:00
Tak Hoffman
dc64a86eb8 fix(tests): refresh mattermost monitor mocks 2026-03-28 21:37:22 -05:00
Vignesh Natarajan
61a0b02931 fix(tui): preserve optimistic user messages during active runs 2026-03-28 19:32:26 -07:00
Jackjin
a0407c7254 fix(line): preserve underscores inside words in stripMarkdown (#47465)
Fixes #46185.

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm test -- extensions/line/src/markdown-to-line.test.ts src/tts/prepare-text.test.ts

Note: `pnpm check` currently fails on unchanged `extensions/microsoft/speech-provider.test.ts` lines 108 and 139 on the rebased base, outside this PR diff.
2026-03-28 21:31:09 -05:00
Tak Hoffman
e61ffa68f1 fix(tests): refresh generated schema contracts 2026-03-28 21:19:11 -05:00
Edward-Qiang-2024
1c8758fbd5 Fix: Correctly estimate CJK character token count in context pruner (openclaw#39985)
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-extensions/context-pruning.test.ts src/utils/cjk-chars.test.ts

Co-authored-by: Edward-Qiang-2024 <176464463+Edward-Qiang-2024@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 21:16:52 -05:00
Vignesh Natarajan
7cf87c4e53 chore(ci): refresh generated schema snapshots 2026-03-28 19:13:36 -07:00
Peter Steinberger
14832ff9f0 build: update appcast for 2026.3.28 2026-03-29 03:12:14 +01:00
Tak Hoffman
38d7e808d9 test: refresh bundled plugin metadata 2026-03-28 21:07:36 -05:00
Extra Small
69a0a0edc5 fix(tts): use Chinese voice for CJK text in edge-tts provider (openclaw#52355)
Verified:
- pnpm test -- extensions/microsoft/speech-provider.test.ts extensions/microsoft/tts.test.ts

Notes:
- Rebases and refactor-port completed onto current main.
- No required GitHub checks were reported for this branch at merge time.

Co-authored-by: Extra Small <littleshuai.bot@gmail.com>
2026-03-28 21:06:48 -05:00
Tak Hoffman
f1970b8aef fix(line): normalize canonical ACP targets (#56713) 2026-03-28 21:05:34 -05:00
Vignesh Natarajan
89881379dc fix(config): allow plugin channels in hooks mappings 2026-03-28 19:00:05 -07:00
Tak Hoffman
3ce48aff66 Memory: add configurable FTS5 tokenizer for CJK text support (openclaw#56707)
Verified:
- pnpm build
- pnpm check
- pnpm test -- extensions/memory-core/src/memory/manager-search.test.ts packages/memory-host-sdk/src/host/query-expansion.test.ts
- pnpm test -- extensions/memory-core/src/memory/index.test.ts -t "reindexes when extraPaths change"
- pnpm test -- src/config/schema.base.generated.test.ts
- pnpm test -- src/media-understanding/image.test.ts
- pnpm test

Co-authored-by: Mitsuyuki Osabe <24588751+carrotRakko@users.noreply.github.com>
2026-03-28 20:53:29 -05:00
Tak Hoffman
6f7ff545dd fix(line): add ACP binding parity (#56700)
* fix(line): support ACP current-conversation binding

* fix(line): add ACP binding routing parity

* docs(changelog): note LINE ACP parity

* fix(line): accept canonical ACP binding targets
2026-03-28 20:52:31 -05:00
Masato Hoshino
9449e54f4f feat(line): add outbound media support for image, video, and audio
pnpm install --frozen-lockfile
pnpm build
pnpm check
pnpm vitest run extensions/line/src/channel.sendPayload.test.ts extensions/line/src/send.test.ts extensions/line/src/outbound-media.test.ts

Co-authored-by: masatohoshino <246810661+masatohoshino@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 20:51:16 -05:00
jnuyao
f93ccc3443 fix(ui): prevent marked from auto-linking adjacent CJK characters (openclaw#48410)
Verified:
- ui: pnpm test -- --run src/ui/markdown.test.ts
- local full gate relaxed for this run; no required GitHub checks reported on the branch

Co-authored-by: jnuyao <2928523+jnuyao@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 20:49:29 -05:00
1044 changed files with 26075 additions and 25284 deletions

View File

@@ -78,7 +78,7 @@ jobs:
needs: [preflight, build-bun-artifacts]
if: needs.preflight.outputs.run_bun_checks == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
timeout-minutes: 60
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.bun_checks_matrix) }}

View File

@@ -282,7 +282,7 @@ jobs:
needs: [preflight]
if: needs.preflight.outputs.run_checks_fast == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
timeout-minutes: 60
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.checks_fast_matrix) }}
@@ -325,7 +325,7 @@ jobs:
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
timeout-minutes: 60
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.checks_matrix) }}
@@ -416,7 +416,7 @@ jobs:
needs: [preflight]
if: needs.preflight.outputs.run_extension_fast == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
timeout-minutes: 60
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.extension_fast_matrix) }}
@@ -716,7 +716,7 @@ jobs:
needs: [preflight, build-artifacts]
if: always() && needs.preflight.outputs.run_checks_windows == 'true' && needs.build-artifacts.result == 'success'
runs-on: blacksmith-32vcpu-windows-2025
timeout-minutes: 20
timeout-minutes: 60
env:
NODE_OPTIONS: --max-old-space-size=6144
# Keep total concurrency predictable on the 32 vCPU runner.

View File

@@ -60,8 +60,11 @@ jobs:
ACTIONLINT_VERSION="1.7.11"
archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
base_url="https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}"
curl -sSfL -o "${archive}" "${base_url}/${archive}"
curl -sSfL -o checksums.txt "${base_url}/actionlint_${ACTIONLINT_VERSION}_checksums.txt"
# GitHub release downloads occasionally return transient 5xx responses.
# Retry all curl errors here so workflow-sanity does not fail closed on
# a one-off release edge outage.
curl --retry 5 --retry-delay 2 --retry-all-errors -sSfL -o "${archive}" "${base_url}/${archive}"
curl --retry 5 --retry-delay 2 --retry-all-errors -sSfL -o checksums.txt "${base_url}/actionlint_${ACTIONLINT_VERSION}_checksums.txt"
grep " ${archive}\$" checksums.txt | sha256sum -c -
tar -xzf "${archive}" actionlint
sudo install -m 0755 actionlint /usr/local/bin/actionlint

View File

@@ -1,7 +1,7 @@
# Repository Guidelines
- Repo: https://github.com/openclaw/openclaw
- In chat replies, file references must be repo-root relative only (example: `extensions/bluebubbles/src/channel.ts:80`); never absolute paths or `~/...`.
- In chat replies, file references must be repo-root relative only (example: `src/telegram/index.ts:80`); never absolute paths or `~/...`.
- Do not edit files covered by security-focused `CODEOWNERS` rules unless a listed owner explicitly asked for the change or is already reviewing it with you. Treat those paths as restricted surfaces, not drive-by cleanup.
## Project Structure & Module Organization
@@ -9,28 +9,28 @@
- Source code: `src/` (CLI wiring in `src/cli`, commands in `src/commands`, web provider in `src/provider-web.ts`, infra in `src/infra`, media pipeline in `src/media`).
- Tests: colocated `*.test.ts`.
- Docs: `docs/` (images, queue, Pi config). Built output lives in `dist/`.
- Nomenclature: use "plugin" / "plugins" in docs, UI, changelogs, and contributor guidance. `extensions/*` remains the internal directory/package path to avoid repo-wide churn from a rename.
- Bundled plugin naming: for repo-owned workspace plugins, keep the canonical plugin id aligned across `openclaw.plugin.json:id`, `extensions/<id>` by default, and package names anchored to the same id (`@openclaw/<id>` or approved suffix forms like `-provider`, `-plugin`, `-speech`, `-sandbox`, `-media-understanding`). Keep `openclaw.install.npmSpec` equal to the package name and `openclaw.channel.id` equal to the plugin id when present. Exceptions must be explicit and covered by the repo invariant test.
- Plugins: live under `extensions/*` (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them.
- Nomenclature: use "plugin" / "plugins" in docs, UI, changelogs, and contributor guidance. The bundled workspace plugin tree remains the internal package layout to avoid repo-wide churn from a rename.
- Bundled plugin naming: for repo-owned workspace plugins, keep the canonical plugin id aligned across `openclaw.plugin.json:id`, the default workspace folder name, and package names anchored to the same id (`@openclaw/<id>` or approved suffix forms like `-provider`, `-plugin`, `-speech`, `-sandbox`, `-media-understanding`). Keep `openclaw.install.npmSpec` equal to the package name and `openclaw.channel.id` equal to the plugin id when present. Exceptions must be explicit and covered by the repo invariant test.
- Plugins: live in the bundled workspace plugin tree (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them.
- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `openclaw` in `devDependencies` or `peerDependencies` instead (runtime resolves `openclaw/plugin-sdk` via jiti alias).
- Import boundaries: extension production code should treat `openclaw/plugin-sdk/*` plus local `api.ts` / `runtime-api.ts` barrels as the public surface. Do not import core `src/**`, `src/plugin-sdk-internal/**`, or another extension's `src/**` directly.
- Installers served from `https://openclaw.ai/*`: live in the sibling repo `../openclaw.ai` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`).
- Messaging channels: always consider **all** built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs).
- Core channel docs: `docs/channels/`
- Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing`
- Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`)
- When adding channels/extensions/apps/docs, update `.github/labeler.yml` and create matching GitHub labels (use existing channel/extension label colors).
- Bundled plugin channels: the workspace plugin tree (for example Matrix, Zalo, ZaloUser, Voice Call)
- When adding channels/plugins/apps/docs, update `.github/labeler.yml` and create matching GitHub labels (use existing channel/plugin label colors).
## Architecture Boundaries
- Start here for the repo map:
- `extensions/*` = bundled plugins and the closest example surface for third-party plugins
- bundled workspace plugin tree = bundled plugins and the closest example surface for third-party plugins
- `src/plugin-sdk/*` = the public plugin contract that extensions are allowed to import
- `src/channels/*` = core channel implementation details behind the plugin/channel boundary
- `src/plugins/*` = plugin discovery, manifest validation, loader, registry, and contract enforcement
- `src/gateway/protocol/*` = typed Gateway control-plane and node wire protocol
- Progressive disclosure lives in local boundary guides:
- `extensions/AGENTS.md`
- bundled-plugin-tree `AGENTS.md`
- `src/plugin-sdk/AGENTS.md`
- `src/channels/AGENTS.md`
- `src/plugins/AGENTS.md`
@@ -39,7 +39,7 @@
- Public docs: `docs/plugins/building-plugins.md`, `docs/plugins/architecture.md`, `docs/plugins/sdk-overview.md`, `docs/plugins/sdk-entrypoints.md`, `docs/plugins/sdk-runtime.md`, `docs/plugins/manifest.md`, `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/sdk-provider-plugins.md`
- Definition files: `src/plugin-sdk/plugin-entry.ts`, `src/plugin-sdk/core.ts`, `src/plugin-sdk/provider-entry.ts`, `src/plugin-sdk/channel-contract.ts`, `scripts/lib/plugin-sdk-entrypoints.json`, `package.json`
- Rule: extensions must cross into core only through `openclaw/plugin-sdk/*`, manifest metadata, and documented runtime helpers. Do not import `src/**` from extension production code.
- Rule: core code and tests must not deep-import bundled plugin internals such as `extensions/<id>/src/**` or `extensions/<id>/onboard.js`. If core needs a bundled plugin helper, expose it through `extensions/<id>/api.ts` and, when it is a real cross-package contract, through `src/plugin-sdk/<id>.ts`.
- Rule: core code and tests must not deep-import bundled plugin internals such as a plugin's `src/**` files or `onboard.js`. If core needs a bundled plugin helper, expose it through that plugin's `api.ts` and, when it is a real cross-package contract, through `src/plugin-sdk/<id>.ts`.
- Compatibility: new plugin seams are allowed, but they must be added as documented, backwards-compatible, versioned contracts. We have third-party plugins in the wild and do not break them casually.
- Channel boundary:
- Public docs: `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/architecture.md`
@@ -57,11 +57,11 @@
- Rule: protocol changes are contract changes. Prefer additive evolution; incompatible changes require explicit versioning, docs, and client/codegen follow-through.
- Bundled plugin contract boundary:
- Public docs: `docs/plugins/architecture.md`, `docs/plugins/manifest.md`, `docs/plugins/sdk-overview.md`
- Definition files: `src/plugins/contracts/registry.ts`, `src/plugins/types.ts`, `src/extensions/public-artifacts.ts`
- Definition files: `src/plugins/contracts/registry.ts`, `src/plugins/types.ts`, `src/plugins/public-artifacts.ts`
- Rule: keep manifest metadata, runtime registration, public SDK exports, and contract tests aligned. Do not create a hidden path around the declared plugin interfaces.
- Extension test boundary:
- Keep extension-owned onboarding/config/provider coverage under `extensions/<id>/**` when feasible.
- If core tests need bundled plugin behavior, consume it through public `src/plugin-sdk/<id>.ts` facades or `extensions/<id>/api.ts`, not private extension modules.
- Keep extension-owned onboarding/config/provider coverage under the owning bundled plugin package when feasible.
- If core tests need bundled plugin behavior, consume it through public `src/plugin-sdk/<id>.ts` facades or the plugin's `api.ts`, not private extension modules.
## Docs Linking (Mintlify)
@@ -145,10 +145,17 @@
- Formatting/linting via Oxlint and Oxfmt.
- Never add `@ts-nocheck` and do not add inline lint suppressions by default. Fix root causes first; only keep a suppression when the code is intentionally correct, the rule cannot express that safely, and the comment explains why.
- Do not disable `no-explicit-any`; prefer real types, `unknown`, or a narrow adapter/helper instead. Update Oxlint/Oxfmt config only when required.
- Prefer `zod` or existing schema helpers at external boundaries such as config, webhook payloads, CLI/JSON output, persisted JSON, and third-party API responses.
- Prefer discriminated unions when parameter shape changes runtime behavior.
- Prefer `Result<T, E>`-style outcomes and closed error-code unions for recoverable runtime decisions.
- Keep human-readable strings for logs, CLI output, and UI; do not use freeform strings as the source of truth for internal branching.
- Avoid `?? 0`, empty-string, empty-object, or magic-string sentinels when they can change runtime meaning silently.
- If introducing a new optional field or nullable semantic in core logic, prefer an explicit union or dedicated type when the value changes behavior.
- New runtime control-flow code should not branch on `error: string` or `reason: string` when a closed code union would be reasonable.
- Dynamic import guardrail: do not mix `await import("x")` and static `import ... from "x"` for the same module in production code paths. If you need lazy loading, create a dedicated `*.runtime.ts` boundary (that re-exports from `x`) and dynamically import that boundary from lazy callers only.
- Dynamic import verification: after refactors that touch lazy-loading/module boundaries, run `pnpm build` and check for `[INEFFECTIVE_DYNAMIC_IMPORT]` warnings before submitting.
- Extension SDK self-import guardrail: inside an extension package, do not import that same extension via `openclaw/plugin-sdk/<extension>` from production files. Route internal imports through a local barrel such as `./api.ts` or `./runtime-api.ts`, and keep the `plugin-sdk/<extension>` path as the external contract only.
- Extension package boundary guardrail: inside `extensions/<id>/**`, do not use relative imports/exports that resolve outside that same `extensions/<id>` package root. If shared code belongs in the plugin SDK, import `openclaw/plugin-sdk/<subpath>` instead of reaching into `src/plugin-sdk/**` or other repo paths via `../`.
- Extension package boundary guardrail: inside a bundled plugin package, do not use relative imports/exports that resolve outside that same package root. If shared code belongs in the plugin SDK, import `openclaw/plugin-sdk/<subpath>` instead of reaching into `src/plugin-sdk/**` or other repo paths via `../`.
- Extension API surface rule: `openclaw/plugin-sdk/<subpath>` is the only public cross-package contract for extension-facing SDK code. If an extension needs a new seam, add a public subpath first; do not reach into `src/plugin-sdk/**` by relative path.
- Never share class behavior via prototype mutation (`applyPrototypeMixins`, `Object.defineProperty` on `.prototype`, or exporting `Class.prototype` for merges). Use explicit inheritance/composition (`A extends B extends C`) or helper composition so TypeScript can typecheck.
- If this pattern is needed, stop and get explicit approval before shipping; default behavior is to split/refactor into an explicit class hierarchy and keep members strongly typed.

View File

@@ -4,8 +4,21 @@ Docs: https://docs.openclaw.ai
## Unreleased
### Changes
- LINE/outbound media: add LINE image, video, and audio outbound sends on the LINE-specific delivery path, including explicit preview/tracking handling for videos while keeping generic media sends on the existing image-only route. (#45826) Thanks @masatohoshino.
### Fixes
- ACP/sessions_spawn: register ACP child runs for completion tracking and lifecycle cleanup, and make registration-failure cleanup explicitly best-effort so callers do not assume an already-started ACP turn was fully aborted. (#40885) Thanks @xaeon2026 and @vincentkoc.
- ACPX/runtime: derive the bundled ACPX expected version from the extension package metadata instead of hardcoding a separate literal, so plugin-local ACPX installs stop drifting out of health-check parity after version bumps. (#49089) Thanks @jiejiesks and @vincentkoc.
- Gateway/auth: make local-direct `trusted-proxy` fallback require the configured shared token instead of silently authenticating same-host callers, while keeping same-host reverse proxy identity-header flows on the normal trusted-proxy path. Thanks @zhangning-agent and @vincentkoc.
- 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.
- LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn ... --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels.
- LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997.
- TTS/Microsoft: auto-switch the default Edge voice to Chinese for CJK-dominant text without overriding explicitly selected Microsoft voices. (#52355) Thanks @extrasmall0.
- Agents/context pruning: count supplementary-plane CJK characters with the shared code-point-aware estimator so context pruning stops underestimating Japanese and Chinese text that uses Extension B ideographs. (#39985) Thanks @Edward-Qiang-2024.
- Slack/status reactions: add a reaction lifecycle for queued, thinking, tool, done, and error phases in Slack monitors, with safer cleanup so queued ack reactions stay correct across silent runs, pre-reply failures, and delayed transitions. (#56430) Thanks @hsiaoa.
- macOS/local gateway: stop OpenClaw.app from killing healthy local gateway listeners after startup by recognizing the current `openclaw-gateway` process title and using the current `openclaw gateway` launch shape.
- Memory/QMD: resolve slugified `memory_search` file hints back to the indexed filesystem path before returning search hits, so `memory_get` works again for mixed-case and spaced paths. (#50313) Thanks @erra9x.
- Memory/QMD: weight CJK-heavy text correctly when estimating chunk sizes, preserve surrogate-pair characters during fine splits, and keep long Latin lines on the old chunk boundaries so memory indexing produces better-sized chunks for CJK notes. (#40271) Thanks @AaronLuo00.
@@ -14,6 +27,26 @@ Docs: https://docs.openclaw.ai
- Gateway/health: carry webhook-vs-polling account mode from channel descriptors into runtime snapshots so passive channels like LINE and BlueBubbles skip false stale-socket health failures. (#47488) Thanks @karesansui-u.
- Memory/QMD: honor `memory.qmd.update.embedInterval` even when regular QMD update cadence is disabled or slower by arming a dedicated embed-cadence maintenance timer, while avoiding redundant timers when regular updates are already frequent enough. (#37326) Thanks @barronlroth.
- Agents/memory flush: keep daily memory flush files append-only during embedded attempts so compaction writes do not overwrite earlier notes. (#53725) Thanks @HPluseven.
- Web UI/markdown: stop bare auto-links from swallowing adjacent CJK text while preserving valid mixed-script path and query characters in rendered links. (#48410) Thanks @jnuyao.
- Sandbox/browser: install `fonts-noto-cjk` in the sandbox browser image so screenshots render Chinese, Japanese, and Korean text correctly instead of tofu boxes. Fixes #35597. Thanks @carrotRakko and @vincentkoc.
- Memory/FTS: add configurable trigram tokenization plus short-CJK substring fallback so memory search can find Chinese, Japanese, and Korean text without breaking mixed long-and-short queries. Thanks @carrotRakko.
- Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
- TUI/chat: keep optimistic outbound user messages visible during active runs by deferring local-run binding until the first gateway chat event reveals the real run id, preventing premature history reloads from wiping pending local sends. (#54722) Thanks @seanturner001.
- TUI/model picker: keep searchable `/model` and `/models` input mode from hijacking `j`/`k` as navigation keys, and harden width bounds under `m`-filtered model lists so search no longer crashes on long rows. (#30156) Thanks @briannicholls.
- Agents/Kimi: preserve already-valid Anthropic-compatible tool call argument objects while still clearing cached repairs when later trailing junk exceeds the repair allowance. (#54491) Thanks @yuanaichi.
- Docker/setup: force BuildKit for local image builds (including sandbox image builds) so `./docker-setup.sh` no longer fails on `RUN --mount=...` when hosts default to Docker's legacy builder. (#56681) Thanks @zhanghui-china.
- Control UI/agents: auto-load agent workspace files on initial Files panel open, and populate overview model/workspace/fallbacks from effective runtime agent metadata so defaulted models no longer show as `Not set`. (#56637) Thanks @dxsx84.
- Control UI/slash commands: make `/steer` and `/redirect` work from the chat command palette with visible pending state for active-run `/steer`, correct redirected-run tracking, and a single canonical `/steer` entry in the command menu. (#54625) Thanks @fuller-stack-dev.
- Exec: fail closed when the implicit sandbox host has no sandbox runtime, and stop denied async approval followups from reusing prior command output from the same session. (#56800) Thanks @scoootscooob.
- Exec/node: stop gateway-side workdir fallback from rewriting explicit `host=node` cwd values to the gateway filesystem, so remote node exec approval and runs keep using the intended node-local directory. (#50961) Thanks @openperf.
- Plugins/ClawHub: sanitize temporary archive filenames for scoped package names and slash-containing skill slugs so `openclaw plugins install @scope/name` no longer fails with `ENOENT` during archive download. (#56452) Thanks @soimy.
- Telegram/polling: keep the watchdog from aborting long-running reply delivery by treating recent non-polling API activity as bounded liveness instead of a hard stall. (#56343) Thanks @openperf.
- Memory/FTS: keep provider-less keyword hits visible at the default memory-search threshold, so FTS-only recall works without requiring `--min-score 0`. (#56473) Thanks @opriz.
- Memory/LanceDB: resolve runtime dependency manifest lookup from the bundled `extensions/memory-lancedb` path (including flattened dist chunks) so startup no longer fails with a missing `@lancedb/lancedb` dependency error. (#56623) Thanks @LUKSOAgent.
- Tools/web_search: localize the shared search cache to module scope so same-process global symbol lookups can no longer inspect or mutate cached web-search responses. Thanks @vincentkoc.
- Agents/silent turns: fail closed on silent memory-flush runs so narrated `NO_REPLY` self-talk cannot stream or finalize into external replies even when block streaming is enabled. (#52593)
- Browser/plugins: auto-enable the bundled browser plugin when browser config or browser tool policy already references it, and show a clearer CLI error when `plugins.allow` excludes `browser`.
- Matrix/plugin loading: ship and source-load the crypto bootstrap runtime sidecar correctly so current `main` stops warning about failed Matrix bootstrap loads and `matrix/index` plugin-id mismatches on every invocation. (#53298) thanks @keithce.
## 2026.3.28
@@ -41,10 +74,12 @@ Docs: https://docs.openclaw.ai
- 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.
- 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.
- Background tasks: keep durable lifecycle records for ACP/subagent spawned work and deliver ACP completion/failure updates through the real requester chat path instead of session-only stream events.
- 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.
- Docs: add `pnpm docs:check-links:anchors` for Mintlify anchor validation while keeping `scripts/docs-link-audit.mjs` as the stable link-audit entrypoint. (#55912) Thanks @velvet-shark.
- Tavily: mark outbound API requests with `X-Client-Source: openclaw` so Tavily can attribute OpenClaw-originated traffic. (#55335) Thanks @lakshyaag-tavily.
- Matrix/streaming: add `streaming: "partial"` draft replies that stay on a single editable preview message, stop preview streaming once text no longer fits one Matrix event, and clear stale previews before media-only finals. (#56387) thanks @jrusz.
### Fixes
@@ -87,11 +122,13 @@ Docs: https://docs.openclaw.ai
- 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.
- MCP/channels: add a Gateway-backed channel MCP bridge with Codex/Claude-facing conversation tools, Claude channel notifications, and safer stdio bridge lifecycle handling for reconnects and routed session discovery.
- 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.
- Plugins/SDK: thread `moduleUrl` through plugin-sdk alias resolution so user-installed plugins outside the openclaw directory 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.
- Subagents/announcements: preserve the requester agent id for inline deterministic tool spawns so named agents without channel bindings can still announce completions through the correct owner session. (#55437) Thanks @kAIborg24.
- Telegram/Anthropic streaming: replace raw invalid stream-order provider errors with a safe retry message so internal `message_start/message_stop` failures do not leak into chats. (#55408) Thanks @imydal.
- 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.
@@ -113,7 +150,7 @@ Docs: https://docs.openclaw.ai
- GitHub Copilot/auth refresh: treat large `expires_at` values as seconds epochs and clamp far-future runtime auth refresh timers so Copilot token refresh cannot fall into a `setTimeout` overflow hot loop. (#55360) Thanks @michael-abdo.
- Agents/status: use the persisted runtime session model in `session_status` when no explicit override exists, and honor per-agent `thinkingDefault` in both `session_status` and `/status`. (#55425) Thanks @scoootscooob, @xaeon2026, and @ysfbsf.
- Heartbeat/runner: guarantee the interval timer is re-armed after heartbeat runs and unexpected runner errors so scheduled heartbeats do not silently stop after an interrupted cycle. (#52270) Thanks @MiloStack.
- Config/Doctor: rewrite stale bundled plugin load paths from legacy `extensions/*` locations to the packaged bundled path, including directory-name mismatches and slash-suffixed config entries. (#55054) Thanks @SnowSky1.
- Config/Doctor: rewrite stale bundled plugin load paths from legacy bundled-plugin locations to the packaged bundled path, including directory-name mismatches and slash-suffixed config entries. (#55054) Thanks @SnowSky1.
- WhatsApp/mentions: stop treating mentions embedded in quoted messages as direct mentions so replying to a message that @mentioned the bot no longer falsely triggers mention gating. (#52711) Thanks @lurebat.
- Matrix: keep separate 2-person rooms out of DM routing after `m.direct` seeds successfully, while still honoring explicit `is_direct` state and startup fallback recovery. (#54890) thanks @private-peter
- Agents/ollama fallback: surface non-2xx Ollama HTTP errors with a leading status code so HTTP 503 responses trigger model fallback again. (#55214) Thanks @bugkill3r.
@@ -145,6 +182,9 @@ Docs: https://docs.openclaw.ai
- Plugins/Matrix: load bundled `@matrix-org/matrix-sdk-crypto-nodejs` through `createRequire(...)` so E2EE media send and receive keep the package-local native binding lookup working in packaged ESM builds. (#54566) thanks @joelnishanth.
- Plugins/Matrix: encrypt E2EE image thumbnails with `thumbnail_file` while keeping unencrypted-room previews on `thumbnail_url`, so encrypted Matrix image events keep thumbnail metadata without leaking plaintext previews. (#54711) thanks @frischeDaten.
- Telegram/forum topics: keep native `/new` and `/reset` routed to the active topic by preserving the topic target on forum-thread command context. (#35963)
- Status/port diagnostics: treat single-process dual-stack loopback gateway listeners as healthy in `openclaw status --all`, suppressing false “port already in use” conflict warnings. (#53398) Thanks @DanWebb1949.
- Memory/builtin: keep memory-file indexing active in FTS-only mode (no embedding provider) so forced reindexes no longer swap in an empty index and wipe existing memory chunks. (#42714) Thanks @asamimei.
- CLI/status: detect node-only hosts in `openclaw status` and `openclaw status --all`, show the configured remote gateway target instead of a false local `ECONNREFUSED`, and suppress contradictory local-gateway diagnosis output.
## 2026.3.24
@@ -170,6 +210,8 @@ Docs: https://docs.openclaw.ai
- 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.
- Agents/BTW: force `/btw` side questions to disable provider reasoning so Anthropic adaptive-thinking sessions stop failing with `No BTW response generated`. Fixes #55376. Thanks @Catteres and @vincentkoc.
- Auth profiles/OAuth: refresh runtime auth snapshots when saving rotated credentials so OAuth providers do not reuse consumed refresh tokens after the first token rotation. Fixes #55389. Thanks @sam26880 and @vincentkoc.
### Fixes
@@ -283,6 +325,7 @@ Docs: https://docs.openclaw.ai
- Security/session policy: require sender ownership for `/send` policy changes so command-authorized non-owners cannot rewrite owner-only session delivery policy.
- Security/bash stop: route `/bash stop` through the hardened process-tree killer so invalid or attacker-influenced SIGKILL targets cannot escape the intended bash-session scope.
- Security/installer: hide staged project `.npmrc` files during skill and package installs so npm registry and git settings inside the stage directory cannot hijack trusted installs.
- Agents/tool-call repair: recover malformed Kimi/OpenRouter tool-call argument streams when provider preambles appear before JSON payloads, and fail closed on non-tool leading text so fragment strings do not leak into filesystem path arguments during sub-agent runs. (#56560) Thanks @Originalwhite.
## 2026.3.23
@@ -437,7 +480,7 @@ Docs: https://docs.openclaw.ai
- 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.
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` surface for plugin-author test helpers, and move bundled-plugin-only test bridges 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.
@@ -592,7 +635,7 @@ Docs: https://docs.openclaw.ai
- 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.
- 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.
- 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.
- Gateway/watch mode: recreate bundled plugin runtime metadata after clean or stale `dist` states, so `pnpm gateway:watch` no longer fails on missing bundled-plugin manifests after a rebuild. Thanks @gumadeiras.
- 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.
- 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.
- 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.
@@ -604,11 +647,11 @@ Docs: https://docs.openclaw.ai
- 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.
- 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.
- 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.
- ACP/acpx: resolve the bundled plugin root from the actual plugin directory so plugin-local installs stay under the bundled dist plugin tree instead of escaping upward 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.
- 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.
- ACP/acpx: keep plugin-local backend installs under the bundled ACPX plugin package in live repo checkouts so rebuilds no longer delete the runtime binary, and avoid package-lock churn during runtime repair.
- 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.
@@ -1378,7 +1421,7 @@ Docs: https://docs.openclaw.ai
- Gateway/loopback announce URLs: treat `http://` and `https://` aliases with the same loopback/private-network policy as websocket URLs so loopback cron announce delivery no longer fails secure URL validation. (#39064) Thanks @Narcooo.
- Models/default provider fallback: when the hardcoded default provider is removed from `models.providers`, resolve defaults from configured providers instead of reporting stale removed-provider defaults in status output. (#38947) Thanks @davidemanuelDEV.
- Agents/cache-trace stability: guard stable stringify against circular references in trace payloads so near-limit payloads no longer crash with `Maximum call stack size exceeded`; adds regression coverage. (#38935) Thanks @MumuTW.
- Extensions/diffs CI stability: add `headers` to the `localReq` test helper in `extensions/diffs/index.test.ts` so forwarding-hint checks no longer crash with `req.headers` undefined. (supersedes #39063) Thanks @Shennng.
- Extensions/diffs CI stability: add `headers` to the diffs plugin `localReq` test helper so forwarding-hint checks no longer crash with `req.headers` undefined. (supersedes #39063) Thanks @Shennng.
- Agents/compaction thresholding: apply `agents.defaults.contextTokens` cap to the model passed into embedded run and `/compact` session creation so auto-compaction thresholds use the effective context window, not native model max context. (#39099) Thanks @MumuTW.
- Models/merge mode provider precedence: when `models.mode: "merge"` is active and config explicitly sets a provider `baseUrl`, keep config as source of truth instead of preserving stale runtime `models.json` `baseUrl` values; includes normalized provider-key coverage. (#39103) Thanks @BigUncle.
- UI/Control chat tool streaming: render tool events live in webchat without requiring refresh by enabling `tool-events` capability, fixing stream/event correlation, and resetting/reloading stream state around tool results and terminal events. (#39104) Thanks @jakepresent.
@@ -2586,7 +2629,7 @@ Docs: https://docs.openclaw.ai
- Security/Agents: make owner-ID obfuscation use a dedicated HMAC secret from configuration (`ownerDisplaySecret`) and update hashing behavior so obfuscation is decoupled from gateway token handling for improved control. (#7343) Thanks @vincentkoc.
- Security/Infra: switch gateway lock and tool-call synthetic IDs from SHA-1 to SHA-256 with unchanged truncation length to strengthen hash basis while keeping deterministic behavior and lock key format. (#7343) Thanks @vincentkoc.
- Dependencies/Tooling: add non-blocking dead-code scans in CI via Knip/ts-prune/ts-unused-exports to surface unused dependencies and exports earlier. (#22468) Thanks @vincentkoc.
- Dependencies/Unused Dependencies: remove or scope unused root and extension deps (`@larksuiteoapi/node-sdk`, `signal-utils`, `ollama`, `lit`, `@lit/context`, `@lit-labs/signals`, `@microsoft/agents-hosting-express`, `@microsoft/agents-hosting-extensions-teams`, and plugin-local `openclaw` devDeps in `extensions/open-prose`, `extensions/lobster`, and `extensions/llm-task`). (#22471, #22495) Thanks @vincentkoc.
- Dependencies/Unused Dependencies: remove or scope unused root and extension deps (`@larksuiteoapi/node-sdk`, `signal-utils`, `ollama`, `lit`, `@lit/context`, `@lit-labs/signals`, `@microsoft/agents-hosting-express`, `@microsoft/agents-hosting-extensions-teams`, and plugin-local `openclaw` devDeps in the Open Prose, Lobster, and LLM Task plugin packages). (#22471, #22495) Thanks @vincentkoc.
- Dependencies/A2UI: harden dependency resolution after root cleanup (resolve `lit`, `@lit/context`, `@lit-labs/signals`, and `signal-utils` from workspace/root) and simplify bundling fallback behavior, including `pnpm dlx rolldown` compatibility. (#22481, #22507) Thanks @vincentkoc.
### Fixes
@@ -3389,7 +3432,7 @@ Docs: https://docs.openclaw.ai
- Feishu: probe status uses the resolved account context for multi-account credential checks. (#11233) Thanks @onevcat.
- Feishu: add streaming card replies via Card Kit API and preserve `renderMode=auto` fallback behavior for plain-text responses. (#10379) Thanks @xzq-xu.
- Feishu DocX: preserve top-level converted block order using `firstLevelBlockIds` when writing/appending documents. (#13994) Thanks @Cynosure159.
- Feishu plugin packaging: remove `workspace:*` `openclaw` dependency from `extensions/feishu` and sync lockfile for install compatibility. (#14423) Thanks @jackcooper2015.
- Feishu plugin packaging: remove `workspace:*` `openclaw` dependency from the Feishu plugin package and sync lockfile for install compatibility. (#14423) Thanks @jackcooper2015.
- CLI/Wizard: exit with code 1 when `configure`, `agents add`, or interactive `onboard` wizards are canceled, so `set -e` automation stops correctly. (#14156) Thanks @0xRaini.
- Media: strip `MEDIA:` lines with local paths instead of leaking as visible text. (#14399) Thanks @0xRaini.
- Config/Cron: exclude `maxTokens` from config redaction and honor `deleteAfterRun` on skipped cron jobs. (#13342) Thanks @niceysam.

View File

@@ -5,15 +5,16 @@
#
# Multi-stage build produces a minimal runtime image without build tools,
# source code, or Bun. Works with Docker, Buildx, and Podman.
# The ext-deps stage extracts only the package.json files we need from
# extensions/, so the main build layer is not invalidated by unrelated
# extension source changes.
# The ext-deps stage extracts only the package.json files we need from the
# bundled plugin workspace tree, so the main build layer is not invalidated by
# unrelated plugin source changes.
#
# Two runtime variants:
# Default (bookworm): docker build .
# Slim (bookworm-slim): docker build --build-arg OPENCLAW_VARIANT=slim .
ARG OPENCLAW_EXTENSIONS=""
ARG OPENCLAW_VARIANT=default
ARG OPENCLAW_BUNDLED_PLUGIN_DIR=extensions
ARG OPENCLAW_DOCKER_APT_UPGRADE=1
ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"
ARG OPENCLAW_NODE_BOOKWORM_DIGEST="sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"
@@ -27,18 +28,20 @@ ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:e8e2e91b1378f83c5b2dd15f0247f3411
FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps
ARG OPENCLAW_EXTENSIONS
COPY extensions /tmp/extensions
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
COPY ${OPENCLAW_BUNDLED_PLUGIN_DIR} /tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}
# Copy package.json for opted-in extensions so pnpm resolves their deps.
RUN mkdir -p /out && \
for ext in $OPENCLAW_EXTENSIONS; do \
if [ -f "/tmp/extensions/$ext/package.json" ]; then \
if [ -f "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" ]; then \
mkdir -p "/out/$ext" && \
cp "/tmp/extensions/$ext/package.json" "/out/$ext/package.json"; \
cp "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" "/out/$ext/package.json"; \
fi; \
done
# ── Stage 2: Build ──────────────────────────────────────────────
FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
# Install Bun (required for build scripts). Retry the whole bootstrap flow to
# tolerate transient 5xx failures from bun.sh/GitHub during CI image builds.
@@ -62,7 +65,7 @@ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY ui/package.json ./ui/package.json
COPY patches ./patches
COPY --from=ext-deps /out/ ./extensions/
COPY --from=ext-deps /out/ ./${OPENCLAW_BUNDLED_PLUGIN_DIR}/
# Reduce OOM risk on low-memory hosts during dependency installation.
# Docker builds on small VMs may otherwise fail with "Killed" (exit 137).
@@ -73,7 +76,7 @@ COPY . .
# Normalize extension paths now so runtime COPY preserves safe modes
# without adding a second full extensions layer.
RUN for dir in /app/extensions /app/.agent /app/.agents; do \
RUN for dir in /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} /app/.agent /app/.agents; do \
if [ -d "$dir" ]; then \
find "$dir" -type d -exec chmod 755 {} +; \
find "$dir" -type f -exec chmod 644 {} +; \
@@ -114,6 +117,7 @@ LABEL org.opencontainers.image.base.name="docker.io/library/node:24-bookworm-sli
# ── Stage 3: Runtime ────────────────────────────────────────────
FROM base-${OPENCLAW_VARIANT}
ARG OPENCLAW_VARIANT
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
ARG OPENCLAW_DOCKER_APT_UPGRADE
# OCI base-image metadata for downstream image consumers.
@@ -148,13 +152,13 @@ COPY --from=runtime-assets --chown=node:node /app/dist ./dist
COPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modules
COPY --from=runtime-assets --chown=node:node /app/package.json .
COPY --from=runtime-assets --chown=node:node /app/openclaw.mjs .
COPY --from=runtime-assets --chown=node:node /app/extensions ./extensions
COPY --from=runtime-assets --chown=node:node /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} ./${OPENCLAW_BUNDLED_PLUGIN_DIR}
COPY --from=runtime-assets --chown=node:node /app/skills ./skills
COPY --from=runtime-assets --chown=node:node /app/docs ./docs
# In npm-installed Docker images, prefer the copied source extension tree for
# bundled discovery so package metadata that points at source entries stays valid.
ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/extensions
ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/${OPENCLAW_BUNDLED_PLUGIN_DIR}
# Keep pnpm available in the runtime image for container-local workflows.
# Use a shared Corepack home so the non-root `node` user does not need a

View File

@@ -14,6 +14,7 @@ RUN --mount=type=cache,id=openclaw-sandbox-bookworm-apt-cache,target=/var/cache/
chromium \
curl \
fonts-liberation \
fonts-noto-cjk \
fonts-noto-color-emoji \
git \
jq \

View File

@@ -2,6 +2,147 @@
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>OpenClaw</title>
<item>
<title>2026.3.28</title>
<pubDate>Sun, 29 Mar 2026 02:10:40 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026032890</sparkle:version>
<sparkle:shortVersionString>2026.3.28</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.3.28</h2>
<h3>Breaking</h3>
<ul>
<li>Providers/Qwen: remove the deprecated <code>qwen-portal-auth</code> OAuth integration for <code>portal.qwen.ai</code>; migrate to Model Studio with <code>openclaw onboard --auth-choice modelstudio-api-key</code>. (#52709) Thanks @pomelo-nwu.</li>
<li>Config/Doctor: drop automatic config migrations older than two months; very old legacy keys now fail validation instead of being rewritten on load or by <code>openclaw doctor</code>.</li>
</ul>
<h3>Changes</h3>
<ul>
<li>xAI/tools: move the bundled xAI provider to the Responses API, add first-class <code>x_search</code>, and auto-enable the xAI plugin from owned web-search and tool config so bundled Grok auth/configured search flows work without manual plugin toggles. (#56048) Thanks @huntharo.</li>
<li>xAI/onboarding: let the bundled Grok web-search plugin offer optional <code>x_search</code> setup during <code>openclaw onboard</code> and <code>openclaw configure --section web</code>, including an x_search model picker with the shared xAI key.</li>
<li>MiniMax: add image generation provider for <code>image-01</code> model, supporting generate and image-to-image editing with aspect ratio control. (#54487) Thanks @liyuan97.</li>
<li>Plugins/hooks: add async <code>requireApproval</code> to <code>before_tool_call</code> hooks, letting plugins pause tool execution and prompt the user for approval via the exec approval overlay, Telegram buttons, Discord interactions, or the <code>/approve</code> command on any channel. The <code>/approve</code> command now handles both exec and plugin approvals with automatic fallback. (#55339) Thanks @vaclavbelak and @joshavant.</li>
<li>ACP/channels: add current-conversation ACP binds for Discord, BlueBubbles, and iMessage so <code>/acp spawn codex --bind here</code> can turn the current chat into a Codex-backed workspace without creating a child thread, and document the distinction between chat surface, ACP session, and runtime workspace.</li>
<li>OpenAI/apply_patch: enable <code>apply_patch</code> by default for OpenAI and OpenAI Codex models, and align its sandbox policy access with <code>write</code> permissions.</li>
<li>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 <code>gateway run --claude-cli-logs</code> with generic <code>--cli-backend-logs</code> while keeping the old flag as a compatibility alias.</li>
<li>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 <code>plugins.allow</code> entries.</li>
<li>Podman: simplify the container setup around the current rootless user, install the launch helper under <code>~/.local/bin</code>, and document the host-CLI <code>openclaw --container <name> ...</code> workflow instead of a dedicated <code>openclaw</code> service user.</li>
<li>Slack/tool actions: add an explicit <code>upload-file</code> Slack action that routes file uploads through the existing Slack upload transport, with optional filename/title/comment overrides for channels and DMs.</li>
<li>Message actions/files: start unifying file-first sends on the canonical <code>upload-file</code> action by adding explicit support for Microsoft Teams and Google Chat, and by exposing BlueBubbles file sends through <code>upload-file</code> while keeping the legacy <code>sendAttachment</code> alias.</li>
<li>Plugins/Matrix TTS: send auto-TTS replies as native Matrix voice bubbles instead of generic audio attachments. (#37080) thanks @Matthew19990919.</li>
<li>CLI: add <code>openclaw config schema</code> to print the generated JSON schema for <code>openclaw.json</code>. (#54523) Thanks @kvokka.</li>
<li>Config/TTS: auto-migrate legacy speech config on normal reads and secret resolution, keep legacy diagnostics for Doctor, and remove regular-mode runtime fallback for old bundled <code>tts.<provider></code> API-key shapes.</li>
<li>Memory/plugins: move the pre-compaction memory flush plan behind the active memory plugin contract so <code>memory-core</code> owns flush prompts and target-path policy instead of hardcoded core logic.</li>
<li>MiniMax: trim model catalog to M2.7 only, removing legacy M2, M2.1, M2.5, and VL-01 models. (#54487) Thanks @liyuan97.</li>
<li>Plugins/runtime: expose <code>runHeartbeatOnce</code> in the plugin runtime <code>system</code> namespace so plugins can trigger a single heartbeat cycle with an explicit delivery target override (e.g. <code>heartbeat: { target: "last" }</code>). (#40299) Thanks @loveyana.</li>
<li>Agents/compaction: preserve the post-compaction AGENTS refresh on stale-usage preflight compaction for both immediate replies and queued followups. (#49479) Thanks @jared596.</li>
<li>Agents/compaction: surface safeguard-specific cancel reasons and relabel benign manual <code>/compact</code> no-op cases as skipped instead of failed. (#51072) Thanks @afurm.</li>
<li>Docs: add <code>pnpm docs:check-links:anchors</code> for Mintlify anchor validation while keeping <code>scripts/docs-link-audit.mjs</code> as the stable link-audit entrypoint. (#55912) Thanks @velvet-shark.</li>
<li>Tavily: mark outbound API requests with <code>X-Client-Source: openclaw</code> so Tavily can attribute OpenClaw-originated traffic. (#55335) Thanks @lakshyaag-tavily.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Agents/Anthropic: recover unhandled provider stop reasons (e.g. <code>sensitive</code>) as structured assistant errors instead of crashing the agent run. (#56639)</li>
<li>Google/models: resolve Gemini 3.1 pro, flash, and flash-lite for all Google provider aliases by passing the actual runtime provider ID and adding a template-provider fallback; fix flash-lite prefix ordering. (#56567)</li>
<li>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 <code>instructions</code>. (#54829) Thanks @neeravmakwana.</li>
<li>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 <code>openrouter</code> and <code>minimax-portal</code>. (#54858) Thanks @MonkeyLeeT.</li>
<li>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</li>
<li>Telegram/splitting: replace proportional text estimate with verified HTML-length search so long messages split at word boundaries instead of mid-word; gracefully degrade when tag overhead exceeds the limit. (#56595)</li>
<li>Telegram/delivery: skip whitespace-only and hook-blanked text replies in bot delivery to prevent GrammyError 400 empty-text crashes. (#56620)</li>
<li>Telegram/send: validate <code>replyToMessageId</code> at all four API sinks with a shared normalizer that rejects non-numeric, NaN, and mixed-content strings. (#56587)</li>
<li>Mistral: normalize OpenAI-compatible request flags so official Mistral API runs no longer fail with remaining <code>422 status code (no body)</code> chat errors.</li>
<li>Control UI/config: keep sensitive raw config hidden by default, replace the blank blocked editor with an explicit reveal-to-edit state, and restore raw JSON editing without auto-exposing secrets. Fixes #55322.</li>
<li>CLI/zsh: defer <code>compdef</code> registration until <code>compinit</code> is available so zsh completion loads cleanly with plugin managers and manual setups. (#56555)</li>
<li>BlueBubbles/debounce: guard debounce flush against null message text by sanitizing at the enqueue boundary and adding an independent combiner guard. (#56573)</li>
<li>Auto-reply: suppress JSON-wrapped <code>{"action":"NO_REPLY"}</code> control envelopes before channel delivery with a strict single-key detector; preserves media when text is only a silent envelope. (#56612)</li>
<li>ACP/ACPX agent registry: align OpenClaw's ACPX built-in agent mirror with the latest <code>openclaw/acpx</code> command defaults and built-in aliases, pin versioned <code>npx</code> built-ins to exact versions, and stop unknown ACP agent ids from falling through to raw <code>--agent</code> command execution on the MCP-proxy path. (#28321) Thanks @m0nkmaster and @vincentkoc.</li>
<li>Security/audit: extend web search key audit to recognize Gemini, Grok/xAI, Kimi, Moonshot, and OpenRouter credentials via a boundary-safe bundled-web-search registry shim. (#56540)</li>
<li>Docs/FAQ: remove broken Xfinity SSL troubleshooting cross-links from English and zh-CN FAQ entries — both sections already contain the full workaround inline. (#56500)</li>
<li>Telegram: deliver verbose tool summaries inside forum topic sessions again, so threaded topic chats now match DM verbose behavior. (#43236) Thanks @frankbuild.</li>
<li>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)</li>
<li>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.</li>
<li>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.</li>
<li>iMessage: stop leaking inline <code>[[reply_to:...]]</code> tags into delivered text by sending <code>reply_to</code> as RPC metadata and stripping stray directive tags from outbound messages. (#39512) Thanks @mvanhorn.</li>
<li>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.</li>
<li>CLI/message send: write manual <code>openclaw message send</code> deliveries into the resolved agent session transcript again by always threading the default CLI agent through outbound mirroring. (#54187) Thanks @KevInTheCloud5617.</li>
<li>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</li>
<li>Agents/status: use provider-aware context window lookup for fresh Anthropic 4.6 model overrides so <code>/status</code> shows the correct 1.0m window instead of an underreported shared-cache minimum. (#54796) Thanks @neeravmakwana.</li>
<li>OpenAI/WebSocket: preserve reasoning replay metadata and tool-call item ids on WebSocket tool turns, and start a fresh response chain when full-context resend is required. (#53856) Thanks @xujingchen1996.</li>
<li>OpenAI/WS: restore reasoning blocks for Responses WebSocket runs and keep reasoning/tool-call replay metadata intact so resumed sessions do not lose or break follow-up reasoning-capable turns. (#53856) Thanks @xujingchen1996.</li>
<li>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.</li>
<li>Claude CLI: switch the bundled Claude CLI backend to <code>stream-json</code> output so watchdogs see progress on long runs, and keep session/usage metadata even when Claude finishes with an empty result line. (#49698) Thanks @felear2022.</li>
<li>Claude CLI/MCP: always pass a strict generated <code>--mcp-config</code> overlay for background Claude CLI runs, including the empty-server case, so Claude does not inherit ambient user/global MCP servers. (#54961) Thanks @markojak.</li>
<li>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 <code>mediaUrl</code>. (#50930) Thanks @infichen.</li>
<li>Chat/UI: move the chat send button onto the shared ghost-button theme styling, while keeping the stop button icon readable on the danger state. (#55075) Thanks @bottenbenny.</li>
<li>WhatsApp/allowFrom: show a specific allowFrom policy error for valid blocked targets instead of the misleading <code><E.164|group JID></code> format hint. Thanks @mcaxtr.</li>
<li>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.</li>
<li>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.</li>
<li>Telegram/pairing: ignore self-authored DM <code>message</code> updates so bot-pinned status cards and similar service updates do not trigger bogus pairing requests or re-enter inbound dispatch. (#54530) thanks @huntharo</li>
<li>Mattermost/replies: keep pairing replies, slash-command fallback replies, and model-picker messages on the resolved config path so <code>exec:</code> SecretRef bot tokens work across all outbound reply branches. (#48347) thanks @mathiasnagler.</li>
<li>Microsoft Teams/config: accept the existing <code>welcomeCard</code>, <code>groupWelcomeCard</code>, <code>promptStarters</code>, and feedback/reflection keys in strict config validation so already-supported Teams runtime settings stop failing schema checks. (#54679) Thanks @gumclaw.</li>
<li>MCP/channels: add a Gateway-backed channel MCP bridge with Codex/Claude-facing conversation tools, Claude channel notifications, and safer stdio bridge lifecycle handling for reconnects and routed session discovery.</li>
<li>Plugins/SDK: thread <code>moduleUrl</code> through plugin-sdk alias resolution so user-installed plugins outside the openclaw directory correctly resolve <code>openclaw/plugin-sdk/*</code> subpath imports, and gate <code>plugin-sdk:check-exports</code> in <code>release:check</code>. (#54283) Thanks @xieyongliang.</li>
<li>Config/web fetch: allow the documented <code>tools.web.fetch.maxResponseBytes</code> setting in runtime schema validation so valid configs no longer fail with unrecognized-key errors. (#53401) Thanks @erhhung.</li>
<li>Message tool/buttons: keep the shared <code>buttons</code> schema optional in merged tool definitions so plain <code>action=send</code> calls stop failing validation when no buttons are provided. (#54418) Thanks @adzendo.</li>
<li>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 <code>tool_call_id</code> values with HTTP 400. (#40996) Thanks @xaeon2026.</li>
<li>Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition <code>strict</code> fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.</li>
<li>Plugins/context engines: retry strict legacy <code>assemble()</code> calls without the new <code>prompt</code> field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.</li>
<li>CLI/update status: explicitly say <code>up to date</code> when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.</li>
<li>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.</li>
<li>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.</li>
<li>Feishu: use the original message <code>create_time</code> instead of <code>Date.now()</code> for inbound timestamps so offline-retried messages carry the correct authoring time, preventing mis-targeted agent actions on stale instructions. (#52809) Thanks @schumilin.</li>
<li>Control UI/Skills: open skill detail dialogs with the browser modal lifecycle so clicking a skill row keeps the panel centered instead of rendering it off-screen at the bottom of the page.</li>
<li>Matrix/replies: include quoted poll question/options in inbound reply context so the agent sees the original poll content when users reply to Matrix poll messages. (#55056) Thanks @alberthild.</li>
<li>Matrix/plugins: keep plugin bootstrap from crashing when built runtime mixes bare and deep <code>matrix-js-sdk</code> entrypoints, so unrelated channels do not get taken down during plugin load. (#56273) Thanks @aquaright1.</li>
<li>Agents/sandbox: honor <code>tools.sandbox.tools.alsoAllow</code>, 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.</li>
<li>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.</li>
<li>Agents/compaction: trigger timeout recovery compaction before retrying high-context LLM timeouts so embedded runs stop repeating oversized requests. (#46417) thanks @joeykrug.</li>
<li>Agents/compaction: reconcile <code>sessions.json.compactionCount</code> after a late embedded auto-compaction success so persisted session counts catch up once the handler reports completion. (#45493) Thanks @jackal092927.</li>
<li>Agents/failover: classify Codex accountId token extraction failures as auth errors so model fallback continues to the next configured candidate. (#55206) Thanks @cosmicnet.</li>
<li>Plugins/runtime: reuse only compatible active plugin registries across tools, providers, web search, and channel bootstrap, align <code>/tools/invoke</code> plugin loading with the session workspace, and retry outbound channel recovery when the pinned channel surface changes so plugin tools and channels stop disappearing or re-registering from mismatched runtime loads. Thanks @gumadeiras.</li>
<li>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.</li>
<li>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.</li>
<li>Discord/gateway shutdown: treat expected reconnect-exhausted events during intentional lifecycle stop as clean shutdowns so startup-abort cleanup no longer surfaces false gateway failures. (#55324) Thanks @joelnishanth.</li>
<li>Discord/gateway shutdown: suppress reconnect-exhausted events that were already buffered before teardown flips <code>lifecycleStopping</code>, so stale-socket Discord restarts no longer crash the whole gateway. Fixes #55403 and #55421. Thanks @lml2468 and @vincentkoc.</li>
<li>GitHub Copilot/auth refresh: treat large <code>expires_at</code> values as seconds epochs and clamp far-future runtime auth refresh timers so Copilot token refresh cannot fall into a <code>setTimeout</code> overflow hot loop. (#55360) Thanks @michael-abdo.</li>
<li>Agents/status: use the persisted runtime session model in <code>session_status</code> when no explicit override exists, and honor per-agent <code>thinkingDefault</code> in both <code>session_status</code> and <code>/status</code>. (#55425) Thanks @scoootscooob, @xaeon2026, and @ysfbsf.</li>
<li>Heartbeat/runner: guarantee the interval timer is re-armed after heartbeat runs and unexpected runner errors so scheduled heartbeats do not silently stop after an interrupted cycle. (#52270) Thanks @MiloStack.</li>
<li>Config/Doctor: rewrite stale bundled plugin load paths from legacy bundled-plugin locations to the packaged bundled path, including directory-name mismatches and slash-suffixed config entries. (#55054) Thanks @SnowSky1.</li>
<li>WhatsApp/mentions: stop treating mentions embedded in quoted messages as direct mentions so replying to a message that @mentioned the bot no longer falsely triggers mention gating. (#52711) Thanks @lurebat.</li>
<li>Matrix: keep separate 2-person rooms out of DM routing after <code>m.direct</code> seeds successfully, while still honoring explicit <code>is_direct</code> state and startup fallback recovery. (#54890) thanks @private-peter</li>
<li>Agents/ollama fallback: surface non-2xx Ollama HTTP errors with a leading status code so HTTP 503 responses trigger model fallback again. (#55214) Thanks @bugkill3r.</li>
<li>Feishu/tools: stop synthetic agent ids like <code>agent-spawner</code> from being treated as Feishu account ids during tool execution, so tools fall back to the configured/default Feishu account unless the contextual id is a real enabled Feishu account. (#55627) Thanks @MonkeyLeeT.</li>
<li>Google/tools: strip empty <code>required: []</code> arrays from Gemini tool schemas so optional-only tool parameters no longer trigger Google validator 400s. (#52106) Thanks @oliviareid-svg.</li>
<li>Onboarding/TUI/local gateways: show the resolved gateway port in setup output, clarify no-daemon local health/dashboard messaging, and preserve loopback Control UI auth on reruns and explicit local gateway URLs so local quickstart flows recover cleanly. (#55730) Thanks @shakkernerd.</li>
<li>TUI/chat log: keep system messages as single logical entries and prune overflow at whole-message boundaries so wrapped system spacing stays intact. (#55732) Thanks @shakkernerd.</li>
<li>TUI/activation: validate <code>/activation</code> arguments in the TUI and reject invalid values instead of silently coercing them to <code>mention</code>. (#55733) Thanks @shakkernerd.</li>
<li>Agents/model switching: apply <code>/model</code> changes to active embedded runs at the next safe retry boundary, so overloaded or retrying turns switch to the newly selected model instead of staying pinned to the old provider.</li>
<li>Agents/Codex fallback: classify Codex <code>server_error</code> payloads as failoverable, sanitize <code>Codex error:</code> payloads before they reach chat, preserve context-overflow guidance for prefixed <code>invalid_request_error</code> payloads, and omit provider <code>request_id</code> values from user-facing UI copy. (#42892) Thanks @xaeon2026.</li>
<li>Memory/search: share memory embedding provider registrations across split plugin runtimes so memory search no longer fails with unknown provider errors after memory-core registers built-in adapters. (#55945) Thanks @glitch418x.</li>
<li>Discord/Carbon beta: update <code>@buape/carbon</code> to the latest beta and pass the new <code>RateLimitError</code> request argument so Discord stays compatible with the upstream beta constructor change. (#55980) Thanks @ngutman.</li>
<li>Plugins/inbound claims: pass full inbound attachment arrays through <code>inbound_claim</code> hook metadata while keeping the legacy singular media attachment fields for compatibility. (#55452) Thanks @huntharo.</li>
<li>Plugins/Matrix: preserve sender filenames for inbound media by forwarding <code>originalFilename</code> to <code>saveMediaBuffer</code>. (#55692) thanks @esrehmki.</li>
<li>Matrix/mentions: recognize <code>matrix.to</code> mentions whose visible label uses the bot's room display name, so <code>requireMention: true</code> rooms respond correctly in modern Matrix clients. (#55393) thanks @nickludlam.</li>
<li>Ollama/thinking off: route <code>thinkingLevel=off</code> through the live Ollama extension request path so thinking-capable Ollama models now receive top-level <code>think: false</code> instead of silently generating hidden reasoning tokens. (#53200) Thanks @BruceMacD.</li>
<li>Plugins/diffs: stage bundled <code>@pierre/diffs</code> runtime dependencies during packaged updates so the bundled diff viewer keeps loading after global installs and updates. (#56077) Thanks @gumadeiras.</li>
<li>Plugins/diffs: load bundled Pierre themes without JSON module imports so diff rendering keeps working on newer Node builds. (#45869) thanks @NickHood1984.</li>
<li>Plugins/uninstall: remove owned <code>channels.<id></code> config when uninstalling channel plugins, and keep the uninstall preview aligned with explicit channel ownership so built-in channels and shared keys stay intact. (#35915) Thanks @wbxl2000.</li>
<li>Plugins/Matrix: prefer explicit DM signals when choosing outbound direct rooms and routing unmapped verification summaries, so strict 2-person fallback rooms do not outrank the real DM. (#56076) thanks @gumadeiras</li>
<li>Plugins/Matrix: resolve env-backed <code>accessToken</code> and <code>password</code> SecretRefs against the active Matrix config env path during startup, and officially accept SecretRef <code>accessToken</code> config values. (#54980) thanks @kakahu2015.</li>
<li>Microsoft Teams/proactive DMs: prefer the freshest personal conversation reference for <code>user:<aadObjectId></code> sends when multiple stored references exist, so replies stop targeting stale DM threads. (#54702) Thanks @gumclaw.</li>
<li>Gateway/plugins: reuse the session workspace when building HTTP <code>/tools/invoke</code> tool lists and harden tool construction to infer the session agent workspace by default, so workspace plugins do not re-register on repeated HTTP tool calls. (#56101) thanks @neeravmakwana</li>
<li>Brave/web search: normalize unsupported Brave <code>country</code> filters to <code>ALL</code> before request and cache-key generation so locale-derived values like <code>VN</code> stop failing with upstream 422 validation errors. (#55695) Thanks @chen-zhang-cs-code.</li>
<li>Discord/replies: preserve leading indentation when stripping inline reply tags so reply-tagged plain text and fenced code blocks keep their formatting. (#55960) Thanks @Nanako0129.</li>
<li>Daemon/status: surface immediate gateway close reasons from lightweight probes and prefer those concrete auth or pairing failures over generic timeouts in <code>openclaw daemon status</code>. (#56282) Thanks @mbelinky.</li>
<li>Agents/failover: classify HTTP 410 errors as retryable timeouts by default while still preserving explicit session-expired, billing, and auth signals from the payload. (#55201) thanks @nikus-pan.</li>
<li>Agents/subagents: restore completion announce delivery for extension channels like BlueBubbles. (#56348)</li>
<li>Plugins/Matrix: load bundled <code>@matrix-org/matrix-sdk-crypto-nodejs</code> through <code>createRequire(...)</code> so E2EE media send and receive keep the package-local native binding lookup working in packaged ESM builds. (#54566) thanks @joelnishanth.</li>
<li>Plugins/Matrix: encrypt E2EE image thumbnails with <code>thumbnail_file</code> while keeping unencrypted-room previews on <code>thumbnail_url</code>, so encrypted Matrix image events keep thumbnail metadata without leaking plaintext previews. (#54711) thanks @frischeDaten.</li>
<li>Telegram/forum topics: keep native <code>/new</code> and <code>/reset</code> routed to the active topic by preserving the topic target on forum-thread command context. (#35963)</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.28/OpenClaw-2026.3.28.zip" length="25811288" type="application/octet-stream" sparkle:edSignature="SJp4ptVaGlOIXRPevS89DbfN2WKP0bKMXQoaT0fmLhy7pataDfHN0kxC3zu6P0Q/HtsxaESEhJUw48SCUNNKDA=="/>
</item>
<item>
<title>2026.3.24</title>
<pubDate>Wed, 25 Mar 2026 17:06:31 +0000</pubDate>
@@ -95,81 +236,5 @@
]]></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>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026031390</sparkle:version>
<sparkle:shortVersionString>2026.3.13</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.3.13</h2>
<h3>Changes</h3>
<ul>
<li>Android/chat settings: redesign the chat settings sheet with grouped device and media sections, refresh the Connect and Voice tabs, and tighten the chat composer/session header for a denser mobile layout. (#44894) Thanks @obviyus.</li>
<li>iOS/onboarding: add a first-run welcome pager before gateway setup, stop auto-opening the QR scanner, and show <code>/pair qr</code> instructions on the connect step. (#45054) Thanks @ngutman.</li>
<li>Browser/existing-session: add an official Chrome DevTools MCP attach mode for signed-in live Chrome sessions, with docs for <code>chrome://inspect/#remote-debugging</code> enablement and direct backlinks to Chromes own setup guides.</li>
<li>Browser/agents: add built-in <code>profile="user"</code> for the logged-in host browser and <code>profile="chrome-relay"</code> for the extension relay, so agent browser calls can prefer the real signed-in browser without the extra <code>browserSession</code> selector.</li>
<li>Browser/act automation: add batched actions, selector targeting, and delayed clicks for browser act requests with normalized batch dispatch. Thanks @vincentkoc.</li>
<li>Docker/timezone override: add <code>OPENCLAW_TZ</code> so <code>docker-setup.sh</code> can pin gateway and CLI containers to a chosen IANA timezone instead of inheriting the daemon default. (#34119) Thanks @Lanfei.</li>
<li>Dependencies/pi: bump <code>@mariozechner/pi-agent-core</code>, <code>@mariozechner/pi-ai</code>, <code>@mariozechner/pi-coding-agent</code>, and <code>@mariozechner/pi-tui</code> to <code>0.58.0</code>.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Dashboard/chat UI: stop reloading full chat history on every live tool result in dashboard v2 so tool-heavy runs no longer trigger UI freeze/re-render storms while the final event still refreshes persisted history. (#45541) Thanks @BunsDev.</li>
<li>Gateway/client requests: reject unanswered gateway RPC calls after a bounded timeout and clear their pending state, so stalled connections no longer leak hanging <code>GatewayClient.request()</code> promises indefinitely.</li>
<li>Build/plugin-sdk bundling: bundle plugin-sdk subpath entries in one shared build pass so published packages stop duplicating shared chunks and avoid the recent plugin-sdk memory blow-up. (#45426) Thanks @TarasShyn.</li>
<li>Ollama/reasoning visibility: stop promoting native <code>thinking</code> and <code>reasoning</code> fields into final assistant text so local reasoning models no longer leak internal thoughts in normal replies. (#45330) Thanks @xi7ang.</li>
<li>Android/onboarding QR scan: switch setup QR scanning to Google Code Scanner so onboarding uses a more reliable scanner instead of the legacy embedded ZXing flow. (#45021) Thanks @obviyus.</li>
<li>Browser/existing-session: harden driver validation and session lifecycle so transport errors trigger reconnects while tool-level errors preserve the session, and extract shared ARIA role sets to deduplicate Playwright and Chrome MCP snapshot paths. (#45682) Thanks @odysseus0.</li>
<li>Browser/existing-session: accept text-only <code>list_pages</code> and <code>new_page</code> responses from Chrome DevTools MCP so live-session tab discovery and new-tab open flows keep working when the server omits structured page metadata.</li>
<li>Control UI/insecure auth: preserve explicit shared token and password auth on plain-HTTP Control UI connects so LAN and reverse-proxy sessions no longer drop shared auth before the first WebSocket handshake. (#45088) Thanks @velvet-shark.</li>
<li>Gateway/session reset: preserve <code>lastAccountId</code> and <code>lastThreadId</code> across gateway session resets so replies keep routing back to the same account and thread after <code>/reset</code>. (#44773) Thanks @Lanfei.</li>
<li>macOS/onboarding: avoid self-restarting freshly bootstrapped launchd gateways and give new daemon installs longer to become healthy, so <code>openclaw onboard --install-daemon</code> no longer false-fails on slower Macs and fresh VM snapshots.</li>
<li>Gateway/status: add <code>openclaw gateway status --require-rpc</code> and clearer Linux non-interactive daemon-install failure reporting so automation can fail hard on probe misses instead of treating a printed RPC error as green.</li>
<li>macOS/exec approvals: respect per-agent exec approval settings in the gateway prompter, including allowlist fallback when the native prompt cannot be shown, so gateway-triggered <code>system.run</code> requests follow configured policy instead of always prompting or denying unexpectedly. (#13707) Thanks @sliekens.</li>
<li>Telegram/media downloads: thread the same direct or proxy transport policy into SSRF-guarded file fetches so inbound attachments keep working when Telegram falls back between env-proxy and direct networking. (#44639) Thanks @obviyus.</li>
<li>Telegram/inbound media IPv4 fallback: retry SSRF-guarded Telegram file downloads once with the same IPv4 fallback policy as Bot API calls so fresh installs on IPv6-broken hosts no longer fail to download inbound images.</li>
<li>Windows/gateway install: bound <code>schtasks</code> calls and fall back to the Startup-folder login item when task creation hangs, so native <code>openclaw gateway install</code> fails fast instead of wedging forever on broken Scheduled Task setups.</li>
<li>Windows/gateway stop: resolve Startup-folder fallback listeners from the installed <code>gateway.cmd</code> port, so <code>openclaw gateway stop</code> now actually kills fallback-launched gateway processes before restart.</li>
<li>Windows/gateway status: reuse the installed service command environment when reading runtime status, so startup-fallback gateways keep reporting the configured port and running state in <code>gateway status --json</code> instead of falling back to <code>gateway port unknown</code>.</li>
<li>Windows/gateway auth: stop attaching device identity on local loopback shared-token and password gateway calls, so native Windows agent replies no longer log stale <code>device signature expired</code> fallback noise before succeeding.</li>
<li>Discord/gateway startup: treat plain-text and transient <code>/gateway/bot</code> metadata fetch failures as transient startup errors so Discord gateway boot no longer crashes on unhandled rejections. (#44397) Thanks @jalehman.</li>
<li>Slack/probe: keep <code>auth.test()</code> bot and team metadata mapping stable while simplifying the probe result path. (#44775) Thanks @Cafexss.</li>
<li>Dashboard/chat UI: render oversized plain-text replies as normal paragraphs instead of capped gray code blocks, so long desktop chat responses stay readable without tab-switching refreshes.</li>
<li>Dashboard/chat UI: restore the <code>chat-new-messages</code> class on the New messages scroll pill so the button uses its existing compact styling instead of rendering as a full-screen SVG overlay. (#44856) Thanks @Astro-Han.</li>
<li>Gateway/Control UI: restore the operator-only device-auth bypass and classify browser connect failures so origin and device-identity problems no longer show up as auth errors in the Control UI and web chat. (#45512) thanks @sallyom.</li>
<li>macOS/voice wake: stop crashing wake-word command extraction when speech segment ranges come from a different transcript instance.</li>
<li>Discord/allowlists: honor raw <code>guild_id</code> when hydrated guild objects are missing so allowlisted channels and threads like <code>#maintainers</code> no longer get false-dropped before channel allowlist checks.</li>
<li>macOS/runtime locator: require Node >=22.16.0 during macOS runtime discovery so the app no longer accepts Node versions that the main runtime guard rejects later. Thanks @sumleo.</li>
<li>Agents/custom providers: preserve blank API keys for loopback OpenAI-compatible custom providers by clearing the synthetic Authorization header at runtime, while keeping explicit apiKey and oauth/token config from silently downgrading into fake bearer auth. (#45631) Thanks @xinhuagu.</li>
<li>Models/google-vertex Gemini flash-lite normalization: apply existing bare-ID preview normalization to <code>google-vertex</code> model refs and provider configs so <code>google-vertex/gemini-3.1-flash-lite</code> resolves as <code>gemini-3.1-flash-lite-preview</code>. (#42435) thanks @scoootscooob.</li>
<li>iMessage/remote attachments: reject unsafe remote attachment paths before spawning SCP, so sender-controlled filenames can no longer inject shell metacharacters into remote media staging. Thanks @lintsinghua.</li>
<li>Telegram/webhook auth: validate the Telegram webhook secret before reading or parsing request bodies, so unauthenticated requests are rejected immediately instead of consuming up to 1 MB first. Thanks @space08.</li>
<li>Security/device pairing: make bootstrap setup codes single-use so pending device pairing requests cannot be silently replayed and widened to admin before approval. Thanks @tdjackey.</li>
<li>Security/external content: strip zero-width and soft-hyphen marker-splitting characters during boundary sanitization so spoofed <code>EXTERNAL_UNTRUSTED_CONTENT</code> markers fall back to the existing hardening path instead of bypassing marker normalization.</li>
<li>Security/exec approvals: unwrap more <code>pnpm</code> runtime forms during approval binding, including <code>pnpm --reporter ... exec</code> and direct <code>pnpm node</code> file runs, with matching regression coverage and docs updates.</li>
<li>Security/exec approvals: fail closed for Perl <code>-M</code> and <code>-I</code> approval flows so preload and load-path module resolution stays outside approval-backed runtime execution unless the operator uses a broader explicit trust path.</li>
<li>Security/exec approvals: recognize PowerShell <code>-File</code> and <code>-f</code> wrapper forms during inline-command extraction so approval and command-analysis paths treat file-based PowerShell launches like the existing <code>-Command</code> variants.</li>
<li>Security/exec approvals: unwrap <code>env</code> dispatch wrappers inside shell-segment allowlist resolution on macOS so <code>env FOO=bar /path/to/bin</code> resolves against the effective executable instead of the wrapper token.</li>
<li>Security/exec approvals: treat backslash-newline as shell line continuation during macOS shell-chain parsing so line-continued <code>$(</code> substitutions fail closed instead of slipping past command-substitution checks.</li>
<li>Security/exec approvals: bind macOS skill auto-allow trust to both executable name and resolved path so same-basename binaries no longer inherit trust from unrelated skill bins.</li>
<li>Build/plugin-sdk bundling: bundle plugin-sdk subpath entries in one shared build pass so published packages stop duplicating shared chunks and avoid the recent plugin-sdk memory blow-up. (#45426) Thanks @TarasShyn.</li>
<li>Cron/isolated sessions: route nested cron-triggered embedded runner work onto the nested lane so isolated cron jobs no longer deadlock when compaction or other queued inner work runs. Thanks @vincentkoc.</li>
<li>Agents/OpenAI-compatible compat overrides: respect explicit user <code>models[].compat</code> opt-ins for non-native <code>openai-completions</code> endpoints so usage-in-streaming capability overrides no longer get forced off when the endpoint actually supports them. (#44432) Thanks @cheapestinference.</li>
<li>Agents/Azure OpenAI startup prompts: rephrase the built-in <code>/new</code>, <code>/reset</code>, and post-compaction startup instruction so Azure OpenAI deployments no longer hit HTTP 400 false positives from the content filter. (#43403) Thanks @xingsy97.</li>
<li>Agents/memory bootstrap: load only one root memory file, preferring <code>MEMORY.md</code> and using <code>memory.md</code> as a fallback, so case-insensitive Docker mounts no longer inject duplicate memory context. (#26054) Thanks @Lanfei.</li>
<li>Agents/compaction: compare post-compaction token sanity checks against full-session pre-compaction totals and skip the check when token estimation fails, so sessions with large bootstrap context keep real token counts instead of falling back to unknown. (#28347) thanks @efe-arv.</li>
<li>Agents/compaction: preserve safeguard compaction summary language continuity via default and configurable custom instructions so persona drift is reduced after auto-compaction. (#10456) Thanks @keepitmello.</li>
<li>Agents/tool warnings: distinguish gated core tools like <code>apply_patch</code> from plugin-only unknown entries in <code>tools.profile</code> warnings, so unavailable core tools now report current runtime/provider/model/config gating instead of suggesting a missing plugin.</li>
<li>Config/validation: accept documented <code>agents.list[].params</code> per-agent overrides in strict config validation so <code>openclaw config validate</code> no longer rejects runtime-supported <code>cacheRetention</code>, <code>temperature</code>, and <code>maxTokens</code> settings. (#41171) Thanks @atian8179.</li>
<li>Config/web fetch: restore runtime validation for documented <code>tools.web.fetch.readability</code> and <code>tools.web.fetch.firecrawl</code> settings so valid web fetch configs no longer fail with unrecognized-key errors. (#42583) Thanks @stim64045-spec.</li>
<li>Signal/config validation: add <code>channels.signal.groups</code> schema support so per-group <code>requireMention</code>, <code>tools</code>, and <code>toolsBySender</code> overrides no longer get rejected during config validation. (#27199) Thanks @unisone.</li>
<li>Config/discovery: accept <code>discovery.wideArea.domain</code> in strict config validation so unicast DNS-SD gateway configs no longer fail with an unrecognized-key error. (#35615) Thanks @ingyukoh.</li>
<li>Telegram/media errors: redact Telegram file URLs before building media fetch errors so failed inbound downloads do not leak bot tokens into logs. Thanks @space08.</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.13/OpenClaw-2026.3.13.zip" length="23640917" type="application/octet-stream" sparkle:edSignature="Me63UHSpFLocTo5Lt7Iqsl0Hq61y3jTcZ9DUkiFl9xQvTE0+ORuqRMFWqPgYwfaKMgcgQmUbrV/uFzEoTIRHBA=="/>
</item>
</channel>
</rss>

View File

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

View File

@@ -1,8 +1,8 @@
// Shared iOS version defaults.
// Generated overrides live in build/Version.xcconfig (git-ignored).
OPENCLAW_GATEWAY_VERSION = 2026.3.28
OPENCLAW_MARKETING_VERSION = 2026.3.28
OPENCLAW_BUILD_VERSION = 2026032800
OPENCLAW_GATEWAY_VERSION = 2026.3.29
OPENCLAW_MARKETING_VERSION = 2026.3.29
OPENCLAW_BUILD_VERSION = 2026032900
#include? "../build/Version.xcconfig"

View File

@@ -65,9 +65,9 @@ Release behavior:
- Beta release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, and `OpenClawPushAPNsEnvironment=production`.
- The beta flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
- Root `package.json.version` is the only version source for iOS.
- A root version like `2026.3.28-beta.1` becomes:
- `CFBundleShortVersionString = 2026.3.28`
- `CFBundleVersion = next TestFlight build number for 2026.3.28`
- A root version like `2026.3.29-beta.1` becomes:
- `CFBundleShortVersionString = 2026.3.29`
- `CFBundleVersion = next TestFlight build number for 2026.3.29`
Required env for beta builds:

View File

@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.3.28</string>
<string>2026.3.29</string>
<key>CFBundleVersion</key>
<string>2026032800</string>
<string>2026032900</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@@ -2235,21 +2235,29 @@ public struct AgentSummary: Codable, Sendable {
public let id: String
public let name: String?
public let identity: [String: AnyCodable]?
public let workspace: String?
public let model: [String: AnyCodable]?
public init(
id: String,
name: String?,
identity: [String: AnyCodable]?)
identity: [String: AnyCodable]?,
workspace: String?,
model: [String: AnyCodable]?)
{
self.id = id
self.name = name
self.identity = identity
self.workspace = workspace
self.model = model
}
private enum CodingKeys: String, CodingKey {
case id
case name
case identity
case workspace
case model
}
}

View File

@@ -2235,21 +2235,29 @@ public struct AgentSummary: Codable, Sendable {
public let id: String
public let name: String?
public let identity: [String: AnyCodable]?
public let workspace: String?
public let model: [String: AnyCodable]?
public init(
id: String,
name: String?,
identity: [String: AnyCodable]?)
identity: [String: AnyCodable]?,
workspace: String?,
model: [String: AnyCodable]?)
{
self.id = id
self.name = name
self.identity = identity
self.workspace = workspace
self.model = model
}
private enum CodingKeys: String, CodingKey {
case id
case name
case identity
case workspace
case model
}
}

View File

@@ -2608,6 +2608,26 @@
"tags": [],
"hasChildren": false
},
{
"path": "agents.defaults.memorySearch.store.fts",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "agents.defaults.memorySearch.store.fts.tokenizer",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "agents.defaults.memorySearch.store.path",
"kind": "core",
@@ -5028,6 +5048,26 @@
"tags": [],
"hasChildren": false
},
{
"path": "agents.list.*.memorySearch.store.fts",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "agents.list.*.memorySearch.store.fts.tokenizer",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "agents.list.*.memorySearch.store.path",
"kind": "core",
@@ -21273,6 +21313,66 @@
],
"hasChildren": false
},
{
"path": "channels.line.accounts.*.threadBindings",
"kind": "channel",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "channels.line.accounts.*.threadBindings.enabled",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.accounts.*.threadBindings.idleHours",
"kind": "channel",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.accounts.*.threadBindings.maxAgeHours",
"kind": "channel",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.accounts.*.threadBindings.spawnAcpSessions",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.accounts.*.threadBindings.spawnSubagentSessions",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.accounts.*.tokenFile",
"kind": "channel",
@@ -21562,6 +21662,66 @@
],
"hasChildren": false
},
{
"path": "channels.line.threadBindings",
"kind": "channel",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "channels.line.threadBindings.enabled",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.threadBindings.idleHours",
"kind": "channel",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.threadBindings.maxAgeHours",
"kind": "channel",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.threadBindings.spawnAcpSessions",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.threadBindings.spawnSubagentSessions",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.line.tokenFile",
"kind": "channel",
@@ -22583,6 +22743,23 @@
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.streaming",
"kind": "channel",
"type": [
"boolean",
"string"
],
"required": false,
"enumValues": [
"partial",
"off"
],
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.matrix.textChunkLimit",
"kind": "channel",

View File

@@ -1,4 +1,4 @@
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5576}
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5593}
{"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}
@@ -217,6 +217,8 @@
{"recordType":"path","path":"agents.defaults.memorySearch.sources.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.store","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"agents.defaults.memorySearch.store.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.store.fts","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"agents.defaults.memorySearch.store.fts.tokenizer","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.store.path","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Memory Search Index Path","help":"Sets where the SQLite memory index is stored on disk for each agent. Keep the default `~/.openclaw/memory/{agentId}.sqlite` unless you need custom storage placement or backup policy alignment.","hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.store.vector","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"agents.defaults.memorySearch.store.vector.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Memory Search Vector Index","help":"Enables the sqlite-vec extension used for vector similarity queries in memory search (default: true). Keep this enabled for normal semantic recall; disable only for debugging or fallback-only operation.","hasChildren":false}
@@ -443,6 +445,8 @@
{"recordType":"path","path":"agents.list.*.memorySearch.sources.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.list.*.memorySearch.store","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"agents.list.*.memorySearch.store.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.list.*.memorySearch.store.fts","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"agents.list.*.memorySearch.store.fts.tokenizer","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.list.*.memorySearch.store.path","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.list.*.memorySearch.store.vector","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"agents.list.*.memorySearch.store.vector.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1893,6 +1897,12 @@
{"recordType":"path","path":"channels.line.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.secretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.threadBindings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.idleHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.maxAgeHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.spawnAcpSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.spawnSubagentSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.tokenFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.webhookPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1918,6 +1928,12 @@
{"recordType":"path","path":"channels.line.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.secretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
{"recordType":"path","path":"channels.line.threadBindings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.line.threadBindings.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.threadBindings.idleHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.threadBindings.maxAgeHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.threadBindings.spawnAcpSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.threadBindings.spawnSubagentSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.tokenFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.webhookPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Matrix","help":"open protocol; install the plugin to enable.","hasChildren":true}
@@ -2011,6 +2027,7 @@
{"recordType":"path","path":"channels.matrix.rooms.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.startupVerification","kind":"channel","type":"string","required":false,"enumValues":["off","if-unverified"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.startupVerificationCooldownHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.streaming","kind":"channel","type":["boolean","string"],"required":false,"enumValues":["partial","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.textChunkLimit","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.threadBindings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.matrix.threadBindings.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}

View File

@@ -1,5 +1,5 @@
---
summary: "Group chat behavior across surfaces (WhatsApp/Telegram/Discord/Slack/Signal/iMessage/Microsoft Teams/Zalo)"
summary: "Group chat behavior across surfaces (Discord/iMessage/Matrix/Microsoft Teams/Signal/Slack/Telegram/WhatsApp/Zalo)"
read_when:
- Changing group chat behavior or mention gating
title: "Groups"
@@ -7,7 +7,7 @@ title: "Groups"
# Groups
OpenClaw treats group chats consistently across surfaces: WhatsApp, Telegram, Discord, Slack, Signal, iMessage, Microsoft Teams, Zalo.
OpenClaw treats group chats consistently across surfaces: Discord, iMessage, Matrix, Microsoft Teams, Signal, Slack, Telegram, WhatsApp, Zalo.
## Beginner intro (2 minutes)
@@ -187,7 +187,7 @@ Notes:
- DM pairing approvals (`*-allowFrom` store entries) apply to DM access only; group sender authorization stays explicit to group allowlists.
- Discord: allowlist uses `channels.discord.guilds.<id>.channels`.
- Slack: allowlist uses `channels.slack.channels`.
- Matrix: allowlist uses `channels.matrix.groups` (room IDs, aliases, or names). Use `channels.matrix.groupAllowFrom` to restrict senders; per-room `users` allowlists are also supported.
- Matrix: allowlist uses `channels.matrix.groups`. Prefer room IDs or aliases; joined-room name lookup is best-effort, and unresolved names are ignored at runtime. Use `channels.matrix.groupAllowFrom` to restrict senders; per-room `users` allowlists are also supported.
- Group DMs are controlled separately (`channels.discord.dm.*`, `channels.slack.dm.*`).
- Telegram allowlist can match user IDs (`"123456789"`, `"telegram:123456789"`, `"tg:123456789"`) or usernames (`"@alice"` or `"alice"`); prefixes are case-insensitive.
- Default is `groupPolicy: "allowlist"`; if your group allowlist is empty, group messages are blocked.

View File

@@ -28,7 +28,7 @@ openclaw plugins install @openclaw/line
Local checkout (when running from a git repo):
```bash
openclaw plugins install ./extensions/line
openclaw plugins install ./path/to/local/line-plugin
```
## Setup
@@ -184,6 +184,25 @@ The LINE plugin also ships a `/card` command for Flex message presets:
/card info "Welcome" "Thanks for joining!"
```
## ACP support
LINE supports ACP (Agent Communication Protocol) conversation bindings:
- `/acp spawn <agent> --bind here` binds the current LINE chat to an ACP session without creating a child thread.
- Configured ACP bindings and active conversation-bound ACP sessions work on LINE like other conversation channels.
See [ACP agents](/tools/acp-agents) for details.
## Outbound media
The LINE plugin supports sending images, videos, and audio files through the agent message tool. Media is sent via the LINE-specific delivery path with appropriate preview and tracking handling:
- **Images**: sent as LINE image messages with automatic preview generation.
- **Videos**: sent with explicit preview and content-type handling.
- **Audio**: sent as LINE audio messages.
Generic media sends fall back to the existing image-only route when a LINE-specific path is not available.
## Troubleshooting
- **Webhook verification fails:** ensure the webhook URL is HTTPS and the

View File

@@ -1,5 +1,5 @@
---
summary: "Inbound channel location parsing (Telegram + WhatsApp) and context fields"
summary: "Inbound channel location parsing (Telegram/WhatsApp/Matrix) and context fields"
read_when:
- Adding or modifying channel location parsing
- Using location context fields in agent prompts or tools

View File

@@ -24,7 +24,7 @@ openclaw plugins install @openclaw/matrix
Install from a local checkout:
```bash
openclaw plugins install ./extensions/matrix
openclaw plugins install ./path/to/local/matrix-plugin
```
See [Plugins](/tools/plugin) for plugin behavior and install rules.
@@ -157,14 +157,41 @@ This is a practical baseline config with DM pairing, room allowlist, and E2EE en
autoJoinAllowlist: ["!roomid:example.org"],
threadReplies: "inbound",
replyToMode: "off",
streaming: "partial",
},
},
}
```
## E2EE setup
## Streaming previews
## Bot to bot rooms
Matrix reply streaming is opt-in.
Set `channels.matrix.streaming` to `"partial"` when you want OpenClaw to send a single draft reply,
edit that draft in place while the model is generating text, and then finalize it when the reply is
done:
```json5
{
channels: {
matrix: {
streaming: "partial",
},
},
}
```
- `streaming: "off"` is the default. OpenClaw waits for the final reply and sends it once.
- `streaming: "partial"` creates one editable preview message instead of sending multiple partial messages.
- If the preview no longer fits in one Matrix event, OpenClaw stops preview streaming and falls back to normal final delivery.
- Media replies still send attachments normally. If a stale preview can no longer be reused safely, OpenClaw redacts it before sending the final media reply.
- Preview edits cost extra Matrix API calls. Leave streaming off if you want the most conservative rate-limit behavior.
## Encryption and verification
In encrypted (E2EE) rooms, outbound image events use `thumbnail_file` so image previews are encrypted alongside the full attachment. Unencrypted rooms still use plain `thumbnail_url`. No configuration is needed — the plugin detects E2EE state automatically.
### Bot to bot rooms
By default, Matrix messages from other configured OpenClaw Matrix accounts are ignored.
@@ -401,6 +428,19 @@ Planned improvement:
- add SecretRef support for persistent Matrix key material so recovery keys and related store-encryption secrets can be sourced from OpenClaw secrets providers instead of only local files
## Profile management
Update the Matrix self-profile for the selected account with:
```bash
openclaw matrix profile set --name "OpenClaw Assistant"
openclaw matrix profile set --avatar-url https://cdn.example.org/avatar.png
```
Add `--account <id>` when you want to target a named Matrix account explicitly.
Matrix accepts `mxc://` avatar URLs directly. When you pass an `http://` or `https://` avatar URL, OpenClaw uploads it to Matrix first and stores the resolved `mxc://` URL back into `channels.matrix.avatarUrl` (or the selected account override).
## Automatic verification notices
Matrix now posts verification lifecycle notices directly into the strict DM verification room as `m.notice` messages.
@@ -673,6 +713,7 @@ Live directory lookup uses the logged-in Matrix account:
- `groupAllowFrom`: allowlist of user IDs for room traffic.
- `groupAllowFrom` entries should be full Matrix user IDs. Unresolved names are ignored at runtime.
- `replyToMode`: `off`, `first`, or `all`.
- `streaming`: `off` (default) or `partial`. `partial` enables single-message draft previews with edit-in-place updates.
- `threadReplies`: `off`, `inbound`, or `always`.
- `threadBindings`: per-channel overrides for thread-bound session routing and lifecycle.
- `startupVerification`: automatic self-verification request mode on startup (`if-unverified`, `off`).
@@ -683,7 +724,7 @@ Live directory lookup uses the logged-in Matrix account:
- `ackReaction`: optional ack reaction override for this channel/account.
- `ackReactionScope`: optional ack reaction scope override (`group-mentions`, `group-all`, `direct`, `all`, `none`, `off`).
- `reactionNotifications`: inbound reaction notification mode (`own`, `off`).
- `mediaMaxMb`: outbound media size cap in MB.
- `mediaMaxMb`: media size cap in MB for Matrix media handling. It applies to outbound sends and inbound media processing.
- `autoJoin`: invite auto-join policy (`always`, `allowlist`, `off`). Default: `off`.
- `autoJoinAllowlist`: rooms/aliases allowed when `autoJoin` is `allowlist`. Alias entries are resolved to room IDs during invite handling; OpenClaw does not trust alias state claimed by the invited room.
- `dm`: DM policy block (`enabled`, `policy`, `allowFrom`).

View File

@@ -25,7 +25,7 @@ openclaw plugins install @openclaw/mattermost
Local checkout (when running from a git repo):
```bash
openclaw plugins install ./extensions/mattermost
openclaw plugins install ./path/to/local/mattermost-plugin
```
If you choose Mattermost during setup and a git checkout is detected,

View File

@@ -30,7 +30,7 @@ openclaw plugins install @openclaw/msteams
Local checkout (when running from a git repo):
```bash
openclaw plugins install ./extensions/msteams
openclaw plugins install ./path/to/local/msteams-plugin
```
If you choose Teams during setup and a git checkout is detected,
@@ -242,7 +242,7 @@ This is often easier than hand-editing JSON manifests.
1. **Install the Microsoft Teams plugin**
- From npm: `openclaw plugins install @openclaw/msteams`
- From a local checkout: `openclaw plugins install ./extensions/msteams`
- From a local checkout: `openclaw plugins install ./path/to/local/msteams-plugin`
2. **Bot registration**
- Create an Azure Bot (see above) and note:

View File

@@ -22,7 +22,7 @@ openclaw plugins install @openclaw/nextcloud-talk
Local checkout (when running from a git repo):
```bash
openclaw plugins install ./extensions/nextcloud-talk
openclaw plugins install ./path/to/local/nextcloud-talk-plugin
```
If you choose Nextcloud Talk during setup and a git checkout is detected,

View File

@@ -35,7 +35,7 @@ openclaw plugins install @openclaw/nostr
Use a local checkout (dev workflows):
```bash
openclaw plugins install --link <path-to-openclaw>/extensions/nostr
openclaw plugins install --link <path-to-local-nostr-plugin>
```
Restart the Gateway after installing or enabling plugins.

View File

@@ -19,7 +19,7 @@ Synology Chat is plugin-based and not part of the default core channel install.
Install from a local checkout:
```bash
openclaw plugins install ./extensions/synology-chat
openclaw plugins install ./path/to/local/synology-chat-plugin
```
Details: [Plugins](/tools/plugin)

View File

@@ -27,7 +27,7 @@ openclaw plugins install @openclaw/tlon
Local checkout (when running from a git repo):
```bash
openclaw plugins install ./extensions/tlon
openclaw plugins install ./path/to/local/tlon-plugin
```
Details: [Plugins](/tools/plugin)

View File

@@ -22,7 +22,7 @@ openclaw plugins install @openclaw/twitch
Local checkout (when running from a git repo):
```bash
openclaw plugins install ./extensions/twitch
openclaw plugins install ./path/to/local/twitch-plugin
```
Details: [Plugins](/tools/plugin)

View File

@@ -20,7 +20,7 @@ Zalo ships as a plugin and is not bundled with the core install.
## Quick setup (beginner)
1. Install the Zalo plugin:
- From a source checkout: `openclaw plugins install ./extensions/zalo`
- From a source checkout: `openclaw plugins install ./path/to/local/zalo-plugin`
- From npm (if published): `openclaw plugins install @openclaw/zalo`
- Or pick **Zalo** in setup and confirm the install prompt
2. Set the token:

View File

@@ -17,7 +17,7 @@ Status: experimental. This integration automates a **personal Zalo account** via
Zalo Personal ships as a plugin and is not bundled with the core install.
- Install via CLI: `openclaw plugins install @openclaw/zalouser`
- Or from a source checkout: `openclaw plugins install ./extensions/zalouser`
- Or from a source checkout: `openclaw plugins install ./path/to/local/zalouser-plugin`
- Details: [Plugins](/tools/plugin)
No external `zca`/`openzca` CLI binary is required.

View File

@@ -32,6 +32,27 @@ openclaw browser --browser-profile openclaw open https://example.com
openclaw browser --browser-profile openclaw snapshot
```
## If the command is missing
If `openclaw browser` is an unknown command, check `plugins.allow` in
`~/.openclaw/openclaw.json`.
When `plugins.allow` is present, the bundled browser plugin must be listed
explicitly:
```json5
{
plugins: {
allow: ["telegram", "browser"],
},
}
```
`browser.enabled=true` does not restore the CLI subcommand when the plugin
allowlist excludes `browser`.
Related: [Browser tool](/tools/browser#missing-browser-command-or-tool)
## Profiles
Profiles are named browser routing configs. In practice:

View File

@@ -1,7 +1,7 @@
---
summary: "CLI reference for `openclaw channels` (accounts, status, login/logout, logs)"
read_when:
- You want to add/remove channel accounts (WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage)
- You want to add/remove channel accounts (WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/Matrix)
- You want to check channel status or tail channel logs
title: "channels"
---

View File

@@ -9,7 +9,7 @@ title: "message"
# `openclaw message`
Single outbound command for sending messages and channel actions
(Discord/Google Chat/Slack/Mattermost (plugin)/Telegram/WhatsApp/Signal/iMessage/Microsoft Teams).
(Discord/Google Chat/iMessage/Matrix/Mattermost (plugin)/Microsoft Teams/Signal/Slack/Telegram/WhatsApp).
## Usage
@@ -21,7 +21,7 @@ Channel selection:
- `--channel` required if more than one channel is configured.
- If exactly one channel is configured, it becomes the default.
- Values: `whatsapp|telegram|discord|googlechat|slack|mattermost|signal|imessage|msteams` (Mattermost requires plugin)
- Values: `discord|googlechat|imessage|matrix|mattermost|msteams|signal|slack|telegram|whatsapp` (Mattermost requires plugin)
Target formats (`--target`):
@@ -33,6 +33,7 @@ Target formats (`--target`):
- Mattermost (plugin): `channel:<id>`, `user:<id>`, or `@username` (bare ids are treated as channels)
- Signal: `+E.164`, `group:<id>`, `signal:+E.164`, `signal:group:<id>`, or `username:<name>`/`u:<name>`
- iMessage: handle, `chat_id:<id>`, `chat_guid:<guid>`, or `chat_identifier:<id>`
- Matrix: `@user:server`, `!room:server`, or `#alias:server`
- Microsoft Teams: conversation id (`19:...@thread.tacv2`) or `conversation:<id>` or `user:<aad-object-id>`
Name lookup:
@@ -65,7 +66,7 @@ Name lookup:
### Core
- `send`
- Channels: WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/Microsoft Teams
- Channels: WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/Matrix/Microsoft Teams
- Required: `--target`, plus `--message` or `--media`
- Optional: `--media`, `--reply-to`, `--thread-id`, `--gif-playback`
- Telegram only: `--buttons` (requires `channels.telegram.capabilities.inlineButtons` to allow it)
@@ -82,7 +83,7 @@ Name lookup:
- Telegram only: `--poll-duration-seconds` (5-600), `--silent`, `--poll-anonymous` / `--poll-public`, `--thread-id`
- `react`
- Channels: Discord/Google Chat/Slack/Telegram/WhatsApp/Signal
- Channels: Discord/Google Chat/Slack/Telegram/WhatsApp/Signal/Matrix
- Required: `--message-id`, `--target`
- Optional: `--emoji`, `--remove`, `--participant`, `--from-me`, `--target-author`, `--target-author-uuid`
- Note: `--remove` requires `--emoji` (omit `--emoji` to clear own reactions where supported; see /tools/reactions)
@@ -90,35 +91,36 @@ Name lookup:
- Signal group reactions: `--target-author` or `--target-author-uuid` required
- `reactions`
- Channels: Discord/Google Chat/Slack
- Channels: Discord/Google Chat/Slack/Matrix
- Required: `--message-id`, `--target`
- Optional: `--limit`
- `read`
- Channels: Discord/Slack
- Channels: Discord/Slack/Matrix
- Required: `--target`
- Optional: `--limit`, `--before`, `--after`
- Discord only: `--around`
- `edit`
- Channels: Discord/Slack
- Channels: Discord/Slack/Matrix
- Required: `--message-id`, `--message`, `--target`
- `delete`
- Channels: Discord/Slack/Telegram
- Channels: Discord/Slack/Telegram/Matrix
- Required: `--message-id`, `--target`
- `pin` / `unpin`
- Channels: Discord/Slack
- Channels: Discord/Slack/Matrix
- Required: `--message-id`, `--target`
- `pins` (list)
- Channels: Discord/Slack
- Channels: Discord/Slack/Matrix
- Required: `--target`
- `permissions`
- Channels: Discord
- Channels: Discord/Matrix
- Required: `--target`
- Matrix only: available when Matrix encryption is enabled and verification actions are allowed
- `search`
- Channels: Discord

View File

@@ -161,7 +161,7 @@ the plugin allowlist, and linked `plugins.load.paths` entries when applicable.
For active memory plugins, the memory slot resets to `memory-core`.
By default, uninstall also removes the plugin install directory under the active
state dir extensions root (`$OPENCLAW_STATE_DIR/extensions/<id>`). Use
state-dir plugin root. Use
`--keep-files` to keep files on disk.
`--keep-config` is supported as a deprecated alias for `--keep-files`.

View File

@@ -247,7 +247,7 @@ OpenClaw ships with the piai catalog. These providers require **no**
- Example model: `kilocode/anthropic/claude-opus-4.6`
- CLI: `openclaw onboard --kilocode-api-key <key>`
- Base URL: `https://api.kilo.ai/api/gateway/`
- Expanded built-in catalog includes GLM-5 Free, MiniMax M2.5 Free, GPT-5.2, Gemini 3 Pro Preview, Gemini 3 Flash Preview, Grok Code Fast 1, and Kimi K2.5.
- Expanded built-in catalog includes GLM-5 Free, MiniMax M2.7 Free, GPT-5.2, Gemini 3 Pro Preview, Gemini 3 Flash Preview, Grok Code Fast 1, and Kimi K2.5.
See [/providers/kilocode](/providers/kilocode) for setup details.
@@ -538,8 +538,8 @@ Example (OpenAIcompatible):
{
agents: {
defaults: {
model: { primary: "lmstudio/minimax-m2.5-gs32" },
models: { "lmstudio/minimax-m2.5-gs32": { alias: "Minimax" } },
model: { primary: "lmstudio/my-local-model" },
models: { "lmstudio/my-local-model": { alias: "Local" } },
},
},
models: {
@@ -550,8 +550,8 @@ Example (OpenAIcompatible):
api: "openai-completions",
models: [
{
id: "minimax-m2.5-gs32",
name: "MiniMax M2.5",
id: "my-local-model",
name: "Local Model",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },

View File

@@ -617,7 +617,7 @@ terms before depending on subscription auth.
{
agent: {
workspace: "~/.openclaw/workspace",
model: { primary: "lmstudio/minimax-m2.5-gs32" },
model: { primary: "lmstudio/my-local-model" },
},
models: {
mode: "merge",
@@ -628,8 +628,8 @@ terms before depending on subscription auth.
api: "openai-responses",
models: [
{
id: "minimax-m2.5-gs32",
name: "MiniMax M2.5 GS32",
id: "my-local-model",
name: "Local Model",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },

View File

@@ -2356,13 +2356,13 @@ Base URL should omit `/v1` (Anthropic client appends it). Shortcut: `openclaw on
```
Set `MINIMAX_API_KEY`. Shortcut: `openclaw onboard --auth-choice minimax-api`.
`MiniMax-M2.5` and `MiniMax-M2.5-highspeed` remain available if you prefer the older text models.
The model catalog now defaults to M2.7 only.
</Accordion>
<Accordion title="Local models (LM Studio)">
See [Local Models](/gateway/local-models). TL;DR: run MiniMax M2.5 via LM Studio Responses API on serious hardware; keep hosted models merged for fallback.
See [Local Models](/gateway/local-models). TL;DR: run a large local model via LM Studio Responses API on serious hardware; keep hosted models merged for fallback.
</Accordion>

View File

@@ -13,34 +13,34 @@ Local is doable, but OpenClaw expects large context + strong defenses against pr
If you want the lowest-friction local setup, start with [Ollama](/providers/ollama) and `openclaw onboard`. This page is the opinionated guide for higher-end local stacks and custom OpenAI-compatible local servers.
## Recommended: LM Studio + MiniMax M2.5 (Responses API, full-size)
## Recommended: LM Studio + large local model (Responses API)
Best current local stack. Load MiniMax M2.5 in LM Studio, enable the local server (default `http://127.0.0.1:1234`), and use Responses API to keep reasoning separate from final text.
Best current local stack. Load a large model in LM Studio (for example, a full-size Qwen, DeepSeek, or Llama build), enable the local server (default `http://127.0.0.1:1234`), and use Responses API to keep reasoning separate from final text.
```json5
{
agents: {
defaults: {
model: { primary: "lmstudio/minimax-m2.5-gs32" },
model: { primary: lmstudio/my-local-model” },
models: {
"anthropic/claude-opus-4-6": { alias: "Opus" },
"lmstudio/minimax-m2.5-gs32": { alias: "Minimax" },
anthropic/claude-opus-4-6: { alias: Opus },
lmstudio/my-local-model”: { alias: “Local” },
},
},
},
models: {
mode: "merge",
mode: merge,
providers: {
lmstudio: {
baseUrl: "http://127.0.0.1:1234/v1",
apiKey: "lmstudio",
api: "openai-responses",
baseUrl: http://127.0.0.1:1234/v1,
apiKey: lmstudio,
api: openai-responses,
models: [
{
id: "minimax-m2.5-gs32",
name: "MiniMax M2.5 GS32",
id: “my-local-model”,
name: “Local Model”,
reasoning: false,
input: ["text"],
input: [text],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 196608,
maxTokens: 8192,
@@ -55,7 +55,8 @@ Best current local stack. Load MiniMax M2.5 in LM Studio, enable the local serve
**Setup checklist**
- Install LM Studio: [https://lmstudio.ai](https://lmstudio.ai)
- In LM Studio, download the **largest MiniMax M2.5 build available** (avoid “small”/heavily quantized variants), start the server, confirm `http://127.0.0.1:1234/v1/models` lists it.
- In LM Studio, download the **largest model build available** (avoid “small”/heavily quantized variants), start the server, confirm `http://127.0.0.1:1234/v1/models` lists it.
- Replace `my-local-model` with the actual model ID shown in LM Studio.
- Keep the model loaded; cold-load adds startup latency.
- Adjust `contextWindow`/`maxTokens` if your LM Studio build differs.
- For WhatsApp, stick to Responses API so only final text is sent.
@@ -70,11 +71,11 @@ Keep hosted models configured even when running local; use `models.mode: "merge"
defaults: {
model: {
primary: "anthropic/claude-sonnet-4-6",
fallbacks: ["lmstudio/minimax-m2.5-gs32", "anthropic/claude-opus-4-6"],
fallbacks: ["lmstudio/my-local-model", "anthropic/claude-opus-4-6"],
},
models: {
"anthropic/claude-sonnet-4-6": { alias: "Sonnet" },
"lmstudio/minimax-m2.5-gs32": { alias: "MiniMax Local" },
"lmstudio/my-local-model": { alias: "Local" },
"anthropic/claude-opus-4-6": { alias: "Opus" },
},
},
@@ -88,8 +89,8 @@ Keep hosted models configured even when running local; use `models.mode: "merge"
api: "openai-responses",
models: [
{
id: "minimax-m2.5-gs32",
name: "MiniMax M2.5 GS32",
id: "my-local-model",
name: "Local Model",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },

View File

@@ -191,7 +191,7 @@ If more than one person can DM your bot:
- **Local disk hygiene** (permissions, symlinks, config includes, “synced folder” paths).
- **Plugins** (extensions exist without an explicit allowlist).
- **Policy drift/misconfig** (sandbox docker settings configured but sandbox mode off; ineffective `gateway.nodes.denyCommands` patterns because matching is exact command-name only (for example `system.run`) and does not inspect shell text; dangerous `gateway.nodes.allowCommands` entries; global `tools.profile="minimal"` overridden by per-agent profiles; extension plugin tools reachable under permissive tool policy).
- **Runtime expectation drift** (for example `tools.exec.host="sandbox"` while sandbox mode is off, which runs directly on the gateway host).
- **Runtime expectation drift** (for example `tools.exec.host="sandbox"` while sandbox mode is off, which now fails closed because no sandbox runtime is available).
- **Model hygiene** (warn when configured models look legacy; not a hard block).
If you run `--deep`, OpenClaw also attempts a best-effort live Gateway probe.
@@ -253,8 +253,8 @@ High-signal `checkId` values you will most likely see in real deployments (not e
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
| `sandbox.dangerous_network_mode` | critical | Sandbox Docker network uses `host` or `container:*` namespace-join mode | `agents.*.sandbox.docker.network` | no |
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` resolves to host exec when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` resolves to host exec when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` fails closed when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` fails closed when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
| `tools.exec.security_full_configured` | warn/critical | Host exec is running with `security="full"` | `tools.exec.security`, `agents.list[].tools.exec.security` | no |
| `tools.exec.auto_allow_skills_enabled` | warn | Exec approvals trust skill bins implicitly | `~/.openclaw/exec-approvals.json` | no |
| `tools.exec.allowlist_interpreter_without_strict_inline_eval` | warn | Interpreter allowlists permit inline eval without forced reapproval | `tools.exec.strictInlineEval`, `agents.list[].tools.exec.strictInlineEval`, exec approvals allowlist | no |
@@ -459,7 +459,7 @@ Plugins run **in-process** with the Gateway. Treat them as trusted code:
- Review plugin config before enabling.
- Restart the Gateway after plugin changes.
- If you install plugins (`openclaw plugins install <package>`), treat it like running untrusted code:
- The install path is `~/.openclaw/extensions/<pluginId>/` (or `$OPENCLAW_STATE_DIR/extensions/<pluginId>/`).
- The install path is the per-plugin directory under the active plugin install root.
- OpenClaw uses `npm pack` and then runs `npm install --omit=dev` in that directory (npm lifecycle scripts can execute code during install).
- Prefer pinned, exact versions (`@scope/pkg@1.2.3`), and inspect the unpacked code on disk before enabling.
@@ -534,7 +534,7 @@ Even with strong system prompts, **prompt injection is not solved**. System prom
- Prefer mention gating in groups; avoid “always-on” bots in public rooms.
- Treat links, attachments, and pasted instructions as hostile by default.
- Run sensitive tool execution in a sandbox; keep secrets out of the agents reachable filesystem.
- Note: sandboxing is opt-in. If sandbox mode is off, exec runs on the gateway host even though tools.exec.host defaults to sandbox, and host exec does not require approvals unless you set host=gateway and configure exec approvals.
- Note: sandboxing is opt-in. If sandbox mode is off, `host=sandbox` fails closed even though tools.exec.host defaults to sandbox. To run on the gateway host, set `host=gateway` and configure exec approvals.
- Limit high-risk tools (`exec`, `browser`, `web_fetch`, `web_search`) to trusted agents or explicit allowlists.
- If you allowlist interpreters (`python`, `node`, `ruby`, `perl`, `php`, `lua`, `osascript`), enable `tools.exec.strictInlineEval` so inline eval forms still need explicit approval.
- **Model choice matters:** older/smaller/legacy models are significantly less robust against prompt injection and tool misuse. For tool-enabled agents, use the strongest latest-generation, instruction-hardened model available.
@@ -859,7 +859,7 @@ Assume anything under `~/.openclaw/` (or `$OPENCLAW_STATE_DIR/`) may contain sec
- `secrets.json` (optional): file-backed secret payload used by `file` SecretRef providers (`secrets.providers`).
- `agents/<agentId>/agent/auth.json`: legacy compatibility file. Static `api_key` entries are scrubbed when discovered.
- `agents/<agentId>/sessions/**`: session transcripts (`*.jsonl`) + routing metadata (`sessions.json`) that can contain private messages and tool output.
- `extensions/**`: installed plugins (plus their `node_modules/`).
- bundled plugin packages: installed plugins (plus their `node_modules/`).
- `sandboxes/**`: tool sandbox workspaces; can accumulate copies of files you read/write inside the sandbox.
Hardening tips:

View File

@@ -287,12 +287,15 @@ openclaw doctor
Look for:
- Whether `plugins.allow` is set and includes `browser`.
- Valid browser executable path.
- CDP profile reachability.
- Local Chrome availability for `existing-session` / `user` profiles.
Common signatures:
- `unknown command "browser"` or `unknown command 'browser'` → the bundled browser plugin is excluded by `plugins.allow`.
- browser tool missing / unavailable while `browser.enabled=true``plugins.allow` excludes `browser`, so the plugin never loaded.
- `Failed to start Chrome CDP on port` → browser process failed to launch.
- `browser.executablePath not found` → configured path is invalid.
- `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs.

View File

@@ -633,7 +633,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
</Accordion>
<Accordion title="Is a local model OK for casual chats?">
Usually no. OpenClaw needs large context + strong safety; small cards truncate and leak. If you must, run the **largest** MiniMax M2.5 build you can locally (LM Studio) and see [/gateway/local-models](/gateway/local-models). Smaller/quantized models increase prompt-injection risk - see [Security](/gateway/security).
Usually no. OpenClaw needs large context + strong safety; small cards truncate and leak. If you must, run the **largest** model build you can locally (LM Studio) and see [/gateway/local-models](/gateway/local-models). Smaller/quantized models increase prompt-injection risk - see [Security](/gateway/security).
</Accordion>
<Accordion title="How do I keep hosted model traffic in a specific region?">

View File

@@ -45,7 +45,7 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
- Command: `pnpm test`
- Config: `scripts/test-parallel.mjs` (runs `vitest.unit.config.ts`, `vitest.extensions.config.ts`, `vitest.gateway.config.ts`)
- Files: `src/**/*.test.ts`, `extensions/**/*.test.ts`
- Files: `src/**/*.test.ts`, bundled plugin `**/*.test.ts`
- Scope:
- Pure unit tests
- In-process integration tests (gateway auth, routing, tooling, parsing, config)

View File

@@ -282,6 +282,7 @@ flowchart TD
Common log signatures:
- `unknown command "browser"` or `unknown command 'browser'` → `plugins.allow` is set and does not include `browser`.
- `Failed to start Chrome CDP on port` → local browser launch failed.
- `browser.executablePath not found` → configured binary path is wrong.
- `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs.
@@ -290,6 +291,7 @@ flowchart TD
Deep pages:
- [/gateway/troubleshooting#browser-tool-fails](/gateway/troubleshooting#browser-tool-fails)
- [/tools/browser#missing-browser-command-or-tool](/tools/browser#missing-browser-command-or-tool)
- [/tools/browser-linux-troubleshooting](/tools/browser-linux-troubleshooting)
- [/tools/browser-wsl2-windows-remote-cdp-troubleshooting](/tools/browser-wsl2-windows-remote-cdp-troubleshooting)

View File

@@ -48,7 +48,7 @@ update **without** changing your persisted channel:
```bash
# Install a specific version
openclaw update --tag 2026.3.28-beta.1
openclaw update --tag 2026.3.29-beta.1
# Install from the beta dist-tag (one-off, does not persist)
openclaw update --tag beta
@@ -57,7 +57,7 @@ openclaw update --tag beta
openclaw update --tag main
# Install a specific npm package spec
openclaw update --tag openclaw@2026.3.28-beta.1
openclaw update --tag openclaw@2026.3.29-beta.1
```
Notes:
@@ -75,7 +75,7 @@ Preview what `openclaw update` would do without making changes:
```bash
openclaw update --dry-run
openclaw update --channel beta --dry-run
openclaw update --tag 2026.3.28-beta.1 --dry-run
openclaw update --tag 2026.3.29-beta.1 --dry-run
openclaw update --dry-run --json
```

View File

@@ -197,7 +197,7 @@ If the old store reports room keys that were never backed up, OpenClaw warns ins
`Legacy Matrix encrypted state was detected, but the Matrix plugin helper is unavailable. Install or repair @openclaw/matrix so OpenClaw can inspect the old rust crypto store before upgrading.`
- Meaning: OpenClaw found old encrypted Matrix state, but it could not load the helper entrypoint from the Matrix plugin that normally inspects that store.
- What to do: reinstall or repair the Matrix plugin (`openclaw plugins install @openclaw/matrix`, or `openclaw plugins install ./extensions/matrix` for a repo checkout), then rerun `openclaw doctor --fix` or restart the gateway.
- What to do: reinstall or repair the Matrix plugin (`openclaw plugins install @openclaw/matrix`, or `openclaw plugins install ./path/to/local/matrix-plugin` for a repo checkout), then rerun `openclaw doctor --fix` or restart the gateway.
`Matrix plugin helper path is unsafe: ... Reinstall @openclaw/matrix and try again.`
@@ -312,7 +312,7 @@ If you accept losing unrecoverable old encrypted history, you can instead reset
`Matrix is installed from a custom path that no longer exists: ...`
- Meaning: your plugin install record points at a local path that is gone.
- What to do: reinstall with `openclaw plugins install @openclaw/matrix`, or if you are running from a repo checkout, `openclaw plugins install ./extensions/matrix`.
- What to do: reinstall with `openclaw plugins install @openclaw/matrix`, or if you are running from a repo checkout, `openclaw plugins install ./path/to/local/matrix-plugin`.
## If encrypted history still does not come back

View File

@@ -28,7 +28,7 @@ pnpm test -- \
"src/agents/pi-tools*.test.ts" \
"src/agents/pi-settings.test.ts" \
"src/agents/pi-tool-definition-adapter*.test.ts" \
"src/agents/pi-extensions/**/*.test.ts"
"src/agents/pi-hooks/**/*.test.ts"
```
To include the live provider exercise:
@@ -44,7 +44,7 @@ This covers the main Pi unit suites:
- `src/agents/pi-tools*.test.ts`
- `src/agents/pi-settings.test.ts`
- `src/agents/pi-tool-definition-adapter.test.ts`
- `src/agents/pi-extensions/*.test.ts`
- `src/agents/pi-hooks/*.test.ts`
## Manual Testing

View File

@@ -88,7 +88,7 @@ src/agents/
├── pi-tools.types.ts # AnyAgentTool type alias
├── pi-tool-definition-adapter.ts # AgentTool -> ToolDefinition adapter
├── pi-settings.ts # Settings overrides
├── pi-extensions/ # Custom pi extensions
├── pi-hooks/ # Custom pi hooks
│ ├── compaction-safeguard.ts # Safeguard extension
│ ├── compaction-safeguard-runtime.ts
│ ├── context-pruning.ts # Cache-TTL context pruning extension
@@ -132,10 +132,10 @@ src/agents/
Channel-specific message action runtimes now live in the plugin-owned extension
directories instead of under `src/agents/tools`, for example:
- `extensions/discord/src/actions/runtime*.ts`
- `extensions/slack/src/action-runtime.ts`
- `extensions/telegram/src/action-runtime.ts`
- `extensions/whatsapp/src/action-runtime.ts`
- the Discord plugin action runtime files
- the Slack plugin action runtime file
- the Telegram plugin action runtime file
- the WhatsApp plugin action runtime file
## Core Integration Flow
@@ -390,7 +390,7 @@ OpenClaw loads custom pi extensions for specialized behavior:
### Compaction Safeguard
`src/agents/pi-extensions/compaction-safeguard.ts` adds guardrails to compaction, including adaptive token budgeting plus tool failure and file operation summaries:
`src/agents/pi-hooks/compaction-safeguard.ts` adds guardrails to compaction, including adaptive token budgeting plus tool failure and file operation summaries:
```typescript
if (resolveCompactionMode(params.cfg) === "safeguard") {
@@ -401,7 +401,7 @@ if (resolveCompactionMode(params.cfg) === "safeguard") {
### Context Pruning
`src/agents/pi-extensions/context-pruning.ts` implements cache-TTL based context pruning:
`src/agents/pi-hooks/context-pruning.ts` implements cache-TTL based context pruning:
```typescript
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
@@ -558,7 +558,7 @@ Pi integration coverage spans these suites:
- `src/agents/pi-tools*.test.ts`
- `src/agents/pi-tool-definition-adapter*.test.ts`
- `src/agents/pi-settings.test.ts`
- `src/agents/pi-extensions/**/*.test.ts`
- `src/agents/pi-hooks/**/*.test.ts`
Live/opt-in:

View File

@@ -969,16 +969,17 @@ authoring plugins:
New code should import the narrower primitives instead.
- Bundled extension internals remain private. External plugins should use only
`openclaw/plugin-sdk/*` subpaths. OpenClaw core/test code may use the repo
public entry points under `extensions/<id>/index.js`, `api.js`, `runtime-api.js`,
`setup-entry.js`, and narrowly scoped files such as `login-qr-api.js`. Never
import `extensions/<id>/src/*` from core or from another extension.
public entry points under a plugin package root such as `index.js`, `api.js`,
`runtime-api.js`, `setup-entry.js`, and narrowly scoped files such as
`login-qr-api.js`. Never import a plugin package's `src/*` from core or from
another extension.
- Repo entry point split:
`extensions/<id>/api.js` is the helper/types barrel,
`extensions/<id>/runtime-api.js` is the runtime-only barrel,
`extensions/<id>/index.js` is the bundled plugin entry,
and `extensions/<id>/setup-entry.js` is the setup plugin entry.
`<plugin-package-root>/api.js` is the helper/types barrel,
`<plugin-package-root>/runtime-api.js` is the runtime-only barrel,
`<plugin-package-root>/index.js` is the bundled plugin entry,
and `<plugin-package-root>/setup-entry.js` is the setup plugin entry.
- No bundled channel-branded public subpaths remain. Channel-specific helper and
runtime seams live under `extensions/<id>/api.js` and `extensions/<id>/runtime-api.js`;
runtime seams live under `<plugin-package-root>/api.js` and `<plugin-package-root>/runtime-api.js`;
the public SDK contract is the generic shared primitives instead.
Compatibility note:
@@ -1216,7 +1217,7 @@ Example:
},
"install": {
"npmSpec": "@openclaw/nextcloud-talk",
"localPath": "extensions/nextcloud-talk",
"localPath": "<bundled-plugin-local-path>",
"defaultChoice": "npm"
}
}

View File

@@ -115,10 +115,10 @@ and provider plugins have dedicated guides linked above.
OpenClaw checks ClawHub first, then falls back to npm.
**In-repo plugins:** place under `extensions/` — automatically discovered.
**In-repo plugins:** place under the bundled plugin workspace tree — automatically discovered.
```bash
pnpm test -- extensions/my-plugin/
pnpm test -- <bundled-plugin-root>/my-plugin/
```
</Step>
@@ -149,9 +149,12 @@ Hook guard semantics to keep in mind:
- `before_tool_call`: `{ block: true }` is terminal and stops lower-priority handlers.
- `before_tool_call`: `{ block: false }` is treated as no decision.
- `before_tool_call`: `{ requireApproval: true }` pauses agent execution and prompts the user for approval via the exec approval overlay, Telegram buttons, Discord interactions, or the `/approve` command on any channel.
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
- `message_sending`: `{ cancel: false }` is treated as no decision.
The `/approve` command handles both exec and plugin approvals with automatic fallback. Plugin approval forwarding can be configured independently via `approvals.plugin` in config.
See [SDK Overview hook decision semantics](/plugins/sdk-overview#hook-decision-semantics) for details.
## Registering agent tools
@@ -222,7 +225,7 @@ internal imports — never import your own plugin through its SDK path.
<Check>Entry point uses `defineChannelPluginEntry` or `definePluginEntry`</Check>
<Check>All imports use focused `plugin-sdk/<subpath>` paths</Check>
<Check>Internal imports use local modules, not SDK self-imports</Check>
<Check>Tests pass (`pnpm test -- extensions/my-plugin/`)</Check>
<Check>Tests pass (`pnpm test -- <bundled-plugin-root>/my-plugin/`)</Check>
<Check>`pnpm check` passes (in-repo plugins)</Check>
## Beta Release Testing

View File

@@ -265,7 +265,7 @@ dispatch.
// Your inbound handler dispatches the message to OpenClaw.
// The exact wiring depends on your platform SDK —
// see a real example in extensions/msteams or extensions/googlechat.
// see a real example in the bundled Microsoft Teams or Google Chat plugin package.
await handleAcmeChatInbound(api, event);
res.statusCode = 200;
@@ -279,7 +279,7 @@ dispatch.
<Note>
Inbound message handling is channel-specific. Each channel plugin owns
its own inbound pipeline. Look at bundled channel plugins
(e.g. `extensions/msteams`, `extensions/googlechat`) for real patterns.
(for example the Microsoft Teams or Google Chat plugin package) for real patterns.
</Note>
</Step>
@@ -320,7 +320,7 @@ dispatch.
```
```bash
pnpm test -- extensions/acme-chat/
pnpm test -- <bundled-plugin-root>/acme-chat/
```
For shared test helpers, see [Testing](/plugins/sdk-testing).
@@ -331,7 +331,7 @@ dispatch.
## File structure
```
extensions/acme-chat/
<bundled-plugin-root>/acme-chat/
├── package.json # openclaw.channel metadata
├── openclaw.plugin.json # Manifest with config schema
├── index.ts # defineChannelPluginEntry

View File

@@ -386,7 +386,7 @@ API key auth, and dynamic model resolution.
## File structure
```
extensions/acme-ai/
<bundled-plugin-root>/acme-ai/
├── package.json # openclaw.providers metadata
├── openclaw.plugin.json # Manifest with providerAuthEnvVars
├── index.ts # definePluginEntry + registerProvider

View File

@@ -274,7 +274,7 @@ const setupWizard: ChannelSetupWizard = {
The `ChannelSetupWizard` type supports `credentials`, `textInputs`,
`dmPolicy`, `allowFrom`, `groupAccess`, `prepare`, `finalize`, and more.
See bundled plugins (e.g. `extensions/discord/src/channel.setup.ts`) for
See bundled plugin packages (for example the Discord plugin `src/channel.setup.ts`) for
full examples.
For DM allowlist prompts that only need the standard
@@ -319,7 +319,7 @@ openclaw plugins install clawhub:@myorg/openclaw-my-plugin # ClawHub only
openclaw plugins install npm:@myorg/openclaw-my-plugin # npm only
```
**In-repo plugins:** place under `extensions/` and they are automatically
**In-repo plugins:** place under the bundled plugin workspace tree and they are automatically
discovered during build.
**Users can browse and install:**

View File

@@ -209,7 +209,7 @@ These tests assert:
For a specific plugin:
```bash
pnpm test -- extensions/my-channel/
pnpm test -- <bundled-plugin-root>/my-channel/
```
For contract tests only:
@@ -240,10 +240,10 @@ OpenClaw uses Vitest with V8 coverage thresholds. For plugin tests:
pnpm test
# Run specific plugin tests
pnpm test -- extensions/my-channel/src/channel.test.ts
pnpm test -- <bundled-plugin-root>/my-channel/src/channel.test.ts
# Run with a specific test name filter
pnpm test -- extensions/my-channel/ -t "resolves account"
pnpm test -- <bundled-plugin-root>/my-channel/ -t "resolves account"
# Run with coverage
pnpm test:coverage

View File

@@ -44,8 +44,9 @@ Restart the Gateway afterwards.
### Option B: install from a local folder (dev, no copying)
```bash
openclaw plugins install ./extensions/voice-call
cd ./extensions/voice-call && pnpm install
PLUGIN_SRC=./path/to/local/voice-call-plugin
openclaw plugins install "$PLUGIN_SRC"
cd "$PLUGIN_SRC" && pnpm install
```
Restart the Gateway afterwards.

View File

@@ -37,8 +37,9 @@ Restart the Gateway afterwards.
### Option B: install from a local folder (dev)
```bash
openclaw plugins install ./extensions/zalouser
cd ./extensions/zalouser && pnpm install
PLUGIN_SRC=./path/to/local/zalouser-plugin
openclaw plugins install "$PLUGIN_SRC"
cd "$PLUGIN_SRC" && pnpm install
```
Restart the Gateway afterwards.

View File

@@ -29,7 +29,7 @@ openclaw plugins enable open-prose
Restart the Gateway after enabling the plugin.
Dev/local checkout: `openclaw plugins install ./extensions/open-prose`
Dev/local checkout: `openclaw plugins install ./path/to/local/open-prose-plugin`
Related docs: [Plugins](/tools/plugin), [Plugin manifest](/plugins/manifest), [Skills](/tools/skills).

View File

@@ -14,6 +14,29 @@ OpenClaw's MiniMax provider defaults to **MiniMax M2.7**.
- `MiniMax-M2.7`: default hosted text model.
- `MiniMax-M2.7-highspeed`: faster M2.7 text tier.
- `image-01`: image generation model (generate and image-to-image editing).
## Image generation
The MiniMax plugin registers the `image-01` model for the `image_generate` tool. It supports:
- **Text-to-image generation** with aspect ratio control.
- **Image-to-image editing** (subject reference) with aspect ratio control.
- Supported aspect ratios: `1:1`, `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9`.
To use MiniMax for image generation, set it as the image generation provider:
```json5
{
agents: {
defaults: {
imageGenerationModel: { primary: "minimax/image-01" },
},
},
}
```
The plugin uses the same `MINIMAX_API_KEY` or OAuth auth as the text models. No additional configuration is needed if MiniMax is already set up.
## Choose a setup
@@ -34,7 +57,7 @@ You will be prompted to select an endpoint:
- **Global** - International users (`api.minimax.io`)
- **CN** - Users in China (`api.minimaxi.com`)
See [MiniMax plugin README](https://github.com/openclaw/openclaw/tree/main/extensions/minimax) for details.
See the MiniMax plugin package README in the OpenClaw repo for details.
### MiniMax M2.7 (API key)

View File

@@ -20,7 +20,7 @@ background.
## Recommended: Model Studio (Alibaba Cloud Coding Plan)
Use [Model Studio](/providers/modelstudio) for officially supported access to
Qwen models (Qwen 3.5 Plus, GLM-4.7, Kimi K2.5, MiniMax M2.5, and more).
Qwen models (Qwen 3.5 Plus, GLM-4.7, Kimi K2.5, and more).
```bash
# Global endpoint

View File

@@ -74,7 +74,7 @@ override with a custom `baseUrl` in config.
- **qwen3-coder-plus**, **qwen3-coder-next** — Qwen coding models
- **GLM-5** — GLM models via Alibaba
- **Kimi K2.5** — Moonshot AI via Alibaba
- **MiniMax-M2.5** — MiniMax via Alibaba
- **MiniMax-M2.7** — MiniMax via Alibaba
Some models (qwen3.5-plus, kimi-k2.5) support image input. Context windows range from 200K to 1M tokens.

View File

@@ -422,6 +422,7 @@ Notes:
- `vectorWeight` + `textWeight` is normalized to 1.0 in config resolution, so weights behave as percentages.
- If embeddings are unavailable (or the provider returns a zero-vector), we still run BM25 and return keyword matches.
- If FTS5 can't be created, we keep vector-only search (no hard failure).
- **CJK support**: FTS5 uses configurable trigram tokenization with a short-substring fallback so Chinese, Japanese, and Korean text is searchable without breaking mixed-length queries. CJK-heavy text is also weighted correctly during chunk size estimation, and surrogate-pair characters are preserved during fine splits.
This isn't "IR-theory perfect", but it's simple, fast, and tends to improve recall/precision on real notes.
If we want to get fancier later, common next steps are Reciprocal Rank Fusion (RRF) or score normalization

View File

@@ -46,7 +46,7 @@ For a high-level overview, see [Onboarding (CLI)](/start/wizard).
- More detail: [Vercel AI Gateway](/providers/vercel-ai-gateway)
- **Cloudflare AI Gateway**: prompts for Account ID, Gateway ID, and `CLOUDFLARE_AI_GATEWAY_API_KEY`.
- More detail: [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway)
- **MiniMax**: config is auto-written; hosted default is `MiniMax-M2.7` and `MiniMax-M2.5` stays available.
- **MiniMax**: config is auto-written; hosted default is `MiniMax-M2.7`.
- More detail: [MiniMax](/providers/minimax)
- **Synthetic (Anthropic-compatible)**: prompts for `SYNTHETIC_API_KEY`.
- More detail: [Synthetic](/providers/synthetic)
@@ -209,7 +209,7 @@ Typical fields in `~/.openclaw/openclaw.json`:
- `tools.profile` (local onboarding defaults to `"coding"` when unset; existing explicit values are preserved)
- `gateway.*` (mode, bind, auth, tailscale)
- `session.dmScope` (behavior details: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals))
- `channels.telegram.botToken`, `channels.discord.token`, `channels.signal.*`, `channels.imessage.*`
- `channels.telegram.botToken`, `channels.discord.token`, `channels.matrix.*`, `channels.signal.*`, `channels.imessage.*`
- Channel allowlists (Slack/Discord/Matrix/Microsoft Teams) when you opt in during the prompts (names resolve to IDs when possible).
- `skills.install.nodeManager`
- `wizard.lastRunAt`

View File

@@ -174,7 +174,7 @@ What you set:
More detail: [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway).
</Accordion>
<Accordion title="MiniMax">
Config is auto-written. Hosted default is `MiniMax-M2.7`; `MiniMax-M2.5` stays available.
Config is auto-written. Hosted default is `MiniMax-M2.7`.
More detail: [MiniMax](/providers/minimax).
</Accordion>
<Accordion title="Synthetic (Anthropic-compatible)">
@@ -258,7 +258,7 @@ Typical fields in `~/.openclaw/openclaw.json`:
- `tools.profile` (local onboarding defaults to `"coding"` when unset; existing explicit values are preserved)
- `gateway.*` (mode, bind, auth, tailscale)
- `session.dmScope` (local onboarding defaults this to `per-channel-peer` when unset; existing explicit values are preserved)
- `channels.telegram.botToken`, `channels.discord.token`, `channels.signal.*`, `channels.imessage.*`
- `channels.telegram.botToken`, `channels.discord.token`, `channels.matrix.*`, `channels.signal.*`, `channels.imessage.*`
- Channel allowlists (Slack, Discord, Matrix, Microsoft Teams) when you opt in during prompts (names resolve to IDs when possible)
- `skills.install.nodeManager`
- `wizard.lastRunAt`

View File

@@ -624,7 +624,7 @@ openclaw config set plugins.entries.acpx.enabled true
Local workspace install during development:
```bash
openclaw plugins install ./extensions/acpx
openclaw plugins install ./path/to/local/acpx-plugin
```
Then verify backend health:
@@ -637,7 +637,7 @@ Then verify backend health:
By default, the bundled acpx backend plugin (`acpx`) uses the plugin-local pinned binary:
1. Command defaults to `extensions/acpx/node_modules/.bin/acpx`.
1. Command defaults to the plugin-local `node_modules/.bin/acpx` inside the ACPX plugin package.
2. Expected version defaults to the extension pin.
3. Startup registers ACP backend immediately as not-ready.
4. A background ensure job verifies `acpx --version`.

View File

@@ -42,6 +42,9 @@ openclaw browser --browser-profile openclaw snapshot
If you get “Browser disabled”, enable it in config (see below) and restart the
Gateway.
If `openclaw browser` is missing entirely, or the agent says the browser tool
is unavailable, jump to [Missing browser command or tool](/tools/browser#missing-browser-command-or-tool).
## Plugin control
The default `browser` tool is now a bundled plugin that ships enabled by
@@ -73,13 +76,52 @@ replacement plugin to reuse.
The bundled browser plugin also owns the browser runtime implementation now.
Core keeps only shared Plugin SDK helpers plus compatibility re-exports for
older internal import paths. In practice, removing or replacing
`extensions/browser` removes the browser feature set instead of leaving a
second core-owned runtime behind.
older internal import paths. In practice, removing or replacing the browser
plugin package removes the browser feature set instead of leaving a second
core-owned runtime behind.
Browser config changes still require a Gateway restart so the bundled plugin
can re-register its browser service with the new settings.
## Missing browser command or tool
If `openclaw browser` suddenly becomes an unknown command after an upgrade, or
the agent reports that the browser tool is missing, the most common cause is a
restrictive `plugins.allow` list that does not include `browser`.
Example broken config:
```json5
{
plugins: {
allow: ["telegram"],
},
}
```
Fix it by adding `browser` to the plugin allowlist:
```json5
{
plugins: {
allow: ["telegram", "browser"],
},
}
```
Important notes:
- `browser.enabled=true` is not enough by itself when `plugins.allow` is set.
- `plugins.entries.browser.enabled=true` is also not enough by itself when `plugins.allow` is set.
- `tools.alsoAllow: ["browser"]` does **not** load the bundled browser plugin. It only adjusts tool policy after the plugin is already loaded.
- If you do not need a restrictive plugin allowlist, removing `plugins.allow` also restores the default bundled browser behavior.
Typical symptoms:
- `openclaw browser` is an unknown command.
- `browser.request` is missing.
- The agent reports the browser tool as unavailable or missing.
## Profiles: `openclaw` vs `user`
- `openclaw`: managed, isolated browser (no extension required).

View File

@@ -85,7 +85,7 @@ For a new capability, expect to touch these areas:
- `src/plugins/runtime/index.ts`
- `src/plugin-sdk/<capability>.ts`
- `src/plugin-sdk/<capability>-runtime.ts`
- one or more `extensions/<vendor>/...`
- one or more bundled plugin packages
- config/docs/tests
## Example: image generation

View File

@@ -30,7 +30,7 @@ Background sessions are scoped per agent; `process` only sees sessions from the
Notes:
- `host` defaults to `sandbox`.
- `elevated` is ignored when sandboxing is off (exec already runs on the host).
- `elevated` forces `host=gateway`; it is only available when elevated access is enabled for the current session/provider.
- `gateway`/`node` approvals are controlled by `~/.openclaw/exec-approvals.json`.
- `node` requires a paired node (companion app or headless node host).
- If multiple nodes are available, set `exec.node` or `tools.exec.node` to select one.
@@ -41,9 +41,9 @@ Notes:
- Host execution (`gateway`/`node`) rejects `env.PATH` and loader overrides (`LD_*`/`DYLD_*`) to
prevent binary hijacking or injected code.
- OpenClaw sets `OPENCLAW_SHELL=exec` in the spawned command environment (including PTY and sandbox execution) so shell/profile rules can detect exec-tool context.
- Important: sandboxing is **off by default**. If sandboxing is off and `host=sandbox` is explicitly
configured/requested, exec now fails closed instead of silently running on the gateway host.
Enable sandboxing or use `host=gateway` with approvals.
- Important: sandboxing is **off by default**. If sandboxing is off and exec resolves to
`host=sandbox` (including the implicit default), exec fails closed instead of silently
running on the gateway host. Enable sandboxing or use `host=gateway` with approvals.
- Script preflight checks (for common Python/Node shell-syntax mistakes) only inspect files inside the
effective `workdir` boundary. If a script path resolves outside `workdir`, preflight is skipped for
that file.

View File

@@ -156,11 +156,11 @@ OpenClaw scans for plugins in this order (first match wins):
</Step>
<Step title="Workspace extensions">
`\<workspace\>/.openclaw/extensions/*.ts` and `\<workspace\>/.openclaw/extensions/*/index.ts`.
`\<workspace\>/.openclaw/<plugin-root>/*.ts` and `\<workspace\>/.openclaw/<plugin-root>/*/index.ts`.
</Step>
<Step title="Global extensions">
`~/.openclaw/extensions/*.ts` and `~/.openclaw/extensions/*/index.ts`.
`~/.openclaw/<plugin-root>/*.ts` and `~/.openclaw/<plugin-root>/*/index.ts`.
</Step>
<Step title="Bundled plugins">

View File

@@ -1,47 +1,57 @@
---
title: Pi 开发工作流程
read_when:
- 在 OpenClaw 中处理 Pi 集成代码或测试时
- 运行 Pi 专用的代码规范检查、类型检查和实时测试流程时
summary: Pi 集成的开发工作流:构建、测试和实时验证
title: Pi 开发工作流
x-i18n:
generated_at: "2026-02-03T10:07:59Z"
model: claude-opus-4-5
provider: pi
source_hash: 65bd0580dd03df05321ced35a036ce6fb815ce3ddac1d35c9976279adcbf87c0
generated_at: "2026-03-29T04:06:57Z"
model: gpt-5.4
provider: openai
source_hash: 7be1c0f9ecf4315115b2e8188f7472eebba2a8424296661184a02bf5ad6e90c5
source_path: pi-dev.md
workflow: 15
---
# Pi 开发工作流
# Pi 开发工作流
本指南总结了在 OpenClaw 中开发 Pi 集成合理工作流
本指南总结了在 OpenClaw 中处理 Pi 集成时的一套合理工作流。
## 类型检查代码检查
## 类型检查代码规范检查
- 类型检查和构建:`pnpm build`
- 代码检查:`pnpm lint`
- 代码规范检查:`pnpm lint`
- 格式检查:`pnpm format`
- 推送前完整检查:`pnpm lint && pnpm build && pnpm test`
- 推送前完整检查:`pnpm lint && pnpm build && pnpm test`
## 运行 Pi 测试
使用专用脚本运行 Pi 集成测试集:
直接使用 Vitest 运行面向 Pi 测试集:
```bash
scripts/pi/run-tests.sh
pnpm test -- \
"src/agents/pi-*.test.ts" \
"src/agents/pi-embedded-*.test.ts" \
"src/agents/pi-tools*.test.ts" \
"src/agents/pi-settings.test.ts" \
"src/agents/pi-tool-definition-adapter*.test.ts" \
"src/agents/pi-hooks/**/*.test.ts"
```
要包含执行真实提供商行为的实时测试
如果还要包含提供商的实时演练
```bash
scripts/pi/run-tests.sh --live
OPENCLAW_LIVE_TEST=1 pnpm test -- src/agents/pi-embedded-runner-extraparams.live.test.ts
```
该脚本通过以下 glob 模式运行所有 Pi 相关的单元测试:
这涵盖了主要的 Pi 单元测试套件
- `src/agents/pi-*.test.ts`
- `src/agents/pi-embedded-*.test.ts`
- `src/agents/pi-tools*.test.ts`
- `src/agents/pi-settings.test.ts`
- `src/agents/pi-tool-definition-adapter.test.ts`
- `src/agents/pi-extensions/*.test.ts`
- `src/agents/pi-hooks/*.test.ts`
## 手动测试
@@ -51,27 +61,27 @@ scripts/pi/run-tests.sh --live
- `pnpm gateway:dev`
- 直接触发智能体:
- `pnpm openclaw agent --message "Hello" --thinking low`
- 使用 TUI 进行交互式调试:
- 使用终端界面进行交互式调试:
- `pnpm tui`
对于工具调用行为,提示执行 `read``exec` 操作,以便查看工具流式传输和负载处理。
对于工具调用行为,提示执行 `read``exec` 操作,这样你就可以看到工具流式传输和负载处理。
## 完全重置
## 彻底重置
状态存储在 OpenClaw 状态目录下。默认 `~/.openclaw`。如果设置了 `OPENCLAW_STATE_DIR`,则使用该目录。
状态存储在 OpenClaw 状态目录下。默认路径是 `~/.openclaw`。如果设置了 `OPENCLAW_STATE_DIR`,则用该目录。
要重置所有内容:
如需重置全部内容:
- `openclaw.json` 用于配置
- `credentials/` 用于证配置文件和 token
- `credentials/` 用于证配置文件和令牌
- `agents/<agentId>/sessions/` 用于智能体会话历史
- `agents/<agentId>/sessions.json` 用于会话索引
- `sessions/` 如果存在旧版路径
- `workspace/` 如果你想要一个空白工作区
- `sessions/`如果存在旧版路径
- `workspace/`如果你想要一个空白工作区
如果只想重置会话,删除该智能体的 `agents/<agentId>/sessions/``agents/<agentId>/sessions.json`。如果不想重新证,保留 `credentials/`
如果只想重置会话,删除该智能体的 `agents/<agentId>/sessions/``agents/<agentId>/sessions.json`。如果不想重新进行身份验证,保留 `credentials/`
## 参考资料
- https://docs.openclaw.ai/testing
- https://docs.openclaw.ai/start/getting-started
- [测试](/help/testing)
- [入门指南](/start/getting-started)

View File

@@ -1,45 +1,49 @@
---
read_when:
- 理解 OpenClaw 中 Pi SDK 集成设计时
- 修改 Pi 的智能体会话生命周期、工具或提供商接线时
summary: OpenClaw 内嵌 Pi 智能体集成与会话生命周期的架构
title: Pi 集成架构
x-i18n:
generated_at: "2026-02-03T07:53:24Z"
model: claude-opus-4-5
provider: pi
source_hash: 98b12f1211f70b1a25f58e68c7a4d0fe3827412ca53ba0ea2cd41ac9c0448458
generated_at: "2026-03-29T04:10:02Z"
model: gpt-5.4
provider: openai
source_hash: 43a5d646ed66fab1492b6f18fb1623d895922ecf539e52e069d99e7e83c0be11
source_path: pi.md
workflow: 15
---
# Pi 集成架构
本文档描述了 OpenClaw 如何与 [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) 及其相关包(`pi-ai``pi-agent-core``pi-tui`)集成以实现其 AI 智能体能力。
本文档介绍 OpenClaw 如何与 [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) 及其同级软件包(`pi-ai``pi-agent-core``pi-tui`)集成,以提供其 AI 智能体能力。
## 概
## 概
OpenClaw 使用 pi SDK 将 AI 编码智能体嵌入到其消息 Gateway 网关架构中。OpenClaw 不将 pi 作为子进程生成或使用 RPC 模式,而是通过 `createAgentSession()` 直接导入并实例化 pi 的 `AgentSession`。这种嵌入式方法提供了:
OpenClaw 使用 pi SDK 将 AI 编码智能体嵌入到其消息 Gateway 网关架构中。OpenClaw 不将 pi 作为子进程启动,也不会使用 RPC 模式,而是通过 `createAgentSession()` 直接导入并实例化 pi 的 `AgentSession`。这种内嵌方式提供了:
- 对会话生命周期和事件处理的完全控制
- 自定义工具注入(消息、沙箱、渠道特定操作)
- 每个渠道/上下文系统提示自定义
- 支持分支/压缩的会话持久化
- 带故障转移的多账户证配置文件轮换
- 按渠道 / 上下文定制系统提示
- 支持分支 / 压缩的会话持久化
- 带故障转移的多账户证配置轮换
- 与提供商无关的模型切换
## 包依赖
## 软件包依赖
```json
{
"@mariozechner/pi-agent-core": "0.49.3",
"@mariozechner/pi-ai": "0.49.3",
"@mariozechner/pi-coding-agent": "0.49.3",
"@mariozechner/pi-tui": "0.49.3"
"@mariozechner/pi-agent-core": "0.61.1",
"@mariozechner/pi-ai": "0.61.1",
"@mariozechner/pi-coding-agent": "0.61.1",
"@mariozechner/pi-tui": "0.61.1"
}
```
| 包 | 用途 |
| 软件包 | 用途 |
| ----------------- | ------------------------------------------------------------------------------------------ |
| `pi-ai` | 核心 LLM 抽象:`Model``streamSimple`、消息类型、提供商 API |
| `pi-agent-core` | 智能体循环、工具执行、`AgentMessage` 类型 |
| `pi-coding-agent` | 高 SDK`createAgentSession``SessionManager``AuthStorage``ModelRegistry`、内置工具 |
| `pi-coding-agent` | 高 SDK`createAgentSession``SessionManager``AuthStorage``ModelRegistry`、内置工具 |
| `pi-tui` | 终端 UI 组件(用于 OpenClaw 的本地 TUI 模式) |
## 文件结构
@@ -91,7 +95,7 @@ src/agents/
├── pi-tools.types.ts # AnyAgentTool type alias
├── pi-tool-definition-adapter.ts # AgentTool -> ToolDefinition adapter
├── pi-settings.ts # Settings overrides
├── pi-extensions/ # Custom pi extensions
├── pi-hooks/ # Custom pi hooks
│ ├── compaction-safeguard.ts # Safeguard extension
│ ├── compaction-safeguard-runtime.ts
│ ├── context-pruning.ts # Cache-TTL context pruning extension
@@ -122,24 +126,28 @@ src/agents/
│ ├── browser-tool.ts
│ ├── canvas-tool.ts
│ ├── cron-tool.ts
│ ├── discord-actions*.ts
│ ├── gateway-tool.ts
│ ├── image-tool.ts
│ ├── message-tool.ts
│ ├── nodes-tool.ts
│ ├── session*.ts
│ ├── slack-actions.ts
│ ├── telegram-actions.ts
│ ├── web-*.ts
│ └── whatsapp-actions.ts
│ └── ...
└── ...
```
渠道特定的消息操作运行时现在位于插件自有的扩展目录中,而不是放在 `src/agents/tools` 下,例如:
- `extensions/discord/src/actions/runtime*.ts`
- `extensions/slack/src/action-runtime.ts`
- `extensions/telegram/src/action-runtime.ts`
- `extensions/whatsapp/src/action-runtime.ts`
## 核心集成流程
### 1. 运行嵌入式智能体
### 1. 运行嵌智能体
主入口`pi-embedded-runner/run.ts` 中的 `runEmbeddedPiAgent()`
主入口是 `pi-embedded-runner/run.ts` 中的 `runEmbeddedPiAgent()`
```typescript
import { runEmbeddedPiAgent } from "./agents/pi-embedded-runner.js";
@@ -161,9 +169,9 @@ const result = await runEmbeddedPiAgent({
});
```
### 2. 会话创建
### 2. 创建会话
`runEmbeddedAttempt()`(由 `runEmbeddedPiAgent()` 调用)内部,使用 pi SDK
`runEmbeddedAttempt()`(由 `runEmbeddedPiAgent()` 调用)内部,使用 pi SDK
```typescript
import {
@@ -200,7 +208,7 @@ applySystemPromptOverrideToSession(session, systemPromptOverride);
### 3. 事件订阅
`subscribeEmbeddedPiSession()` 订阅 pi 的 `AgentSession` 事件:
`subscribeEmbeddedPiSession()` 订阅 pi 的 `AgentSession` 事件:
```typescript
const subscription = subscribeEmbeddedPiSession({
@@ -219,37 +227,39 @@ const subscription = subscribeEmbeddedPiSession({
处理的事件包括:
- `message_start` / `message_end` / `message_update`(流式文本/思考)
- `message_start` / `message_end` / `message_update`(流式文本 / 思考)
- `tool_execution_start` / `tool_execution_update` / `tool_execution_end`
- `turn_start` / `turn_end`
- `agent_start` / `agent_end`
- `auto_compaction_start` / `auto_compaction_end`
### 4. 提示
### 4. 发送提示
设置完成后,会话被提示:
完成设置后,会向会话发送提示:
```typescript
await session.prompt(effectivePrompt, { images: imageResult.images });
```
SDK 处理完整的智能体循环:发送 LLM、执行工具调用、流式响应。
SDK 处理完整的智能体循环:发送 LLM、执行工具调用、流式返回响应。
图像注入仅限当前提示OpenClaw 会从当前提示中加载图像引用,并仅通过 `images` 将其传入该轮。它不会重新扫描较早的历史轮次来重新注入图像负载。
## 工具架构
### 工具管道
### 工具流水线
1. **基础工具**pi 的 `codingTools`read、bash、edit、write
2. **自定义替换**OpenClaw 将 bash 替换为 `exec`/`process`,为沙箱自定义 read/edit/write
3. **OpenClaw 工具**:消息、浏览器、画布、会话、定时任务、Gateway 网关等
4. **渠道工具**Discord/Telegram/Slack/WhatsApp 特定操作工具
5. **策略过滤**工具按配置文件、提供商、智能体、群组、沙箱策略过滤
6. **Schema 规范化**:为 Gemini/OpenAI 的特殊情况清理 Schema
7. **AbortSignal 包装**工具被包装以尊重中止信号
2. **自定义替换**OpenClaw `exec` / `process` 替换 bash并为沙箱定制 read / edit / write
3. **OpenClaw 工具**:消息、浏览器、画布、会话、cron、Gateway 网关
4. **渠道工具**Discord / Telegram / Slack / WhatsApp 特定操作工具
5. **策略过滤**:按配置、提供商、智能体、群组、沙箱策略过滤工具
6. **模式归一化**:清理模式以适配 Gemini / OpenAI 的特殊行为
7. **AbortSignal 包装**包装工具以遵循中止信号
### 工具定义适配器
pi-agent-core 的 `AgentTool` 与 pi-coding-agent 的 `ToolDefinition` 有不同的 `execute` 签名。`pi-tool-definition-adapter.ts` 中的适配器桥接这一
pi-agent-core 的 `AgentTool` 与 pi-coding-agent 的 `ToolDefinition` `execute` 签名上不同`pi-tool-definition-adapter.ts` 中的适配器用于桥接这一差异
```typescript
export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
@@ -268,7 +278,7 @@ export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
### 工具拆分策略
`splitSdkTools()` 通过 `customTools`所有工具:
`splitSdkTools()` 通过 `customTools`所有工具:
```typescript
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) {
@@ -279,13 +289,13 @@ export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled:
}
```
这确保 OpenClaw 的策略过滤、沙箱集成和扩展工具集在提供商之间保持一致。
样可以确保 OpenClaw 的策略过滤、沙箱集成和扩展工具集在不同提供商之间保持一致。
## 系统提示构建
## 系统提示构建
系统提示在 `buildAgentSystemPrompt()``system-prompt.ts`)中构建。它组装一个完整提示包含工具、工具调用风格、安全护栏、OpenClaw CLI 参考、Skills、文档、工作区、沙箱、消息、回复标签、语音、静默回复、心跳、运行时元数据等部分以及启用时的记忆和反应,还有可选的上下文文件和额外系统提示内容。部分内容在子智能体使用的最小提示模式下会被裁剪。
系统提示`buildAgentSystemPrompt()``system-prompt.ts`)中构建。它组装完整提示包含工具、工具调用风格、安全护栏、OpenClaw CLI 参考、Skills、文档、工作区、沙箱、消息、回复标签、语音、静默回复、心跳、运行时元数据等部分并在启用时包含 Memory 和 Reactions以及可选的上下文文件和额外系统提示内容。子智能体使用的最小提示模式会对各部分进行裁剪。
提示在会话创建后通过 `applySystemPromptOverrideToSession()` 应用:
系统提示词会在会话创建后通过 `applySystemPromptOverrideToSession()` 应用:
```typescript
const systemPromptOverride = createSystemPromptOverride(appendPrompt);
@@ -296,17 +306,17 @@ applySystemPromptOverrideToSession(session, systemPromptOverride);
### 会话文件
会话是具有树结构(id/parentId 链接)的 JSONL 文件。Pi 的 `SessionManager` 处理持久化:
会话是具有树结构(通过 id / parentId 关联)的 JSONL 文件。Pi 的 `SessionManager` 负责持久化:
```typescript
const sessionManager = SessionManager.open(params.sessionFile);
```
OpenClaw `guardSessionManager()` 包装它以确保工具结果安全。
OpenClaw 通过 `guardSessionManager()` 对其进行包装,以保证工具结果安全。
### 会话缓存
`session-manager-cache.ts` 缓存 SessionManager 实例以避免重复的文件解析:
`session-manager-cache.ts` 缓存 `SessionManager` 实例以避免重复解析文件
```typescript
await prewarmSessionFile(params.sessionFile);
@@ -316,11 +326,11 @@ trackSessionManagerAccess(params.sessionFile);
### 历史限制
`limitHistoryTurns()` 根据渠道类型(私信 vs 群组)裁剪对话历史。
`limitHistoryTurns()` 根据渠道类型(私信 群组)裁剪对话历史。
### 压缩
自动压缩在上下文溢出时触发。`compactEmbeddedPiSessionDirect()` 处理手动压缩:
上下文溢出时触发自动压缩`compactEmbeddedPiSessionDirect()` 负责手动压缩:
```typescript
const compactResult = await compactEmbeddedPiSessionDirect({
@@ -328,18 +338,18 @@ const compactResult = await compactEmbeddedPiSessionDirect({
});
```
## 证与模型解析
## 身份验证与模型解析
### 证配置文件
### 证配置
OpenClaw 维护一个证配置文件存储,每个提供商多个 API 密钥:
OpenClaw 维护一个证配置存储,每个提供商保存多个 API 密钥:
```typescript
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });
```
配置文件在失败时轮换,并带有冷却跟踪:
配置在失败时轮换,并跟踪冷却状态
```typescript
await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });
@@ -364,7 +374,7 @@ authStorage.setRuntimeApiKey(model.provider, apiKeyInfo.apiKey);
### 故障转移
`FailoverError` 在配置了回退时触发模型回退
配置了回退时,`FailoverError` 会触发模型故障转移
```typescript
if (fallbackConfigured && isFailoverErrorMessage(errorText)) {
@@ -380,11 +390,11 @@ if (fallbackConfigured && isFailoverErrorMessage(errorText)) {
## Pi 扩展
OpenClaw 加载自定义 pi 扩展以实现特殊行为:
OpenClaw 加载自定义 pi 扩展以实现专门行为:
### 压缩安全护栏
### 压缩保护
`pi-extensions/compaction-safeguard.ts` 为压缩添加护栏,包括自适应令牌预算以及工具失败和文件操作摘要:
`src/agents/pi-hooks/compaction-safeguard.ts` 为压缩添加护栏,包括自适应 token 预算以及工具失败和文件操作摘要:
```typescript
if (resolveCompactionMode(params.cfg) === "safeguard") {
@@ -395,7 +405,7 @@ if (resolveCompactionMode(params.cfg) === "safeguard") {
### 上下文裁剪
`pi-extensions/context-pruning.ts` 实现基于缓存 TTL 的上下文裁剪:
`src/agents/pi-hooks/context-pruning.ts` 实现基于 cache-TTL 的上下文裁剪:
```typescript
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
@@ -409,19 +419,19 @@ if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
}
```
## 流式传输与块回复
## 流式传输与块回复
### 分块
### 分块处理
`EmbeddedBlockChunker` 管理将流式文本分成离散的回复块:
`EmbeddedBlockChunker` 负责将流式文本管理为离散的回复块:
```typescript
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;
```
### 思考/最终标签剥离
### 思考 / 最终标签剥离
流式输出被处理以剥离 `<think>`/`<thinking>` 块并提取 `<final>` 内容:
流式输出会经过处理,以去除 `<think>` / `<thinking>` 块并提取 `<final>` 内容:
```typescript
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => {
@@ -432,7 +442,7 @@ const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean
### 回复指令
回复指令`[[media:url]]``[[voice]]``[[reply:id]]` 被解析和提取
会解析并提取诸`[[media:url]]``[[voice]]``[[reply:id]]` 之类的回复指令
```typescript
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);
@@ -442,7 +452,7 @@ const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDi
### 错误分类
`pi-embedded-helpers.ts` 对错误进行分类进行适当处理:
`pi-embedded-helpers.ts` 对错误进行分类,以便进行适当处理:
```typescript
isContextOverflowError(errorText) // Context too large
@@ -455,7 +465,7 @@ classifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "time
### 思考级别回退
如果思考级别不受支持,它会回退:
如果某个思考级别不受支持,它会回退:
```typescript
const fallbackThinking = pickFallbackThinkingLevel({
@@ -470,7 +480,7 @@ if (fallbackThinking) {
## 沙箱集成
启用沙箱模式时,工具和路径受到约束:
启用沙箱模式时,工具和路径都会受到约束:
```typescript
const sandbox = await resolveSandboxContext({
@@ -490,24 +500,24 @@ if (sandboxRoot) {
### Anthropic
- 拒绝魔字符串清除
- 连续角色的回合验证
- 清理拒绝魔字符串
- 针对连续角色的轮次验证
- Claude Code 参数兼容性
### Google/Gemini
### Google / Gemini
- 回合排序修复(`applyGoogleTurnOrderingFix`
- 工具 schema 清理`sanitizeToolsForGoogle`
- 会话历史清理`sanitizeSessionHistory`
- 轮次顺序修复(`applyGoogleTurnOrderingFix`
- 工具模式净化`sanitizeToolsForGoogle`
- 会话历史净化`sanitizeSessionHistory`
### OpenAI
- Codex 模型的 `apply_patch` 工具
- 面向 Codex 模型的 `apply_patch` 工具
- 思考级别降级处理
## TUI 集成
OpenClaw 还有一个本地 TUI 模式,直接使用 pi-tui 组件:
OpenClaw 还提供本地 TUI 模式,直接使用 pi-tui 组件:
```typescript
// src/tui/tui.ts
@@ -516,104 +526,46 @@ import { ... } from "@mariozechner/pi-tui";
这提供了与 pi 原生模式类似的交互式终端体验。
## 与 Pi CLI 的主要区别
## 与 Pi CLI 的关键差异
| 方面 | Pi CLI | OpenClaw 嵌入式 |
| -------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
| 调用方式 | `pi` 命令 / RPC | 通过 `createAgentSession()` SDK |
| 工具 | 默认编码工具 | 自定义 OpenClaw 工具套件 |
| 系统提示 | AGENTS.md + prompts | 按渠道/上下文动态生成 |
| 会话存储 | `~/.pi/agent/sessions/` | `~/.openclaw/agents/<agentId>/sessions/`(或 `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` |
| 认证 | 单一凭证 | 轮换的多配置文件 |
| 扩展 | 从磁盘加载 | 编程方式 + 磁盘路径 |
| 事件处理 | TUI 渲染 | 基于回调onBlockReply 等) |
| 方面 | Pi CLI | OpenClaw 内嵌版 |
| ---------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
| 调用方式 | `pi` 命令 / RPC | 通过 `createAgentSession()` 使用 SDK |
| 工具 | 默认编码工具 | 自定义 OpenClaw 工具套件 |
| 系统提示 | AGENTS.md + prompts | 按渠道 / 上下文动态生成 |
| 会话存储 | `~/.pi/agent/sessions/` | `~/.openclaw/agents/<agentId>/sessions/`(或 `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` |
| 身份验证 | 单一凭证 | 支持轮换的多配置 |
| 扩展 | 从磁盘加载 | 通过编程方式 + 磁盘路径 |
| 事件处理 | TUI 渲染 | 基于回调(`onBlockReply` 等) |
## 未来考虑
可能需要重构的领域
潜在的重构方向包括
1. **工具签名对齐**目前在 pi-agent-core pi-coding-agent 签名之间适配
2. **会话管理器包装**`guardSessionManager` 增加了安全性但增加了复杂
1. **工具签名对齐**当前需要在 pi-agent-core pi-coding-agent 签名之间进行适配
2. **会话管理器包装**`guardSessionManager` 增加了安全性,但也提高了复杂
3. **扩展加载**:可以更直接地使用 pi 的 `ResourceLoader`
4. **流式处理器复杂**`subscribeEmbeddedPiSession` 已经变得
5. **提供商特殊情况**:许多提供商特定代码路径,pi 可能可以处理
4. **流式处理器复杂**`subscribeEmbeddedPiSession` 已经变得
5. **提供商特殊行为**存在许多提供商特定代码路径,未来 pi 或许可以统一处理
## 测试
所有涵盖 pi 集成及其扩展的现有测试
Pi 集成的覆盖范围包括以下测试套件
- `src/agents/pi-embedded-block-chunker.test.ts`
- `src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts`
- `src/agents/pi-embedded-helpers.classifyfailoverreason.test.ts`
- `src/agents/pi-embedded-helpers.downgradeopenai-reasoning.test.ts`
- `src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts`
- `src/agents/pi-embedded-helpers.formatrawassistanterrorforui.test.ts`
- `src/agents/pi-embedded-helpers.image-dimension-error.test.ts`
- `src/agents/pi-embedded-helpers.image-size-error.test.ts`
- `src/agents/pi-embedded-helpers.isautherrormessage.test.ts`
- `src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts`
- `src/agents/pi-embedded-helpers.iscloudcodeassistformaterror.test.ts`
- `src/agents/pi-embedded-helpers.iscompactionfailureerror.test.ts`
- `src/agents/pi-embedded-helpers.iscontextoverflowerror.test.ts`
- `src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts`
- `src/agents/pi-embedded-helpers.islikelycontextoverflowerror.test.ts`
- `src/agents/pi-embedded-helpers.ismessagingtoolduplicate.test.ts`
- `src/agents/pi-embedded-helpers.messaging-duplicate.test.ts`
- `src/agents/pi-embedded-helpers.normalizetextforcomparison.test.ts`
- `src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.test.ts`
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.keeps-tool-call-tool-result-ids-unchanged.test.ts`
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.test.ts`
- `src/agents/pi-embedded-helpers.sanitizegoogleturnordering.test.ts`
- `src/agents/pi-embedded-helpers.sanitizesessionmessagesimages-thought-signature-stripping.test.ts`
- `src/agents/pi-embedded-helpers.sanitizetoolcallid.test.ts`
- `src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts`
- `src/agents/pi-embedded-helpers.stripthoughtsignatures.test.ts`
- `src/agents/pi-embedded-helpers.validate-turns.test.ts`
- `src/agents/pi-embedded-runner-extraparams.live.test.ts`(实时)
- `src/agents/pi-embedded-runner-extraparams.test.ts`
- `src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts`
- `src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts`
- `src/agents/pi-embedded-runner.createsystempromptoverride.test.ts`
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts`
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts`
- `src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts`
- `src/agents/pi-embedded-runner.guard.test.ts`
- `src/agents/pi-embedded-runner.limithistoryturns.test.ts`
- `src/agents/pi-embedded-runner.resolvesessionagentids.test.ts`
- `src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts`
- `src/agents/pi-embedded-runner.sanitize-session-history.test.ts`
- `src/agents/pi-embedded-runner.splitsdktools.test.ts`
- `src/agents/pi-embedded-runner.test.ts`
- `src/agents/pi-embedded-subscribe.code-span-awareness.test.ts`
- `src/agents/pi-embedded-subscribe.reply-tags.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.calls-onblockreplyflush-before-tool-execution-start-preserve.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-append-text-end-content-is.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-call-onblockreplyflush-callback-is-not.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-duplicate-text-end-repeats-full.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.includes-canvas-action-metadata-tool-summaries.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-indented-fenced-blocks-intact.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.reopens-fenced-blocks-splitting-inside-them.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.streams-soft-chunks-paragraph-preference.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.test.ts`
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.waits-multiple-compaction-retries-before-resolving.test.ts`
- `src/agents/pi-embedded-subscribe.tools.test.ts`
- `src/agents/pi-embedded-utils.test.ts`
- `src/agents/pi-extensions/compaction-safeguard.test.ts`
- `src/agents/pi-extensions/context-pruning.test.ts`
- `src/agents/pi-*.test.ts`
- `src/agents/pi-auth-json.test.ts`
- `src/agents/pi-embedded-*.test.ts`
- `src/agents/pi-embedded-helpers*.test.ts`
- `src/agents/pi-embedded-runner*.test.ts`
- `src/agents/pi-embedded-runner/**/*.test.ts`
- `src/agents/pi-embedded-subscribe*.test.ts`
- `src/agents/pi-tools*.test.ts`
- `src/agents/pi-tool-definition-adapter*.test.ts`
- `src/agents/pi-settings.test.ts`
- `src/agents/pi-tool-definition-adapter.test.ts`
- `src/agents/pi-tools-agent-config.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-b.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-d.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-f.test.ts`
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.test.ts`
- `src/agents/pi-tools.policy.test.ts`
- `src/agents/pi-tools.safe-bins.test.ts`
- `src/agents/pi-tools.workspace-paths.test.ts`
- `src/agents/pi-hooks/**/*.test.ts`
实时 / 按需启用:
- `src/agents/pi-embedded-runner-extraparams.live.test.ts`(启用 `OPENCLAW_LIVE_TEST=1`
有关当前运行命令,请参见 [Pi 开发工作流](/pi-dev)。

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/acpx",
"version": "2026.3.28",
"version": "2026.3.29",
"description": "OpenClaw ACP runtime backend via acpx",
"type": "module",
"dependencies": {

View File

@@ -102,7 +102,7 @@ Required behavior when ACP backend is unavailable:
1. Do not immediately ask the user to pick an alternate path.
2. First attempt automatic local repair:
- ensure plugin-local pinned acpx is installed in `extensions/acpx`
- ensure plugin-local pinned acpx is installed in the bundled ACPX plugin package
- verify `${ACPX_CMD} --version`
3. After reinstall/repair, restart the gateway and explicitly offer to run that restart for the user.
4. Retry ACP thread spawn once after repair.
@@ -120,20 +120,21 @@ Do not default to subagent runtime for these requests.
For this repo, direct `acpx` calls must follow the same pinned policy as the `@openclaw/acpx` extension package.
1. Prefer plugin-local binary, not global PATH:
- `./extensions/acpx/node_modules/.bin/acpx`
- `${ACPX_PLUGIN_ROOT}/node_modules/.bin/acpx`
2. Resolve pinned version from extension dependency:
- `node -e "console.log(require('./extensions/acpx/package.json').dependencies.acpx)"`
- `node -e "console.log(require(process.env.ACPX_PLUGIN_ROOT + '/package.json').dependencies.acpx)"`
3. If binary is missing or version mismatched, install plugin-local pinned version:
- `cd extensions/acpx && npm install --omit=dev --no-save acpx@<pinnedVersion>`
- `cd "$ACPX_PLUGIN_ROOT" && npm install --omit=dev --no-save acpx@<pinnedVersion>`
4. Verify before use:
- `./extensions/acpx/node_modules/.bin/acpx --version`
- `${ACPX_PLUGIN_ROOT}/node_modules/.bin/acpx --version`
5. If install/repair changed ACPX artifacts, restart the gateway and offer to run the restart.
6. Do not run `npm install -g acpx` unless the user explicitly asks for global install.
Set and reuse:
```bash
ACPX_CMD="./extensions/acpx/node_modules/.bin/acpx"
ACPX_PLUGIN_ROOT="<bundled-acpx-plugin-root>"
ACPX_CMD="$ACPX_PLUGIN_ROOT/node_modules/.bin/acpx"
```
## Direct acpx path ("telephone game")
@@ -227,7 +228,7 @@ If your local Cursor install still exposes ACP as `agent acp`, set that as the `
### Failure handling
- `acpx: command not found`:
- for thread-spawn ACP requests, install plugin-local pinned acpx in `extensions/acpx` immediately
- for thread-spawn ACP requests, install plugin-local pinned acpx in the bundled ACPX plugin package immediately
- restart gateway after install and offer to run the restart automatically
- then retry once
- do not ask for install permission first unless policy explicitly requires it

View File

@@ -3,6 +3,10 @@ import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { describe, expect, it } from "vitest";
import {
bundledDistPluginRootAt,
bundledPluginRootAt,
} from "../../../test/helpers/bundled-plugin-paths.js";
import {
ACPX_BUNDLED_BIN,
ACPX_PINNED_VERSION,
@@ -39,10 +43,10 @@ describe("acpx plugin config parsing", () => {
}
});
it("prefers the workspace plugin root for dist/extensions/acpx bundles", () => {
it("prefers the workspace plugin root for dist plugin bundles", () => {
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "acpx-root-workspace-"));
const workspacePluginRoot = path.join(repoRoot, "extensions", "acpx");
const bundledPluginRoot = path.join(repoRoot, "dist", "extensions", "acpx");
const workspacePluginRoot = bundledPluginRootAt(repoRoot, "acpx");
const bundledPluginRoot = bundledDistPluginRootAt(repoRoot, "acpx");
try {
fs.mkdirSync(workspacePluginRoot, { recursive: true });
fs.mkdirSync(bundledPluginRoot, { recursive: true });

View File

@@ -11,7 +11,6 @@ export type AcpxPermissionMode = (typeof ACPX_PERMISSION_MODES)[number];
export const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"] as const;
export type AcpxNonInteractivePermissionPolicy = (typeof ACPX_NON_INTERACTIVE_POLICIES)[number];
export const ACPX_PINNED_VERSION = "0.3.1";
export const ACPX_VERSION_ANY = "any";
const ACPX_BIN_NAME = process.platform === "win32" ? "acpx.cmd" : "acpx";
@@ -58,6 +57,14 @@ export function resolveAcpxPluginRoot(moduleUrl: string = import.meta.url): stri
}
export const ACPX_PLUGIN_ROOT = resolveAcpxPluginRoot();
const pluginPkg = JSON.parse(fs.readFileSync(path.join(ACPX_PLUGIN_ROOT, "package.json"), "utf8"));
const acpxVersion: unknown = pluginPkg?.dependencies?.acpx;
if (typeof acpxVersion !== "string" || acpxVersion.trim() === "") {
throw new Error(
`Could not read acpx version from ${path.join(ACPX_PLUGIN_ROOT, "package.json")} — expected a non-empty string at dependencies.acpx`
);
}
export const ACPX_PINNED_VERSION: string = acpxVersion.replace(/^[^0-9]*/, "");
export const ACPX_BUNDLED_BIN = path.join(ACPX_PLUGIN_ROOT, "node_modules", ".bin", ACPX_BIN_NAME);
export function buildAcpxLocalInstallCommand(version: string = ACPX_PINNED_VERSION): string {
return `npm install --omit=dev --no-save --package-lock=false acpx@${version}`;

View File

@@ -3,9 +3,10 @@ import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { bundledPluginFile } from "../../../../test/helpers/bundled-plugin-paths.js";
const tempDirs: string[] = [];
const proxyPath = path.resolve("extensions/acpx/src/runtime-internals/mcp-proxy.mjs");
const proxyPath = path.resolve(bundledPluginFile("acpx", "src/runtime-internals/mcp-proxy.mjs"));
async function makeTempScript(name: string, content: string): Promise<string> {
const dir = await mkdtemp(path.join(os.tmpdir(), "openclaw-acpx-mcp-proxy-"));

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { registerSingleProviderPlugin } from "../../test/helpers/extensions/plugin-registration.js";
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
import amazonBedrockPlugin from "./index.js";
describe("amazon-bedrock provider plugin", () => {

View File

@@ -41,6 +41,10 @@ export default definePluginEntry({
},
},
resolveConfigApiKey: ({ env }) => resolveBedrockConfigApiKey(env),
capabilities: {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
},
wrapStreamFn: ({ modelId, streamFn }) =>
isAnthropicBedrockModel(modelId) ? streamFn : createBedrockNoCacheWrapper(streamFn),
resolveDefaultThinkingLevel: ({ modelId }) =>

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/amazon-bedrock-provider",
"version": "2026.3.28",
"version": "2026.3.29",
"private": true,
"description": "OpenClaw Amazon Bedrock provider plugin",
"type": "module",

View File

@@ -1,3 +1,3 @@
import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js";
import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js";
describeProviderContracts("amazon-bedrock");

View File

@@ -0,0 +1,59 @@
import { describe, expect, it } from "vitest";
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
import anthropicVertexPlugin from "./index.js";
describe("anthropic-vertex provider plugin", () => {
it("resolves the ADC marker through the provider hook", () => {
const provider = registerSingleProviderPlugin(anthropicVertexPlugin);
expect(
provider.resolveConfigApiKey?.({
provider: "anthropic-vertex",
env: {
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
} as NodeJS.ProcessEnv,
} as never),
).toBe("gcp-vertex-credentials");
});
it("merges the implicit Vertex catalog into explicit provider overrides", async () => {
const provider = registerSingleProviderPlugin(anthropicVertexPlugin);
const result = await provider.catalog?.run({
config: {
models: {
providers: {
"anthropic-vertex": {
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
headers: { "x-test-header": "1" },
},
},
},
},
env: {
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
GOOGLE_CLOUD_LOCATION: "us-east5",
} as NodeJS.ProcessEnv,
resolveProviderApiKey: () => ({ apiKey: undefined }),
resolveProviderAuth: () => ({
apiKey: undefined,
discoveryApiKey: undefined,
mode: "none",
source: "none",
}),
} as never);
expect(result).toEqual({
provider: {
api: "anthropic-messages",
apiKey: "gcp-vertex-credentials",
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
headers: { "x-test-header": "1" },
models: [
expect.objectContaining({ id: "claude-opus-4-6" }),
expect.objectContaining({ id: "claude-sonnet-4-6" }),
],
},
});
});
});

View File

@@ -0,0 +1,44 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import {
mergeImplicitAnthropicVertexProvider,
resolveAnthropicVertexConfigApiKey,
resolveImplicitAnthropicVertexProvider,
} from "./api.js";
const PROVIDER_ID = "anthropic-vertex";
export default definePluginEntry({
id: PROVIDER_ID,
name: "Anthropic Vertex Provider",
description: "Bundled Anthropic Vertex provider plugin",
register(api) {
api.registerProvider({
id: PROVIDER_ID,
label: "Anthropic Vertex",
docsPath: "/providers/models",
auth: [],
catalog: {
order: "simple",
run: async (ctx) => {
const implicit = await resolveImplicitAnthropicVertexProvider({
env: ctx.env,
});
if (!implicit) {
return null;
}
return {
provider: mergeImplicitAnthropicVertexProvider({
existing: ctx.config.models?.providers?.[PROVIDER_ID],
implicit,
}),
};
},
},
resolveConfigApiKey: ({ env }) => resolveAnthropicVertexConfigApiKey(env),
capabilities: {
providerFamily: "anthropic",
dropThinkingBlockModelHints: ["claude"],
},
});
},
});

View File

@@ -0,0 +1,10 @@
{
"id": "anthropic-vertex",
"enabledByDefault": true,
"providers": ["anthropic-vertex"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}

View File

@@ -0,0 +1,12 @@
{
"name": "@openclaw/anthropic-vertex-provider",
"version": "2026.3.29",
"private": true,
"description": "OpenClaw Anthropic Vertex provider plugin",
"type": "module",
"openclaw": {
"extensions": [
"./index.ts"
]
}
}

View File

@@ -0,0 +1,3 @@
import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js";
describeProviderContracts("anthropic-vertex");

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/anthropic-provider",
"version": "2026.3.28",
"version": "2026.3.29",
"private": true,
"description": "OpenClaw Anthropic provider plugin",
"type": "module",

View File

@@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
describePluginRegistrationContract({
pluginId: "anthropic",

View File

@@ -1,3 +1,3 @@
import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js";
import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
describeAnthropicProviderRuntimeContract();

View File

@@ -1,3 +1,3 @@
import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js";
import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js";
describeProviderContracts("anthropic");

View File

@@ -1,6 +1,6 @@
# BlueBubbles extension (developer reference)
This directory contains the **BlueBubbles external channel plugin** for OpenClaw.
This package contains the **BlueBubbles external channel plugin** for OpenClaw.
If youre looking for **how to use BlueBubbles as an agent/tool user**, see:
@@ -8,22 +8,22 @@ If youre looking for **how to use BlueBubbles as an agent/tool user**, see:
## Layout
- Extension package: `extensions/bluebubbles/` (entry: `index.ts`).
- Channel implementation: `extensions/bluebubbles/src/channel.ts`.
- Webhook handling: `extensions/bluebubbles/src/monitor.ts` (register per-account route via `registerPluginHttpRoute`).
- REST helpers: `extensions/bluebubbles/src/send.ts` + `extensions/bluebubbles/src/probe.ts`.
- Runtime bridge: `extensions/bluebubbles/src/runtime.ts` (set via `api.runtime`).
- Package entry: `index.ts`.
- Channel implementation: `src/channel.ts`.
- Webhook handling: `src/monitor.ts` (register per-account route via `registerPluginHttpRoute`).
- REST helpers: `src/send.ts` + `src/probe.ts`.
- Runtime bridge: `src/runtime.ts` (set via `api.runtime`).
- Catalog entry for setup selection: `src/channels/plugins/catalog.ts`.
## Internal helpers (use these, not raw API calls)
- `probeBlueBubbles` in `extensions/bluebubbles/src/probe.ts` for health checks.
- `sendMessageBlueBubbles` in `extensions/bluebubbles/src/send.ts` for text delivery.
- `resolveChatGuidForTarget` in `extensions/bluebubbles/src/send.ts` for chat lookup.
- `sendBlueBubblesReaction` in `extensions/bluebubbles/src/reactions.ts` for tapbacks.
- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `extensions/bluebubbles/src/chat.ts`.
- `downloadBlueBubblesAttachment` in `extensions/bluebubbles/src/attachments.ts` for inbound media.
- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `extensions/bluebubbles/src/types.ts` for shared REST plumbing.
- `probeBlueBubbles` in `src/probe.ts` for health checks.
- `sendMessageBlueBubbles` in `src/send.ts` for text delivery.
- `resolveChatGuidForTarget` in `src/send.ts` for chat lookup.
- `sendBlueBubblesReaction` in `src/reactions.ts` for tapbacks.
- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `src/chat.ts`.
- `downloadBlueBubblesAttachment` in `src/attachments.ts` for inbound media.
- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `src/types.ts` for shared REST plumbing.
## Webhooks

View File

@@ -1,4 +1,4 @@
import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js";
import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js";
describePackageManifestContract({
pluginId: "bluebubbles",

View File

@@ -1,13 +1,13 @@
{
"name": "@openclaw/bluebubbles",
"version": "2026.3.28",
"version": "2026.3.29",
"description": "OpenClaw BlueBubbles channel plugin",
"type": "module",
"devDependencies": {
"openclaw": "workspace:*"
},
"peerDependencies": {
"openclaw": ">=2026.3.28"
"openclaw": ">=2026.3.29"
},
"peerDependenciesMeta": {
"openclaw": {
@@ -38,9 +38,8 @@
},
"install": {
"npmSpec": "@openclaw/bluebubbles",
"localPath": "extensions/bluebubbles",
"defaultChoice": "npm",
"minHostVersion": ">=2026.3.28"
"minHostVersion": ">=2026.3.29"
},
"release": {
"publishToNpm": true

View File

@@ -5,9 +5,11 @@ import {
EMPTY_DISPATCH_RESULT,
resetBlueBubblesMonitorTestState,
type DispatchReplyParams,
} from "../../../test/helpers/extensions/bluebubbles-monitor.js";
} from "../../../test/helpers/plugins/bluebubbles-monitor.js";
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
import { fetchBlueBubblesHistory } from "./history.js";
import { createBlueBubblesDebounceRegistry } from "./monitor-debounce.js";
import type { NormalizedWebhookMessage } from "./monitor-normalize.js";
import { resetBlueBubblesSelfChatCache } from "./monitor-self-chat-cache.js";
import { handleBlueBubblesWebhookRequest, resolveBlueBubblesMessageId } from "./monitor.js";
import {
@@ -27,8 +29,6 @@ import {
resetBlueBubblesParticipantContactNameCacheForTest,
setBlueBubblesParticipantContactDepsForTest,
} from "./participant-contact-names.js";
import { createBlueBubblesDebounceRegistry } from "./monitor-debounce.js";
import type { NormalizedWebhookMessage } from "./monitor-normalize.js";
import type { OpenClawConfig, PluginRuntime } from "./runtime-api.js";
// Mock dependencies
@@ -155,7 +155,10 @@ function installTimingAwareInboundDebouncer(core: PluginRuntime) {
core.channel.debounce.createInboundDebouncer = vi.fn((params: any) => {
// oxlint-disable-next-line typescript/no-explicit-any
type Item = any;
const buckets = new Map<string, { items: Item[]; timer: ReturnType<typeof setTimeout> | null }>();
const buckets = new Map<
string,
{ items: Item[]; timer: ReturnType<typeof setTimeout> | null }
>();
const flush = async (key: string) => {
const bucket = buckets.get(key);

View File

@@ -4,7 +4,7 @@ import {
EMPTY_DISPATCH_RESULT,
resetBlueBubblesMonitorTestState,
type DispatchReplyParams,
} from "../../../test/helpers/extensions/bluebubbles-monitor.js";
} from "../../../test/helpers/plugins/bluebubbles-monitor.js";
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
import { fetchBlueBubblesHistory } from "./history.js";
import { handleBlueBubblesWebhookRequest, resolveBlueBubblesMessageId } from "./monitor.js";

View File

@@ -6,7 +6,7 @@ import {
createTestWizardPrompter,
runSetupWizardConfigure,
type WizardPrompter,
} from "../../../test/helpers/extensions/setup-wizard.js";
} from "../../../test/helpers/plugins/setup-wizard.js";
import { resolveBlueBubblesAccount } from "./accounts.js";
import { BlueBubblesConfigSchema } from "./config-schema.js";
import {

View File

@@ -1,3 +1,3 @@
import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js";
import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js";
describeBundledWebSearchFastPathContract("brave");

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/brave-plugin",
"version": "2026.3.28",
"version": "2026.3.29",
"private": true,
"description": "OpenClaw Brave plugin",
"type": "module",

View File

@@ -1,4 +1,4 @@
import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js";
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
describePluginRegistrationContract({
pluginId: "brave",

View File

@@ -1,3 +1,3 @@
import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js";
import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js";
describeWebSearchProviderContracts("brave");

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.js";
import type { OpenClawPluginApi } from "./runtime-api.js";
const runtimeApiMocks = vi.hoisted(() => ({

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/browser-plugin",
"version": "2026.3.28",
"version": "2026.3.29",
"private": true,
"description": "OpenClaw browser tool plugin",
"type": "module",

View File

@@ -5,7 +5,7 @@ export {
type CliRuntimeCapture,
} from "../../src/cli/test-runtime-capture.js";
export type { OpenClawConfig } from "openclaw/plugin-sdk/browser-support";
export { expectGeneratedTokenPersistedToGatewayAuth } from "../../test/helpers/extensions/auth-token-assertions.ts";
export { withEnv, withEnvAsync } from "../../test/helpers/extensions/env.ts";
export { withFetchPreconnect, type FetchMock } from "../../test/helpers/extensions/fetch-mock.ts";
export { createTempHomeEnv, type TempHomeEnv } from "../../test/helpers/extensions/temp-home.ts";
export { expectGeneratedTokenPersistedToGatewayAuth } from "../../test/helpers/plugins/auth-token-assertions.ts";
export { withEnv, withEnvAsync } from "../../test/helpers/plugins/env.ts";
export { withFetchPreconnect, type FetchMock } from "../../test/helpers/plugins/fetch-mock.ts";
export { createTempHomeEnv, type TempHomeEnv } from "../../test/helpers/plugins/temp-home.ts";

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/byteplus-provider",
"version": "2026.3.28",
"version": "2026.3.29",
"private": true,
"description": "OpenClaw BytePlus provider plugin",
"type": "module",

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