Compare commits

...

718 Commits

Author SHA1 Message Date
Peter Steinberger
be8c24633a docs: consolidate 2026.4.26 changelog 2026-04-28 02:12:58 +01:00
Peter Steinberger
414b68df6f chore(release): prepare 2026.4.26 stable 2026-04-28 02:10:33 +01:00
Peter Steinberger
2c7f65e025 docs: record npm provenance in release evidence 2026-04-28 01:57:17 +01:00
Peter Steinberger
7c44230fda ci: support ref-backed telegram package e2e 2026-04-28 01:45:13 +01:00
Peter Steinberger
09d044e0f4 test: tolerate old Windows updater restart handoff 2026-04-28 00:59:29 +01:00
Peter Steinberger
a230f0f558 test: tolerate transient release validation output 2026-04-27 23:56:18 +01:00
Peter Steinberger
aed6ccf090 test: stabilize live release checks 2026-04-27 23:34:20 +01:00
Peter Steinberger
17596fc484 fix(update): restart Windows startup gateway after update 2026-04-27 23:14:17 +01:00
Peter Steinberger
42220c929a ci: record package proof in release evidence 2026-04-27 21:58:17 +01:00
Peter Steinberger
0518ff6f35 chore(release): refresh config baseline after coven removal 2026-04-27 19:54:51 +01:00
Peter Steinberger
6b97dee4c1 chore(release): refresh plugin sdk api baseline 2026-04-27 19:50:53 +01:00
Peter Steinberger
4623bba650 chore(release): refresh config docs baseline 2026-04-27 19:50:53 +01:00
Peter Steinberger
f02ced0fab chore(release): refresh bundled channel metadata 2026-04-27 19:50:53 +01:00
Peter Steinberger
49aa1d07b0 chore(release): prepare 2026.4.26 beta 1 2026-04-27 19:50:53 +01:00
Val Alexander
f7797ca62b chore: remove coven extension 2026-04-27 13:22:32 -05:00
NVIDIAN
dc96886378 fix: clean up bundled LSP process trees on shutdown
Fixes #72357
2026-04-27 11:10:56 -07:00
Vincent Koc
d9bef3fe7c fix(ui): discard stale config state on explicit reload (#72624)
* fix(ui): discard stale config state on explicit reload

* fix(clownfish): address review for ghcrawl-156594-autonomous-smoke (1)

* fix(clownfish): address review for ghcrawl-156594-autonomous-smoke (1)

* test(ui): align channel config host state
2026-04-27 11:10:38 -07:00
Vincent Koc
be6263da4f fix(gateway): preserve runtime-backed health state (#72417)
* fix(gateway): preserve runtime-backed health state

* fix(clownfish): address review for ghcrawl-207035-agentic-merge (1)

* fix(gateway): harden health snapshot exposure
2026-04-27 11:04:59 -07:00
Vincent Koc
2161b46032 fix(feishu): support native interactive card payload sends (#72667)
* fix(feishu): support native interactive card payload sends

* fix(clownfish): address review for ghcrawl-156608-autonomous-smoke (1)

* fix(feishu): harden native card payload rendering
2026-04-27 11:02:15 -07:00
Gustavo Madeira Santana
c5678194d4 docs(qa): document Telegram and Discord QA lanes against code
Both lanes had only one paragraph each in qa-e2e-automation.md. Adds a
"Telegram and Discord QA reference" section verified against
extensions/qa-lab/src/live-transports/{telegram,discord}/* with:

- shared CLI flags table (--scenario, --output-dir, --repo-root, --sut-account,
  --provider-mode, --model, --alt-model, --fast, --credential-source,
  --credential-role) — none of these were enumerated for either lane.
- Telegram QA: 8 scenario ids
  (telegram-canary/-mention-gating/-mentioned-message-reply/-help-command/
  -commands-command/-tools-compact-command/-whoami-command/-context-command),
  output artifact paths (telegram-qa-report.md, -summary.json,
  -observed-messages.json), and the redaction toggle.
- Discord QA: 3 scenario ids
  (discord-canary/-mention-gating/-native-help-command-registration), output
  artifact paths, and the SUT-application-id-must-match-bot-user-id check.
- Convex credential pool: documents Discord support (only Telegram was
  mentioned before) and the per-kind payload shapes for the
  admin/add validator. Cross-links to testing.md for the broker endpoint
  contract.

Slims the duplicate Operator-flow paragraphs for Telegram and Discord into a
single one-block pointer that links to the new reference section.
2026-04-27 13:48:03 -04:00
Peter Steinberger
b39d80835f test: retry transient openai websocket live stream 2026-04-27 18:43:45 +01:00
openclaw-test-performance-agent[bot]
2f909b0b21 test: optimize slow tests 2026-04-27 17:42:22 +00:00
Gustavo Madeira Santana
dd1a94f089 docs(qa): reorg, audit against code, and refresh stale content
Reorg
- Rename the architecture page title to "QA overview" (slug stays
  /concepts/qa-e2e-automation so inbound links keep working).
- Move "Adding a channel to QA" + scenario-helper-name reference from
  testing.md into qa-e2e-automation.md under "Transport adapters". Architecture
  belongs with the architecture page.
- Drop the duplicate live-transport coverage table from testing.md; canonical
  copy stays in qa-e2e-automation.md under a new "Live transport coverage"
  heading so qa-matrix.md can deep-link to it.
- Slim testing.md QA-specific runners section to ops only, with cross-links.

Audit (against extensions/qa-lab/src/cli.ts, qa-channel/src/config-schema.ts,
and live-transport runtimes)
- qa-e2e-automation.md gains a "Command surface" table covering all 14
  openclaw qa <subcommand> forms; previously only ~7 of 14 were named.
- Document missing OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT and
  OPENCLAW_QA_DISCORD_CAPTURE_CONTENT env vars (Matrix already had it).
- Cross-link qa coverage from the Reporting section.
- qa-channel.md completes the config-key list (enabled, name, accounts,
  defaultAccount were missing from the schema doc) and pollTimeoutMs range.
- Drop stale "Follow-up work" framing in qa-channel.md (provider/model matrix,
  scenario discovery, orchestration) — all three already shipped.
- Replace "vertical slice" language with current behavior; fix misplaced
  debugger-UI paragraph.

Discoverability
- Add a Note callout to testing.md pointing at the three QA pages
  (QA overview, Matrix QA, QA channel) so maintainers landing on testing.md
  see the QA stack in the prologue.

Glossary entries for the renamed/new doc titles.
2026-04-27 13:40:11 -04:00
Gustavo Madeira Santana
abca187df5 docs(qa): add dedicated Matrix QA reference page
Adds a focused reference for the Docker-backed Matrix QA lane (CLI flags,
seven scenario profiles, eight env vars including the redaction toggle and
Tuwunel image override, scenario taxonomy, output artifact layout, and triage
tips). Source-of-truth checked against extensions/qa-matrix/src/cli.ts,
shared/live-transport-cli.ts, runners/contract/{runtime,scenario-catalog}.ts,
and substrate/harness.runtime.ts.

Registered in docs/docs.json alongside QA E2E automation.
2026-04-27 13:40:11 -04:00
Val Alexander
ea92dc9202 chore(coven): enable ClawHub publishing
Mark the Coven ACP runtime bridge as a ClawHub-publishable code plugin and align its workspace lockfile metadata.
2026-04-27 12:35:34 -05:00
Peter Steinberger
8f8ba8af40 test: harden live release validation flakes 2026-04-27 18:32:31 +01:00
Vincent Koc
a2ec5a7d72 fix(plugins): break metadata snapshot cycle 2026-04-27 10:29:27 -07:00
Peter Steinberger
147752ecc3 refactor: split plugin metadata normalizer 2026-04-27 18:27:29 +01:00
Val Alexander
e3ad82d86d fix(control-ui): polish tweakcn theme imports
Summary:
- Improve Control UI tweakcn theme import parsing and labeling.
- Apply imported theme names consistently across appearance controls.
- Document tweakcn share link and slug import flows.

Verification:
- pnpm test ui/src/ui/custom-theme.test.ts ui/src/ui/views/config.browser.test.ts ui/src/ui/views/config-quick.test.ts ui/src/ui/app-settings.test.ts ui/src/ui/storage.node.test.ts
- pnpm check:changed
- pnpm --dir ui build
2026-04-27 12:24:14 -05:00
Val Alexander
fc8ccde542 feat(acp): add opt-in Coven runtime bridge
Add the opt-in Coven ACP runtime bridge as a bundled extension while keeping ACPX as the default path.

Security hardening included before merge:
- fail closed by default instead of silently falling back;
- bounded health/socket requests and daemon response sizes;
- fixed Coven socket trust anchor and symlink/path validation;
- reject untrusted harness/session/event ids before exposing them;
- sanitize daemon-controlled terminal/status/error strings;
- use incremental event polling with bounded dedupe state;
- clean up launched Coven sessions before fallback when daemon ids are invalid.

Validation:
- pnpm test extensions/coven/src/config.test.ts extensions/coven/src/client.test.ts extensions/coven/src/runtime.test.ts
- pnpm check:changed
- GitHub CI green on a64eac20b9
- Greptile Review green
2026-04-27 12:22:29 -05:00
Peter Steinberger
fd6e1c089b test: accept current codex status wording 2026-04-27 18:11:19 +01:00
Shakker
5531502cb0 fix: normalize provider metadata live filters 2026-04-27 18:04:54 +01:00
Shakker
4cd2cabe7f fix: include cli backend owners in provider metadata filters 2026-04-27 18:04:54 +01:00
Shakker
498af508d0 fix: avoid default workspace metadata for agent models 2026-04-27 18:04:54 +01:00
Shakker
51c7f544f3 fix: reject unscoped workspace plugin metadata 2026-04-27 18:04:54 +01:00
Shakker
4ceae8262f fix: scope model provider discovery metadata to workspace 2026-04-27 18:04:54 +01:00
Shakker
4e7de4b5c9 feat: reuse current plugin metadata for provider discovery 2026-04-27 18:04:54 +01:00
Shakker
a478ab3dfa refactor: let provider discovery reuse plugin metadata 2026-04-27 18:04:54 +01:00
Peter Steinberger
f20a295782 test: align release validation expectations 2026-04-27 17:46:31 +01:00
Vincent Koc
efc3a52947 fix(sessions_spawn): tolerate ACP-only fields for subagent runtime
Preserve contributor credit and land the narrowed sessions_spawn ACP-field handling with follow-up transcript redaction and ACP resume ownership hardening. Targeted Blacksmith validation passed for the touched sessions/ACP tests.
2026-04-27 09:42:24 -07:00
Peter Steinberger
aeba1d6b47 test: keep stateful tests out of unit-fast 2026-04-27 17:34:05 +01:00
Shakker
be0c1a9835 test: update model list suppression mocks 2026-04-27 17:13:11 +01:00
Shakker
c896d42cc4 fix: keep manifest suppression on static model lists 2026-04-27 17:13:11 +01:00
Shakker
13feb1b284 fix: narrow manifest alias overrides 2026-04-27 17:13:11 +01:00
Shakker
1056a9ea81 refactor: reuse manifest catalog provider refs 2026-04-27 17:13:11 +01:00
Shakker
e535b313cd docs: document manifest alias suppression behavior 2026-04-27 17:13:11 +01:00
Shakker
df07a89b52 test: cover manifest suppression precedence 2026-04-27 17:13:11 +01:00
Shakker
6e893eaee4 refactor: expose model catalog aliases in plugin lookup 2026-04-27 17:13:11 +01:00
Shakker
03c4c319e3 feat: declare openai catalog suppressions 2026-04-27 17:13:11 +01:00
Shakker
d014b36347 feat: resolve model suppressions from manifests 2026-04-27 17:13:11 +01:00
Shakker
b2685e72c1 refactor: plan manifest catalog aliases and suppressions 2026-04-27 17:13:11 +01:00
Peter Steinberger
6d269f62d6 perf(test): route more stable tests through unit-fast 2026-04-27 17:07:29 +01:00
Shakker
b72414c94e fix: include startup plan in lookup timing 2026-04-27 17:02:57 +01:00
Shakker
94591c3cb3 fix: fingerprint plugin metadata index reuse 2026-04-27 17:02:57 +01:00
Shakker
58b4407cda fix: reject stale plugin metadata inventory 2026-04-27 17:02:57 +01:00
Shakker
197c83138e fix: reuse startup metadata in plugin bootstrap 2026-04-27 17:02:57 +01:00
Shakker
5a72378b27 fix: keep plugin metadata out of config snapshots 2026-04-27 17:02:57 +01:00
Shakker
ab28cfa9d4 fix: guard plugin metadata snapshot reuse 2026-04-27 17:02:57 +01:00
Shakker
5240422f03 docs: describe plugin metadata snapshot 2026-04-27 17:02:57 +01:00
Shakker
d62cc59388 fix: reuse startup metadata for auto enable 2026-04-27 17:02:57 +01:00
Shakker
9de2bc6ffc refactor: reuse startup plugin metadata snapshot 2026-04-27 17:02:57 +01:00
Shakker
ca4f964547 refactor: let config validation use plugin metadata snapshot 2026-04-27 17:02:57 +01:00
Shakker
440fc73448 refactor: extract plugin metadata snapshot 2026-04-27 17:02:57 +01:00
Peter Steinberger
04b5dd097d test: skip bootstrap in release validation onboarding 2026-04-27 17:01:52 +01:00
Peter Steinberger
1fd0802b88 perf(test): route more unit tests through fast lane 2026-04-27 17:01:16 +01:00
Omar Shahine
8ce4f8fc84 fix(gateway): redact SecretRef apiKey through talk.config without throwing
The talk.config discovery RPC was handing the source-snapshot's
talkProviderConfig (with the unresolved SecretRef wrapper still on
apiKey) to speechProvider.resolveTalkConfig. ElevenLabs/OpenAI's
strict normalizeResolvedSecretInputString helper threw 'unresolved
SecretRef' there, so iOS / macOS / Control UI Talk overlays never
learned the configured provider and silently fell back to local
AVSpeechSynthesizer ('robot voice') even though talk.realtime.session
and talk.speak both worked end-to-end with the same SecretRef.

Prefer the runtime-resolved provider config when calling
resolveTalkConfig, strip the apiKey field if it's still a SecretRef
wrapper at the call site, and restore the source-shaped apiKey onto
the response so the UI keeps the SecretRef context. Redaction strips
the value when includeSecrets=false.

Adds a regression test using a strict resolver speech provider that
mirrors ElevenLabs/OpenAI behavior so the path stays covered for
SecretRef apiKeys.

Fixes #72496

Thanks @omarshahine
2026-04-27 08:59:12 -07:00
Peter Steinberger
ee140ae570 perf(test): route memory package tests through unit-fast 2026-04-27 16:43:55 +01:00
Val Alexander
1cf68b9243 fix(control-ui): keep google talk off webrtc
Keep Google Live Talk browser sessions on the supported WebSocket/gateway-relay paths instead of falling back to browser WebRTC, remove stale browser-native voice controls that bypass Talk/TTS provider settings, and harden the Google Live URL plus realtime relay resource controls.

Verification:
- pnpm test ui/src/ui/realtime-talk.test.ts ui/src/ui/realtime-talk-google-live.test.ts src/gateway/talk-realtime-relay.test.ts src/gateway/server-methods/talk.test.ts
- pnpm check:changed
2026-04-27 10:35:34 -05:00
Peter Steinberger
1560e26f3d fix(ci): align yuanbao channel catalog contract 2026-04-27 16:32:21 +01:00
Peter Steinberger
56fa69a48a fix(ci): pin yuanbao official channel catalog source 2026-04-27 16:28:56 +01:00
Peter Steinberger
32bbb5b18f test: harden release validation smokes 2026-04-27 16:28:44 +01:00
cxy
5ccf179a34 feat(qqbot): group chat support, C2C streaming, chunked media upload, and architecture refactor (#70624)
* feat(qqbot): implement unified media upload handling and introduce chunked upload support

This commit enhances the media upload functionality by introducing a unified `sendMedia` method that consolidates the previous separate methods for sending images, voice messages, videos, and files. Key changes include:

- Added `uploadChunked` function for future chunked media uploads, currently marked as not implemented.
- Introduced `MediaSource` abstraction to handle various media types (URLs, base64, local files, buffers) uniformly.
- Updated existing media handling logic to utilize the new `sendMedia` method, ensuring consistent media processing across different types.
- Enhanced error handling and validation for media uploads, including MIME type checks and file size limits.

These changes aim to streamline the media upload process and prepare for future enhancements in handling larger files through chunked uploads.

* feat(qqbot): enhance media upload capabilities with chunked upload support

This commit updates the media upload functionality by implementing chunked upload support for larger files. Key changes include:

- Revised the `SKILL.md` documentation to clarify media file size limits and local file path requirements.
- Introduced a new test suite for the chunked media upload functionality, ensuring robust error handling and upload processes.
- Updated the media handling logic to enforce per-file-type upload ceilings, allowing for seamless integration of chunked uploads.
- Enhanced error handling for daily upload limits, providing user-friendly messages when limits are exceeded.

These improvements aim to streamline the media upload process and accommodate larger files effectively.

* feat(qqbot): add C2C streaming API support for message delivery

This commit introduces support for the QQ C2C official `stream_messages` API, enabling single-message typing-style updates. Key changes include:

- Updated the configuration schema to include a new `c2cStreamApi` boolean option for enabling the C2C streaming API.
- Enhanced the `QQBotAccountConfig` interface to accommodate the new streaming option.
- Implemented a `StreamingController` to manage the lifecycle of C2C stream messages, ensuring proper handling of media tags and message boundaries.
- Updated the outbound dispatch logic to utilize the new streaming capabilities, allowing for more dynamic message delivery in one-to-one chats.

These enhancements aim to improve the responsiveness and interactivity of message delivery within the QQBot framework.

* feat(qqbot): implement group chat support and unify adapter/DI architecture

- Implement group message history tracking with pending history buffer
  (record on skip, render on @-mention reply)
- Add mention detection and gating: explicit @bot, implicit quote-reply,
  ignoreOtherMentions, configurable activation mode (mention/always)
- Add group activation resolution with session store persistence
- Add message queue with per-peer FIFO and group message merging
  (batch multiple rapid messages into one merged payload)
- Add deliver debounce to merge rapid outbound text bursts into
  single messages, with media flush and maxWait cap
- Add group config resolution: per-group prompt, history limit,
  wildcard and specific group overrides
- Enrich history attachments with local paths from processAttachments
  so that history context renders downloaded paths instead of ephemeral
  QQ CDN URLs

- Merge ports/ directory into adapter/ as single entry point
- Expand EngineAdapters to 5 required ports: history, mentionGate,
  audioConvert, outboundAudio, commands
- Remove global register/get singletons in favor of constructor
  injection and one-time init
- Add createEngineAdapters() in bridge/gateway.ts as single assembly point

- Extract monolithic buildInboundContext into 11 discrete stages:
  access, content, quote, refidx, group-gate, envelope, assembly
- Extract group chat modules: history, mention, activation,
  message-gating, deliver-debounce
- Extract config/group.ts, utils/attachment-tags.ts

* feat(qqbot): add /bot-streaming command for C2C message streaming control

This commit introduces the `/bot-streaming` command, allowing users to enable or disable streaming for message delivery in C2C chats. Key changes include:

- Implementation of the `isStreamingConfigEnabled` function to check the current streaming configuration.
- Command handler for `/bot-streaming` that provides usage instructions and manages the streaming state.
- Updates to the command's response messages to inform users of the current streaming status and how to toggle it.

These enhancements aim to improve user experience by providing a straightforward way to manage streaming message delivery in private chats.

* feat(qqbot): extract interaction handler and add remote config query/update support

- Extract INTERACTION_CREATE handler from gateway.ts into a dedicated
  interaction-handler.ts module for better separation of concerns
- Add config query (type=2001) and config update (type=2002) interaction
  branches that read/write claw_cfg via runtime.config API
- Register INTERACTION intent (1<<26) in FULL_INTENTS to receive
  INTERACTION_CREATE events from the gateway
- Add InteractionType constants (CONFIG_QUERY, CONFIG_UPDATE)
- Extend GatewayPluginRuntime with optional config API (loadConfig,
  writeConfigFile) for interaction handler access
- Add QQBotAccountConfigView interface for typed config field access
- Extend acknowledgeInteraction to accept optional data payload for
  rich ACK responses (e.g. claw_cfg snapshot)
- Export getFrameworkVersion from slash-commands-impl for version
  reporting in config snapshots
- Remove unused eslint-disable directive in streaming-media-send.ts

* feat(qqbot): enhance account management and logging capabilities

- Introduced `toGatewayAccount` function to map resolved QQBot accounts to the engine's gateway account structure.
- Added `persistAccountCredentialSnapshot` function to streamline credential backup during gateway events.
- Updated the `qqbotPlugin` to utilize the new account mapping and credential persistence functions, improving the handling of account data.
- Enhanced logging functionality by modifying the `EngineLogger` interface to support metadata in log messages.
- Implemented new commands for managing logs and clearing storage, providing users with better control over their data and system resources.
- Registered multiple built-in commands for improved user interaction, including `/bot-logs` for exporting logs and `/bot-clear-storage` for managing downloaded files.
- Updated configuration schemas to reflect new options and improve clarity for users.

* fix(qqbot): resolve oxlint errors and update raw-fetch allowlist

- Replace unnecessary `else` after `return` in outbound-media-send.ts (6 occurrences)
- Use `Number.parseInt` instead of global `parseInt` in outbound.ts and streaming-media-send.ts
- Use `Number.isNaN` instead of global `isNaN` in register-basic.ts
- Prefer `**` over `Math.pow` in media-chunked.ts
- Convert interface with call signature to function type in commands.port.ts
- Update api-client.ts allowlist line number (108→124) and add media-chunked.ts:552 to raw-fetch allowlist

* docs(qqbot): translate streaming-c2c.ts header comments to English

* feat(qqbot): add voiceMediaTypes

* feat: restore dispatch changes

* fix(qqbot): align test files with updated engine interfaces after rebase

- inbound-attachments.test: replace removed registerAudioConvertAdapter
  with AudioConvertPort, pass audioConvert in ProcessContext
- inbound-pipeline.self-echo.test: add required adapters field to
  InboundPipelineDeps mock (history, mentionGate, audioConvert,
  outboundAudio, commands)
- outbound-dispatch.test: add required skipped field to InboundContext

* fix(qqbot): update test assertions to match refactored engine interfaces

- inbound-pipeline.self-echo.test: self-echo blocking was moved upstream;
  update test to expect non-blocked pipeline behavior
- outbound-dispatch.test: TTS voice path now uses unified sendMedia
  instead of sendVoiceMessage; add sendMedia mock and update assertion
- format-ref-entry.test: attachment format changed from [image: ...]
  to MEDIA: tag syntax via renderAttachmentTags; update expected output

* refactor(qqbot): migrate from deprecated config API to current/replaceConfigFile

Replace all usages of deprecated runtime config methods:
- loadConfig() → current()
- writeConfigFile(cfg) → replaceConfigFile({ nextConfig, afterWrite })

Updated files:
- bridge/narrowing.ts: writeOpenClawConfigThroughRuntime
- adapter/commands.port.ts: ApproveRuntimeGetter type signature
- commands/builtin/register-approve.ts: loadExecConfig, writeExecConfig, reset
- commands/builtin/register-streaming.ts: config read/write
- gateway/interaction-handler.ts: config query/update handlers
- gateway/types.ts: GatewayPluginRuntime.config interface

* feat(qqbot): update package.json

* fix(qqbot): replace deprecated config-runtime import with config-types subpath

Bundled plugin lint requires focused plugin-sdk subpaths.
- gateway.ts: openclaw/plugin-sdk/config-runtime → config-types
- narrowing.ts: openclaw/plugin-sdk/config-runtime → config-types

* feat(qqbot): group chat support, C2C streaming, chunked media upload, and architecture refactor (#70624) (thanks @cxyhhhhh)

---------

Co-authored-by: Bobby <zkd8907@live.com>
Co-authored-by: sliverp <870080352@qq.com>
2026-04-27 23:19:12 +08:00
Peter Steinberger
8304635258 perf(test): route speech provider registry through unit-fast 2026-04-27 16:16:12 +01:00
loongfay
3120401f53 feat(channel) yuanbao (#72756)
* feat(channel) yuanbao

* feat(channel) yuanbao

* docs(changelog): note Yuanbao channel plugin (#72756) (thanks @loongfay)

---------

Co-authored-by: loongzhao <loongzhao@tencent.com>
Co-authored-by: sliverp <870080352@qq.com>
2026-04-27 23:04:33 +08:00
Peter Steinberger
c41126dbbb ci: capture dispatched full validation runs 2026-04-27 15:51:03 +01:00
Peter Steinberger
708b42c4dc docs(changelog): link proxy fix to issue 2026-04-27 15:44:37 +01:00
Peter Steinberger
dc859584a3 fix(gateway): honor all_proxy in env dispatcher 2026-04-27 15:36:12 +01:00
Shakker
fd6c9fc7f5 fix: reuse plugin registry during config validation 2026-04-27 15:35:39 +01:00
Peter Steinberger
42fc176093 test: isolate speech provider registry mocks 2026-04-27 15:30:21 +01:00
Shakker
246fd9d3c0 fix: preserve manifest fallback for derived provider indexes 2026-04-27 15:29:11 +01:00
Shakker
7f316b917b docs: add model source plan changelog 2026-04-27 15:29:11 +01:00
Shakker
4fe7303a1f test: cover model list source planning 2026-04-27 15:29:11 +01:00
Shakker
25dda844b7 refactor: use source plan for models list 2026-04-27 15:29:11 +01:00
Shakker
f5417f626c refactor: add model list source plan 2026-04-27 15:29:11 +01:00
Peter Steinberger
d22ced122d test: isolate speech provider registry test 2026-04-27 15:28:37 +01:00
Shakker
ca444af891 fix: restore npm shims on swap failure 2026-04-27 15:27:43 +01:00
Shakker
2186080963 fix: stage npm updates under global root 2026-04-27 15:27:43 +01:00
Shakker
b0127b9f1f fix: harden npm update staging 2026-04-27 15:27:43 +01:00
Shakker
6985c6751c fix: make npm global updates atomic 2026-04-27 15:27:43 +01:00
Peter Steinberger
9b4c1f0fa3 test: update compaction token test contexts 2026-04-27 15:18:28 +01:00
Peter Steinberger
467ee701ef fix(ci): align tests with runtime barrels 2026-04-27 15:15:07 +01:00
Peter Steinberger
9090457da7 test(plugin-sdk): use narrow config runtime mocks 2026-04-27 15:14:02 +01:00
Peter Steinberger
a2af8054e1 test: harden live release checks 2026-04-27 15:11:46 +01:00
Peter Steinberger
016a0b4de9 fix(gateway): avoid echoing rotated device tokens 2026-04-27 15:10:05 +01:00
Peter Steinberger
dacf43640a fix(ci): repair main test gates 2026-04-27 15:03:39 +01:00
Peter Steinberger
a9648664c1 perf(test): route memory dreaming through unit-fast 2026-04-27 15:03:21 +01:00
Peter Steinberger
22e2e45c57 fix(cli): skip respawn for foreground gateway 2026-04-27 15:01:33 +01:00
Peter Steinberger
d69eeeb2a8 fix: skip test-only plugin install scan findings 2026-04-27 15:00:55 +01:00
Peter Steinberger
82b4049744 refactor: narrow discord slack runtime api barrels 2026-04-27 15:00:03 +01:00
Peter Steinberger
75a96bafcf docs: fix changelog attribution credits 2026-04-27 15:00:03 +01:00
Peter Steinberger
4336a7f3a9 refactor(plugin-sdk): narrow config runtime imports 2026-04-27 14:58:32 +01:00
Peter Steinberger
f3e8a8a319 fix(agents): persist compaction token snapshots 2026-04-27 14:58:15 +01:00
Peter Steinberger
f9946eb069 fix(memory): parse qmd vector status variants 2026-04-27 14:57:28 +01:00
Peter Steinberger
1f7b7c249a fix(google-meet): grant browser media permissions 2026-04-27 14:54:07 +01:00
Peter Steinberger
713cc74bff fix: quiet installed plugin override warnings 2026-04-27 14:53:36 +01:00
Peter Steinberger
2e99c1d227 fix(subagents): enforce explicit spawn allowlists 2026-04-27 14:53:17 +01:00
Peter Steinberger
58a4ca4423 refactor: narrow whatsapp runtime api barrel 2026-04-27 14:52:21 +01:00
Peter Steinberger
1ed6d04014 ci: guard plugin sdk wildcard reexports 2026-04-27 14:52:21 +01:00
Peter Steinberger
877b5a14f1 fix(sessions): batch store cap maintenance 2026-04-27 14:51:53 +01:00
Peter Steinberger
0ac0357486 docs(memory): explain qmd collection compatibility 2026-04-27 14:44:01 +01:00
Peter Steinberger
63011fcbb0 ci: update generated protocol swift models 2026-04-27 14:42:27 +01:00
Peter Steinberger
e035300d8e fix(acp): allow manual spawn with dispatch paused 2026-04-27 14:40:12 +01:00
Peter Steinberger
c3b3da41fe fix: allow trusted openclaw peer symlinks 2026-04-27 14:40:02 +01:00
Peter Steinberger
cbf6ed2b35 chore: publish OpenClaw 2026.4.25 appcast 2026-04-27 14:39:56 +01:00
Peter Steinberger
4c544e649c test: move more stateful tests to unit-fast 2026-04-27 14:37:51 +01:00
Peter Steinberger
4ebec8b5dc fix(memory): group qmd collection searches 2026-04-27 14:37:12 +01:00
Peter Steinberger
eb1a201060 refactor: narrow media core plugin api barrels 2026-04-27 14:34:00 +01:00
Peter Steinberger
0f996ad4b0 ci: enforce changelog attribution policy in pr gates 2026-04-27 14:33:59 +01:00
Peter Steinberger
ad0f600450 fix(gateway): avoid systemd service split-brain 2026-04-27 14:32:49 +01:00
Peter Steinberger
c00ef238be docs(tools): clarify sessions_spawn profile gating 2026-04-27 14:31:54 +01:00
Peter Steinberger
23d047dff5 ci: update generated protocol models 2026-04-27 14:31:13 +01:00
Peter Steinberger
1382fb5bd7 fix(agents): fail closed missing requester completion routes 2026-04-27 14:30:59 +01:00
Peter Steinberger
6956e8406d fix: honor profile plugin install roots 2026-04-27 14:30:12 +01:00
Shakker
f88c330657 fix: preserve runtime config during source plugin activation 2026-04-27 14:29:49 +01:00
Shakker
a964dcbddb fix: honor source plugin activation at startup 2026-04-27 14:29:49 +01:00
Shakker
a88f2ba939 fix: avoid startup auto-enable runtime defaults 2026-04-27 14:29:48 +01:00
Peter Steinberger
6ced6bc4a3 ci: satisfy live shard lint 2026-04-27 14:29:41 +01:00
Peter Steinberger
bbbc80ddcc ci: guard changelog bot attributions 2026-04-27 14:29:41 +01:00
Peter Steinberger
6e8aaef1cc fix(google-meet): avoid duplicate test speech 2026-04-27 14:29:08 +01:00
Peter Steinberger
73ba282b54 docs(memory): clarify qmd mask compatibility 2026-04-27 14:26:56 +01:00
Peter Steinberger
8e09105bd3 test: route more mock-only tests through unit-fast 2026-04-27 14:24:43 +01:00
Peter Steinberger
2243a68a1d ci: shard release live validation 2026-04-27 14:24:10 +01:00
Peter Steinberger
f6bda8d36b refactor(providers): share Claude thinking profiles 2026-04-27 14:23:12 +01:00
Peter Steinberger
93bbbe5e37 feat: add browser realtime talk transports 2026-04-27 14:22:32 +01:00
Peter Steinberger
5dd1e264eb refactor(config): tighten plugin config guardrails 2026-04-27 14:20:27 +01:00
Peter Steinberger
ef9d108436 fix(gateway): include client in hello snapshot 2026-04-27 14:20:27 +01:00
Peter Steinberger
c3c8f25bab fix(memory): report qmd dirty watcher state 2026-04-27 14:20:10 +01:00
Peter Steinberger
28f264034b fix: discover symlinked plugin directories 2026-04-27 14:17:32 +01:00
sfuminya
2c57d70a10 fix: preserve requester route for subagent completion delivery (#72806)
* fix: preserve requester route for subagent completion delivery

* fix(agents): preserve requester subagent completion routes

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-04-27 14:17:14 +01:00
Peter Steinberger
dfd9dbe4e1 docs: clarify changelog attribution exclusions 2026-04-27 14:16:43 +01:00
Peter Steinberger
4300a6165e test: route more setup-free tests through unit-fast 2026-04-27 14:16:20 +01:00
Peter Steinberger
67a447c175 refactor: tighten plugin runtime sdk boundaries 2026-04-27 14:15:53 +01:00
Peter Steinberger
b181930c23 fix(memory): skip qmd vectors in lexical mode 2026-04-27 14:09:42 +01:00
Peter Steinberger
6a0dc3a9bc fix: cache plugin discovery realpaths 2026-04-27 14:09:15 +01:00
Peter Steinberger
9ca4049861 ci: match package Telegram harness to release ref 2026-04-27 14:06:05 +01:00
Peter Steinberger
52a1cbc1c6 fix(qa-lab): keep gateway client on generic sdk seam 2026-04-27 14:05:09 +01:00
Peter Steinberger
57401f1581 fix(google-meet): use OpenClaw browser for local joins 2026-04-27 14:03:46 +01:00
Peter Steinberger
8de458c6c0 fix(qa-lab): use generic gateway runtime SDK 2026-04-27 14:03:28 +01:00
harish ganeshmurthy
f75d8827f2 fix(opencode): expose Claude thinking levels (#72778)
* fix(opencode): expose claude thinking levels

* test(opencode): cover adaptive claude thinking bounds

* docs(changelog): credit opencode thinking contributor

---------

Co-authored-by: haishmg <4529977+haishmg@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-04-27 14:03:26 +01:00
Peter Steinberger
1b1916053f ci: inline Docker release planning for old refs 2026-04-27 14:03:17 +01:00
Peter Steinberger
fd4b59a906 ci: keep release checks compatible with stable refs 2026-04-27 13:59:49 +01:00
Peter Steinberger
d0e4472616 test(vitest): keep speech registry out of unit-fast 2026-04-27 13:57:15 +01:00
Peter Steinberger
f15c9f1d5f test: move more setup-free tests to unit-fast 2026-04-27 13:56:21 +01:00
Peter Steinberger
df65a75f92 fix(memory): avoid live embedding probes in status 2026-04-27 13:55:51 +01:00
Peter Steinberger
dc495e6d62 refactor(discord): isolate model picker apply flow 2026-04-27 13:50:43 +01:00
Peter Steinberger
951a0d89d8 fix(discord): persist stale model picker overrides 2026-04-27 13:50:43 +01:00
Peter Steinberger
1fbe83d09f fix: keep link understanding from dropping replies 2026-04-27 13:45:05 +01:00
Peter Steinberger
fa1f670716 test: route setup-free tests through unit-fast 2026-04-27 13:42:32 +01:00
Peter Steinberger
770978b8d3 build(config): refresh generated schema baseline 2026-04-27 13:42:12 +01:00
Peter Steinberger
8e37ee4bf2 fix(voice-call): avoid blocking gateway startup 2026-04-27 13:40:30 +01:00
Peter Steinberger
2d90dbe512 docs: add release feature redirect URLs 2026-04-27 13:40:00 +01:00
Peter Steinberger
f3528e7755 fix(openrouter): retire stealth model catalog entries 2026-04-27 13:36:49 +01:00
Peter Steinberger
9cde9261c6 docs(changelog): move post-release entries to 2026.4.26 2026-04-27 13:34:02 +01:00
Peter Steinberger
cae492374c test: reduce repeated test setup overhead 2026-04-27 13:33:05 +01:00
Peter Steinberger
0931a1f11e ci: fix release validation dispatch and protocol drift 2026-04-27 13:32:03 +01:00
Peter Steinberger
d9b8001502 build(protocol): refresh swift gateway models 2026-04-27 13:31:15 +01:00
Peter Steinberger
252c63429e fix(providers): map native reasoning efforts 2026-04-27 13:27:58 +01:00
Peter Steinberger
4119d65e82 test(doctor): keep repair sequencing unit isolated 2026-04-27 13:27:36 +01:00
Peter Steinberger
3c6d178f4e docs: clarify malformed plugin tool guards 2026-04-27 13:27:19 +01:00
Peter Steinberger
41d5c27894 fix(docker): install runtime ca certificates 2026-04-27 13:24:42 +01:00
Peter Steinberger
98b441edb1 ci: split release docker integration chunks 2026-04-27 13:24:30 +01:00
Peter Steinberger
750c180a6c fix(ollama): warn on WSL2 CUDA crash loop risk 2026-04-27 13:24:04 +01:00
Peter Steinberger
0a076bc0fc fix: isolate malformed plugin tools 2026-04-27 13:22:28 +01:00
Peter Steinberger
7fb2a356e8 fix(nodes): allow removing stale paired nodes 2026-04-27 13:20:52 +01:00
Peter Steinberger
400be3b63f test(agents): align failure-kind protocol expectations 2026-04-27 13:18:16 +01:00
Peter Steinberger
4bd356d03a fix(channels): clarify message target syntax 2026-04-27 13:18:04 +01:00
Peter Steinberger
6fe9285f64 fix(ci): sync locale and cron contract tests 2026-04-27 13:15:59 +01:00
Peter Steinberger
cff1bdb491 ci: trim duplicate release package lanes 2026-04-27 13:15:10 +01:00
Peter Steinberger
4260bb0418 fix: quarantine invalid plugin configs 2026-04-27 13:14:59 +01:00
Alex Knight
b1e530b204 fix(cli): mark embedded agent fallback (#72730)
* fix(cli): mark embedded agent fallback

* refactor(cli): structure embedded fallback metadata

* refactor(cli): move fallback metadata types out of EmbeddedPiRunMeta

---------

Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
2026-04-27 22:14:11 +10:00
github-actions[bot]
bef28fcf1a chore(ui): refresh th control ui locale 2026-04-27 12:13:31 +00:00
Peter Steinberger
7e45272319 test(ci): align docker scenario guard with sharded sweep 2026-04-27 13:13:04 +01:00
github-actions[bot]
ef87620c5b chore(ui): refresh id control ui locale 2026-04-27 12:12:56 +00:00
github-actions[bot]
973fbcd65b chore(ui): refresh pl control ui locale 2026-04-27 12:12:51 +00:00
github-actions[bot]
a3ef1938b6 chore(ui): refresh uk control ui locale 2026-04-27 12:12:39 +00:00
github-actions[bot]
6c0d9b1642 chore(ui): refresh tr control ui locale 2026-04-27 12:12:32 +00:00
github-actions[bot]
e7d3cfa7ca chore(ui): refresh fr control ui locale 2026-04-27 12:11:55 +00:00
github-actions[bot]
3769a93752 chore(ui): refresh ko control ui locale 2026-04-27 12:11:51 +00:00
github-actions[bot]
1633e38a77 chore(ui): refresh ja-JP control ui locale 2026-04-27 12:11:46 +00:00
github-actions[bot]
1a466d5a44 chore(ui): refresh es control ui locale 2026-04-27 12:11:42 +00:00
github-actions[bot]
1ad36486b8 chore(ui): refresh zh-CN control ui locale 2026-04-27 12:11:01 +00:00
github-actions[bot]
3352f8a569 chore(ui): refresh zh-TW control ui locale 2026-04-27 12:10:55 +00:00
github-actions[bot]
61633b5ca7 chore(ui): refresh pt-BR control ui locale 2026-04-27 12:10:52 +00:00
github-actions[bot]
450eae0ecf chore(ui): refresh de control ui locale 2026-04-27 12:10:48 +00:00
Peter Steinberger
0e586bb48a fix(agents): improve fallback failure observability 2026-04-27 13:10:12 +01:00
Peter Steinberger
63eaf8ea51 fix(models): default local custom providers to completions 2026-04-27 13:09:59 +01:00
Peter Steinberger
b6c8e51dcb fix(gateway): build hello snapshot after presence update 2026-04-27 13:09:30 +01:00
Peter Steinberger
3517b25482 fix: remove duplicate hello snapshot build 2026-04-27 13:09:16 +01:00
Peter Steinberger
c6ebd99a46 fix(control-ui): surface lazy panel load failures 2026-04-27 13:09:02 +01:00
Peter Steinberger
0141471dd5 refactor: move shared helpers off reserved sdk seams 2026-04-27 13:07:54 +01:00
Peter Steinberger
e91f9a3f67 fix: include connected client in hello snapshot 2026-04-27 13:07:45 +01:00
Peter Steinberger
fef4b57b39 fix(gateway): include connected client in hello snapshot 2026-04-27 13:06:30 +01:00
Peter Steinberger
f68ef1ae7c ci: shard bundled plugin release sweep 2026-04-27 13:05:14 +01:00
Peter Steinberger
0dfea099d6 test: speed up focused test setup 2026-04-27 13:00:43 +01:00
Peter Steinberger
e9986aa787 fix(ci): make full validation rerun-aware 2026-04-27 13:00:09 +01:00
Peter Steinberger
6a55a00da4 fix(agents): scope loop detection to runs 2026-04-27 12:59:54 +01:00
Peter Steinberger
d73e2ee774 fix(google-meet): use PCM audio for Chrome realtime 2026-04-27 12:55:00 +01:00
Val Alexander
27a4bba90a fix(ui): render cron markdown summaries
## Summary
- render cron job prompts and run summaries through the sanitized markdown pipeline in the Control UI
- keep system-event cron payloads plain and prevent markdown link clicks from triggering row selection
- handle failed runs with missing or empty summaries without duplicating or hiding the error text

## Verification
- pnpm test ui/src/ui/views/cron.test.ts
- pnpm test src/plugins/doctor-contract-registry.test.ts src/plugins/setup-registry.test.ts
- pnpm check:changed
- GitHub CI green on 251f01a3b0
2026-04-27 06:53:51 -05:00
Peter Steinberger
769d04b4ce docs(models): clarify local chat completions routing 2026-04-27 12:53:46 +01:00
Peter Steinberger
10257114ac test: speed up focused unit tests 2026-04-27 12:52:54 +01:00
Peter Steinberger
a041ea7ca7 docs(plugins): clarify runtime config access 2026-04-27 12:52:20 +01:00
Peter Steinberger
9d5a211019 refactor(plugins): enforce config API deprecations 2026-04-27 12:52:20 +01:00
Peter Steinberger
94a9d3f0be refactor(config): track runtime config revisions 2026-04-27 12:52:20 +01:00
Peter Steinberger
047c03cc88 fix(gateway): drop stale webchat handshakes 2026-04-27 12:51:17 +01:00
Peter Steinberger
eaae63d288 refactor: keep plugin sdk owner seams explicit 2026-04-27 12:50:31 +01:00
Peter Steinberger
189535308f test: align plugin jiti Windows expectations 2026-04-27 12:49:00 +01:00
Peter Steinberger
22a51de422 fix: tolerate stale channel plugin config 2026-04-27 12:48:13 +01:00
Peter Steinberger
c0ea89cfd2 fix(agents): recover unclosed reasoning-only replies 2026-04-27 12:45:11 +01:00
Peter Steinberger
74fb6be716 fix(ui): scope agent identity to active session
Co-authored-by: Sahil Satralkar <62758655+sahilsatralkar@users.noreply.github.com>
2026-04-27 12:45:00 +01:00
Peter Steinberger
d25dd7c2bd test: cache dockerfile fixture reads 2026-04-27 12:42:29 +01:00
Vincent Koc
9be54044eb docs(doctor): document stale channel plugin cleanup (edb3e84898)
Trace to edb3e84898 (fix: clean stale plugin channel config). When
openclaw doctor --fix removes a missing channel plugin, it also cascades
the cleanup to dangling channel config, heartbeat targets, and channel
model overrides, preventing gateway boot loops after failed plugin
reinstalls. Added an Accordion 11d to docs/gateway/doctor.md listing the
exact config keys that get pruned alongside the plugin entry.
2026-04-27 04:40:49 -07:00
Peter Steinberger
9b2f10dcf8 fix(agents): preserve distinct empty exec failures 2026-04-27 12:40:41 +01:00
martingarramon
4f50921e0f fix(gateway/schema): require hello-ok auth
Fixes #68160.

Drops stale optionality from the hello-ok auth schema and keeps generated Swift models, macOS fixtures, browser client types, protocol docs, and merged-base test boundaries aligned.
2026-04-27 06:40:36 -05:00
Peter Steinberger
00d4099526 fix(discord): inherit thread model overrides without transcript fork 2026-04-27 12:40:32 +01:00
Peter Steinberger
b056d594b4 fix(plugins): normalize Windows Jiti paths 2026-04-27 12:39:21 +01:00
Vincent Koc
c85065eb7f fix(cli): tighten Windows restart policy-close health checks
Preserve contributor credit and land the narrowed restart-health fix after ProjectClownfish review/follow-up.
2026-04-27 04:38:29 -07:00
Peter Steinberger
3da6d6ee18 fix(qwen): use plugin test boundary helpers 2026-04-27 12:36:50 +01:00
Peter Steinberger
c59af3caf7 docs(plugins): document runtime config APIs 2026-04-27 12:35:59 +01:00
Peter Steinberger
7f3f108521 refactor(config): migrate plugin config access 2026-04-27 12:35:58 +01:00
Peter Steinberger
48ebed3ed3 fix(plugins): normalize bundled sidecar jiti imports 2026-04-27 12:35:51 +01:00
Peter Steinberger
da8576c0bf test: guard plugin boundary classifications 2026-04-27 12:35:43 +01:00
Peter Steinberger
7ec97c010c test: speed up plugin activation boundary test 2026-04-27 12:35:31 +01:00
Vincent Koc
727927aae0 fix(docker): repair named-volume state directory ownership
Preserve contributor credit and land the narrowed Docker ownership fix after ProjectClownfish review/follow-up.
2026-04-27 04:34:35 -07:00
Peter Steinberger
e9bce3f81c fix(agents): stabilize exec loop outcome hashing 2026-04-27 12:33:37 +01:00
Peter Steinberger
35335214b3 fix(compaction): avoid preserving duplicate user turns 2026-04-27 12:30:59 +01:00
Peter Steinberger
dae09d26b9 test(live): tolerate provider-specific live probe variance 2026-04-27 12:30:12 +01:00
Peter Steinberger
053aff6d35 fix(mcp): normalize streamable http server aliases 2026-04-27 12:29:24 +01:00
Peter Steinberger
3da4b28d1b fix(agents): avoid overload classification for live model switches 2026-04-27 12:28:33 +01:00
Peter Steinberger
82e164c018 test: speed up acp rate-limit coverage 2026-04-27 12:28:09 +01:00
Peter Steinberger
db087a4be7 fix(doctor): stream bundled runtime dep repair progress 2026-04-27 12:27:44 +01:00
Shakker
05fce28ec0 docs: document installed manifest fallback cache 2026-04-27 12:26:10 +01:00
Peter Steinberger
7363fb4a44 refactor: move telegram poll visibility out of core 2026-04-27 12:25:57 +01:00
Peter Steinberger
3bc29dd604 fix(sqlite): bound WAL sidecar growth 2026-04-27 12:25:10 +01:00
Peter Steinberger
bbfdb38e4e fix: show doctor runtime dependency install progress 2026-04-27 12:25:05 +01:00
Peter Steinberger
5afa24a9fc fix(qwen): preserve custom modelstudio providers 2026-04-27 12:24:25 +01:00
Peter Steinberger
dca9fa471f fix(ui): preserve session assistant identity 2026-04-27 12:20:36 +01:00
Shakker
6f6e2765e2 test: reset installed manifest cache in web search provider tests 2026-04-27 12:19:51 +01:00
Shakker
ac7aef6c5b docs: frame installed manifest cache as fallback 2026-04-27 12:19:51 +01:00
Marcus Castro
b7a1bfd2d7 fix(plugins): cache installed manifest registry 2026-04-27 12:19:51 +01:00
Peter Steinberger
e59e0393f5 fix(acpx): mark claude acp package test-only 2026-04-27 12:18:59 +01:00
Peter Steinberger
da822a56d8 refactor(vllm): own nemotron thinking payloads 2026-04-27 12:15:54 +01:00
Peter Steinberger
22bb53ac9a docs(changelog): note tool cache channel invalidation 2026-04-27 12:14:51 +01:00
Peter Steinberger
2cfe6bf4e5 fix(ollama): dedupe latest models during setup 2026-04-27 12:14:10 +01:00
Peter Steinberger
78577ac147 fix: route tasks json through lean cli path 2026-04-27 12:13:51 +01:00
Peter Steinberger
e20f755ac5 fix(gateway): invalidate tool inventory on channel registry changes 2026-04-27 12:13:39 +01:00
Peter Steinberger
277cc640b1 fix(acp): wait for claude results before idle completion 2026-04-27 12:12:48 +01:00
Peter Steinberger
eebdda92f0 fix(media): keep audio input repair in doctor 2026-04-27 12:12:41 +01:00
Peter Steinberger
e98f976a70 refactor: centralize provider stream fallback ownership 2026-04-27 12:11:29 +01:00
清秋
8200d878a3 fix(ui): harden webchat input history behavior
Harden WebChat input history handling so draft, navigation, and render-state behavior stay consistent across the chat UI.

Validated locally on the rebased PR head 742a5f22f1:
- CI=true OPENCLAW_LOCAL_CHECK=0 pnpm check:changed
- CI=true OPENCLAW_LOCAL_CHECK=0 pnpm test:changed

Closes #38702.
2026-04-27 06:08:55 -05:00
Peter Steinberger
1971db0dc5 fix(media): expand legacy audio input placeholder 2026-04-27 12:06:58 +01:00
Peter Steinberger
8e14f5c749 fix(agents): drop malformed reasoning before orphan close tags 2026-04-27 12:06:37 +01:00
Egor Dementyev
b081b195a3 feat(hooks): emit gateway shutdown lifecycle events (#63084)
Merged via squash.

Prepared head SHA: 188d6fef24
Co-authored-by: eyev0 <22837926+eyev0@users.noreply.github.com>
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Reviewed-by: @BunsDev
2026-04-27 06:05:43 -05:00
Peter Steinberger
45bc7f69f2 fix(gateway): cache effective tool inventory 2026-04-27 12:04:40 +01:00
Peter Steinberger
496964fced test: speed up subagent announce format e2e 2026-04-27 12:03:54 +01:00
Peter Steinberger
a3144b6bfd fix(agents): preserve explicit Ollama local auth marker 2026-04-27 12:00:51 +01:00
Peter Steinberger
9dd01b5e49 fix: align plugin runtime dependency contracts 2026-04-27 11:58:28 +01:00
Peter Steinberger
9bc703213b fix(control-ui): preserve loopback client version labels 2026-04-27 11:56:58 +01:00
Peter Steinberger
7ef899ad96 test: speed up channel onboarding e2e 2026-04-27 11:55:16 +01:00
Peter Steinberger
583f32f56f test: align auth and config help expectations 2026-04-27 11:52:54 +01:00
Peter Steinberger
4f7498f6df chore: update config help baseline 2026-04-27 11:51:55 +01:00
Peter Steinberger
6ae2e9e9dc fix(gateway): keep effective tools on hot registry path 2026-04-27 11:51:15 +01:00
Peter Steinberger
9dcd53c0b6 fix(memory): avoid watchers for memory CLI commands 2026-04-27 11:50:44 +01:00
Peter Steinberger
c9b9887583 test: speed up embedded runner e2e mocks 2026-04-27 11:50:37 +01:00
Peter Steinberger
836d4b4105 refactor(vllm): own qwen thinking payloads 2026-04-27 11:50:25 +01:00
Peter Steinberger
4f7038ae33 fix(anthropic): drop prefill with thinking 2026-04-27 11:50:25 +01:00
Peter Steinberger
75c8c1bebe fix(agents): honor qwen chat-template thinking compat 2026-04-27 11:50:24 +01:00
Peter Steinberger
3db407da40 test(security): cover bundled plugin allowlist audit 2026-04-27 11:50:24 +01:00
Peter Steinberger
4a65b69073 fix: accept local markers for custom ollama providers 2026-04-27 11:47:09 +01:00
Peter Steinberger
5a81c4000c chore: tighten plugin boundary export audit 2026-04-27 11:47:09 +01:00
Peter Steinberger
236ca49998 docs: clarify memory search input type help 2026-04-27 11:47:06 +01:00
Peter Steinberger
f487ed160e test(agents): fix compatible retry fixture 2026-04-27 11:44:56 +01:00
Peter Steinberger
769994eb04 test(agents): cover compatible empty retries 2026-04-27 11:44:55 +01:00
Peter Steinberger
fd9d32f022 fix(agents): retry empty compatible turns 2026-04-27 11:44:55 +01:00
Peter Steinberger
edb3e84898 fix: clean stale plugin channel config 2026-04-27 11:41:53 +01:00
harish ganeshmurthy
fa0f7d1e73 fix(webchat): hide reset startup prompt from history
Closes #72369.

Remote validation (Blacksmith Testbox tbx_01kq7874j733m8pxesmgvfz1x1):
- pnpm test src/auto-reply/reply/get-reply-run.media-only.test.ts src/gateway/server-methods/server-methods.test.ts
- node scripts/run-vitest.mjs run --config test/vitest/vitest.unit-ui.config.ts ui/src/ui/controllers/chat.test.ts
- pnpm check:changed

Co-authored-by: haishmg <4529977+haishmg@users.noreply.github.com>
2026-04-27 11:41:33 +01:00
Peter Steinberger
ae86541364 fix: export tts runtime plugin sdk subpath 2026-04-27 11:40:56 +01:00
Vincent Koc
9ef0131e1c docs(local-models): note LAN-local auth marker support (fee16865b2 + 0dd2844991)
Trace to fee16865b2 (fix(agents): accept LAN local auth markers) and the
companion 0dd2844991 (fix: preserve Ollama local marker auth). The fix
extends ollama-local marker handling to any custom OpenAI-compatible
provider whose baseUrl resolves to loopback, a private LAN, .local, or a
bare hostname, so persisted local markers no longer fail with missing-auth
errors for non-Ollama-typed local providers (LM Studio, vLLM, LiteLLM).

The Ollama provider page already covers ollama-local for Ollama-typed
providers; this note lives in docs/gateway/local-models.md where custom
OpenAI-compatible local stacks are documented.
2026-04-27 03:39:26 -07:00
Peter Steinberger
7688b696de refactor: remove bundled plugin sdk self imports 2026-04-27 11:36:08 +01:00
Peter Steinberger
8a8cc8dc9f fix(memory): refresh tool config at execution 2026-04-27 11:36:02 +01:00
Peter Steinberger
fa468d0c2d fix(bonjour): default mdns host to system hostname 2026-04-27 11:35:19 +01:00
Vincent Koc
3a73826e28 fix(docs-sync): prune orphan locale docs whose English source no longer exists
The publish workflow rsyncs source docs/ into the publish repo with --delete,
but explicitly protects locale directories so translation files survive
non-translation-pipeline syncs. When an English source file is renamed (for
example install/migrating-matrix.md -> channels/matrix-migration.md), the
locale copies at <locale>/install/migrating-matrix.md become orphans:
deleted from the English nav but still present on disk.

Mintlify's hosted build appears to silently fall back to the previous
deployment when nav references a path with mixed locale availability, so
recent docs changes (the migration hub rework, matrix-migration move) are
not propagating to docs.openclaw.ai even though every CI run reports
success and the publish repo has the right English content.

Add a pruneOrphanLocaleDocs() pass that walks every generated-locale
directory in the publish target and removes any .md/.mdx file whose
matching English path no longer exists in source docs. Runs after rsync
and before composing docs.json so the regenerated nav and the on-disk
files stay consistent. Verified the logic against the live publish repo:
identifies all ja-JP/es/pt-BR/ko/de/fr/ar/it/tr/uk/id/pl/zh-CN orphans of
install/migrating-matrix.md (12 entries) and would also catch any future
renames the same way.
2026-04-27 03:34:57 -07:00
Peter Steinberger
ca88daad1e test(agents): keep openai image cache probe non-blocking 2026-04-27 11:34:15 +01:00
Peter Steinberger
169d33ded2 test: speed up auth rotation e2e 2026-04-27 11:33:36 +01:00
Peter Steinberger
d337fa8946 test: align build profile guard expectations 2026-04-27 11:31:57 +01:00
Peter Steinberger
f50fb73560 fix(whatsapp): honor env proxy during QR login 2026-04-27 11:30:29 +01:00
Bartok
f0b327cf68 fix(media): gate markdown image extraction by channel (#72718)
Closes #72642

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-04-27 11:27:35 +01:00
Peter Steinberger
775ed36c16 feat(memory): support asymmetric embedding input types 2026-04-27 11:25:40 +01:00
Peter Steinberger
0dd2844991 fix: preserve Ollama local marker auth 2026-04-27 11:25:06 +01:00
Peter Steinberger
a421e0be84 test: fix plugin registry CI contracts 2026-04-27 11:25:06 +01:00
Peter Steinberger
a0aedea63d fix: guard cli bootstrap imports 2026-04-27 11:24:35 +01:00
Peter Steinberger
fa0d81ed13 fix(agents): retry empty openai-compatible turns 2026-04-27 11:24:14 +01:00
Peter Steinberger
f820f89f14 test(agents): align local marker auth expectation 2026-04-27 11:23:56 +01:00
Peter Steinberger
f6ee2877e0 refactor: share memory dreaming cron constants 2026-04-27 11:19:09 +01:00
Peter Steinberger
9b0a0fb0a7 refactor: tighten plugin boundary surfaces 2026-04-27 11:19:09 +01:00
Peter Steinberger
c4fe72b8d6 ci: pin full release validation child refs 2026-04-27 11:16:16 +01:00
Peter Steinberger
bc0b02b2a6 fix(channels): avoid bundled plugin load paths 2026-04-27 11:15:42 +01:00
Peter Steinberger
4067d78a4c fix(exec): enforce default timeout on node runs 2026-04-27 11:15:33 +01:00
Peter Steinberger
c20bcc59a8 fix(git-hooks): skip ignored staged paths 2026-04-27 11:12:55 +01:00
Vincent Koc
0e4be1e3d3 docs(matrix): move migration guide from install/ to channels/
The Matrix migration guide is plugin-upgrade content (encrypted-state recovery,
device verification, room-key restore) rather than a cross-system import or
machine move, so it belongs alongside the Matrix channel docs rather than under
Install > Maintenance > Migrating.

- Move docs/install/migrating-matrix.md to docs/channels/matrix-migration.md
- Update inbound link in docs/channels/matrix.md
- Update the migrating.md hub: replace the Matrix Card with a one-line link in 'Upgrade a plugin in place'
- Refresh Related list on the moved page (link Matrix push rules and Migration guide hub)
- docs.json: remove install/migrating-matrix from Maintenance > Migrating, slot channels/matrix-migration between channels/matrix and channels/matrix-push-rules in the Mainstream channels group, and add a /install/migrating-matrix -> /channels/matrix-migration redirect
2026-04-27 03:12:32 -07:00
Peter Steinberger
7630322f64 docs: format migration guides 2026-04-27 11:11:27 +01:00
Peter Steinberger
6778e44333 test(exec): cover background timeout opt-out 2026-04-27 11:10:51 +01:00
Shakker
07946a404d chore: update a2ui bundle hash 2026-04-27 11:10:12 +01:00
Shakker
06de1d2080 fix: reuse web provider candidate manifests 2026-04-27 11:10:12 +01:00
Peter Steinberger
4003e4389a fix(memory-core): support dreaming model override 2026-04-27 11:08:21 +01:00
Peter Steinberger
b8a9dc9d78 test(moonshot): avoid redundant live result type 2026-04-27 11:07:21 +01:00
Peter Steinberger
9d52b615ad feat(ollama): prefix memory embedding queries 2026-04-27 11:07:20 +01:00
Peter Steinberger
92100efa04 fix(exec): honor default timeout for background runs 2026-04-27 11:06:23 +01:00
Peter Steinberger
ca882aeb42 test: remove discord sdk path references 2026-04-27 11:03:14 +01:00
Peter Steinberger
9f62c73893 fix(cron): verify delivery before clearing message warnings 2026-04-27 11:02:09 +01:00
Peter Steinberger
a4b97075ae fix: align support URL redaction 2026-04-27 11:00:42 +01:00
Peter Steinberger
5757d1bb69 ci: harden live release validation lane 2026-04-27 10:59:25 +01:00
Peter Steinberger
fee16865b2 fix(agents): accept LAN local auth markers 2026-04-27 10:57:35 +01:00
Vincent Koc
a6eb051b3a docs(migration): convert migrating.md to a hub, nest per-source guides, reorder nav
- install/migrating: convert to a hub page with three clear paths (CardGroup for cross-system imports linking Claude+Hermes, machine-to-machine move with Steps and AccordionGroup, plugin upgrade Card linking Matrix)
- install/migrating-claude: align with Hermes page structure (add Restart-and-verify Step, JSON output for automation, Troubleshooting AccordionGroup with 4 entries, cross-link to Hermes guide)
- cli/migrate: tighten intro to mention both bundled providers and link the migration hub
- docs.json: move Maintenance group to immediately after Install overview, nest the four migrating pages (migrating, migrating-claude, migrating-hermes, migrating-matrix) under a 'Migrating' subgroup so they collapse into a dropdown
2026-04-27 02:57:15 -07:00
Peter Steinberger
a0023f4978 fix(logging): redact URL query secrets 2026-04-27 10:56:47 +01:00
Peter Steinberger
1b581b4c71 fix(ci): stabilize live release validation 2026-04-27 10:56:35 +01:00
Peter Steinberger
e7432ae01d fix: redact URL query credentials in diagnostics 2026-04-27 10:55:22 +01:00
Peter Steinberger
d33eebd050 fix(cron): ignore delivered presentation warnings 2026-04-27 10:53:35 +01:00
VACInc
614a2846a2 fix: continue Google Live consult responses (#72189) (thanks @VACInc)
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
2026-04-27 10:52:00 +01:00
Peter Steinberger
8f262211ee docs(ollama): clarify qwen stability settings 2026-04-27 10:49:44 +01:00
Peter Steinberger
7dc9a367ef fix: avoid persisting proxy env in gateway services 2026-04-27 10:46:31 +01:00
Shakker
021ef1220d fix: reuse provider discovery plugin metadata 2026-04-27 10:46:09 +01:00
Peter Steinberger
c9e6f371e4 fix(memory-core): quiet request-scoped fallback 2026-04-27 10:45:55 +01:00
Peter Steinberger
dfe58a1b8e fix(agents): treat TUI client label as current session 2026-04-27 10:45:26 +01:00
ziyitan
27ee5c0098 fix(gateway): redact secrets in skills.update response (#69998)
Merged via squash.

Prepared head SHA: 61fc06f33f
Co-authored-by: Ziy1-Tan <49604965+Ziy1-Tan@users.noreply.github.com>
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Reviewed-by: @hxy91819
2026-04-27 17:45:16 +08:00
Peter Steinberger
16eae4b4b4 fix(memory-core): skip cleanup after narrative fallback 2026-04-27 10:44:21 +01:00
Val Alexander
14a27e11f7 feat(ui): show raw config pending changes
Adds a raw config pending-changes diff panel in Control UI raw mode, with JSON5 parsing, sensitive-value redaction until explicit reveal, bounded diff work, and tests for redaction/reveal and stale reveal-state reset.

Also aligns provider manifest contract coverage for google-vertex and Qwen aliases to unblock the rebased CI matrix.

Supersedes stale PRs #48621 and #46654. PR #48621 had gone stale without maintainer follow-up, so this maintainer-authored PR carries the implementation forward transparently while preserving changelog credit for the original contributor and @BunsDev.
2026-04-27 04:42:10 -05:00
Peter Steinberger
531a0ddfe4 fix(config): repair retired llm timeout key 2026-04-27 10:39:56 +01:00
Vincent Koc
a50edbdc60 fix(cli): keep nodes list aligned with nodes status (#72619)
* fix(cli): keep nodes list aligned with nodes status

* fix(clownfish): address review for ghcrawl-156588-autonomous-smoke (1)

* fix(cli): keep nodes list aligned with nodes status
2026-04-27 02:39:33 -07:00
Vincent Koc
af03f9248d docs(feishu): clarify @all is not a bot mention (b642ebece9)
Trace to b642ebece9 (fix(feishu): do not treat @all as a bot mention).
Document the new behavior in the mention requirement section: broadcast-only
@all/@_all messages no longer wake the bot, while messages that combine @all
with a direct bot mention still count as a bot mention.
2026-04-27 02:38:52 -07:00
Peter Steinberger
733aaa0117 docs(cli): disambiguate migration import headings 2026-04-27 10:38:47 +01:00
Peter Steinberger
e862e0acb5 fix(providers): guard self-hosted model discovery 2026-04-27 10:38:17 +01:00
Peter Steinberger
f9b78fb08e docs(models): clarify local tool call workaround 2026-04-27 10:37:52 +01:00
Vincent Koc
59fb5fd3a7 fix(mattermost): prevent DM replies from creating threads (#72659)
* fix(mattermost): prevent DM replies from creating threads

* fix(mattermost): prevent DM replies from creating threads

* fix(mattermost): prevent DM replies from creating threads
2026-04-27 02:37:47 -07:00
Peter Steinberger
72f7d7e4ea fix(gateway): scope plugin subagent cleanup ownership 2026-04-27 10:36:33 +01:00
Vincent Koc
600df95c8c feat(migrate): add Claude importer
Add a bundled Claude migration provider for Claude Code and Claude Desktop imports.\n\nIncludes source discovery, preview/apply behavior for instructions, MCP servers, skills and command prompts, archive/manual handling for unsafe Claude state, docs, labeler, and tests.
2026-04-27 02:35:44 -07:00
Peter Steinberger
cf499101a2 fix(agents): normalize Windows runtime imports (#72731)
* fix(agents): normalize Windows runtime imports

* test(providers): align manifest contract coverage
2026-04-27 10:34:25 +01:00
Peter Steinberger
8b85f2c163 test: align provider contract aliases 2026-04-27 10:33:56 +01:00
Peter Steinberger
1ee885123f docs(models): document required tool choice workaround 2026-04-27 10:32:20 +01:00
Shakker
7d9dc8cf24 fix: reuse plugin manifests for model pricing refresh 2026-04-27 10:25:41 +01:00
Peter Steinberger
3af34316f2 fix: preserve clawhub install selectors 2026-04-27 10:25:21 +01:00
Peter Steinberger
1b81f75654 docs(providers): document cerebras setup 2026-04-27 10:22:21 +01:00
Peter Steinberger
4de235f908 feat(providers): add cerebras plugin 2026-04-27 10:22:20 +01:00
Peter Steinberger
08a002d8ab docs: document npm-only plugin installs 2026-04-27 10:20:30 +01:00
Peter Steinberger
13f9deb619 fix: audit windows task managed env drift 2026-04-27 10:19:50 +01:00
Peter Steinberger
cb9955dd5c fix: support npm-only plugin installs 2026-04-27 10:16:59 +01:00
Peter Steinberger
e899b32e1d fix(agents): collapse local model timeout knobs 2026-04-27 10:16:50 +01:00
Peter Steinberger
67f1266fe8 fix: repair managed service env install migration 2026-04-27 10:13:01 +01:00
Vincent Koc
b642ebece9 fix(feishu): do not treat @all as a bot mention (#72658)
* fix(feishu): do not treat @all as a bot mention

* fix(feishu): do not treat @all as a bot mention
2026-04-27 02:10:17 -07:00
Val Alexander
14ab00755f feat(ui): display agent identities in session list
Display friendly agent identity labels in the Control UI Sessions key column when identity data is available, keep raw-key fallback behavior, and allow filtering by agent identity name.

This is the maintainer-owned replacement for #54212 by @dingtao416. Thanks @dingtao416 for the original feature idea and implementation direction.

Includes follow-up fixes from maintainer review automation: normalized key-cell classes, own-property identity lookup, and friendly-label tooltips.

Validation:
- pnpm test ui/src/ui/format.test.ts ui/src/ui/views/sessions.test.ts
- pnpm check:changed

Closes #54163.
Supersedes #54212.
2026-04-27 04:09:39 -05:00
Peter Steinberger
9f450dcf06 fix: reject malformed clawhub plugin specs 2026-04-27 10:08:27 +01:00
Samuel Rodda
6c252cc54c fix(update): require applied gateway restarts
Require Control UI updates to observe a real gateway process replacement, surface skipped/error update outcomes, and verify the running gateway version after restart.\n\nAdds update.status restart-sentinel plumbing, docs, generated protocol model updates, and changelog attribution.\n\nLocal verification:\n- pnpm test src/gateway/server-methods/update.test.ts src/cli/gateway-cli/run-loop.test.ts src/infra/restart-sentinel.test.ts src/infra/process-respawn.test.ts src/infra/update-runner.test.ts ui/src/ui/app-gateway.node.test.ts ui/src/ui/controllers/config.test.ts\n- git diff --check\n- pnpm exec oxfmt --check --threads=1 CHANGELOG.md docs/gateway/protocol.md docs/gateway/configuration.md docs/web/control-ui.md\n- pnpm docs:check-mdx
2026-04-27 04:07:43 -05:00
Peter Steinberger
b74f35ee6f refactor(plugins): move provider routing metadata to manifests 2026-04-27 10:06:30 +01:00
Peter Steinberger
57092a1794 ci: harden cross-os release harness on Windows 2026-04-27 10:03:38 +01:00
Peter Steinberger
3f895e5b49 test: dedupe hot unit fast coverage 2026-04-27 10:02:46 +01:00
Peter Steinberger
edbab0e2db fix: harden Google Live tool responses (#72426) (thanks @BsnizND) 2026-04-27 09:58:23 +01:00
BSnizND
409e762810 Fix Google Live tool response names 2026-04-27 09:58:23 +01:00
Peter Steinberger
b4b21cbc93 fix(browser): circuit-break managed launch failures 2026-04-27 09:58:14 +01:00
Vincent Koc
36a936af66 fix(update): add auto-update kill switch 2026-04-27 01:58:02 -07:00
Vincent Koc
caba05b94a fix(plugins): harden bundled install/uninstall sweep
Fix bundled plugin install/uninstall sweep coverage and avoid persisting invalid placeholder config for config-gated bundled plugins.
2026-04-27 01:57:40 -07:00
Peter Steinberger
7421112898 fix(agents): pass OpenAI SDK request timeouts 2026-04-27 09:55:39 +01:00
Peter Steinberger
cb45f16330 docs: clarify cron concurrency lanes 2026-04-27 09:54:58 +01:00
Peter Steinberger
04f76a8fdb test: remove duplicate plugin enable mock 2026-04-27 09:54:58 +01:00
Vincent Koc
b81eaf8a4e fix(agents): keep claude live streams valid 2026-04-27 01:53:37 -07:00
Peter Steinberger
6fddf17632 fix: accept clawhub plugin api wildcards 2026-04-27 09:48:01 +01:00
Peter Steinberger
6c8f0d04c3 test: trim unit-fast hotspots 2026-04-27 09:46:06 +01:00
Peter Steinberger
981cb89ea3 fix(agents): strip stale gemini assistant prefill 2026-04-27 09:41:37 +01:00
Peter Steinberger
a35ad200d1 test: shrink image sanitizer fixtures 2026-04-27 09:39:28 +01:00
Peter Steinberger
7d74c29dcc fix: isolate cron nested lane concurrency 2026-04-27 09:39:10 +01:00
Vincent Koc
231eb7b52a docs(migrating-hermes): note partial-apply guard introduced by 8bdfa58cbb
Trace to 8bdfa58cbb (fix(migrations): avoid partial Hermes config apply after
conflict). Hermes apply now marks remaining dependent config items as
"blocked by earlier apply conflict" when a conflict surfaces mid-apply,
instead of writing them partially. Document the user-visible reason string
and where to find blocked items in the migration report.
2026-04-27 01:38:49 -07:00
Peter Steinberger
f97cc58760 fix(browser): auto-start configured browser plugin 2026-04-27 09:37:10 +01:00
Shakker
e792f96a84 fix: cache capability provider manifest ids 2026-04-27 09:36:53 +01:00
Peter Steinberger
e21c909bd0 fix(agents): strip stale anthropic assistant prefill 2026-04-27 09:36:25 +01:00
Peter Steinberger
3be8e68898 test: dedupe fast lane imports 2026-04-27 09:35:41 +01:00
Vincent Koc
56ca4e2269 fix(daemon): handle sudo user-systemd gateway install failures
* fix(daemon): handle sudo user-systemd gateway install failures

* fix(daemon): harden sudo systemctl user scope

* fix(plugins): remove static type-cycle edges

* test(plugins): update bundle command config mock
2026-04-27 01:34:57 -07:00
Peter Steinberger
c25082f92e fix: apply cron concurrency to nested lane 2026-04-27 09:33:26 +01:00
Peter Steinberger
b9b15bec85 fix(ci): stabilize full validation probes 2026-04-27 09:30:53 +01:00
BsnizND
916eda16c1 fix(google-meet): keep tool sessions gateway-owned
Routes stateful Google Meet tool actions through the gateway-owned runtime so create/join/status/speak/leave share the same session owner instead of losing tool-created realtime sessions after the agent turn.

Also preserves structured gateway error details for missing session ids and tightens node-host child cleanup for already-closed sessions.

Fixes #72440.

Co-authored-by: BSnizND <199837910+BsnizND@users.noreply.github.com>
2026-04-27 09:28:14 +01:00
Peter Steinberger
b09afa2993 fix: keep auto model fallbacks pinned until reset 2026-04-27 09:27:19 +01:00
Peter Steinberger
a60f15c611 refactor(gateway): move model pricing policy to manifests 2026-04-27 09:26:53 +01:00
Vincent Koc
a494eea6d4 fix(gateway): defer hook request handler imports 2026-04-27 01:26:38 -07:00
Peter Steinberger
a95da5b52d fix(models): enrich local transport failure diagnostics 2026-04-27 09:25:38 +01:00
Peter Steinberger
c2d82b87ee test(plugins): mock registry contribution seam 2026-04-27 09:23:59 +01:00
Peter Steinberger
444acde1de feat: support layered plugin runtime deps 2026-04-27 09:21:25 +01:00
Peter Steinberger
9611260225 fix: retry primary after auto model fallback 2026-04-27 09:19:03 +01:00
Peter Steinberger
983bac7afa fix(plugins): keep registry lookup types acyclic 2026-04-27 09:16:43 +01:00
Peter Steinberger
3eb6a5b209 docs: format migration docs 2026-04-27 09:16:36 +01:00
Peter Steinberger
f9181835e8 fix(agents): warn on fake local tool calls 2026-04-27 09:14:59 +01:00
Shakker
51bd95fff3 fix: reuse extractor manifest resolution pass 2026-04-27 09:12:51 +01:00
Shakker
c60581740a fix: reuse manifest pass for runtime contract owners 2026-04-27 09:12:51 +01:00
Shakker
e547070ba9 fix: avoid repeated plugin metadata load for channel command defaults 2026-04-27 09:12:50 +01:00
Peter Steinberger
3913aa999d test: lighten fast lane imports 2026-04-27 09:12:17 +01:00
github-actions[bot]
b09345e3f6 chore(ui): refresh th control ui locale 2026-04-27 08:12:10 +00:00
Peter Steinberger
d76f924be3 fix(plugins): avoid registry barrel topology cycle 2026-04-27 09:09:31 +01:00
Peter Steinberger
5b616e2bec fix(agents): narrow session lock scope 2026-04-27 09:09:19 +01:00
Peter Steinberger
5ff49ae03e fix(gateway): skip local model pricing refreshes 2026-04-27 09:09:19 +01:00
bbddbb
563718c2e4 feat(control-ui): confirm dreaming restart changes
Require explicit confirmation before applying restart-impacting Dreaming mode changes in the Control UI.

- Add pending/confirm/loading state for the Dreaming toggle path
- Render a restart confirmation dialog before sending the config patch
- Sync Control UI locale metadata and cover the confirmation flow in browser tests

Fixes #63804
2026-04-27 03:08:59 -05:00
Peter Steinberger
276291d399 fix: hide bonjour Windows ARP shell probe 2026-04-27 09:08:40 +01:00
Peter Steinberger
8bdfa58cbb fix(migrations): avoid partial Hermes config apply after conflict 2026-04-27 09:07:59 +01:00
Vincent Koc
0055e404cf docs(hermes): rework CLI migrate page and add user-facing migration guide
- cli/migrate: convert flat reference into structured Mintlify page (Tip pointer, ParamField for flags, AccordionGroup for safety model, sub-sections for Hermes provider with what's imported, .env keys, archive-only state, and plugin contract)
- install/migrating-hermes: new dedicated user guide modeled after migrating-matrix.md (Tabs for onboarding vs CLI, AccordionGroup for what gets imported, Steps for recommended flow, Warning for --overwrite, Troubleshooting accordions)
- docs.json: add install/migrating-hermes to Maintenance group alongside migrating and migrating-matrix
2026-04-27 01:04:00 -07:00
Peter Steinberger
184b024fb6 test(migrate-hermes): keep config runtime stateful 2026-04-27 09:02:14 +01:00
Peter Steinberger
87b8072a85 test: cover qqbot channel guardrails 2026-04-27 08:58:13 +01:00
Peter Steinberger
f7081a3879 fix(lmstudio): trust configured local endpoints 2026-04-27 08:55:45 +01:00
Peter Steinberger
9510906669 fix: stop hook fallback after security blocks 2026-04-27 08:55:38 +01:00
Peter Steinberger
5a3d01e480 docs: format plugin sdk subpaths 2026-04-27 08:53:31 +01:00
Peter Steinberger
f21c8c3f0c test(migrate-hermes): use OpenClaw temp root 2026-04-27 08:53:27 +01:00
Peter Steinberger
58037cc89d fix: resolve browser playwright runtime deps 2026-04-27 08:50:56 +01:00
Peter Steinberger
c1d827844c test: speed up unit fast lane 2026-04-27 08:49:09 +01:00
Shakker
45b0d5ccc2 chore: add plugin lookup startup trace metrics 2026-04-27 08:48:18 +01:00
Shakker
bed76c26e7 fix: reuse lookup table for deferred plugin reload 2026-04-27 08:48:18 +01:00
Shakker
e068165036 docs: note plugin lookup reuse followups 2026-04-27 08:48:18 +01:00
Shakker
8b396bcfd2 docs: document plugin lookup table 2026-04-27 08:48:18 +01:00
Shakker
7c985890af refactor: reuse lookup table during gateway plugin load 2026-04-27 08:48:18 +01:00
Shakker
b2deb74694 fix: include setup cli backends in plugin lookup 2026-04-27 08:48:18 +01:00
Shakker
5228b24927 fix: avoid spread in provider owner lookup 2026-04-27 08:48:18 +01:00
Shakker
af29ccd98f fix: copy lookup startup plugin ids for gateway load 2026-04-27 08:48:18 +01:00
Shakker
f41126bc2e refactor: resolve contribution owners from lookup maps 2026-04-27 08:48:17 +01:00
Shakker
fbf0a29195 refactor: expand plugin lookup owner maps 2026-04-27 08:48:17 +01:00
Shakker
dc6ac472db refactor: use plugin lookup table for gateway load fallback 2026-04-27 08:48:17 +01:00
Shakker
123dee0513 fix: avoid duplicate plugin lookup diagnostics 2026-04-27 08:48:17 +01:00
Shakker
635af612d5 refactor: expose plugin lookup table normalizer 2026-04-27 08:48:17 +01:00
Shakker
354eb37ff5 refactor: reuse manifest registry for plugin id normalization 2026-04-27 08:48:17 +01:00
Shakker
b8c9426911 refactor: reuse plugin lookup table for contribution owners 2026-04-27 08:48:17 +01:00
Shakker
e985acbc1c docs: note plugin startup lookup table 2026-04-27 08:48:17 +01:00
Shakker
3f38d3af88 refactor: add plugin lookup table 2026-04-27 08:48:17 +01:00
Peter Steinberger
66f4b52db3 fix(docker): route local provider setup to host gateway 2026-04-27 08:46:33 +01:00
Alex Knight
4e19bc80c9 Fix null params for parameterless tools (#72673)
* fix tool null params for parameterless schemas

* guard composite required tool schemas
2026-04-27 17:45:59 +10:00
Vincent Koc
f4ca0612b2 Merge branch 'main' of https://github.com/openclaw/openclaw
* 'main' of https://github.com/openclaw/openclaw:
  docs: point maintainer triage at gitcrawl
  fix: clean runtime deps backup owner marker
  test(browser): close hanging attach-only sockets
  fix(plugins): normalize windows override imports
  fix: preserve live runtime deps temp dirs
  fix(lmstudio): promote bracketed tool calls
  Add Google Meet realtime consult agentId (#72381)
  fix: normalize lazy service override imports
  test: split ui unit tests from generic lane
  feat(migrations): add plugin-owned Hermes import
  fix(ci): expose package deps to Telegram QA harness (#72680)
  fix: hide bundled runtime npm windows
2026-04-27 00:44:30 -07:00
Vincent Koc
0286bb9817 docs: point maintainer triage at gitcrawl
Update the OpenClaw PR maintainer skill to use gitcrawl for local triage commands.
2026-04-27 00:43:07 -07:00
Peter Steinberger
84929bf85b fix: clean runtime deps backup owner marker 2026-04-27 08:43:03 +01:00
Peter Steinberger
bfdee5fa72 test(browser): close hanging attach-only sockets 2026-04-27 08:40:25 +01:00
Peter Steinberger
15e634d50c fix(plugins): normalize windows override imports 2026-04-27 08:39:42 +01:00
Peter Steinberger
4514a73170 fix: preserve live runtime deps temp dirs 2026-04-27 08:39:35 +01:00
Peter Steinberger
da55212c6e fix(lmstudio): promote bracketed tool calls 2026-04-27 08:38:53 +01:00
BsnizND
d5e6abcb3d Add Google Meet realtime consult agentId (#72381)
Remote proof:
- CI run 24982271745 passed on 6122e13c9f.
- Blacksmith Testbox tbx_01kq6vwehcszjfpp52f0pb3v1q passed focused Google Meet formatting, docs/link checks, realtime consult runtime tests, Google Meet tests, extension test typecheck, the core-unit-fast-support shard, and the core support boundary shard.

Thanks @BsnizND.

Co-authored-by: BSnizND <199837910+BsnizND@users.noreply.github.com>
2026-04-27 08:36:59 +01:00
Vincent Koc
29f4cdfcbb docs: point maintainer triage at gitcrawl 2026-04-27 00:36:32 -07:00
Peter Steinberger
f6db86f9a0 fix: normalize lazy service override imports 2026-04-27 08:35:45 +01:00
Peter Steinberger
98e7242b53 test: split ui unit tests from generic lane 2026-04-27 08:35:04 +01:00
Vincent Koc
1fc5b2b703 feat(migrations): add plugin-owned Hermes import
* feat: add migration providers

* feat: offer Hermes migration during onboarding

* feat(hermes): map imported config surfaces

* feat(onboard): require fresh migration imports

* docs(cli): clarify Hermes import coverage

* chore(migrations): rename Hermes importer package

* chore(migrations): rewire Hermes importer id

* fix(migrations): redact migration JSON details

* fix(hermes): use provider runtime for config imports

* test(hermes): cover missing source planning

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-04-27 00:34:29 -07:00
Vincent Koc
75c52b6c41 fix(ci): expose package deps to Telegram QA harness (#72680)
* fix(ci): expose package deps to telegram QA harness

* fix(ci): link QA package runtime deps

* fix(agents): guard replay metadata in empty retries

* fix(ci): keep plugin update smoke migration-stable
2026-04-27 00:33:29 -07:00
Peter Steinberger
d23ee2f702 fix: hide bundled runtime npm windows 2026-04-27 08:31:07 +01:00
Peter Steinberger
720ea766e6 fix(release): stabilize release validation probes 2026-04-27 08:27:45 +01:00
Vincent Koc
3200378ab4 fix(gateway): defer hook agent runner imports 2026-04-27 00:26:55 -07:00
Peter Steinberger
556c3e87df fix(agents): strip Gemma reasoning from local replay 2026-04-27 08:26:28 +01:00
Peter Steinberger
f427ddc220 fix(cli): keep update completion refresh lightweight 2026-04-27 08:24:26 +01:00
Vincent Koc
1ee893bc5f fix(gateway): defer http auth imports 2026-04-27 00:22:36 -07:00
Peter Steinberger
735890d955 fix(agents): dedupe subagent startup task 2026-04-27 08:20:45 +01:00
Peter Steinberger
daf8e14874 docs: fix msteams federated auth anchor 2026-04-27 08:20:20 +01:00
Peter Steinberger
708d833a76 test(ui): reuse ui test module environment 2026-04-27 08:19:24 +01:00
Peter Steinberger
646a268d27 fix: stage mirrored logger runtime deps 2026-04-27 08:17:18 +01:00
Peter Steinberger
729147dcb5 fix(cron): start isolated timeout after execution begins 2026-04-27 08:15:59 +01:00
Peter Steinberger
45778a840d fix(lmstudio): allow keyless local onboarding 2026-04-27 08:15:17 +01:00
Peter Steinberger
37cd6027cf test(gateway): harden session event setup timeout 2026-04-27 08:11:11 +01:00
Peter Steinberger
d1a8e2b17c docs: note vitest serial flag 2026-04-27 08:08:09 +01:00
Peter Steinberger
ec9b20263c fix(docker): expose QA harness exports for package Telegram 2026-04-27 08:07:27 +01:00
Vincent Koc
5333b1e2cc fix(gateway): defer channel runtime imports 2026-04-27 00:07:13 -07:00
Peter Steinberger
49ce7fe90c test: cover slack bolt auth verification suppression 2026-04-27 08:03:38 +01:00
Vincent Koc
baace37fef docs: sentence-case sweep across 10 more pages
- tools/plugin: Package Entrypoints
- tools/code-execution: How To Use It
- tools/browser-linux-troubleshooting: Root Cause, Config Reference
- install/bun: Lifecycle Scripts
- nodes/audio: Mention Detection in Groups
- nodes/images: Inbound Media to Commands (Pi)
- platforms/android: Connection Runbook
- plugins/building-plugins: Beta Release Testing
- web/control-ui: Content Security Policy
- security/THREAT-MODEL-ATLAS: Framework Attribution
2026-04-27 00:03:18 -07:00
Peter Steinberger
9d33da6ddf fix(agents): sanitize blank Bedrock user replay 2026-04-27 08:03:02 +01:00
Peter Steinberger
3d6d08116d fix(release): expose QA package exports in Telegram acceptance 2026-04-27 08:02:23 +01:00
Vincent Koc
2a17abcf5d docs: sentence-case sweep across 4 more pages
- platforms/digitalocean: Cost Comparison (2026), Oracle Cloud Free Alternative
- gateway/remote-gateway-readme: Quick Setup, How It Works
- cli/crestodian: Setup Bootstrap
- plugins/codex-harness: Computer Use
(Brand-named headings preserved across azure.md, web.md, firecrawl.md, clawhub.md, config-channels.md, security/index.md.)
2026-04-27 00:01:52 -07:00
Vincent Koc
6a03b76c9a docs: full-page sentence-case sweep across 6 pages
- platforms/oracle: Cost Comparison (2026), Still Recommended, Verify Security Posture
- install/exe-dev: Automated Install with Shelley, Remote Access
- platforms/mac/dev-setup: Build Fails: Toolchain or SDK Mismatch, App Crashes on Permission Grant
- reference/AGENTS.default: What OpenClaw Does, Core Skills, Usage Notes
- install/docker: Containerized Gateway, Agent Sandbox
- ci: Package Acceptance, Job Overview, Local Equivalents
2026-04-27 00:00:31 -07:00
Vincent Koc
89230f2480 fix(gateway): defer mcp loopback imports 2026-04-27 00:00:04 -07:00
Peter Steinberger
090063bd43 fix(ci): harden cron and Docker validation 2026-04-27 07:59:28 +01:00
Vincent Koc
41268ded2d docs: full-page sentence-case sweep across 5 worst-offender pages
- channels/msteams: 8 H2/H3 (Federated Authentication, Local Development, Known Limitations, Reply Style, Presentation Cards, Private Channels, etc.)
- auth-credential-semantics: 4 H2 (Stable Probe Reason Codes, Token Credentials, Explicit Auth Order Filtering, Probe Target Resolution)
- tools/browser: preserve brand-named headings (Browserless, WebSocket CDP, Chrome MCP, Control API, Brave); minor cleanup
- security/CONTRIBUTING-THREAT-MODEL: 4 H2/H3 (What We Use, Risk Levels, Review Process; Threat IDs preserved as branded label)
- gateway/multiple-gateways: 4 H2 (Best Recommended Setup, Why This Works, General Multi-Gateway Setup, Isolation Checklist)
2026-04-26 23:58:35 -07:00
Peter Steinberger
f89d0f7c53 fix(cron): preserve telegram direct thread inference 2026-04-27 07:58:03 +01:00
Vincent Koc
ca9a04b271 docs: full-page readability pass on 5 worst-offender pages
- automation/standing-orders: sentence-case all H2/H3 headings (Why Standing Orders, Anatomy, Execute-Verify-Report Pattern, Multi-Program Architecture, Best Practices, etc.) and clean up Related link styling
- platforms/raspberry-pi: sentence-case 10 headings (Hardware Requirements, Performance Optimizations, Cost Comparison, etc.)
- install/fly: sentence-case troubleshooting and Private Deployment headings
- pi-dev: drop 'this guide summarizes' filler, sentence-case 4 H2 headings, restore brand-cased Pi
- concepts/model-providers: sentence-case Kimi Coding (other brand-named providers preserved as-is: Google Gemini, Google Vertex, Kilo Gateway, Volcano Engine)
2026-04-26 23:57:10 -07:00
Peter Steinberger
37d37d3779 fix(cron): tolerate legacy flat schedule identity 2026-04-27 07:56:26 +01:00
Peter Steinberger
53f536b368 fix: avoid slack startup auth rejection leak 2026-04-27 07:55:57 +01:00
Peter Steinberger
725938f0f5 test: avoid heavy registry imports in web provider tests 2026-04-27 07:53:09 +01:00
Vincent Koc
d43bf6de0a docs: batch sentence-case headings across high-Title-Case offenders
- pi.md: 9 H2 + 14 H3 (Package Dependencies, File Structure, Tool Pipeline, etc.)
- cli/hooks.md: 6 H2 (List All Hooks, Get Hook Information, etc.)
- plugins/message-presentation.md: 8 H2 (Producer Examples, Renderer Contract, etc.)
- plan/ui-channels.md: 7 H2 (Non Goals, Target Model, Refactor Steps, etc.)
- install/ansible.md: 6 H2 + 1 H3 (What You Get, Quick Start, etc.)

Mintlify anchor generation prefers sentence case for predictable URLs.
2026-04-26 23:52:28 -07:00
Peter Steinberger
18b6c3bb61 fix(docker): recognize current gateway readiness logs 2026-04-27 07:51:59 +01:00
Vincent Koc
4a30ae182b fix(gateway): defer embedded runner imports 2026-04-26 23:51:08 -07:00
Vincent Koc
69c30e37d9 fix(memory-lancedb): skip processed auto-capture messages safely (#72663) 2026-04-26 23:51:04 -07:00
Peter Steinberger
9ced682a9d fix(cron): omit disabled delivery trace errors 2026-04-27 07:50:50 +01:00
Peter Steinberger
4db1faaafc chore: install discord clawd skill 2026-04-27 07:50:15 +01:00
Peter Steinberger
c754370100 docs: document maintainer testbox opt-in 2026-04-27 07:49:28 +01:00
BsnizND
2785be2604 Fix Google Meet realtime interruption playback (#72524)
Fixes #72523.

Remote proof:
- CI run 24980529154 passed on 29f825bea5.
- Blacksmith Testbox tbx_01kq6tsgbaxgstxmtearwy9n4w passed focused formatting, Google Meet tests, Google realtime provider tests, and extension test typecheck.

Thanks @BsnizND.

Co-authored-by: BSnizND <199837910+BsnizND@users.noreply.github.com>
2026-04-27 07:49:10 +01:00
Peter Steinberger
8811112ab3 fix(release): stabilize full validation lanes 2026-04-27 07:46:44 +01:00
Peter Steinberger
ddcd9d62c4 fix(cron): invalidate stale external schedule slots 2026-04-27 07:46:08 +01:00
Peter Steinberger
3173842913 fix: keep staged plugin mirrors idempotent 2026-04-27 07:44:15 +01:00
Peter Steinberger
566295cd34 fix: materialize stale runtime mirror symlinks 2026-04-27 07:42:47 +01:00
Peter Steinberger
04be516926 fix(gateway): keep liveness probes independent of config load 2026-04-27 07:42:14 +01:00
Peter Steinberger
7559845597 fix(ollama): avoid implicit native num_ctx override 2026-04-27 07:42:14 +01:00
Vincent Koc
c4194b8345 docs(voice-call): note SecretRef support for twilio.authToken and tts.providers.*.apiKey
Trace to db09f68ce5 (Support SecretRef for voice-call credentials and bundled
plugin SecretInputs #72607). The reference page docs/reference/secretref-credential-surface.md
listed the new entries in the same SHA, but docs/plugins/voice-call.md showed
only plain-string credentials without pointing to the SecretRef surface.
2026-04-26 23:39:51 -07:00
Vincent Koc
015f7dc747 fix(agents): refresh bootstrap snapshot when workspace files change (#72406)
* fix(agents): refresh bootstrap snapshot when workspace files change

* fix(clownfish): address review for ghcrawl-207042-agentic-merge (1)
2026-04-26 23:39:33 -07:00
Peter Steinberger
c110f8c028 fix(docker): stabilize bundled channel release lanes 2026-04-27 07:37:28 +01:00
BsnizND
f2a17b2991 Fix Google Meet chrome-node bridge cleanup (#72372)
Fixes #72371.

Remote proof:
- CI run 24980121791 passed on d583a6b615.
- Blacksmith Testbox tbx_01kq6t5jk2f51gxq30j9veyjhy passed focused Google Meet formatting and tests.

Thanks @BsnizND.

Co-authored-by: BSnizND <199837910+BsnizND@users.noreply.github.com>
2026-04-27 07:37:18 +01:00
Vincent Koc
5c591a4e13 fix(test): build missing Docker images in Testbox 2026-04-26 23:33:43 -07:00
Peter Steinberger
67e6410e0f ci: accept legacy bundled docker lane 2026-04-27 07:31:18 +01:00
Peter Steinberger
4bca42d933 fix(cron): alert on persistent skipped runs 2026-04-27 07:31:04 +01:00
Vincent Koc
b246c06fa5 fix(daemon): surface systemd user-bus hints during gateway install (#72617) 2026-04-26 23:30:54 -07:00
Vincent Koc
dcff28d285 fix(telegram): hide acknowledged failed-tool warnings from chat (#72410)
* fix(telegram): hide acknowledged failed-tool warnings from chat

* fix(clownfish): address review for ghcrawl-207034-agentic-merge (1)

* fix(clownfish): address review for ghcrawl-207034-agentic-merge (1)
2026-04-26 23:29:19 -07:00
Peter Steinberger
ca44ab65e6 ci(release): allow live E2E actions reads 2026-04-27 07:26:33 +01:00
Peter Steinberger
9313554a8d test: stabilize matrix block streaming prompt 2026-04-27 07:25:52 +01:00
Peter Steinberger
edf43dfc88 ci: fix update channel package version probe 2026-04-27 07:23:23 +01:00
Peter Steinberger
cf04fa24d8 test(openai): prefer exact live registry models 2026-04-27 07:22:26 +01:00
Bek
aac83e00cf fix: Slack inbound thread session routing (#72498)
Normalize actionable Slack thread roots and follow-up replies onto the same thread parent session key.
2026-04-27 02:19:27 -04:00
Peter Steinberger
93ac2cefaa ci(docker): resolve short refs before checkout 2026-04-27 07:18:57 +01:00
Peter Steinberger
a3fcb8db79 ci(docker): split bundled release lanes 2026-04-27 07:17:14 +01:00
Josh Avant
db09f68ce5 Support SecretRef for voice-call credentials and bundled plugin SecretInputs (#72607)
* fix: support voice-call secretrefs

* test: classify plugin secretref targets

* docs: credit voice-call secretref change

* fix: keep plugin secret target discovery lightweight
2026-04-27 01:16:50 -05:00
Vincent Koc
ab237fe7b0 fix(gateway): defer chat startup helpers 2026-04-26 23:15:26 -07:00
Peter Steinberger
1dac448ff0 fix: wait for qa gateway restart boundary 2026-04-27 07:13:39 +01:00
Vincent Koc
1427c3a78d fix(sessions_spawn): tolerate ACP-only fields for subagent runtime (#72331) 2026-04-26 23:11:42 -07:00
Peter Steinberger
44a504cd39 ci: time-box package acceptance legacy compat 2026-04-27 07:11:14 +01:00
Vincent Koc
e6d2c9b080 fix(process): decode Windows command output with console codepage awareness (#72393)
* fix(process): decode Windows command output with console codepage awareness

* fix(clownfish): address review for ghcrawl-199248-agentic-merge (1)
2026-04-26 23:10:59 -07:00
Peter Steinberger
5cc06c69a9 fix(discord): preserve explicit delivery target kind 2026-04-27 07:09:45 +01:00
Peter Steinberger
ca67762b88 fix(image): honor media timeouts 2026-04-27 07:09:36 +01:00
Peter Steinberger
19cb9ca6bf fix: materialize staged plugin runtime chunks 2026-04-27 07:08:44 +01:00
Vincent Koc
8440f67935 fix(gateway): defer chat event imports 2026-04-26 23:07:05 -07:00
Peter Steinberger
6175309c01 fix: normalize openai legacy image sizes 2026-04-27 07:05:56 +01:00
Vincent Koc
b1812387a0 fix(agent): harden empty attempt retry handling 2026-04-26 23:04:40 -07:00
Josh Avant
b3d9948c4c fix: use runtime snapshot for TTS SecretRefs (#72581)
* fix: use runtime snapshot for tts secrets

* fix: keep tts secret snapshot selection local

* docs: add tts secretref changelog entry
2026-04-27 01:02:17 -05:00
Peter Steinberger
ac5a1d1622 ci: forward package acceptance live secrets 2026-04-27 07:00:11 +01:00
Peter Steinberger
2a6fab9d22 docs: point release evidence at public checks 2026-04-27 06:57:47 +01:00
Vincent Koc
c7d77f8c7b fix(gateway): defer plugin HTTP dispatch 2026-04-26 22:55:26 -07:00
Peter Steinberger
32aa631e19 test: relax matrix block streaming qa timeout 2026-04-27 06:54:43 +01:00
Peter Steinberger
8de02c318b fix: reclaim orphan session write locks 2026-04-27 06:54:43 +01:00
Peter Steinberger
e962381dbf ci: fix plugin update smoke quoting 2026-04-27 06:50:59 +01:00
Vincent Koc
b02cca4e00 fix(gateway): trim startup imports 2026-04-26 22:48:31 -07:00
Alex Knight
06b3e4ef8a Fail invalid plugin registration gates loudly (#72577)
* fix plugin registration gate failures
2026-04-27 15:46:50 +10:00
Peter Steinberger
85148f3b20 refactor(cron): split notification routing 2026-04-27 06:44:53 +01:00
Peter Steinberger
4b9c85776d ci: allow package plugin metadata migrations 2026-04-27 06:42:14 +01:00
Vincent Koc
6bbb1b79e1 fix(doctor): treat gateway memory probe timeout as inconclusive (#72618) 2026-04-26 22:40:26 -07:00
Peter Steinberger
45bdfb5f72 ci(docker): keep release path at three chunks 2026-04-27 06:39:46 +01:00
Vincent Koc
60d4d5e1fa fix(daemon): reconcile macOS LaunchAgent supervision state (#72616) 2026-04-26 22:39:15 -07:00
Peter Steinberger
8c2f894d3a docs(ollama): expand setup recipes 2026-04-27 06:37:49 +01:00
Josh Avant
510718bedf fix(runtime): resolve web search SecretRefs from snapshots (#72563) 2026-04-27 00:35:21 -05:00
Peter Steinberger
332cdd7aca fix(cron): route failure alerts via target session 2026-04-27 06:34:38 +01:00
Peter Steinberger
422fa99197 fix(models): honor provider context defaults 2026-04-27 06:32:24 +01:00
Peter Steinberger
5e9a96fafb ci(docker): reuse cached e2e images for reruns 2026-04-27 06:29:09 +01:00
Peter Steinberger
679e476183 ci: always shard full Matrix QA 2026-04-27 06:28:35 +01:00
Vincent Koc
3d59e8192b fix(cli): restore help registration and descriptor graph 2026-04-26 22:26:59 -07:00
Peter Steinberger
02dae3e1d1 ci: fix telegram package acceptance harness 2026-04-27 06:26:44 +01:00
Peter Steinberger
835c6bc0c1 ci: tolerate legacy package acceptance metadata 2026-04-27 06:26:08 +01:00
Peter Steinberger
52249927ac fix(ollama): skip localhost discovery for remote providers 2026-04-27 06:24:43 +01:00
Peter Steinberger
b94ad7c9d8 fix(ollama): retry non-visible reasoning turns 2026-04-27 06:19:22 +01:00
Peter Steinberger
32b1f0ce74 ci: narrow package acceptance to artifact lanes 2026-04-27 06:17:05 +01:00
Peter Steinberger
1ea12fe3e2 fix: stage bundled plugin runtime deps safely 2026-04-27 06:16:26 +01:00
Vincent Koc
6038725501 docs: batch convert remaining prose callouts to Mintlify components
- platforms/android: blockquote Note for Android app status, Note for canvas host port
- platforms/macos: Tip component for app vs CLI discovery comparison
- plugins/zalouser, channels/zalouser: blockquote Warning components for unofficial automation risk
- channels/pairing: convert two Important paragraphs to Note components for DM-vs-group scope and silent-upgrade behavior
2026-04-26 22:15:11 -07:00
Vincent Koc
a108169127 fix(gateway): lazy-load setup wizard runtime 2026-04-26 22:12:46 -07:00
Vincent Koc
5bba899a70 docs: batch fix filler Note/page openers and one TUI auth Warning
- gateway/authentication: tighten model-provider Note opener
- help/debugging: drop 'this page covers' filler
- reference/session-management-compaction: rephrase end-to-end intro
- reference/transcript-hygiene: drop 'this document describes' filler
- web/index: collapse 'this page focuses' filler
- web/tui: convert prose --url Note to Warning component
2026-04-26 22:12:17 -07:00
Vincent Koc
9df7fe3986 docs: fix live docs callout formatting 2026-04-26 22:08:22 -07:00
Vincent Koc
5c3e2a6b44 docs: batch fix filler openings across providers, platforms, install, tools, and pi
- platforms/mac/dev-setup: sentence-case heading and direct opener
- tools/browser-wsl2-windows-remote-cdp-troubleshooting: collapse three-bullet split-host setup into one direct sentence
- install/migrating-matrix: drop 'this page covers' filler
- providers/perplexity-provider: rephrase Note opener
- pi: drop 'this document describes' filler
2026-04-26 22:07:47 -07:00
Vincent Koc
51dbda3f3d docs(automation+start): batch fix filler openings and prose Tip
- start/openclaw: workspace-as-memory Tip component
- automation/tasks: drop 'this page covers' filler in Note
- automation/auth-monitoring, clawflow, cron-vs-heartbeat: collapse 'this page moved... See X' redirects to single direct sentences
2026-04-26 22:04:56 -07:00
Peter Steinberger
488a1ee146 fix(cron): preserve silent tool results 2026-04-27 06:04:27 +01:00
Vincent Koc
a167e687ce docs: fix live docs CI 2026-04-26 22:04:16 -07:00
Peter Steinberger
2dcc4605d4 fix(llm-task): normalize provider-prefixed model overrides 2026-04-27 06:02:16 +01:00
Vincent Koc
05ebfa4146 docs(help+tools): batch convert prose callouts to Mintlify components
- testing-live: Tip components for model-discovery and authoritative-list guidance
- debugging: --dev flag Note and non-dev gateway stop Tip
- testing: narrowing live tests Tip
- tools/lobster: optional-plugin allowlist Note
- tools/acp-agents-setup: blockquote Important to Warning component
2026-04-26 22:01:55 -07:00
Peter Steinberger
86da88c120 ci: request release evidence after full validation 2026-04-27 06:01:06 +01:00
Vincent Koc
9624d81bb3 docs(install): batch convert callouts and sentence-case headings
- macos-vm: download-time Note component
- hetzner: community-maintained Note component
- exe-dev: stateful-VM Tip component
- development-channels: parallel clones Tip component
- migrating: sentence-case top heading and section headings, replace bullet -- separators with em-dashes, drop 'this guide' filler
2026-04-26 21:59:42 -07:00
Peter Steinberger
751c7f32a5 fix(cli): preserve Matrix QA profile flag 2026-04-27 05:57:37 +01:00
Vincent Koc
6c49039a23 docs(gateway): batch convert callouts and fix JSON5 smart quotes
- security/index: 3 prose callouts (Note/Warning) for remote credential rules, sandbox scope, elevated mode
- tailscale: loopback Note component
- pairing: bulleted Important warning to Warning component
- openshell: host-edit warning to Warning component
- local-models: replace 13 smart quotes inside the LM Studio JSON5 example so it parses
2026-04-26 21:56:59 -07:00
Vincent Koc
91e835ebe0 docs(concepts): batch readability and Mintlify component pass
- memory: replace en-dash list separators with em-dashes, sentence-case Further reading link titles
- messages: rewrite filler 'this page ties together' opener to a direct one
- delegate-architecture: convert 4 blockquote security warnings to Warning and Note components
- system-prompt: convert blockquote daily-memory note to Note component
2026-04-26 21:54:23 -07:00
Peter Steinberger
5d5c37775e fix(ollama): estimate usage when counters are omitted 2026-04-27 05:54:03 +01:00
Peter Steinberger
377553e41a ci: link package deps for telegram acceptance 2026-04-27 05:52:13 +01:00
Gustavo Madeira Santana
241d0cb88e chore(docs): dedupe and simplify matrix docs 2026-04-27 00:52:04 -04:00
Vincent Koc
dc8b881c11 fix(gateway): defer startup runtime imports 2026-04-26 21:50:50 -07:00
Vincent Koc
f4129cdd2b docs(channels): batch convert prose callouts to Mintlify components
- msteams: 5 callouts (Note/Warning) for preview status, devtunnel auth, group policy, multi-tenant deprecation, user-prefix targeting
- slack: replyToMode threading note
- whatsapp: dms vs direct prompt override note
- group-messages: mentionPatterns cross-channel note
- signal: signal-cli main session de-auth warning
2026-04-26 21:49:56 -07:00
Vincent Koc
6908bd3167 docs(cli): batch readability pass for 5 CLI pages
- channels: convert Tip prose to component, fix /channels/index link, sentence-case heading
- configure: convert Note and Tip prose to components
- devices: convert Note and Warning prose to components
- models: sentence-case scan/status subheadings
- agents: clean up related links and Title Case body link
2026-04-26 21:47:29 -07:00
Peter Steinberger
7564af24e6 fix(providers): preserve configured model input modalities 2026-04-27 05:46:53 +01:00
Peter Steinberger
748daa4857 ci: make package acceptance legacy-safe 2026-04-27 05:46:06 +01:00
Peter Steinberger
6987132aed ci: add Matrix QA profiles 2026-04-27 05:43:14 +01:00
Peter Steinberger
382e03a2d8 fix(cron): fail isolated runs on run-level errors 2026-04-27 05:42:59 +01:00
Peter Steinberger
390b965460 docs: document release evidence workflow 2026-04-27 05:40:21 +01:00
Vincent Koc
edbcfe1a1d docs(agents): keep testbox policy out of root rules 2026-04-26 21:39:23 -07:00
Vincent Koc
e2ecf292bc docs(doctor): document models.providers.api migration and stale-enum skip
Add the legacy `models.providers.*.api: "openai"` → `"openai-completions"`
migration to doctor's Current migrations list, and note the gateway startup
behavior that skips providers with future or unknown api enum values instead
of failing closed.

Traces to:
- 6a7980e984 fix(doctor): migrate legacy OpenAI provider api
- 147f4f50f5 fix(gateway): skip stale model provider api entries
2026-04-26 21:39:00 -07:00
Peter Steinberger
fd06aeac04 test(docker): fixture ClawHub plugin smoke 2026-04-27 05:38:27 +01:00
Vincent Koc
f83e424a5d docs: fix onboarding docs formatting 2026-04-26 21:33:58 -07:00
Vincent Koc
0eac6432c3 docs: fix docs formatting drift 2026-04-26 21:29:38 -07:00
Vincent Koc
ebbc7dcfeb docs(updating): group advanced npm topics in AccordionGroup 2026-04-26 21:29:03 -07:00
Vincent Koc
8cd68487d9 docs(remote): rename numbered headings and use Note components 2026-04-26 21:29:03 -07:00
Vincent Koc
4519b29419 docs(update): convert flow steps to Steps component 2026-04-26 21:29:02 -07:00
Vincent Koc
c881d8da48 docs(sandbox): replace bold-callout patterns with Note and Tip components 2026-04-26 21:29:02 -07:00
Vincent Koc
00300b85d0 docs(onboard): convert related-guides to CardGroup and group flow notes 2026-04-26 21:29:01 -07:00
Peter Steinberger
7c0fdae9b9 docs(providers): document local model request timeout 2026-04-27 05:27:41 +01:00
Gustavo Madeira Santana
e0956a0853 fix(cli): skip startup work for positional help 2026-04-27 00:24:06 -04:00
Vincent Koc
9c07579a95 docs(testbox): align maintainer testbox mode 2026-04-26 21:23:28 -07:00
Vincent Koc
166a6d9088 docs(feishu): convert blockquote callouts to Note components 2026-04-26 21:22:58 -07:00
Vincent Koc
5a88d8502f docs(gateway): split lifecycle notes accordion 2026-04-26 21:22:57 -07:00
Vincent Koc
4db066d102 docs(ollama): restructure auth rules and fix duplicate card titles 2026-04-26 21:22:57 -07:00
Vincent Koc
3f1ce689a1 docs(compaction): dedupe sections and consolidate config 2026-04-26 21:22:57 -07:00
Vincent Koc
d4bb4912fc docs(cron): regroup notes into themed sections 2026-04-26 21:22:56 -07:00
Peter Steinberger
02455c0c52 ci: include telegram in release package acceptance 2026-04-27 05:14:19 +01:00
Peter Steinberger
d857989111 docs: clarify package acceptance release role 2026-04-27 05:13:41 +01:00
Vincent Koc
4c3c3abe1a fix(cli): keep startup help metadata on fast path 2026-04-26 21:11:23 -07:00
Vincent Koc
716b3faf7e Revert "docs(agents): document testbox maintainer workflow"
This reverts commit 4340cb74c2.
2026-04-26 21:10:09 -07:00
Vincent Koc
3e95927df7 Merge branches 'main' and 'main' of https://github.com/openclaw/openclaw
* 'main' of https://github.com/openclaw/openclaw:
  docs: explain telegram package artifact testing
  ci: let telegram e2e use package artifacts
  docs: explain release validation entrypoints
  ci: tolerate legacy qa inventory entries
  ci(testbox): save build artifact cache before wait
  fix: allow heavyweight docker lanes at low parallelism
  test(docker): use packaged gateway expect-final smoke
  test(live): accept current Codex status text

* 'main' of https://github.com/openclaw/openclaw:
  docs: explain telegram package artifact testing
  ci: let telegram e2e use package artifacts
  docs: explain release validation entrypoints
  ci: tolerate legacy qa inventory entries
  ci(testbox): save build artifact cache before wait
  fix: allow heavyweight docker lanes at low parallelism
  test(docker): use packaged gateway expect-final smoke
  test(live): accept current Codex status text
2026-04-26 21:09:46 -07:00
Peter Steinberger
cc79f4982c docs: explain telegram package artifact testing 2026-04-27 05:09:17 +01:00
Peter Steinberger
09107e0b7f ci: let telegram e2e use package artifacts 2026-04-27 05:09:16 +01:00
Peter Steinberger
720ab99307 docs: explain release validation entrypoints 2026-04-27 05:07:22 +01:00
Peter Steinberger
0ff0c7ce57 ci: tolerate legacy qa inventory entries 2026-04-27 05:07:15 +01:00
Vincent Koc
a33a2c97a3 ci(testbox): save build artifact cache before wait 2026-04-26 21:07:02 -07:00
Vincent Koc
4cc572a813 ci(testbox): save build artifact cache before wait 2026-04-26 21:06:29 -07:00
Peter Steinberger
3c8760f16d fix: allow heavyweight docker lanes at low parallelism 2026-04-27 05:04:52 +01:00
Peter Steinberger
940f67e524 test(docker): use packaged gateway expect-final smoke 2026-04-27 05:01:36 +01:00
Vincent Koc
ef828d55af test(live): accept current Codex status text
Accept current Codex harness status prose while still requiring the OpenClaw status shape, active model, and live harness session.
2026-04-26 21:01:22 -07:00
Vincent Koc
9626ef274a ci(testbox): add build artifact cache warmup 2026-04-26 20:58:14 -07:00
Val Alexander
5e8cb77e79 Polish Control UI quick settings layout
Polish the Control UI quick settings dashboard layout.

- Rework quick settings into a 12-column desktop grid with matched top-row card heights.
- Pair Personal with a right-side Appearance/Automations stack on large screens while preserving tablet/mobile ordering.
- Add render/style guards plus an Unreleased changelog entry crediting @BunsDev.

Validated with focused UI tests, formatting, git diff checks, local changed gate, and full PR CI.
2026-04-26 22:56:35 -05:00
Val Alexander
461c10bb51 feat(onboard): support non-interactive GitHub Copilot token auth
Add manifest-owned GitHub Copilot token support for non-interactive onboarding, including documented env fallback, ref-mode tokenRef storage, saved-profile reuse, and default model wiring that preserves existing primary model configuration.

Validation:
- pnpm test extensions/github-copilot/index.test.ts src/plugins/contracts/registry.contract.test.ts src/commands/onboard-non-interactive/local/auth-choice-inference.test.ts
- pnpm check:changed
- CI green on aadac2c8d4
2026-04-26 22:56:20 -05:00
Peter Steinberger
18b76e3995 fix(ollama): scope request timeouts to providers 2026-04-27 04:55:11 +01:00
joshavant
6b6f8ab1aa Revert "fix: resolve tts secret refs for local infer (#72549)"
This reverts commit 4878d3e059.
2026-04-26 22:54:08 -05:00
Peter Steinberger
36c08e0288 test(docker): keep web search smoke on one gateway connection 2026-04-27 04:51:55 +01:00
Peter Steinberger
6590e0e872 docs: expand release validation runbook 2026-04-27 04:50:51 +01:00
Vincent Koc
4340cb74c2 docs(agents): document testbox maintainer workflow 2026-04-26 20:49:56 -07:00
Peter Steinberger
5f9506f7fd ci: avoid inherited package acceptance secrets 2026-04-27 04:44:29 +01:00
Gustavo Madeira Santana
e1cdaa3c88 docs(matrix): note E2EE setup improvements 2026-04-26 23:42:32 -04:00
Gustavo Madeira Santana
2b40416314 test(matrix): speed up CLI metadata entry test 2026-04-26 23:40:53 -04:00
Gustavo Madeira Santana
3b74b913e3 fix(matrix): avoid device cleanup sync races 2026-04-26 23:40:52 -04:00
Gustavo Madeira Santana
99159f89da fix(matrix): stabilize e2ee qa flows 2026-04-26 23:40:52 -04:00
Peter Steinberger
02d266c6c4 ci: split package acceptance refs 2026-04-27 04:39:19 +01:00
Ayaan Zaidi
34f81c6a8a docs(changelog): note model provider api recovery 2026-04-27 09:07:31 +05:30
Ayaan Zaidi
147f4f50f5 fix(gateway): skip stale model provider api entries 2026-04-27 09:07:31 +05:30
Ayaan Zaidi
6a7980e984 fix(doctor): migrate legacy OpenAI provider api 2026-04-27 09:07:31 +05:30
Vincent Koc
831f03b814 fix(cli): speed up gateway status config reads 2026-04-26 20:34:49 -07:00
Peter Steinberger
b0c70786fd fix(cron): preserve structured denial failures 2026-04-27 04:34:38 +01:00
Peter Steinberger
e6eea6cfe2 docs: clarify package acceptance npm selection 2026-04-27 04:34:13 +01:00
Peter Steinberger
67650c4c0a fix(ollama): resolve custom local provider auth 2026-04-27 04:33:18 +01:00
Vincent Koc
f60378519c test(plugins): cover bundled dependency edge cases 2026-04-26 20:31:54 -07:00
Josh Avant
4878d3e059 fix: resolve tts secret refs for local infer (#72549) 2026-04-26 22:31:39 -05:00
Peter Steinberger
6a05b9eec5 ci: fix package acceptance permissions 2026-04-27 04:27:45 +01:00
Peter Steinberger
2c092a0eff docs: document release validation test workflows 2026-04-27 04:27:07 +01:00
Peter Steinberger
76de167ca1 ci: add package acceptance workflow 2026-04-27 04:25:31 +01:00
jnuyao
2a08848dd1 feat(feishu): display group names in session labels
Resolve Feishu group chat labels through getChatInfo so session labels prefer human-readable group names over raw chat IDs.\n\nPreserve topic/thread label priority and defer the lookup until after broadcast dedup claims to avoid duplicate account API calls.\n\nValidation:\n- pnpm test extensions/feishu/src/bot-group-name.test.ts extensions/feishu/src/bot.broadcast.test.ts\n- pnpm check:changed\n- GitHub CI green on c154dc0a41fd715dce95ef1fb5d0c269533b8c22\n\nCloses #35675
2026-04-26 22:22:51 -05:00
Peter Steinberger
d3fd275aa5 test: cover gateway wrapper persistence in docker e2e 2026-04-27 04:15:33 +01:00
Peter Steinberger
6c1cffa7f8 ci: fix targeted live model provider run 2026-04-27 04:08:16 +01:00
Peter Steinberger
e0141946b2 ci: allow targeted live model providers 2026-04-27 04:04:38 +01:00
Peter Steinberger
cbbd860ef9 test(docker): isolate installer smoke sessions 2026-04-27 04:01:46 +01:00
Peter Steinberger
9bd4200f3c docs: prefer targeted test reruns 2026-04-27 04:00:05 +01:00
Peter Steinberger
a72522d05d test: prefer glm 5 in live sweeps 2026-04-27 03:56:16 +01:00
Peter Steinberger
313a19c940 fix(ollama): scope auth to local hosts 2026-04-27 03:54:12 +01:00
Peter Steinberger
29af4add2a feat: trigger compaction for oversized transcripts 2026-04-27 03:46:11 +01:00
Vincent Koc
d5063d5b16 fix(telegram): avoid materializing tool-progress drafts
Address Clownfish follow-up on Telegram native draft finalization. Requires real streamed assistant partials before materializing drafts, clears stale native draft previews, and keeps media/buttons on normal send path.
2026-04-26 19:43:23 -07:00
Peter Steinberger
6d0e84aadb test(docker): skip bootstrap ritual in install smoke 2026-04-27 03:41:47 +01:00
Peter Steinberger
ef31a333f7 docs: add gateway wrapper install examples 2026-04-27 03:40:32 +01:00
Peter Steinberger
0b3f13b337 fix: preserve wrapper env during gateway reinstall 2026-04-27 03:40:32 +01:00
Peter Steinberger
9f9bd41f40 fix: persist gateway service wrappers 2026-04-27 03:40:32 +01:00
Peter Steinberger
414fd41a1f fix(ollama): avoid timing out active model pulls 2026-04-27 03:40:28 +01:00
Peter Steinberger
8b27c489f5 test: bound openai websocket live e2e 2026-04-27 03:39:24 +01:00
Vincent Koc
f39f4629d9 docs(changelog): credit update fixture repair
Add the missing Unreleased changelog credit for the Docker update-channel fixture repair.
2026-04-26 19:38:07 -07:00
Peter Steinberger
348728c28c fix(providers): bound native fetch timeouts 2026-04-27 03:33:51 +01:00
Peter Steinberger
dc78d58448 fix(ollama): honor baseURL provider aliases 2026-04-27 03:28:23 +01:00
Vincent Koc
ae89d44760 chore(plugin-sdk): refresh api baseline 2026-04-26 19:24:37 -07:00
Vincent Koc
ead76f61d8 fix(cli): skip plugin preload for plugin updates 2026-04-26 19:24:37 -07:00
Vincent Koc
a5f6603e61 fix(release): clarify control ui build requirement 2026-04-26 19:24:37 -07:00
Vincent Koc
a313c4db92 chore(config): refresh bundled channel metadata 2026-04-26 19:24:36 -07:00
Peter Steinberger
b72c0bdfad ci: force gemini api key auth in acp bind 2026-04-27 03:23:00 +01:00
Peter Steinberger
bd42f35097 fix(ui): show configured thinking defaults 2026-04-27 03:21:49 +01:00
Peter Steinberger
90ad79cbcd test(docker): generate update fixture ui asset 2026-04-27 03:13:51 +01:00
Peter Steinberger
0b46227d6c fix(ollama): keep configured max thinking compatible 2026-04-27 03:13:15 +01:00
Peter Steinberger
1882a8e5ea fix: refresh preflight rotated runs 2026-04-27 03:12:45 +01:00
Vincent Koc
f5f4f514d8 docs(changelog): backfill gateway memory fixes 2026-04-26 19:11:13 -07:00
Vincent Koc
0c30d0d0b8 fix(gateway): resolve configured thinking default in session rows (#72324)
* fix(gateway): resolve configured thinking default in session rows

* fix(gateway): preserve model thinking precedence
2026-04-26 19:10:21 -07:00
Peter Steinberger
de0ece20d1 test: accept live release validation variance 2026-04-27 03:08:29 +01:00
Peter Steinberger
aa071e0b60 fix(ollama): forward native model params 2026-04-27 03:08:11 +01:00
Peter Steinberger
f4cf7e3b4f test(docker): recreate update fixture ui asset after install 2026-04-27 03:06:07 +01:00
Peter Steinberger
2dba9e6a76 fix(ollama): honor configured num_ctx params 2026-04-27 03:02:24 +01:00
Peter Steinberger
fc3abc139b fix(cron): classify denied isolated runs 2026-04-27 03:01:55 +01:00
Peter Steinberger
22c9e82e83 test(docker): track update fixture control ui asset 2026-04-27 02:58:24 +01:00
Vincent Koc
8c2bc951a9 fix(plugins): hydrate bundled channel config metadata
Hydrate bundled channel schema metadata through opt-in registry schema paths while keeping ordinary manifest registry loads lightweight.
2026-04-26 18:58:04 -07:00
Peter Steinberger
c45a7d7a7a ci: use available macOS release runner 2026-04-27 02:56:19 +01:00
Vincent Koc
b96a75c95b fix(gateway): scope memory runtime plugin loading 2026-04-26 18:54:59 -07:00
Peter Steinberger
20b71e18b2 test(docker): seed update fixture control ui asset 2026-04-27 02:50:48 +01:00
Peter Steinberger
9b79eef750 fix(memory-core): honor configured index concurrency 2026-04-27 02:47:39 +01:00
Vincent Koc
988cb1ebfe fix(test): stabilize restart sentinel mocks 2026-04-26 18:45:13 -07:00
Vincent Koc
3e020a1650 fix(memory-lancedb): force float embedding encoding (#72391) 2026-04-27 02:43:31 +01:00
Peter Steinberger
5176dba8a0 test(docker): stub update fixture lint preflight 2026-04-27 02:43:15 +01:00
Peter Steinberger
d8c1140235 ci: fix full release validation gh repo context 2026-04-27 02:36:20 +01:00
Peter Steinberger
69daef8246 fix: honor Ollama Modelfile num_ctx discovery 2026-04-27 02:32:30 +01:00
Shadow
3f59cd0a09 Adjust message for stale workflow 2026-04-26 20:31:00 -05:00
pashpashpash
90de4bd855 fix: address successor transcript review follow-ups
Fixes the post-merge review follow-ups from #72471 by deduping stale pre-compaction state entries and preserving parent-before-child ordering for successor transcripts.
2026-04-26 18:27:38 -07:00
Vincent Koc
6a5ecb955c refactor(plugins): drop provider discovery alias 2026-04-26 18:19:05 -07:00
Vincent Koc
eed7b13b62 fix(doctor): scope bundled runtime deps to active plugins 2026-04-26 18:17:56 -07:00
Peter Steinberger
efec8a4a84 docs: note Vitest cache race footgun 2026-04-27 02:17:02 +01:00
Peter Steinberger
bf08dc2ed6 test(docker): fix packaged docker harness lanes 2026-04-27 02:13:56 +01:00
Peter Steinberger
110fa97f2a fix: repair release validation follow-up checks 2026-04-27 02:09:40 +01:00
Peter Steinberger
8c18df02f3 docs: update Ollama fix changelog 2026-04-27 02:08:01 +01:00
Peter Steinberger
e28ad0f84f fix: list configured provider models 2026-04-27 02:08:01 +01:00
Peter Steinberger
c6617c3155 fix: silence Ollama memory doctor key warning 2026-04-27 02:08:00 +01:00
Peter Steinberger
1316ca9aa8 fix: gate Ollama ambient discovery 2026-04-27 02:08:00 +01:00
Peter Steinberger
acfa9877b3 fix: parse Ollama tool call arguments 2026-04-27 02:07:59 +01:00
Peter Steinberger
6a20c83cf7 docs: clarify Ollama web search auth 2026-04-27 02:07:59 +01:00
Peter Steinberger
f0b758fba2 test(docker): stub package-derived update fixture builds 2026-04-27 02:07:29 +01:00
pashpashpash
b99540964c Fix compaction rotation follow-ups 2026-04-26 18:06:57 -07:00
Vincent Koc
b9c7a4306b fix(ci): declare Lobster Ajv runtime dependency 2026-04-26 18:04:46 -07:00
Peter Steinberger
658240de74 ci: add full release validation workflow 2026-04-27 02:02:34 +01:00
Vincent Koc
67d00826b2 fix(gateway): bound Lobster Ajv schema compilation 2026-04-26 17:57:59 -07:00
Peter Steinberger
3c95327b34 Fix compacted session transcript rotation 2026-04-26 17:51:00 -07:00
Vincent Koc
0a117b5960 test(plugins): guard persisted status replay 2026-04-26 17:47:41 -07:00
Peter Steinberger
ddac6f73e5 fix(approvals): accept allowlist metadata 2026-04-27 01:46:30 +01:00
Peter Steinberger
ffbb4d4ae7 test(docker): fix update preflight fixture patches 2026-04-27 01:43:55 +01:00
Peter Steinberger
3937d16c44 fix(exec): fallback when node lacks run prepare 2026-04-27 01:43:03 +01:00
Peter Steinberger
b109c1f99c ci: limit node 22 compatibility to manual ci 2026-04-27 01:39:32 +01:00
Peter Steinberger
92c1924d27 ci: remove duplicate extension fast lane 2026-04-27 01:36:45 +01:00
Peter Steinberger
acd1bd7d31 fix(exec): skip node approval prepare in yolo mode 2026-04-27 01:27:58 +01:00
Peter Steinberger
11e17793e1 ci: include node22 compat in manual full ci 2026-04-27 01:27:27 +01:00
Peter Steinberger
90b3cdb6a7 test(docker): fix update fixture pnpm patch config 2026-04-27 01:25:00 +01:00
Peter Steinberger
7ca2f9fed5 test(docker): align package harness image 2026-04-27 01:22:58 +01:00
Vincent Koc
732a5842ee fix(gateway): defer implicit qmd memory startup 2026-04-26 17:21:50 -07:00
Vincent Koc
d7c173b694 fix(gateway): harden macOS launchd service startup 2026-04-26 17:18:49 -07:00
Peter Steinberger
6fed787297 test: align release boundary expectations 2026-04-27 01:16:15 +01:00
Vincent Koc
7cecbe1002 test(plugins): guard cold status snapshots
Add a reusable cold plugin fixture and status snapshot guard proving read-only plugin metadata paths do not import plugin runtime entries.
2026-04-26 17:15:39 -07:00
Peter Steinberger
0f672dcc73 fix(ollama): align web search endpoint routing 2026-04-27 01:10:41 +01:00
Peter Steinberger
b825c8d34b test: fix full ci suite follow-ups 2026-04-27 01:10:32 +01:00
Peter Steinberger
3b514ad5f3 test(docker): run mounted harnesses with image tsx 2026-04-27 01:05:20 +01:00
Peter Steinberger
82b928232e test(docker): stabilize package update lanes 2026-04-27 01:02:36 +01:00
Peter Steinberger
30d9e70988 test(gateway): stabilize session cleanup gates 2026-04-27 01:02:13 +01:00
Peter Steinberger
a3e0674261 fix(ollama): harden native provider routing 2026-04-27 01:02:13 +01:00
Peter Steinberger
be56f172ab fix: scope qmd root memory collection 2026-04-27 01:01:58 +01:00
2724 changed files with 110010 additions and 20117 deletions

View File

@@ -16,6 +16,19 @@ warm caches, local build state, and fast feedback.
Testbox is the expensive path. Reach for it deliberately.
OpenClaw maintainers can opt into Testbox-first validation by setting
`OPENCLAW_TESTBOX=1` in their environment or standing agent rules. This mode is
maintainers-only and requires Blacksmith access.
When `OPENCLAW_TESTBOX=1` is set in OpenClaw:
- Pre-warm a Testbox early for longer, wider, or uncertain work.
- Prefer Testbox for `pnpm` gates, e2e, package-like proof, and broad suites.
- Reuse the same Testbox ID for every run command in the same task/session.
- Use local commands only when the task explicitly sets
`OPENCLAW_LOCAL_CHECK_MODE=throttled|full`, or when the user asks for local
proof.
## Install the CLI
If `blacksmith` is not installed, install it:
@@ -81,7 +94,8 @@ Prefer Testbox when:
- you are reproducing CI-only failures
- you need the exact workflow image/job environment from GitHub Actions
For OpenClaw specifically, normal local iteration should stay local:
For OpenClaw specifically, normal local iteration stays local unless maintainer
Testbox mode is enabled with `OPENCLAW_TESTBOX=1`:
- `pnpm check:changed`
- `pnpm test:changed`
@@ -89,27 +103,49 @@ For OpenClaw specifically, normal local iteration should stay local:
- `pnpm test:serial`
- `pnpm build`
Only use Testbox in OpenClaw when the user explicitly wants CI-parity or the
check truly depends on remote secrets/services that the local repo loop cannot
provide.
If `OPENCLAW_TESTBOX=1` is enabled, run those same repo commands inside the
warm Testbox. If the user wants laptop-friendly local proof for one command, use
the explicit escape hatch `OPENCLAW_LOCAL_CHECK_MODE=throttled`.
For installable-package product proof, prefer the GitHub `Package Acceptance`
workflow over an ad hoc Testbox command. It resolves one package candidate
(`source=npm`, `source=ref`, `source=url`, or `source=artifact`), uploads it as
`package-under-test`, and runs the reusable Docker E2E lanes against that exact
tarball on GitHub/Blacksmith runners. Use `workflow_ref` for the trusted
workflow/harness code and `package_ref` for the source ref to pack when testing
an older trusted branch, tag, or SHA.
## Setup: Warmup before coding
If you decided Testbox is actually warranted, warm one up early. This returns
an ID instantly and boots the CI environment in the background while you work:
If you decided Testbox is warranted, warm one up early. This returns an ID
instantly and boots the CI environment in the background while you work:
blacksmith testbox warmup ci-check-testbox.yml
# → tbx_01jkz5b3t9...
Save this ID. You need it for every `run` command.
For OpenClaw maintainer Testbox mode, pre-warm at the start of longer or wider
tasks:
blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90
Use the build-artifact warmup when e2e/package/build proof benefits from seeded
`dist/`, `dist-runtime/`, and build-all caches:
blacksmith testbox warmup ci-build-artifacts-testbox.yml --ref main --idle-timeout 90
Warmup dispatches a GitHub Actions workflow that provisions a VM with the
full CI environment: dependencies installed, services started, secrets
injected, and a clean checkout of the repo at the default branch.
In OpenClaw, raw commit SHAs are not reliable dispatch refs for `warmup --ref`;
use a branch or tag. The build-artifact workflow resolves `openclaw@beta` and
`openclaw@latest` to SHA cache keys internally.
Options:
--ref <branch> Git ref to dispatch against (default: repo's default branch)
--ref <branch|tag> Git ref to dispatch against (default: repo's default branch)
--job <name> Specific job within the workflow (if it has multiple)
--idle-timeout <min> Idle timeout in minutes (default: 30)
@@ -226,6 +262,11 @@ services, CI-only runners, or reproducibility against the workflow image.
If the repo says local tests/builds are the normal path, follow the repo.
OpenClaw maintainer exception: if `OPENCLAW_TESTBOX=1` is set by the user or
agent environment, treat Testbox as the normal validation path for this repo.
Use `OPENCLAW_LOCAL_CHECK_MODE=throttled|full` as the explicit local escape
hatch.
## When to use
Use Testbox when:
@@ -242,12 +283,13 @@ checks that need parity or remote state.
## Workflow
1. Decide whether the repo's local loop is the right default.
2. Only if Testbox is warranted, warm up early:
`blacksmith testbox warmup ci-check-testbox.yml` → save the ID
1. Decide whether the repo's local loop is the right default. For OpenClaw,
`OPENCLAW_TESTBOX=1` makes Testbox the maintainer default.
2. If Testbox is warranted, warm up early:
`blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90` → save the ID
3. Write code while the testbox boots in the background.
4. Run the remote command when needed:
`blacksmith testbox run --id <ID> "npm test"`
`blacksmith testbox run --id <ID> "pnpm check:changed"`
5. If tests fail, fix code and re-run against the same warm box.
6. If you changed dependency manifests (package.json, etc.), prepend
the install command: `blacksmith testbox run --id <ID> "npm install && npm test"`
@@ -268,9 +310,9 @@ Observed full-suite time on Blacksmith Testbox is about 3-4 minutes:
- 173-180s on a warmed box
- 219s on a fresh 32-vCPU box
When validating before commit/push, run `pnpm check:changed` first when
appropriate, then the full suite with the profile above if broad confidence is
needed.
When validating before commit/push in maintainer Testbox mode, run
`pnpm check:changed` inside the warmed box first when appropriate, then the full
suite with the profile above if broad confidence is needed.
## Examples
@@ -324,12 +366,14 @@ timeout is reached). Default timeout is 5m; use `--wait-timeout` for longer
blacksmith testbox stop --id <ID>
Testboxes automatically shut down after being idle (default: 30 minutes).
If you need a longer session, increase the timeout at warmup time:
If you need a longer session, increase the timeout at warmup time. For OpenClaw
maintainer work, use 90 minutes for long-running sessions:
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 60
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 90
blacksmith testbox warmup ci-build-artifacts-testbox.yml --idle-timeout 90
## With options
blacksmith testbox warmup ci-check-testbox.yml --ref main
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 60
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 90
blacksmith testbox run --id <ID> "go test ./..."

View File

@@ -0,0 +1,37 @@
---
name: discord-clawd
description: Use to talk to the Discord-backed OpenClaw agent/session; not for archive search.
---
# Discord Clawd
Use this when the task is to talk with the Discord-backed agent/session, ask it a question, or post through that route.
For Discord archive/history/search, use `$discrawl` instead.
## Transport
Use the OpenClaw relay helper:
```bash
cd ~/Projects/agent-scripts
python3 skills/openclaw-relay/scripts/openclaw_relay.py targets
python3 skills/openclaw-relay/scripts/openclaw_relay.py resolve --target maintainers
```
If the target alias exists, prefer a private ask first:
```bash
python3 skills/openclaw-relay/scripts/openclaw_relay.py ask \
--target maintainers \
--message "Reply with exactly OK."
```
Use `publish` when the session should decide whether to post. Use `force-send` only when the user explicitly wants a message posted.
## Guardrails
- Resolve the target before sending real content.
- Report the target and delivery mode used.
- Do not use this for local Discord archive queries.
- Do not expose gateway tokens or session secrets.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Discord Clawd"
short_description: "Talk to the Discord-backed OpenClaw agent"
default_prompt: "Use $discord-clawd to route a private ask or explicit post through the Discord-backed OpenClaw agent/session."

View File

@@ -7,20 +7,20 @@ description: Review, triage, close, label, comment on, or land OpenClaw PRs/issu
Use this skill for maintainer-facing GitHub workflow, not for ordinary code changes.
## Start issue and PR triage with ghcrawl
## Start issue and PR triage with gitcrawl
- Anytime you inspect OpenClaw issues or PRs, check local `ghcrawl` data first for related threads, duplicate attempts, and already-landed fixes.
- Use `ghcrawl` for candidate discovery and clustering; use `gh`, `gh api`, and the current checkout to verify live state before commenting, labeling, closing, or landing.
- If `ghcrawl` is missing, stale, lacks the target thread, or has no embeddings for neighbor/search commands, fall back to the GitHub search workflow below.
- Do not run expensive/update commands such as `ghcrawl refresh`, `ghcrawl embed`, or `ghcrawl cluster` unless the user asked to update the local store or the stale data is blocking the decision.
- Anytime you inspect OpenClaw issues or PRs, check local `gitcrawl` data first for related threads, duplicate attempts, and already-landed fixes.
- Use `gitcrawl` for candidate discovery and clustering; use `gh`, `gh api`, and the current checkout to verify live state before commenting, labeling, closing, or landing.
- If `gitcrawl` is missing, stale, lacks the target thread, or has no embeddings for neighbor/search commands, fall back to the GitHub search workflow below.
- Do not run expensive/update commands such as `gitcrawl sync --include-comments`, future enrichment commands, or broad reclustering unless the user asked to update the local store or stale data is blocking the decision.
Common read-only path:
```bash
ghcrawl threads openclaw/openclaw --numbers <issue-or-pr-number> --include-closed --json
ghcrawl neighbors openclaw/openclaw --number <issue-or-pr-number> --limit 12 --json
ghcrawl search openclaw/openclaw --query "<scope or title keywords>" --mode hybrid --json
ghcrawl cluster-detail openclaw/openclaw --id <cluster-id> --member-limit 20 --body-chars 280 --json
gitcrawl threads openclaw/openclaw --numbers <issue-or-pr-number> --include-closed --json
gitcrawl neighbors openclaw/openclaw --number <issue-or-pr-number> --limit 12 --json
gitcrawl search openclaw/openclaw --query "<scope or title keywords>" --mode hybrid --json
gitcrawl cluster-detail openclaw/openclaw --id <cluster-id> --member-limit 20 --body-chars 280 --json
```
## Apply close and triage labels correctly
@@ -75,7 +75,7 @@ ghcrawl cluster-detail openclaw/openclaw --id <cluster-id> --member-limit 20 --b
## Search broadly before deciding
- Prefer `ghcrawl` first. Then use targeted GitHub keyword search to verify gaps, live status, comments, and candidates not present in the local store.
- Prefer `gitcrawl` first. Then use targeted GitHub keyword search to verify gaps, live status, comments, and candidates not present in the local store.
- Use `--repo openclaw/openclaw` with `--match title,body` first when using `gh search`.
- Add `--match comments` when triaging follow-up discussion or closed-as-duplicate chains.
- Do not stop at the first 500 results when the task requires a full search.

View File

@@ -62,6 +62,24 @@ scenario through qa-channel, decodes the emitted protobuf spans, and verifies
the exported trace names and privacy contract. It does not require Opik,
Langfuse, or external collector credentials.
## Matrix live profiles
`pnpm openclaw qa matrix` defaults to the full `all` profile. Use explicit
profiles for faster CI/release proof:
```bash
OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS=3000 \
pnpm openclaw qa matrix --profile fast --fail-fast
```
- `fast`: release-critical transport contract, excluding generated image and
deep E2EE recovery inventory.
- `transport`, `media`, `e2ee-smoke`, `e2ee-deep`, `e2ee-cli`: sharded full
Matrix coverage.
- `QA-Lab - All Lanes` uses explicit `fast` Matrix on scheduled runs. Manual
dispatch keeps `matrix_profile=all` as the default and always shards that full
Matrix selection.
## QA credentials and 1Password
- Use `op` only inside `tmux` for QA secret lookup in this repo.

View File

@@ -324,12 +324,14 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
- install/update smoke against the published beta channel
- Docker install/update coverage that exercises the published beta package
- published npm Telegram proof: dispatch Actions > `NPM Telegram Beta E2E`
from `main` with `package_spec=openclaw@<beta-version>` and
`provider_mode=mock-openai`, and require success. This workflow is
from `main` with `source=npm`, `package_spec=openclaw@<beta-version>`,
and `provider_mode=mock-openai`, and require success. Before publishing,
use the same workflow with `source=ref` and `package_ref=<branch-or-sha>`
for focused tarball-backed Telegram preflight. This workflow is
maintainer-dispatched and intentionally has no `npm-release` approval gate;
`qa-live-shared` only supplies the shared QA secrets. This is the default
button path for installed-package onboarding, Telegram setup, and real
Telegram E2E against the published npm package.
`qa-live-shared` only supplies the shared QA secrets. The npm source is the
default button path for installed-package onboarding, Telegram setup, and
real Telegram E2E against the exact published npm package.
Use the local `pnpm test:docker:npm-telegram-live` lane with the matching
`OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC` and Convex CI env only as a fallback
or debugging path.
@@ -380,6 +382,27 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
retesting.
- If any required build, packaging step, or release workflow is red, do not say the release is ready.
## Record release evidence with npm provenance
- Every release validation evidence report should identify whether it matches a
published npm package. When dispatching `.github/workflows/full-release-validation.yml`
for a package that is already published or expected to be published before
evidence is finalized, pass `evidence_package_spec=openclaw@<version>`.
- When the post-publish Telegram npm lane is part of the same full validation,
also pass `npm_telegram_package_spec=openclaw@<version>` so the validation
proves the exact registry package, not only a branch/ref tarball.
- If a full validation was started before the npm package existed, regenerate
the private evidence after publish with
`openclaw/releases-private/.github/workflows/openclaw-release-evidence-from-full-validation.yml`
and pass `package_spec=openclaw@<version>`, the original
`full_validation_run_id`, and a human release id such as `YYYY.M.D`.
- Use SHA evidence ids for immutable debugging, but also create/update the
human stable evidence bucket (`evidence/YYYY.M.D/`) after stable publish so
maintainers can find the final npm/release proof quickly.
- Do not claim npm proof from a ref-backed or local tarball-backed run. Label
those as pre-publish package/tarball proof, and keep the npm registry proof
tied to `source=npm` or `package_spec=openclaw@<published-version>`.
## Use the right auth flow
- OpenClaw publish uses GitHub trusted publishing.
@@ -610,6 +633,11 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
`appcast.xml` artifact and do not update the shared production feed unless a
separate beta feed exists.
32. After publish, verify npm and the attached release artifacts.
33. After any beta or stable npm publish, ensure the private release evidence
report includes `package_spec=openclaw@<published-version>` and shows the
npm package match. If the original full validation omitted the package spec,
rerun the private evidence generation workflow with the same
`full_validation_run_id` and the published package spec.
## GHSA advisory work

View File

@@ -96,6 +96,171 @@ gh run view <run-id> --job <job-id> --log
- For cancelled same-branch runs, confirm whether a newer run superseded it.
- Fetch full logs only for failed or relevant jobs.
## GitHub Release Workflows
Use the smallest workflow that proves the current risk. The full umbrella is
available, but it is usually the last step after narrower proof, not the first
rerun after a focused patch.
### Full Release Validation
`Full Release Validation` (`.github/workflows/full-release-validation.yml`) is
the manual "everything before release" umbrella. It resolves a target ref, then
dispatches:
- manual `CI` for the full normal CI graph
- `OpenClaw Release Checks` for install smoke, cross-OS release checks, live and
E2E checks, Docker release-path suites, OpenWebUI, QA Lab, fast Matrix, and
Telegram release lanes
- optional post-publish Telegram E2E when a package spec is supplied
Run it only when validating an actual release candidate, after broad shared CI
or release orchestration changes, or when explicitly asked:
```bash
gh workflow run full-release-validation.yml \
--repo openclaw/openclaw \
--ref main \
-f ref=<branch-or-sha> \
-f provider=openai \
-f mode=both
```
Run the workflow itself from the trusted current ref, normally `--ref main`;
child workflows are dispatched from that same ref even when `ref` points at an
older release branch or tag. Full Release Validation has no separate child
workflow ref input; choose the trusted harness by choosing the workflow run ref.
If a full run is already active on a newer `origin/main`, prefer watching that
run over dispatching a duplicate. If you accidentally dispatch a stale duplicate,
cancel it and monitor the current run.
The child-dispatch jobs record the child run ids. The final
`Verify full validation` job re-queries those child runs and is the canonical
parent gate. If a child workflow failed but was later rerun successfully, rerun
only the failed parent verifier job; do not dispatch a new full umbrella unless
the release evidence is stale.
### Release Evidence
After release-candidate validation or before a release decision, record the
important run ids in the private `openclaw/releases-private` evidence ledger.
Use the manual `OpenClaw Release Evidence`
(`openclaw-release-evidence.yml`) workflow there. It writes durable summaries
under `evidence/<release-id>/` and commits:
- `release-evidence.md`
- `release-evidence.json`
- `index.json`
- `runs/<label>.json`
Use one run per line:
```text
full-release-validation openclaw/openclaw <run-id> blocking
package-acceptance openclaw/openclaw <run-id> blocking
release-checks openclaw/openclaw <run-id> blocking
```
Store summaries, run URLs, artifact metadata, timings, pass/fail state, and
short release-manager notes there. Do not store raw logs, provider
prompts/responses, channel transcripts, signing material, or secret-bearing
config in git; raw logs stay in Actions artifacts.
When `Full Release Validation` completes and
`OPENCLAW_RELEASES_PRIVATE_DISPATCH_TOKEN` is configured in the public repo, it
requests the private `OpenClaw Release Evidence From Full Validation` workflow.
That private workflow reads the parent full-validation run, extracts the child
CI/release-checks/Telegram run ids from the parent logs, and opens the evidence
PR automatically. If the token is absent or the run predates this wiring, trigger
that private workflow manually with the full-validation run id.
### Release Checks
`OpenClaw Release Checks` (`openclaw-release-checks.yml`) is the release child
workflow. It is broader than normal CI but narrower than the umbrella because it
does not dispatch the separate full normal CI child. It runs Package Acceptance
with artifact-native delta lanes and `telegram_mode=mock-openai`, so the release
package tarball also goes through offline plugin proof, bundled-channel compat,
and Telegram package QA. The Docker release-path chunks cover the overlapping
package/update/plugin lanes. Use it when release-path validation is needed
without rerunning the entire umbrella.
```bash
gh workflow run openclaw-release-checks.yml \
--repo openclaw/openclaw \
--ref main \
-f ref=<branch-or-sha> \
-f provider=openai \
-f mode=both
```
### QA Lab Matrix Profiles
`pnpm openclaw qa matrix` defaults to `--profile all`. Do not assume the CLI
default is the fast release path. Use explicit profiles:
- `--profile fast`: release-critical Matrix transport contract; add
`--fail-fast` only when the target CLI supports it
- `--profile transport|media|e2ee-smoke|e2ee-deep|e2ee-cli`: sharded full
Matrix proof
- `OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS=3000`: CI-friendly no-reply quiet
window when paired with fast or sharded gates
`QA-Lab - All Lanes` uses explicit fast Matrix on scheduled runs; manual
dispatch keeps `matrix_profile=all` as the default and always shards that full
Matrix selection. `OpenClaw Release Checks` uses explicit fast Matrix; run the
all-lanes workflow when release investigation needs full Matrix media/E2EE
inventory.
### Reusable Live/E2E Checks
`OpenClaw Live And E2E Checks (Reusable)`
(`openclaw-live-and-e2e-checks-reusable.yml`) is the preferred entry point for
targeted live, Docker, model, and E2E proof. Inputs let you turn off unrelated
lanes:
```bash
gh workflow run openclaw-live-and-e2e-checks-reusable.yml \
--repo openclaw/openclaw \
--ref main \
-f ref=<sha> \
-f include_repo_e2e=false \
-f include_release_path_suites=false \
-f include_openwebui=false \
-f include_live_suites=true \
-f live_models_only=true \
-f live_model_providers=fireworks
```
Useful knobs:
- `docker_lanes='<lane[,lane]>'`: run selected Docker scheduler lanes against
prepared artifacts instead of the three release chunks.
- `include_live_suites=false`: skip live/provider suites when testing Docker
scheduler or release packaging only.
- `live_models_only=true`: run only Docker live model coverage.
- `live_model_providers=fireworks` (or comma/space separated providers): run one
targeted Docker live model job instead of the full provider matrix.
- blank `live_model_providers`: run the full live-model provider matrix.
When live suites are enabled, the workflow shards broad native `pnpm test:live`
coverage through `scripts/test-live-shard.mjs` instead of one serial `live-all`
job:
- `native-live-src-agents`
- `native-live-src-gateway`
- `native-live-test`
- `native-live-extensions-a-k`
- `native-live-extensions-l-z`
Use `node scripts/test-live-shard.mjs <shard> --list` to see the exact files
before rerunning a failed native live shard.
For model-list or provider-selection fixes, use `live_models_only=true` plus the
specific `live_model_providers` allowlist. Confirm logs show the expected
`OPENCLAW_LIVE_PROVIDERS` and selected model ids before declaring proof.
## Docker
Docker is expensive. First inspect the scheduler without running Docker:
@@ -131,16 +296,133 @@ Multiple lanes are allowed:
docker_lanes: install-e2e bundled-channel-update-acpx
```
That skips the three chunk matrix and runs one targeted Docker job against the
prepared GHCR images and a fresh OpenClaw npm tarball for the selected ref.
Reruns usually need that new tarball because the fix being tested changed the
package contents even if the SHA-tagged GHCR Docker image can be reused.
That skips the release chunk matrix and runs one targeted Docker job against the
prepared GHCR images and the selected package artifact. Rerun commands
generated inside GitHub artifacts include `package_artifact_run_id`,
`package_artifact_name`, `docker_e2e_bare_image`, and
`docker_e2e_functional_image` when available, so failed lanes can reuse the
exact tarball and prepared images from the failed run. When the fix changes
package contents, omit those reuse inputs so the workflow packs a new tarball.
Live-only targeted reruns skip the E2E images and build only the live-test
image. Release-path normal mode remains max three Docker chunk jobs:
image. Release-path normal mode fans out into four Docker chunk jobs:
- `core`
- `package-update`
- `plugins-integrations`
- `plugins-runtime`
- `bundled-channels`
OpenWebUI is folded into `plugins-runtime` for full release-path coverage and
keeps a standalone `openwebui` chunk only for OpenWebUI-only dispatches. The
legacy `plugins-integrations` chunk still works as an aggregate alias for manual
reruns, but the release workflow uses the split chunks so plugin runtime checks
and bundled-channel checks can run on separate machines. The bundled-channel
runtime-dependency coverage inside `bundled-channels`
uses the split `bundled-channel-*` and `bundled-channel-update-*` lanes rather
than the serial `bundled-channel-deps` lane, so failures produce cheap targeted
reruns for the exact channel/update scenario. The bundled plugin
install/uninstall sweep is also split into
`bundled-plugin-install-uninstall-0` through
`bundled-plugin-install-uninstall-7`; selecting the legacy
`bundled-plugin-install-uninstall` lane expands to all eight shards.
## Package Acceptance
Use the manual `Package Acceptance` workflow when the question is "does this
installable package work as a product?" rather than "does this source diff pass
Vitest?"
In release validation, treat Package Acceptance as the package-candidate shard
inside the larger release umbrella, not as a competing full-test path. Full
Release Validation and private release gauntlets should call Package Acceptance
for tarball resolution, Docker product/package proof, and optional Telegram QA
against the same resolved `package-under-test` artifact; keep orchestration,
secret policy, blocking/advisory status, and evidence rollup in the caller.
Good defaults:
```bash
gh workflow run package-acceptance.yml --ref main \
-f source=npm \
-f workflow_ref=main \
-f package_spec=openclaw@beta \
-f suite_profile=product \
-f telegram_mode=mock-openai
```
Npm candidate selection:
- Resolve the registry immediately before dispatch:
`npm view openclaw dist-tags --json --prefer-online --cache /tmp/openclaw-npm-cache-verify-$$`
and `npm view openclaw@beta version dist.tarball dist.integrity --json --prefer-online --cache /tmp/openclaw-npm-cache-verify-$$`.
- If Peter asks for "latest beta", use `source=npm` with
`package_spec=openclaw@beta`, then record the resolved version from `npm view`
or the workflow summary.
- For reruns, release proof, or comparing one known package, prefer the exact
immutable spec: `package_spec=openclaw@YYYY.M.D-beta.N` or
`package_spec=openclaw@YYYY.M.D`.
- For stable package proof, use `package_spec=openclaw@latest` only when the
question is explicitly the current stable dist-tag; otherwise pin the exact
version.
- `source=npm` only accepts registry specs for `openclaw@beta`,
`openclaw@latest`, or exact OpenClaw release versions. Do not pass semver
ranges, git refs, file paths, tarball URLs, or plugin package names there.
- If the candidate is a tarball URL, use `source=url` with `package_sha256`. If
it is an Actions tarball artifact, use `source=artifact`. If it is an
unpublished source candidate, use `source=ref` with a trusted ref or SHA.
- Package acceptance tests exactly the selected package candidate. Do not apply
`openclaw update --channel beta` fallback semantics here; if `beta` is absent,
stale, older than `latest`, or points at a broken tarball, report that tag
state instead of silently testing `latest`.
Profiles:
- `smoke`: quick confidence that the tarball installs, can onboard a channel,
can run an agent turn, and basic gateway/config lanes work.
- `package`: release-package contract. Adds installer/update, doctor install
switching, bundled plugin runtime deps, plugin install/update, and package
repair lanes. This is the default native replacement for most Parallels
package/update coverage.
- `product`: package profile plus broader product surfaces: MCP channels,
cron/subagent cleanup, OpenAI web search, and OpenWebUI.
- `full`: split Docker release-path chunks with OpenWebUI.
- `custom`: exact `docker_lanes` list for a focused rerun.
Candidate sources:
- `source=npm`: `openclaw@beta`, `openclaw@latest`, or an exact release version.
- `source=ref`: pack `package_ref` using the trusted `workflow_ref` harness.
This intentionally separates old package commits from new workflow/test code.
- `source=url`: HTTPS `.tgz` plus required `package_sha256`.
- `source=artifact`: download one `.tgz` from `artifact_run_id`/`artifact_name`.
Ref model:
- `gh workflow run ... --ref <workflow-ref>` selects the workflow file revision
GitHub executes.
- `workflow_ref` is the trusted harness/script ref passed to reusable Docker
E2E.
- `package_ref` is the source ref to build when `source=ref`. It can be an
older branch/tag/SHA as long as it is reachable from an OpenClaw branch or
release tag.
Example: run latest package acceptance harness against an older trusted commit:
```bash
gh workflow run package-acceptance.yml --ref main \
-f workflow_ref=main \
-f source=ref \
-f package_ref=<branch-or-sha> \
-f suite_profile=package \
-f telegram_mode=mock-openai
```
Use `telegram_mode=mock-openai` or `telegram_mode=live-frontier` when the same
resolved `package-under-test` tarball should also run through the Telegram QA
workflow in the `qa-live-shared` environment. The standalone Telegram workflow
still accepts a published npm spec for post-publish checks, but Package
Acceptance passes the resolved artifact for `source=npm`, `ref`, `url`, and
`artifact`. Use `telegram_mode=none` only when intentionally skipping Telegram
credentialed package proof for a focused rerun.
Docker E2E images never copy repo sources as the app under test: the bare image
is a Node/Git runner, and the functional image installs the same prebuilt npm
@@ -187,7 +469,7 @@ gh workflow run openclaw-live-and-e2e-checks-reusable.yml \
That path still runs the prepare job, so it creates a new tarball for `<sha>`.
If the SHA-tagged GHCR bare/functional image already exists, CI skips rebuilding
that image and only uploads the fresh package artifact before the targeted lane
job. Do not rerun the full three-chunk release path unless the failed lane list
job. Do not rerun the full release path unless the failed lane list
or touched surface really requires it.
## Docker Expected Timings
@@ -204,7 +486,7 @@ these rough bands:
`session-runtime-context` ~20s, `gateway-network` ~34s, `qr` ~44s.
- Medium deterministic lanes, ~1-5 minutes:
`npm-onboard-channel-agent` ~96s, `openai-image-auth` ~99s,
bundled channel/update lanes usually ~90-300s, `openwebui` ~225s,
bundled channel/update lanes usually ~90-300s when split, `openwebui` ~225s,
`mcp-channels` ~274s.
- Heavy deterministic lanes, ~6-10 minutes:
`bundled-channel-root-owned` ~429s,

View File

@@ -26,6 +26,10 @@ inputs:
description: Whether to download/pull artifacts required by the plan.
required: false
default: "true"
package-artifact-name:
description: Workflow artifact name containing openclaw-current.tgz.
required: false
default: docker-e2e-package
outputs:
credentials:
description: Comma-separated credential groups required by selected lanes.
@@ -108,7 +112,7 @@ runs:
if: inputs.hydrate-artifacts == 'true' && steps.plan.outputs.needs_package == '1'
uses: actions/download-artifact@v8
with:
name: docker-e2e-package
name: ${{ inputs.package-artifact-name }}
path: .artifacts/docker-e2e-package
- name: Pull shared bare Docker E2E image

21
.github/labeler.yml vendored
View File

@@ -35,6 +35,17 @@
- any-glob-to-any-file:
- "extensions/google-meet/**"
- "docs/plugins/google-meet.md"
"plugin: migrate-hermes":
- changed-files:
- any-glob-to-any-file:
- "extensions/migrate-hermes/**"
- "docs/cli/migrate.md"
"plugin: migrate-claude":
- changed-files:
- any-glob-to-any-file:
- "extensions/migrate-claude/**"
- "docs/cli/migrate.md"
- "docs/install/migrating-claude.md"
"plugin: bonjour":
- changed-files:
- any-glob-to-any-file:
@@ -101,6 +112,11 @@
- any-glob-to-any-file:
- "extensions/slack/**"
- "docs/channels/slack.md"
"channel: synology-chat":
- changed-files:
- any-glob-to-any-file:
- "extensions/synology-chat/**"
- "docs/channels/synology-chat.md"
"channel: telegram":
- changed-files:
- any-glob-to-any-file:
@@ -289,6 +305,11 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/byteplus/**"
"extensions: cerebras":
- changed-files:
- any-glob-to-any-file:
- "extensions/cerebras/**"
- "docs/providers/cerebras.md"
"extensions: deepseek":
- changed-files:
- any-glob-to-any-file:

View File

@@ -0,0 +1,198 @@
name: Blacksmith Build Artifacts Testbox
on:
workflow_dispatch:
inputs:
testbox_id:
type: string
description: "Testbox session ID"
required: true
pull_request:
paths:
- ".github/workflows/**"
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
build-artifacts:
permissions:
contents: read
name: "build-artifacts"
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 35
steps:
- name: Begin Testbox
uses: useblacksmith/begin-testbox@v2
with:
testbox_id: ${{ inputs.testbox_id }}
- name: Checkout
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
workdir="$GITHUB_WORKSPACE"
auth_header="$(printf 'x-access-token:%s' "$CHECKOUT_TOKEN" | base64 | tr -d '\n')"
reset_checkout_dir() {
mkdir -p "$workdir"
find "$workdir" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
}
checkout_attempt() {
local attempt="$1"
reset_checkout_dir
git init "$workdir" >/dev/null
git config --global --add safe.directory "$workdir"
git -C "$workdir" remote add origin "https://github.com/${CHECKOUT_REPO}"
git -C "$workdir" config gc.auto 0
timeout --signal=TERM 30s git -C "$workdir" \
-c protocol.version=2 \
-c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${auth_header}" \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/ci-target" || return 1
git -C "$workdir" checkout --force --detach "$CHECKOUT_SHA" || return 1
test -f "$workdir/.github/actions/setup-node-env/action.yml" || return 1
echo "checkout attempt ${attempt}/5 succeeded"
}
for attempt in 1 2 3 4 5; do
if checkout_attempt "$attempt"; then
exit 0
fi
echo "checkout attempt ${attempt}/5 failed"
sleep $((attempt * 5))
done
echo "checkout failed after 5 attempts" >&2
exit 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
- name: Resolve release dist cache seeds
id: dist-cache-seeds
shell: bash
run: |
set -euo pipefail
cache_prefix="${RUNNER_OS}-dist-build-"
declare -A seen=()
resolve_tag_sha() {
local tag="$1"
local direct=""
local peeled=""
while read -r sha ref; do
if [[ "$ref" == "refs/tags/${tag}^{}" ]]; then
peeled="$sha"
elif [[ "$ref" == "refs/tags/${tag}" ]]; then
direct="$sha"
fi
done < <(git ls-remote --tags origin "refs/tags/${tag}" "refs/tags/${tag}^{}")
printf '%s\n' "${peeled:-$direct}"
}
{
echo "restore-keys<<EOF"
for dist_tag in beta latest; do
version="$(npm view "openclaw@${dist_tag}" version 2>/dev/null || true)"
if [[ -z "$version" ]]; then
echo "Could not resolve npm dist-tag ${dist_tag}; skipping cache seed." >&2
continue
fi
sha="$(resolve_tag_sha "v${version}")"
if [[ -z "$sha" ]]; then
echo "Could not resolve git tag v${version}; skipping cache seed." >&2
continue
fi
key="${cache_prefix}${sha}"
if [[ -z "${seen[$key]+x}" ]]; then
echo "$key"
seen[$key]=1
fi
done
echo "${cache_prefix}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Restore dist build cache
id: dist-cache
uses: actions/cache/restore@v5
with:
path: |
.artifacts/build-all-cache/
dist/
dist-runtime/
key: ${{ runner.os }}-dist-build-${{ github.sha }}
restore-keys: ${{ steps.dist-cache-seeds.outputs.restore-keys }}
- name: Build dist on cache miss
if: steps.dist-cache.outputs.cache-hit != 'true'
run: pnpm build:ci-artifacts
- name: Build Control UI on cache miss
if: steps.dist-cache.outputs.cache-hit != 'true'
run: pnpm ui:build
- name: Verify build artifacts
shell: bash
run: |
set -euo pipefail
test -d dist
test -d dist-runtime
if [[ ! -f dist/index.js && ! -f dist/index.mjs ]]; then
echo "Missing dist/index.js or dist/index.mjs" >&2
exit 1
fi
test -f dist/build-info.json
test -f dist/control-ui/index.html
- name: Save dist build cache
if: steps.dist-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: |
.artifacts/build-all-cache/
dist/
dist-runtime/
key: ${{ runner.os }}-dist-build-${{ github.sha }}
- name: Prepare Testbox shell
shell: bash
run: |
set -euo pipefail
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
node_bin="$(dirname "$(node -p 'process.execPath')")"
pnpm_bin="$(command -v pnpm)"
sudo ln -sf "$node_bin/node" /usr/local/bin/node
sudo ln -sf "$node_bin/npm" /usr/local/bin/npm
sudo ln -sf "$node_bin/npx" /usr/local/bin/npx
sudo ln -sf "$node_bin/corepack" /usr/local/bin/corepack
sudo ln -sf "$pnpm_bin" /usr/local/bin/pnpm
- name: Run Testbox
uses: useblacksmith/run-testbox@v2
if: always()
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -2,6 +2,12 @@ name: CI
on:
workflow_dispatch:
inputs:
target_ref:
description: Optional branch, tag, or full commit SHA to validate instead of the workflow ref
required: false
default: ""
type: string
push:
branches: [main]
paths-ignore:
@@ -30,6 +36,7 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 20
outputs:
checkout_sha: ${{ steps.checkout_ref.outputs.sha }}
docs_only: ${{ steps.manifest.outputs.docs_only }}
docs_changed: ${{ steps.manifest.outputs.docs_changed }}
run_node: ${{ steps.manifest.outputs.run_node }}
@@ -38,8 +45,6 @@ jobs:
run_skills_python: ${{ steps.manifest.outputs.run_skills_python }}
run_skills_python_job: ${{ steps.manifest.outputs.run_skills_python_job }}
run_windows: ${{ steps.manifest.outputs.run_windows }}
has_changed_extensions: ${{ steps.manifest.outputs.has_changed_extensions }}
changed_extensions_matrix: ${{ steps.manifest.outputs.changed_extensions_matrix }}
run_build_artifacts: ${{ steps.manifest.outputs.run_build_artifacts }}
run_checks_fast_core: ${{ steps.manifest.outputs.run_checks_fast_core }}
run_checks_fast: ${{ steps.manifest.outputs.run_checks_fast }}
@@ -52,8 +57,6 @@ jobs:
checks_node_core_nondist_matrix: ${{ steps.manifest.outputs.checks_node_core_nondist_matrix }}
run_checks_node_core_dist: ${{ steps.manifest.outputs.run_checks_node_core_dist }}
checks_node_core_dist_matrix: ${{ steps.manifest.outputs.checks_node_core_dist_matrix }}
run_extension_fast: ${{ steps.manifest.outputs.run_extension_fast }}
extension_fast_matrix: ${{ steps.manifest.outputs.extension_fast_matrix }}
run_check: ${{ steps.manifest.outputs.run_check }}
run_check_additional: ${{ steps.manifest.outputs.run_check_additional }}
run_build_smoke: ${{ steps.manifest.outputs.run_build_smoke }}
@@ -70,11 +73,16 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ inputs.target_ref || github.sha }}
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Resolve checkout SHA
id: checkout_ref
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Ensure preflight base commit
if: github.event_name != 'workflow_dispatch'
uses: ./.github/actions/ensure-base-commit
@@ -102,29 +110,6 @@ jobs:
node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD
- name: Detect changed extensions
id: changed_extensions
if: github.event_name != 'workflow_dispatch' && steps.docs_scope.outputs.docs_only != 'true' && steps.changed_scope.outputs.run_node == 'true'
env:
BASE_SHA: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
BASE_REF: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }}
run: |
node --input-type=module <<'EOF'
import { appendFileSync } from "node:fs";
import { listChangedExtensionIds } from "./scripts/lib/changed-extensions.mjs";
const extensionIds = listChangedExtensionIds({
base: process.env.BASE_SHA,
head: "HEAD",
fallbackBaseRef: process.env.BASE_REF,
unavailableBaseBehavior: "all",
});
const matrix = JSON.stringify({ include: extensionIds.map((extension) => ({ extension })) });
appendFileSync(process.env.GITHUB_OUTPUT, `has_changed_extensions=${extensionIds.length > 0}\n`, "utf8");
appendFileSync(process.env.GITHUB_OUTPUT, `changed_extensions_matrix=${matrix}\n`, "utf8");
EOF
- name: Build CI manifest
id: manifest
env:
@@ -139,8 +124,6 @@ jobs:
OPENCLAW_CI_RUN_NODE_FAST_CI_ROUTING: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_scope.outputs.run_node_fast_ci_routing || 'false' }}
OPENCLAW_CI_RUN_SKILLS_PYTHON: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_skills_python || 'false' }}
OPENCLAW_CI_RUN_CONTROL_UI_I18N: ${{ github.event_name == 'workflow_dispatch' && 'true' || steps.changed_scope.outputs.run_control_ui_i18n || 'false' }}
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: ${{ github.event_name == 'workflow_dispatch' && 'false' || steps.changed_extensions.outputs.has_changed_extensions || 'false' }}
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: ${{ github.event_name == 'workflow_dispatch' && '{"include":[]}' || steps.changed_extensions.outputs.changed_extensions_matrix || '{"include":[]}' }}
OPENCLAW_CI_REPOSITORY: ${{ github.repository }}
run: |
node --input-type=module <<'EOF'
@@ -164,18 +147,8 @@ jobs:
return fallback;
};
const parseJson = (value, fallback) => {
try {
return value ? JSON.parse(value) : fallback;
} catch {
return fallback;
}
};
const createMatrix = (include) => ({ include });
const outputPath = process.env.GITHUB_OUTPUT;
const eventName = process.env.GITHUB_EVENT_NAME ?? "pull_request";
const isPush = eventName === "push";
const isCanonicalRepository = process.env.OPENCLAW_CI_REPOSITORY === "openclaw/openclaw";
const docsOnly = parseBoolean(process.env.OPENCLAW_CI_DOCS_ONLY);
const docsChanged = parseBoolean(process.env.OPENCLAW_CI_DOCS_CHANGED);
@@ -200,11 +173,6 @@ jobs:
const runSkillsPython = parseBoolean(process.env.OPENCLAW_CI_RUN_SKILLS_PYTHON) && !docsOnly;
const runControlUiI18n =
parseBoolean(process.env.OPENCLAW_CI_RUN_CONTROL_UI_I18N) && !docsOnly;
const hasChangedExtensions =
parseBoolean(process.env.OPENCLAW_CI_HAS_CHANGED_EXTENSIONS) && !docsOnly;
const changedExtensionsMatrix = hasChangedExtensions
? parseJson(process.env.OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX, { include: [] })
: { include: [] };
const extensionTestShardCount = isCanonicalRepository
? DEFAULT_EXTENSION_TEST_SHARD_COUNT
: Math.max(DEFAULT_EXTENSION_TEST_SHARD_COUNT, 36);
@@ -274,8 +242,6 @@ jobs:
run_android: runAndroid,
run_skills_python: runSkillsPython,
run_windows: runWindows,
has_changed_extensions: hasChangedExtensions,
changed_extensions_matrix: changedExtensionsMatrix,
run_build_artifacts: runNodeFull,
run_checks_fast_core: runChecksFastCore,
run_checks_fast: runNodeFull,
@@ -296,15 +262,6 @@ jobs:
checks_node_core_nondist_matrix: createMatrix(nodeTestNonDistShards),
run_checks_node_core_dist: nodeTestDistShards.length > 0,
checks_node_core_dist_matrix: createMatrix(nodeTestDistShards),
run_extension_fast: hasChangedExtensions && !isPush,
extension_fast_matrix: createMatrix(
hasChangedExtensions && !isPush
? (changedExtensionsMatrix.include ?? []).map((entry) => ({
check_name: `extension-fast-${entry.extension}`,
extension: entry.extension,
}))
: [],
),
run_check: runNodeFull,
run_check_additional: runNodeFull,
run_build_smoke: runNodeFull,
@@ -357,12 +314,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ inputs.target_ref || github.sha }}
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Ensure security base commit
if: github.event_name != 'workflow_dispatch'
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
@@ -446,6 +405,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ inputs.target_ref || github.sha }}
fetch-depth: 1
fetch-tags: false
persist-credentials: false
@@ -508,7 +468,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -580,7 +540,7 @@ jobs:
path: |
dist/
dist-runtime/
key: ${{ runner.os }}-dist-build-${{ github.sha }}
key: ${{ runner.os }}-dist-build-${{ needs.preflight.outputs.checkout_sha }}
- name: Pack built runtime artifacts
run: tar --posix -cf dist-runtime-build.tar.zst --use-compress-program zstdmt dist dist-runtime
@@ -709,7 +669,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -804,7 +764,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -907,7 +867,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -975,7 +935,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -1087,7 +1047,7 @@ jobs:
contents: read
name: checks-node-compat-node22
needs: [preflight]
if: needs.preflight.outputs.run_build_artifacts == 'true' && github.event_name == 'push'
if: needs.preflight.outputs.run_build_artifacts == 'true' && github.event_name == 'workflow_dispatch'
runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04' }}
timeout-minutes: 60
steps:
@@ -1095,7 +1055,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -1175,7 +1135,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -1326,84 +1286,6 @@ jobs:
exit 1
fi
extension-fast:
permissions:
contents: read
name: "extension-fast"
needs: [preflight]
if: needs.preflight.outputs.run_extension_fast == 'true'
runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-24.04' }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.preflight.outputs.extension_fast_matrix) }}
steps:
- name: Checkout
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
workdir="$GITHUB_WORKSPACE"
auth_header="$(printf 'x-access-token:%s' "$CHECKOUT_TOKEN" | base64 | tr -d '\n')"
reset_checkout_dir() {
mkdir -p "$workdir"
find "$workdir" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
}
checkout_attempt() {
local attempt="$1"
reset_checkout_dir
git init "$workdir" >/dev/null
git config --global --add safe.directory "$workdir"
git -C "$workdir" remote add origin "https://github.com/${CHECKOUT_REPO}"
git -C "$workdir" config gc.auto 0
timeout --signal=TERM 30s git -C "$workdir" \
-c protocol.version=2 \
-c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${auth_header}" \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/ci-target" || return 1
git -C "$workdir" checkout --force --detach "$CHECKOUT_SHA" || return 1
test -f "$workdir/.github/actions/setup-node-env/action.yml" || return 1
echo "checkout attempt ${attempt}/5 succeeded"
}
for attempt in 1 2 3 4 5; do
if checkout_attempt "$attempt"; then
exit 0
fi
echo "checkout attempt ${attempt}/5 failed"
sleep $((attempt * 5))
done
echo "checkout failed after 5 attempts" >&2
exit 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
- name: Run changed extension tests
env:
OPENCLAW_CHANGED_EXTENSION: ${{ matrix.extension }}
run: |
set -euo pipefail
if [ "$OPENCLAW_CHANGED_EXTENSION" = "telegram" ]; then
export OPENCLAW_VITEST_MAX_WORKERS=1
export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--max-old-space-size=6144"
pnpm test:extension "$OPENCLAW_CHANGED_EXTENSION" -- --pool=forks
exit 0
fi
pnpm test:extension "$OPENCLAW_CHANGED_EXTENSION"
# Types, lint, and format check shards.
check-shard:
permissions:
@@ -1440,7 +1322,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -1572,7 +1454,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -1770,7 +1652,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
@@ -1833,6 +1715,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.checkout_sha }}
persist-credentials: false
submodules: false
@@ -1875,6 +1758,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.checkout_sha }}
persist-credentials: false
submodules: false
@@ -1979,6 +1863,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.checkout_sha }}
persist-credentials: false
submodules: false
@@ -2019,6 +1904,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.checkout_sha }}
persist-credentials: false
submodules: false
@@ -2119,7 +2005,7 @@ jobs:
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ github.sha }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_sha }}
CHECKOUT_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

View File

@@ -0,0 +1,461 @@
name: Full Release Validation
on:
workflow_dispatch:
inputs:
ref:
description: Branch, tag, or full commit SHA to validate
required: true
default: main
type: string
provider:
description: Provider lane for cross-OS onboarding and the end-to-end agent turn
required: false
default: openai
type: choice
options:
- openai
- anthropic
- minimax
mode:
description: Which cross-OS release lanes to run
required: false
default: both
type: choice
options:
- fresh
- upgrade
- both
npm_telegram_package_spec:
description: Optional published package spec for the post-publish Telegram E2E lane
required: false
default: ""
type: string
evidence_package_spec:
description: Optional published package spec to prove in the private release evidence report
required: false
default: ""
type: string
npm_telegram_provider_mode:
description: Provider mode for the optional post-publish Telegram E2E lane
required: false
default: mock-openai
type: choice
options:
- mock-openai
- live-frontier
npm_telegram_scenario:
description: Optional comma-separated Telegram scenario ids for the post-publish lane
required: false
default: ""
type: string
permissions:
actions: write
contents: read
concurrency:
group: full-release-validation-${{ inputs.ref }}
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
GH_REPO: ${{ github.repository }}
jobs:
resolve_target:
name: Resolve target ref
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
sha: ${{ steps.resolve.outputs.sha }}
steps:
- name: Checkout target ref
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
fetch-depth: 0
persist-credentials: false
submodules: false
- name: Resolve target SHA
id: resolve
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Summarize target
env:
TARGET_REF: ${{ inputs.ref }}
TARGET_SHA: ${{ steps.resolve.outputs.sha }}
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
NPM_TELEGRAM_PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec }}
EVIDENCE_PACKAGE_SPEC: ${{ inputs.evidence_package_spec }}
run: |
{
echo "## Full release validation"
echo
echo "- Target ref: \`${TARGET_REF}\`"
echo "- Target SHA: \`${TARGET_SHA}\`"
echo "- Child workflow ref: \`${CHILD_WORKFLOW_REF}\`"
echo "- Normal CI: \`CI\` with \`target_ref=${TARGET_SHA}\`"
echo "- Release/live/Docker/package/QA: \`OpenClaw Release Checks\`"
echo "- Pre-publish Telegram package E2E: included through \`OpenClaw Release Checks\` Package Acceptance for \`${TARGET_SHA}\`"
if [[ -n "${NPM_TELEGRAM_PACKAGE_SPEC// }" ]]; then
echo "- Post-publish Telegram E2E: \`${NPM_TELEGRAM_PACKAGE_SPEC}\`"
else
echo "- Post-publish Telegram E2E: skipped because no published package spec was provided"
fi
if [[ -n "${EVIDENCE_PACKAGE_SPEC// }" ]]; then
echo "- Private evidence package proof: \`${EVIDENCE_PACKAGE_SPEC}\`"
fi
} >> "$GITHUB_STEP_SUMMARY"
normal_ci:
name: Run normal full CI
needs: [resolve_target]
runs-on: ubuntu-24.04
timeout-minutes: 240
outputs:
run_id: ${{ steps.dispatch.outputs.run_id }}
url: ${{ steps.dispatch.outputs.url }}
conclusion: ${{ steps.dispatch.outputs.conclusion }}
steps:
- name: Dispatch and monitor CI
id: dispatch
env:
GH_TOKEN: ${{ github.token }}
TARGET_REF: ${{ inputs.ref }}
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
run: |
set -euo pipefail
dispatch_and_wait() {
local workflow="$1"
shift
local before_json dispatch_output run_id status conclusion url
before_json="$(gh run list --workflow "$workflow" --event workflow_dispatch --limit 100 --json databaseId --jq '[.[].databaseId]')"
dispatch_output="$(gh workflow run "$workflow" --ref "$CHILD_WORKFLOW_REF" "$@" 2>&1)"
printf '%s\n' "$dispatch_output"
run_id="$(
printf '%s\n' "$dispatch_output" |
sed -nE 's#.*actions/runs/([0-9]+).*#\1#p' |
tail -n 1
)"
if [[ -z "$run_id" ]]; then
for _ in $(seq 1 60); do
run_id="$(
BEFORE_IDS="$before_json" gh run list --workflow "$workflow" --event workflow_dispatch --limit 50 --json databaseId,createdAt \
--jq 'map(select(.databaseId as $id | (env.BEFORE_IDS | fromjson | index($id) | not))) | sort_by(.createdAt) | reverse | .[0].databaseId // empty'
)"
if [[ -n "$run_id" ]]; then
break
fi
sleep 5
done
fi
if [[ -z "${run_id:-}" ]]; then
echo "Could not find dispatched run for ${workflow}." >&2
exit 1
fi
echo "Dispatched ${workflow}: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
while true; do
status="$(gh run view "$run_id" --json status --jq '.status')"
if [[ "$status" == "completed" ]]; then
break
fi
sleep 30
done
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
url="$(gh run view "$run_id" --json url --jq '.url')"
echo "${workflow} finished with ${conclusion}: ${url}"
echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "conclusion=${conclusion}" >> "$GITHUB_OUTPUT"
if [[ "$conclusion" != "success" ]]; then
gh run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, conclusion, url}' || true
fi
}
{
echo "### Normal CI"
echo
echo "- Target ref: \`${TARGET_REF}\`"
echo "- Target SHA: \`${TARGET_SHA}\`"
} >> "$GITHUB_STEP_SUMMARY"
dispatch_and_wait ci.yml -f target_ref="$TARGET_SHA"
release_checks:
name: Run release/live/Docker/QA validation
needs: [resolve_target]
runs-on: ubuntu-24.04
timeout-minutes: 720
outputs:
run_id: ${{ steps.dispatch.outputs.run_id }}
url: ${{ steps.dispatch.outputs.url }}
conclusion: ${{ steps.dispatch.outputs.conclusion }}
steps:
- name: Dispatch and monitor release checks
id: dispatch
env:
GH_TOKEN: ${{ github.token }}
TARGET_REF: ${{ inputs.ref }}
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
PROVIDER: ${{ inputs.provider }}
MODE: ${{ inputs.mode }}
run: |
set -euo pipefail
dispatch_and_wait() {
local workflow="$1"
shift
local before_json dispatch_output run_id status conclusion url
before_json="$(gh run list --workflow "$workflow" --event workflow_dispatch --limit 100 --json databaseId --jq '[.[].databaseId]')"
dispatch_output="$(gh workflow run "$workflow" --ref "$CHILD_WORKFLOW_REF" "$@" 2>&1)"
printf '%s\n' "$dispatch_output"
run_id="$(
printf '%s\n' "$dispatch_output" |
sed -nE 's#.*actions/runs/([0-9]+).*#\1#p' |
tail -n 1
)"
if [[ -z "$run_id" ]]; then
for _ in $(seq 1 60); do
run_id="$(
BEFORE_IDS="$before_json" gh run list --workflow "$workflow" --event workflow_dispatch --limit 50 --json databaseId,createdAt \
--jq 'map(select(.databaseId as $id | (env.BEFORE_IDS | fromjson | index($id) | not))) | sort_by(.createdAt) | reverse | .[0].databaseId // empty'
)"
if [[ -n "$run_id" ]]; then
break
fi
sleep 5
done
fi
if [[ -z "${run_id:-}" ]]; then
echo "Could not find dispatched run for ${workflow}." >&2
exit 1
fi
echo "Dispatched ${workflow}: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
while true; do
status="$(gh run view "$run_id" --json status --jq '.status')"
if [[ "$status" == "completed" ]]; then
break
fi
sleep 30
done
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
url="$(gh run view "$run_id" --json url --jq '.url')"
echo "${workflow} finished with ${conclusion}: ${url}"
echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "conclusion=${conclusion}" >> "$GITHUB_OUTPUT"
if [[ "$conclusion" != "success" ]]; then
gh run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, conclusion, url}' || true
fi
}
{
echo "### Release/live/Docker/QA validation"
echo
echo "- Target ref: \`${TARGET_REF}\`"
echo "- Target SHA: \`${TARGET_SHA}\`"
echo "- Provider: \`${PROVIDER}\`"
echo "- Cross-OS mode: \`${MODE}\`"
} >> "$GITHUB_STEP_SUMMARY"
dispatch_and_wait openclaw-release-checks.yml \
-f ref="$TARGET_SHA" \
-f provider="$PROVIDER" \
-f mode="$MODE"
npm_telegram:
name: Run post-publish Telegram E2E
needs: [resolve_target]
if: inputs.npm_telegram_package_spec != ''
runs-on: ubuntu-24.04
timeout-minutes: 120
outputs:
run_id: ${{ steps.dispatch.outputs.run_id }}
url: ${{ steps.dispatch.outputs.url }}
conclusion: ${{ steps.dispatch.outputs.conclusion }}
steps:
- name: Dispatch and monitor npm Telegram E2E
id: dispatch
env:
GH_TOKEN: ${{ github.token }}
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec }}
PROVIDER_MODE: ${{ inputs.npm_telegram_provider_mode }}
SCENARIO: ${{ inputs.npm_telegram_scenario }}
run: |
set -euo pipefail
before_json="$(gh run list --workflow npm-telegram-beta-e2e.yml --event workflow_dispatch --limit 100 --json databaseId --jq '[.[].databaseId]')"
args=(-f package_spec="$PACKAGE_SPEC" -f harness_ref="$TARGET_SHA" -f provider_mode="$PROVIDER_MODE")
if [[ -n "${SCENARIO// }" ]]; then
args+=(-f scenario="$SCENARIO")
fi
gh workflow run npm-telegram-beta-e2e.yml --ref "$CHILD_WORKFLOW_REF" "${args[@]}"
run_id=""
for _ in $(seq 1 60); do
run_id="$(
BEFORE_IDS="$before_json" gh run list --workflow npm-telegram-beta-e2e.yml --event workflow_dispatch --limit 50 --json databaseId,createdAt \
--jq 'map(select(.databaseId as $id | (env.BEFORE_IDS | fromjson | index($id) | not))) | sort_by(.createdAt) | reverse | .[0].databaseId // empty'
)"
if [[ -n "$run_id" ]]; then
break
fi
sleep 5
done
if [[ -z "$run_id" ]]; then
echo "Could not find dispatched run for npm-telegram-beta-e2e.yml." >&2
exit 1
fi
echo "Dispatched npm-telegram-beta-e2e.yml: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}"
echo "run_id=${run_id}" >> "$GITHUB_OUTPUT"
while true; do
status="$(gh run view "$run_id" --json status --jq '.status')"
if [[ "$status" == "completed" ]]; then
break
fi
sleep 30
done
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
url="$(gh run view "$run_id" --json url --jq '.url')"
echo "npm-telegram-beta-e2e.yml finished with ${conclusion}: ${url}"
echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "conclusion=${conclusion}" >> "$GITHUB_OUTPUT"
if [[ "$conclusion" != "success" ]]; then
gh run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, conclusion, url}' || true
fi
summary:
name: Verify full validation
needs: [normal_ci, release_checks, npm_telegram]
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Request private evidence update
env:
RELEASE_PRIVATE_DISPATCH_TOKEN: ${{ secrets.OPENCLAW_RELEASES_PRIVATE_DISPATCH_TOKEN }}
TARGET_REF: ${{ inputs.ref }}
PACKAGE_SPEC: ${{ inputs.evidence_package_spec || inputs.npm_telegram_package_spec }}
GITHUB_RUN_ID_VALUE: ${{ github.run_id }}
run: |
set -euo pipefail
if [[ -z "${RELEASE_PRIVATE_DISPATCH_TOKEN// }" ]]; then
echo "OPENCLAW_RELEASES_PRIVATE_DISPATCH_TOKEN is not configured; skipping automatic private evidence update."
exit 0
fi
release_id="${TARGET_REF#refs/tags/}"
release_id="${release_id#v}"
if [[ "$PACKAGE_SPEC" =~ ^openclaw@(.+)$ ]]; then
release_id="${BASH_REMATCH[1]}"
fi
release_id="$(printf '%s' "$release_id" | tr '/:@ ' '----' | tr -cd 'A-Za-z0-9._-')"
if [[ -z "$release_id" ]]; then
echo "::error::Could not derive release evidence id from target ref '${TARGET_REF}'."
exit 1
fi
payload="$(
jq -cn \
--arg full_validation_run_id "$GITHUB_RUN_ID_VALUE" \
--arg release_id "$release_id" \
--arg release_ref "$TARGET_REF" \
--arg package_spec "$PACKAGE_SPEC" \
--arg notes "Automatically requested by Full Release Validation ${GITHUB_RUN_ID_VALUE} after child workflows completed; the parent summary re-checks current child run conclusions." \
'{
event_type: "openclaw_full_release_validation_completed",
client_payload: {
full_validation_run_id: $full_validation_run_id,
release_id: $release_id,
release_ref: $release_ref,
package_spec: $package_spec,
notes: $notes
}
}'
)"
curl --fail-with-body \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${RELEASE_PRIVATE_DISPATCH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/openclaw/releases-private/dispatches \
-d "$payload"
- name: Verify child workflow results
env:
GH_TOKEN: ${{ github.token }}
NORMAL_CI_RUN_ID: ${{ needs.normal_ci.outputs.run_id }}
RELEASE_CHECKS_RUN_ID: ${{ needs.release_checks.outputs.run_id }}
NPM_TELEGRAM_RUN_ID: ${{ needs.npm_telegram.outputs.run_id }}
NPM_TELEGRAM_RESULT: ${{ needs.npm_telegram.result }}
run: |
set -euo pipefail
check_child() {
local label="$1"
local run_id="$2"
local required="$3"
if [[ -z "${run_id// }" ]]; then
if [[ "$required" == "0" ]]; then
echo "${label}: skipped"
return 0
fi
echo "::error::${label} did not record a child run id."
return 1
fi
local status conclusion url attempt
status="$(gh run view "$run_id" --json status --jq '.status')"
conclusion="$(gh run view "$run_id" --json conclusion --jq '.conclusion')"
url="$(gh run view "$run_id" --json url --jq '.url')"
attempt="$(gh run view "$run_id" --json attempt --jq '.attempt')"
echo "${label}: ${status}/${conclusion} attempt ${attempt}: ${url}"
if [[ "$status" != "completed" || "$conclusion" != "success" ]]; then
echo "::error::${label} child run ended with ${status}/${conclusion}: ${url}"
gh run view "$run_id" --json jobs --jq '.jobs[] | select(.conclusion != "success" and .conclusion != "skipped") | {name, status, conclusion, url}' || true
return 1
fi
}
failed=0
check_child "normal_ci" "$NORMAL_CI_RUN_ID" 1 || failed=1
check_child "release_checks" "$RELEASE_CHECKS_RUN_ID" 1 || failed=1
if [[ "$NPM_TELEGRAM_RESULT" == "skipped" && -z "${NPM_TELEGRAM_RUN_ID// }" ]]; then
check_child "npm_telegram" "" 0 || failed=1
else
check_child "npm_telegram" "$NPM_TELEGRAM_RUN_ID" 1 || failed=1
fi
exit "$failed"

View File

@@ -4,10 +4,60 @@ on:
workflow_dispatch:
inputs:
package_spec:
description: Published OpenClaw package spec to test
required: true
description: Published OpenClaw package spec to test when no artifact is supplied
required: false
default: openclaw@beta
type: string
source:
description: Package candidate source when no package-under-test artifact is supplied
required: true
default: npm
type: choice
options:
- npm
- ref
- url
- artifact
package_ref:
description: Trusted package source ref when source=ref
required: false
default: main
type: string
package_url:
description: HTTPS .tgz URL when source=url
required: false
default: ""
type: string
package_sha256:
description: Expected package SHA-256 for source=url or source=artifact
required: false
default: ""
type: string
artifact_run_id:
description: GitHub Actions run id when source=artifact
required: false
default: ""
type: string
artifact_name:
description: Artifact name containing one .tgz when source=artifact
required: false
default: package-under-test
type: string
package_label:
description: Optional display label for an artifact-backed package candidate
required: false
default: ""
type: string
package_artifact_name:
description: Advanced package-under-test artifact name; leave blank for registry install
required: false
default: ""
type: string
harness_ref:
description: Source ref for the private QA harness; defaults to the dispatched workflow ref
required: false
default: ""
type: string
provider_mode:
description: QA provider mode
required: true
@@ -20,8 +70,78 @@ on:
description: Optional comma-separated Telegram scenario ids
required: false
type: string
workflow_call:
inputs:
package_spec:
description: Published OpenClaw package spec to test when no artifact is supplied
required: false
default: openclaw@beta
type: string
source:
description: "Package candidate source when no package-under-test artifact is supplied: npm, ref, url, or artifact"
required: false
default: npm
type: string
package_ref:
description: Trusted package source ref when source=ref
required: false
default: main
type: string
package_url:
description: HTTPS .tgz URL when source=url
required: false
default: ""
type: string
package_sha256:
description: Expected package SHA-256 for source=url or source=artifact
required: false
default: ""
type: string
artifact_run_id:
description: GitHub Actions run id when source=artifact
required: false
default: ""
type: string
artifact_name:
description: Artifact name containing one .tgz when source=artifact
required: false
default: package-under-test
type: string
package_artifact_name:
description: Optional package-under-test artifact from the current workflow run
required: false
default: ""
type: string
package_label:
description: Optional display label for an artifact-backed package candidate
required: false
default: ""
type: string
harness_ref:
description: Source ref for the private QA harness; defaults to the called workflow ref
required: false
default: ""
type: string
provider_mode:
description: QA provider mode
required: false
default: mock-openai
type: string
scenario:
description: Optional comma-separated Telegram scenario ids
required: false
default: ""
type: string
secrets:
OPENAI_API_KEY:
required: false
OPENCLAW_QA_CONVEX_SITE_URL:
required: false
OPENCLAW_QA_CONVEX_SECRET_CI:
required: false
permissions:
actions: read
contents: read
concurrency:
@@ -34,12 +154,13 @@ env:
PNPM_VERSION: "10.33.0"
jobs:
run_npm_telegram_beta_e2e:
name: Run published npm Telegram E2E
run_package_telegram_e2e:
name: Run package Telegram E2E
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 60
environment: qa-live-shared
permissions:
actions: read
contents: read
env:
DOCKER_BUILD_SUMMARY: "false"
@@ -48,8 +169,8 @@ jobs:
- name: Checkout dispatch ref
uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
fetch-depth: 1
ref: ${{ inputs.harness_ref || github.sha }}
fetch-depth: 0
- name: Set up Blacksmith Docker Builder
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
@@ -78,6 +199,13 @@ jobs:
- name: Validate inputs and secrets
env:
PACKAGE_SPEC: ${{ inputs.package_spec }}
SOURCE: ${{ inputs.source }}
PACKAGE_REF: ${{ inputs.package_ref }}
PACKAGE_URL: ${{ inputs.package_url }}
PACKAGE_SHA256: ${{ inputs.package_sha256 }}
ARTIFACT_RUN_ID: ${{ inputs.artifact_run_id }}
ARTIFACT_NAME: ${{ inputs.artifact_name }}
PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || '' }}
PROVIDER_MODE: ${{ inputs.provider_mode }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
@@ -86,10 +214,43 @@ jobs:
run: |
set -euo pipefail
if [[ ! "${PACKAGE_SPEC}" =~ ^openclaw@(beta|latest|[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-[1-9][0-9]*|-beta\.[1-9][0-9]*)?)$ ]]; then
echo "package_spec must be openclaw@beta, openclaw@latest, or an exact OpenClaw release version; got: ${PACKAGE_SPEC}" >&2
case "${SOURCE}" in
npm | ref | url | artifact) ;;
*)
echo "source must be npm, ref, url, or artifact; got: ${SOURCE}" >&2
exit 1
;;
esac
if [[ -z "${PACKAGE_ARTIFACT_NAME// }" && "${SOURCE}" == "npm" ]]; then
if [[ ! "${PACKAGE_SPEC}" =~ ^openclaw@(beta|latest|[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-[1-9][0-9]*|-beta\.[1-9][0-9]*)?)$ ]]; then
echo "package_spec must be openclaw@beta, openclaw@latest, or an exact OpenClaw release version; got: ${PACKAGE_SPEC}" >&2
exit 1
fi
fi
if [[ -z "${PACKAGE_ARTIFACT_NAME// }" && "${SOURCE}" == "ref" && -z "${PACKAGE_REF// }" ]]; then
echo "package_ref is required when source=ref." >&2
exit 1
fi
if [[ -z "${PACKAGE_ARTIFACT_NAME// }" && "${SOURCE}" == "url" ]]; then
if [[ -z "${PACKAGE_URL// }" || -z "${PACKAGE_SHA256// }" ]]; then
echo "package_url and package_sha256 are required when source=url." >&2
exit 1
fi
fi
if [[ -z "${PACKAGE_ARTIFACT_NAME// }" && "${SOURCE}" == "artifact" ]]; then
if [[ -z "${ARTIFACT_RUN_ID// }" || -z "${ARTIFACT_NAME// }" ]]; then
echo "artifact_run_id and artifact_name are required when source=artifact." >&2
exit 1
fi
fi
case "${PROVIDER_MODE}" in
mock-openai | live-frontier) ;;
*)
echo "provider_mode must be mock-openai or live-frontier; got: ${PROVIDER_MODE}" >&2
exit 1
;;
esac
require_var() {
local key="$1"
@@ -105,7 +266,54 @@ jobs:
require_var OPENAI_API_KEY
fi
- name: Run npm Telegram beta E2E
- name: Download package-under-test artifact
if: inputs.package_artifact_name != ''
uses: actions/download-artifact@v8
with:
name: ${{ inputs.package_artifact_name }}
path: .artifacts/telegram-package-under-test
- name: Download package artifact input
if: inputs.package_artifact_name == '' && inputs.source == 'artifact'
env:
GH_TOKEN: ${{ github.token }}
ARTIFACT_RUN_ID: ${{ inputs.artifact_run_id }}
ARTIFACT_NAME: ${{ inputs.artifact_name }}
shell: bash
run: |
set -euo pipefail
mkdir -p .artifacts/telegram-package-input
gh run download "$ARTIFACT_RUN_ID" -n "$ARTIFACT_NAME" -D .artifacts/telegram-package-input
- name: Resolve package-under-test candidate
if: inputs.package_artifact_name == '' && inputs.source != 'npm'
id: resolve_candidate
env:
SOURCE: ${{ inputs.source }}
PACKAGE_REF: ${{ inputs.package_ref }}
PACKAGE_SPEC: ${{ inputs.package_spec }}
PACKAGE_URL: ${{ inputs.package_url }}
PACKAGE_SHA256: ${{ inputs.package_sha256 }}
shell: bash
run: |
set -euo pipefail
artifact_dir="."
if [[ "${SOURCE}" == "artifact" ]]; then
artifact_dir=".artifacts/telegram-package-input"
fi
node scripts/resolve-openclaw-package-candidate.mjs \
--source "$SOURCE" \
--package-ref "$PACKAGE_REF" \
--package-spec "$PACKAGE_SPEC" \
--package-url "$PACKAGE_URL" \
--package-sha256 "$PACKAGE_SHA256" \
--artifact-dir "$artifact_dir" \
--output-dir .artifacts/telegram-package-under-test \
--output-name openclaw-current.tgz \
--metadata .artifacts/telegram-package-under-test/package-candidate.json \
--github-output "$GITHUB_OUTPUT"
- name: Run package Telegram E2E
id: run_lane
shell: bash
env:
@@ -113,6 +321,7 @@ jobs:
OPENCLAW_SKIP_DOCKER_BUILD: "1"
OPENCLAW_DOCKER_E2E_IMAGE: openclaw-docker-e2e:local
OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC: ${{ inputs.package_spec }}
OPENCLAW_NPM_TELEGRAM_PACKAGE_LABEL: ${{ inputs.package_label }}
OPENCLAW_NPM_TELEGRAM_PROVIDER_MODE: ${{ inputs.provider_mode }}
OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE: convex
OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE: ci
@@ -121,6 +330,7 @@ jobs:
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT: "1"
INPUT_SCENARIO: ${{ inputs.scenario }}
PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || '' }}
run: |
set -euo pipefail
@@ -128,6 +338,25 @@ jobs:
echo "output_dir=${output_dir}" >> "$GITHUB_OUTPUT"
export OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="${output_dir}"
package_dir=".artifacts/telegram-package-under-test"
if [[ -d "${package_dir}" ]]; then
mapfile -t package_tgzs < <(find "${package_dir}" -type f -name "*.tgz" | sort)
else
package_tgzs=()
fi
if [[ "${#package_tgzs[@]}" -gt 0 ]]; then
if [[ "${#package_tgzs[@]}" -ne 1 ]]; then
echo "package-under-test candidate must contain exactly one .tgz; found ${#package_tgzs[@]}" >&2
exit 1
fi
export OPENCLAW_NPM_TELEGRAM_PACKAGE_TGZ="${package_tgzs[0]}"
if [[ -z "${OPENCLAW_NPM_TELEGRAM_PACKAGE_LABEL// }" ]]; then
export OPENCLAW_NPM_TELEGRAM_PACKAGE_LABEL="$(basename "${package_tgzs[0]}")"
fi
elif [[ -z "${OPENCLAW_NPM_TELEGRAM_PACKAGE_LABEL// }" ]]; then
export OPENCLAW_NPM_TELEGRAM_PACKAGE_LABEL="${OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC}"
fi
if [[ -n "${INPUT_SCENARIO// }" ]]; then
export OPENCLAW_NPM_TELEGRAM_SCENARIOS="${INPUT_SCENARIO}"
fi

View File

@@ -286,7 +286,7 @@ jobs:
env:
OUTPUT_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare
run: |
pnpm dlx "tsx@${TSX_VERSION}" workflow/scripts/openclaw-cross-os-release-checks.ts \
bash workflow/scripts/github/run-openclaw-cross-os-release-checks.sh \
--prepare-only \
--source-dir source \
--output-dir "${OUTPUT_DIR}"
@@ -370,7 +370,7 @@ jobs:
VAR_WINDOWS_RUNNER: ${{ vars.OPENCLAW_RELEASE_CHECKS_WINDOWS_RUNNER }}
VAR_MACOS_RUNNER: ${{ vars.OPENCLAW_RELEASE_CHECKS_MACOS_RUNNER }}
run: |
MATRIX_JSON="$(pnpm dlx "tsx@${TSX_VERSION}" workflow/scripts/openclaw-cross-os-release-checks.ts \
MATRIX_JSON="$(bash workflow/scripts/github/run-openclaw-cross-os-release-checks.sh \
--resolve-matrix \
--ref "${INPUT_REF}" \
--mode "${INPUT_MODE}" \
@@ -448,7 +448,7 @@ jobs:
if [[ -n "${OPENCLAW_DISCORD_SMOKE_BOT_TOKEN}" ]] && [[ -n "${OPENCLAW_DISCORD_SMOKE_GUILD_ID}" ]] && [[ -n "${OPENCLAW_DISCORD_SMOKE_CHANNEL_ID}" ]]; then
DISCORD_ARGS+=(--run-discord-roundtrip true)
fi
pnpm dlx "tsx@${TSX_VERSION}" workflow/scripts/openclaw-cross-os-release-checks.ts \
bash workflow/scripts/github/run-openclaw-cross-os-release-checks.sh \
--candidate-tgz "${CANDIDATE_TGZ}" \
--candidate-version "${CANDIDATE_VERSION}" \
--source-sha "${SOURCE_SHA}" \

View File

@@ -28,6 +28,26 @@ on:
required: false
default: ""
type: string
package_artifact_name:
description: Existing workflow artifact containing openclaw-current.tgz; blank packs the selected ref
required: false
default: ""
type: string
package_artifact_run_id:
description: Prior run id containing package_artifact_name; blank uses this run or packs the selected ref
required: false
default: ""
type: string
docker_e2e_bare_image:
description: Existing bare Docker E2E image to reuse; blank derives from package SHA/ref
required: false
default: ""
type: string
docker_e2e_functional_image:
description: Existing functional Docker E2E image to reuse; blank derives from package SHA/ref
required: false
default: ""
type: string
include_live_suites:
description: Whether to run live-provider coverage
required: false
@@ -38,6 +58,11 @@ on:
required: false
default: false
type: boolean
live_model_providers:
description: Comma/space separated provider ids for the Docker live model matrix; blank runs all providers
required: false
default: ""
type: string
workflow_call:
inputs:
ref:
@@ -64,6 +89,26 @@ on:
required: false
default: ""
type: string
package_artifact_name:
description: Existing workflow artifact containing openclaw-current.tgz; blank packs the selected ref
required: false
default: ""
type: string
package_artifact_run_id:
description: Prior run id containing package_artifact_name; blank uses this run or packs the selected ref
required: false
default: ""
type: string
docker_e2e_bare_image:
description: Existing bare Docker E2E image to reuse; blank derives from package SHA/ref
required: false
default: ""
type: string
docker_e2e_functional_image:
description: Existing functional Docker E2E image to reuse; blank derives from package SHA/ref
required: false
default: ""
type: string
include_live_suites:
description: Whether to run live-provider coverage
required: false
@@ -74,6 +119,11 @@ on:
required: false
default: false
type: boolean
live_model_providers:
description: Comma/space separated provider ids for the Docker live model matrix; blank runs all providers
required: false
default: ""
type: string
secrets:
OPENAI_API_KEY:
required: false
@@ -181,51 +231,42 @@ jobs:
selected_sha: ${{ steps.validate.outputs.selected_sha }}
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
steps:
- name: Checkout selected ref
- name: Checkout workflow repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
fetch-depth: 0
- name: Validate selected ref
id: validate
env:
GH_TOKEN: ${{ github.token }}
INPUT_REF: ${{ inputs.ref }}
WORKFLOW_REF_NAME: ${{ github.ref_name }}
shell: bash
run: |
set -euo pipefail
selected_sha="$(git rev-parse HEAD)"
trusted_reason=""
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
if [[ "${WORKFLOW_REF_NAME}" =~ ^release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*$ ]]; then
git fetch --no-tags origin "+refs/heads/${WORKFLOW_REF_NAME}:refs/remotes/origin/${WORKFLOW_REF_NAME}"
git fetch --no-tags origin '+refs/heads/*:refs/remotes/origin/*'
git fetch --tags origin '+refs/tags/*:refs/tags/*'
# Resolve here instead of in actions/checkout so short SHAs work too.
if ! selected_sha="$(git rev-parse --verify "${INPUT_REF}^{commit}")"; then
echo "Ref '${INPUT_REF}' could not be resolved to a commit." >&2
exit 1
fi
if git merge-base --is-ancestor "$selected_sha" refs/remotes/origin/main; then
trusted_reason="main-ancestor"
elif [[ "${WORKFLOW_REF_NAME}" =~ ^release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*$ ]] &&
[[ "$selected_sha" == "$(git rev-parse "refs/remotes/origin/${WORKFLOW_REF_NAME}")" ]]; then
trusted_reason="release-branch-head"
elif git tag --points-at "$selected_sha" | grep -Eq '^v'; then
trusted_reason="release-tag"
elif git for-each-ref --format='%(refname:short)' --contains "$selected_sha" refs/remotes/origin | grep -Eq '^origin/'; then
trusted_reason="repository-branch-history"
else
pr_head_count="$(
gh api \
-H "Accept: application/vnd.github+json" \
"repos/${GITHUB_REPOSITORY}/commits/${selected_sha}/pulls" \
--jq '[.[] | select(.state == "open" and .head.repo.full_name == "'"${GITHUB_REPOSITORY}"'" and .head.sha == "'"${selected_sha}"'")] | length'
)"
if [[ "$pr_head_count" != "0" ]]; then
trusted_reason="open-pr-head"
fi
trusted_reason=""
fi
if [[ -z "$trusted_reason" ]]; then
echo "Ref '${INPUT_REF}' resolved to $selected_sha, which is not trusted for secret-bearing live/E2E checks." >&2
echo "Allowed refs must be on main, match the current release branch head, point to a release tag, or match an open PR head in ${GITHUB_REPOSITORY}." >&2
echo "Allowed refs must be reachable from an OpenClaw branch or release tag." >&2
exit 1
fi
@@ -394,8 +435,11 @@ jobs:
- chunk_id: package-update
label: package/update
timeout_minutes: 180
- chunk_id: plugins-integrations
label: plugins/integrations
- chunk_id: plugins-runtime
label: plugins/runtime
timeout_minutes: 180
- chunk_id: bundled-channels
label: bundled channels
timeout_minutes: 180
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -445,6 +489,8 @@ jobs:
OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
OPENCLAW_DOCKER_E2E_BARE_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.bare_image }}
OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }}
OPENCLAW_DOCKER_E2E_PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }}
OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz
OPENCLAW_SKIP_DOCKER_BUILD: "1"
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
@@ -473,13 +519,67 @@ jobs:
- name: Hydrate live auth/profile inputs
run: bash scripts/ci-hydrate-live-auth.sh
- name: Plan and hydrate Docker E2E chunk
- name: Plan Docker E2E chunk
id: plan
uses: ./.github/actions/docker-e2e-plan
shell: bash
env:
CHUNK: ${{ matrix.chunk_id }}
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
run: |
set -euo pipefail
if [[ -z "$CHUNK" ]]; then
echo "chunk input is required for Docker E2E chunk planning." >&2
exit 1
fi
mkdir -p .artifacts/docker-tests
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
export OPENCLAW_DOCKER_ALL_CHUNK="$CHUNK"
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="$INCLUDE_OPENWEBUI"
plan_path=".artifacts/docker-tests/release-${CHUNK}-plan.json"
node scripts/test-docker-all.mjs --plan-json > "$plan_path"
node scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT"
echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT"
- name: Download OpenClaw Docker E2E package
if: steps.plan.outputs.needs_package == '1'
uses: actions/download-artifact@v8
with:
mode: chunk
chunk: ${{ matrix.chunk_id }}
include-openwebui: ${{ inputs.include_openwebui }}
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
path: .artifacts/docker-e2e-package
- name: Pull shared bare Docker E2E image
if: steps.plan.outputs.needs_bare_image == '1'
shell: bash
run: |
set -euo pipefail
docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
- name: Pull shared functional Docker E2E image
if: steps.plan.outputs.needs_functional_image == '1'
shell: bash
run: |
set -euo pipefail
docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
- name: Validate Docker E2E credentials
shell: bash
env:
CREDENTIALS: ${{ steps.plan.outputs.credentials }}
run: |
set -euo pipefail
credentials=",$CREDENTIALS,"
if [[ "$credentials" == *",openai,"* ]]; then
[[ -n "${OPENAI_API_KEY:-}" ]] || {
echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2
exit 1
}
fi
if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
exit 1
fi
- name: Run Docker E2E chunk
shell: bash
@@ -571,6 +671,8 @@ jobs:
OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
OPENCLAW_DOCKER_E2E_BARE_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.bare_image }}
OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }}
OPENCLAW_DOCKER_E2E_PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }}
OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz
OPENCLAW_SKIP_DOCKER_BUILD: "1"
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
@@ -599,13 +701,66 @@ jobs:
- name: Hydrate live auth/profile inputs
run: bash scripts/ci-hydrate-live-auth.sh
- name: Plan and hydrate targeted Docker E2E lanes
- name: Plan targeted Docker E2E lanes
id: plan
uses: ./.github/actions/docker-e2e-plan
shell: bash
env:
LANES: ${{ inputs.docker_lanes }}
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
run: |
set -euo pipefail
if [[ -z "$LANES" ]]; then
echo "lanes input is required for Docker E2E targeted planning." >&2
exit 1
fi
mkdir -p .artifacts/docker-tests
export OPENCLAW_DOCKER_ALL_LANES="$LANES"
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="$INCLUDE_OPENWEBUI"
plan_path=".artifacts/docker-tests/targeted-plan.json"
node scripts/test-docker-all.mjs --plan-json > "$plan_path"
node scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT"
echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT"
- name: Download OpenClaw Docker E2E package
if: steps.plan.outputs.needs_package == '1'
uses: actions/download-artifact@v8
with:
mode: targeted
lanes: ${{ inputs.docker_lanes }}
include-openwebui: ${{ inputs.include_openwebui }}
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
path: .artifacts/docker-e2e-package
- name: Pull shared bare Docker E2E image
if: steps.plan.outputs.needs_bare_image == '1'
shell: bash
run: |
set -euo pipefail
docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
- name: Pull shared functional Docker E2E image
if: steps.plan.outputs.needs_functional_image == '1'
shell: bash
run: |
set -euo pipefail
docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
- name: Validate Docker E2E credentials
shell: bash
env:
CREDENTIALS: ${{ steps.plan.outputs.credentials }}
run: |
set -euo pipefail
credentials=",$CREDENTIALS,"
if [[ "$credentials" == *",openai,"* ]]; then
[[ -n "${OPENAI_API_KEY:-}" ]] || {
echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2
exit 1
}
fi
if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
exit 1
fi
- name: Run targeted Docker E2E lanes
shell: bash
@@ -648,6 +803,7 @@ jobs:
validate_docker_openwebui:
needs: [validate_selected_ref, prepare_docker_e2e_image]
if: inputs.include_openwebui && !inputs.include_release_path_suites && inputs.docker_lanes == ''
name: Docker E2E (openwebui)
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 75
env:
@@ -655,6 +811,9 @@ jobs:
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }}
OPENCLAW_DOCKER_E2E_PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }}
OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz
OPENCLAW_SKIP_DOCKER_BUILD: "1"
steps:
- name: Checkout selected ref
@@ -686,8 +845,77 @@ jobs:
exit 1
}
- name: Run Open WebUI Docker E2E
run: pnpm test:docker:openwebui
- name: Plan Open WebUI Docker E2E chunk
id: plan
shell: bash
run: |
set -euo pipefail
mkdir -p .artifacts/docker-tests
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
export OPENCLAW_DOCKER_ALL_CHUNK=openwebui
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI=true
plan_path=".artifacts/docker-tests/release-openwebui-plan.json"
node scripts/test-docker-all.mjs --plan-json > "$plan_path"
node scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT"
echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT"
- name: Download OpenClaw Docker E2E package
if: steps.plan.outputs.needs_package == '1'
uses: actions/download-artifact@v8
with:
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
path: .artifacts/docker-e2e-package
- name: Pull shared bare Docker E2E image
if: steps.plan.outputs.needs_bare_image == '1'
shell: bash
run: |
set -euo pipefail
docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
- name: Pull shared functional Docker E2E image
if: steps.plan.outputs.needs_functional_image == '1'
shell: bash
run: |
set -euo pipefail
docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
- name: Run Open WebUI Docker E2E chunk
shell: bash
run: |
set -euo pipefail
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
export OPENCLAW_DOCKER_ALL_CHUNK=openwebui
export OPENCLAW_DOCKER_ALL_BUILD=0
export OPENCLAW_DOCKER_ALL_PREFLIGHT=0
export OPENCLAW_DOCKER_ALL_FAIL_FAST=0
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI=1
export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/release-openwebui"
export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/release-openwebui-timings.json"
export OPENCLAW_DOCKER_ALL_PNPM_COMMAND="$(command -v pnpm)"
pnpm test:docker:all
- name: Summarize Open WebUI Docker E2E chunk
if: always()
shell: bash
run: |
set -euo pipefail
summary=".artifacts/docker-tests/release-openwebui/summary.json"
if [[ ! -f "$summary" ]]; then
echo "Docker Open WebUI summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
node scripts/docker-e2e.mjs summary "$summary" "Docker E2E chunk: openwebui" >> "$GITHUB_STEP_SUMMARY"
- name: Upload Open WebUI Docker E2E artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: docker-e2e-openwebui
path: .artifacts/docker-tests/
if-no-files-found: ignore
prepare_docker_e2e_image:
needs: validate_selected_ref
@@ -695,6 +923,7 @@ jobs:
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 90
permissions:
actions: read
contents: read
packages: write
outputs:
@@ -716,43 +945,58 @@ jobs:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 1
- name: Resolve shared Docker E2E image tags
id: image
shell: bash
env:
SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }}
run: |
set -euo pipefail
repository="${GITHUB_REPOSITORY,,}"
bare_image="ghcr.io/${repository}-docker-e2e-bare:${SELECTED_SHA}"
functional_image="ghcr.io/${repository}-docker-e2e-functional:${SELECTED_SHA}"
image="$functional_image"
echo "image=$image" >> "$GITHUB_OUTPUT"
echo "bare_image=$bare_image" >> "$GITHUB_OUTPUT"
echo "functional_image=$functional_image" >> "$GITHUB_OUTPUT"
echo "Shared Docker E2E bare image: \`$bare_image\`" >> "$GITHUB_STEP_SUMMARY"
echo "Shared Docker E2E functional image: \`$functional_image\`" >> "$GITHUB_STEP_SUMMARY"
- name: Plan Docker E2E images
id: plan
uses: ./.github/actions/docker-e2e-plan
with:
mode: prepare
lanes: ${{ inputs.docker_lanes }}
include-release-path-suites: ${{ inputs.include_release_path_suites }}
include-openwebui: ${{ inputs.include_openwebui }}
hydrate-artifacts: "false"
shell: bash
env:
LANES: ${{ inputs.docker_lanes }}
INCLUDE_RELEASE_PATH_SUITES: ${{ inputs.include_release_path_suites }}
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
run: |
set -euo pipefail
mkdir -p .artifacts/docker-tests
if [[ "$INCLUDE_RELEASE_PATH_SUITES" == "true" ]]; then
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
export OPENCLAW_DOCKER_ALL_PLAN_RELEASE_ALL=1
elif [[ -n "$LANES" ]]; then
export OPENCLAW_DOCKER_ALL_LANES="$LANES"
elif [[ "$INCLUDE_OPENWEBUI" == "true" ]]; then
export OPENCLAW_DOCKER_ALL_LANES=openwebui
fi
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="$INCLUDE_OPENWEBUI"
plan_path=".artifacts/docker-tests/plan.json"
node scripts/test-docker-all.mjs --plan-json > "$plan_path"
node scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT"
echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT"
- name: Setup Node environment
if: steps.plan.outputs.needs_package == '1'
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name == '' && inputs.package_artifact_run_id == ''
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
install-bun: "true"
- name: Download current-run OpenClaw Docker E2E package
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name != '' && inputs.package_artifact_run_id == ''
uses: actions/download-artifact@v8
with:
name: ${{ inputs.package_artifact_name }}
path: .artifacts/docker-e2e-package
- name: Download previous-run OpenClaw Docker E2E package
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_run_id != ''
uses: actions/download-artifact@v8
with:
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
path: .artifacts/docker-e2e-package
run-id: ${{ inputs.package_artifact_run_id }}
github-token: ${{ github.token }}
- name: Pack OpenClaw package for Docker E2E
if: steps.plan.outputs.needs_package == '1'
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name == '' && inputs.package_artifact_run_id == ''
shell: bash
run: |
set -euo pipefail
@@ -761,14 +1005,62 @@ jobs:
--output-dir .artifacts/docker-e2e-package \
--output-name openclaw-current.tgz
- name: Upload OpenClaw Docker E2E package
- name: Validate OpenClaw Docker E2E package
id: package
if: steps.plan.outputs.needs_package == '1'
shell: bash
run: |
set -euo pipefail
mkdir -p .artifacts/docker-e2e-package
target=".artifacts/docker-e2e-package/openclaw-current.tgz"
if [[ ! -f "$target" ]]; then
mapfile -t tgzs < <(find .artifacts/docker-e2e-package -type f -name '*.tgz' | sort)
if [[ "${#tgzs[@]}" -ne 1 ]]; then
echo "Expected exactly one package tarball in .artifacts/docker-e2e-package; found ${#tgzs[@]}." >&2
printf '%s\n' "${tgzs[@]}" >&2
exit 1
fi
cp "${tgzs[0]}" "$target"
fi
node scripts/check-openclaw-package-tarball.mjs "$target"
digest="$(sha256sum "$target" | awk '{print $1}')"
tag="pkg-${digest:0:32}"
echo "sha256=$digest" >> "$GITHUB_OUTPUT"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
{
echo "Docker E2E package: \`$target\`"
echo "Docker E2E package SHA-256: \`$digest\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload OpenClaw Docker E2E package
if: steps.plan.outputs.needs_package == '1' && (inputs.package_artifact_name == '' || inputs.package_artifact_run_id != '')
uses: actions/upload-artifact@v7
with:
name: docker-e2e-package
name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }}
path: .artifacts/docker-e2e-package/openclaw-current.tgz
if-no-files-found: error
- name: Resolve shared Docker E2E image tags
id: image
shell: bash
env:
PACKAGE_TAG: ${{ steps.package.outputs.tag }}
SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }}
PROVIDED_BARE_IMAGE: ${{ inputs.docker_e2e_bare_image }}
PROVIDED_FUNCTIONAL_IMAGE: ${{ inputs.docker_e2e_functional_image }}
run: |
set -euo pipefail
repository="${GITHUB_REPOSITORY,,}"
image_tag="${PACKAGE_TAG:-$SELECTED_SHA}"
bare_image="${PROVIDED_BARE_IMAGE:-ghcr.io/${repository}-docker-e2e-bare:${image_tag}}"
functional_image="${PROVIDED_FUNCTIONAL_IMAGE:-ghcr.io/${repository}-docker-e2e-functional:${image_tag}}"
image="$functional_image"
echo "image=$image" >> "$GITHUB_OUTPUT"
echo "bare_image=$bare_image" >> "$GITHUB_OUTPUT"
echo "functional_image=$functional_image" >> "$GITHUB_OUTPUT"
echo "Shared Docker E2E bare image: \`$bare_image\`" >> "$GITHUB_STEP_SUMMARY"
echo "Shared Docker E2E functional image: \`$functional_image\`" >> "$GITHUB_STEP_SUMMARY"
- name: Log in to GHCR
if: steps.plan.outputs.needs_e2e_image == '1'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
@@ -781,6 +1073,9 @@ jobs:
id: image_exists
if: steps.plan.outputs.needs_e2e_image == '1'
shell: bash
env:
PROVIDED_BARE_IMAGE: ${{ inputs.docker_e2e_bare_image }}
PROVIDED_FUNCTIONAL_IMAGE: ${{ inputs.docker_e2e_functional_image }}
run: |
set -euo pipefail
bare_exists=0
@@ -791,6 +1086,9 @@ jobs:
if docker manifest inspect "${{ steps.image.outputs.bare_image }}" >/dev/null 2>&1; then
bare_exists=1
echo "Shared Docker E2E bare image already exists: ${{ steps.image.outputs.bare_image }}"
elif [[ -n "$PROVIDED_BARE_IMAGE" ]]; then
echo "Provided bare Docker E2E image does not exist: $PROVIDED_BARE_IMAGE" >&2
exit 1
else
needs_build=1
fi
@@ -800,6 +1098,9 @@ jobs:
if docker manifest inspect "${{ steps.image.outputs.functional_image }}" >/dev/null 2>&1; then
functional_exists=1
echo "Shared Docker E2E functional image already exists: ${{ steps.image.outputs.functional_image }}"
elif [[ -n "$PROVIDED_FUNCTIONAL_IMAGE" ]]; then
echo "Provided functional Docker E2E image does not exist: $PROVIDED_FUNCTIONAL_IMAGE" >&2
exit 1
else
needs_build=1
fi
@@ -815,14 +1116,12 @@ jobs:
- name: Build and push bare Docker E2E image
if: steps.plan.outputs.needs_bare_image == '1' && steps.image_exists.outputs.bare_exists != '1'
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
with:
context: .
file: ./scripts/e2e/Dockerfile
target: bare
platforms: linux/amd64
cache-from: type=gha,scope=docker-e2e-bare
cache-to: type=gha,mode=max,scope=docker-e2e-bare
tags: ${{ steps.image.outputs.bare_image }}
sbom: true
provenance: mode=max
@@ -830,7 +1129,7 @@ jobs:
- name: Build and push functional Docker E2E image
if: steps.plan.outputs.needs_functional_image == '1' && steps.image_exists.outputs.functional_exists != '1'
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
with:
context: .
file: ./scripts/e2e/Dockerfile
@@ -838,10 +1137,6 @@ jobs:
build-contexts: |
openclaw_package=.artifacts/docker-e2e-package
platforms: linux/amd64
cache-from: |
type=gha,scope=docker-e2e-bare
type=gha,scope=docker-e2e-functional
cache-to: type=gha,mode=max,scope=docker-e2e-functional
tags: ${{ steps.image.outputs.functional_image }}
sbom: true
provenance: mode=max
@@ -850,7 +1145,7 @@ jobs:
validate_live_models_docker:
name: Docker live models (${{ matrix.provider_label }})
needs: validate_selected_ref
if: inputs.include_live_suites
if: inputs.include_live_suites && inputs.live_model_providers == ''
runs-on: ubuntu-24.04
timeout-minutes: 75
strategy:
@@ -964,6 +1259,163 @@ jobs:
- name: Run Docker live model sweep
run: pnpm test:docker:live-models
validate_live_models_docker_targeted:
name: Docker live models (selected providers)
needs: validate_selected_ref
if: inputs.include_live_suites && inputs.live_model_providers != ''
runs-on: ubuntu-24.04
timeout-minutes: 75
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
REQUESTED_LIVE_MODEL_PROVIDERS: ${{ inputs.live_model_providers }}
OPENCLAW_VITEST_MAX_WORKERS: "2"
steps:
- name: Checkout selected ref
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
install-bun: "true"
- name: Normalize provider allowlist
shell: bash
run: |
set -euo pipefail
all_providers=(anthropic google minimax openai opencode-go openrouter xai zai fireworks)
normalize_provider() {
local value="${1,,}"
case "$value" in
z.ai|z-ai) echo "zai" ;;
opencode|opencode-go) echo "opencode-go" ;;
open-router|openrouter) echo "openrouter" ;;
*) echo "$value" ;;
esac
}
is_known_provider() {
local value="$1"
local provider
for provider in "${all_providers[@]}"; do
[[ "$provider" == "$value" ]] && return 0
done
return 1
}
selected=()
declare -A seen=()
raw="${REQUESTED_LIVE_MODEL_PROVIDERS:-}"
normalized_all="${raw,,}"
normalized_all="${normalized_all//[[:space:],]/}"
if [[ -z "$normalized_all" || "$normalized_all" == "all" ]]; then
selected=("${all_providers[@]}")
else
while IFS= read -r entry; do
[[ -z "$entry" ]] && continue
provider="$(normalize_provider "$entry")"
if ! is_known_provider "$provider"; then
echo "Unknown live model provider '${entry}'. Expected one of: ${all_providers[*]}" >&2
exit 1
fi
if [[ -z "${seen[$provider]:-}" ]]; then
selected+=("$provider")
seen[$provider]=1
fi
done < <(printf '%s\n' "$raw" | tr ',' '\n' | tr '[:space:]' '\n')
fi
if [[ "${#selected[@]}" -eq 0 ]]; then
echo "No live model providers selected." >&2
exit 1
fi
providers_csv="$(IFS=,; echo "${selected[*]}")"
echo "OPENCLAW_LIVE_PROVIDERS=$providers_csv" >> "$GITHUB_ENV"
{
echo "Live model providers: \`$providers_csv\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Hydrate live auth/profile inputs
run: bash scripts/ci-hydrate-live-auth.sh
- name: Validate provider credentials
shell: bash
run: |
set -euo pipefail
require_any() {
local label="$1"
shift
local key
for key in "$@"; do
if [[ -n "${!key:-}" ]]; then
return 0
fi
done
echo "Missing credential for ${label}: expected one of $*" >&2
exit 1
}
IFS=',' read -r -a providers <<<"${OPENCLAW_LIVE_PROVIDERS}"
for provider in "${providers[@]}"; do
case "$provider" in
anthropic) require_any Anthropic ANTHROPIC_API_KEY ANTHROPIC_API_KEY_OLD ANTHROPIC_API_TOKEN ;;
google) require_any Google GEMINI_API_KEY GOOGLE_API_KEY ;;
minimax) require_any MiniMax MINIMAX_API_KEY ;;
openai) require_any OpenAI OPENAI_API_KEY ;;
opencode-go) require_any OpenCode OPENCODE_API_KEY OPENCODE_ZEN_API_KEY ;;
openrouter) require_any OpenRouter OPENROUTER_API_KEY ;;
xai) require_any xAI XAI_API_KEY ;;
zai) require_any Z.ai ZAI_API_KEY Z_AI_API_KEY ;;
fireworks) require_any Fireworks FIREWORKS_API_KEY ;;
*)
echo "Unhandled live model provider shard: ${provider}" >&2
exit 1
;;
esac
done
- name: Run Docker live model sweep
run: pnpm test:docker:live-models
validate_live_provider_suites:
needs: validate_selected_ref
if: inputs.include_live_suites && !inputs.live_models_only
@@ -973,30 +1425,59 @@ jobs:
fail-fast: false
matrix:
include:
- suite_id: live-all
label: pnpm test:live
command: pnpm test:live
timeout_minutes: 180
- suite_id: native-live-src-agents
label: Native live agents
command: node scripts/test-live-shard.mjs native-live-src-agents
timeout_minutes: 90
needs_ffmpeg: false
profile_env_only: false
- suite_id: native-live-src-gateway
label: Native live gateway
command: node scripts/test-live-shard.mjs native-live-src-gateway
timeout_minutes: 90
needs_ffmpeg: false
profile_env_only: false
- suite_id: native-live-test
label: Native live test harnesses
command: node scripts/test-live-shard.mjs native-live-test
timeout_minutes: 90
needs_ffmpeg: false
profile_env_only: false
- suite_id: native-live-extensions-a-k
label: Native live plugins A-K
command: node scripts/test-live-shard.mjs native-live-extensions-a-k
timeout_minutes: 90
needs_ffmpeg: true
profile_env_only: false
- suite_id: native-live-extensions-l-z
label: Native live plugins L-Z
command: node scripts/test-live-shard.mjs native-live-extensions-l-z
timeout_minutes: 90
needs_ffmpeg: true
profile_env_only: false
- suite_id: live-gateway-docker
label: Docker live gateway
command: pnpm test:docker:live-gateway
timeout_minutes: 120
needs_ffmpeg: false
profile_env_only: false
- suite_id: live-cli-backend-docker
label: Docker live CLI backend
command: pnpm test:docker:live-cli-backend
timeout_minutes: 120
needs_ffmpeg: false
profile_env_only: false
- suite_id: live-acp-bind-docker
label: Docker live ACP bind
command: pnpm test:docker:live-acp-bind
timeout_minutes: 120
needs_ffmpeg: false
profile_env_only: false
- suite_id: live-codex-harness-docker
label: Docker live Codex harness
command: pnpm test:docker:live-codex-harness
timeout_minutes: 120
needs_ffmpeg: false
profile_env_only: false
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -1063,6 +1544,25 @@ jobs:
- name: Hydrate live auth/profile inputs
run: bash scripts/ci-hydrate-live-auth.sh
- name: Install live media dependencies
if: matrix.needs_ffmpeg
shell: bash
run: |
set -euo pipefail
if ! command -v ffmpeg >/dev/null 2>&1; then
for attempt in 1 2 3; do
if sudo apt-get update -o Acquire::Retries=3; then
break
fi
if [[ "${attempt}" == "3" ]]; then
exit 1
fi
sleep $((attempt * 5))
done
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ffmpeg
fi
ffmpeg -version | head -1
- name: Configure suite-specific env
shell: bash
run: |

View File

@@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
ref:
description: Existing release tag or current full 40-character workflow-branch commit SHA to validate (for example v2026.4.12 or 0123456789abcdef0123456789abcdef01234567)
description: Branch, tag, or full commit SHA to validate
required: true
type: string
provider:
@@ -63,8 +63,8 @@ jobs:
RELEASE_REF: ${{ inputs.ref }}
run: |
set -euo pipefail
if [[ ! "${RELEASE_REF}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-beta\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]] && [[ ! "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "Expected an existing release tag or current full 40-character workflow-branch commit SHA, got: ${RELEASE_REF}" >&2
if [[ -z "${RELEASE_REF// }" ]] || [[ "${RELEASE_REF}" == -* ]]; then
echo "Expected a branch, tag, or full commit SHA; got: ${RELEASE_REF}" >&2
exit 1
fi
@@ -78,24 +78,27 @@ jobs:
id: ref
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Validate selected ref is on workflow branch
- name: Validate selected ref belongs to this repository
env:
RELEASE_REF: ${{ inputs.ref }}
WORKFLOW_REF_NAME: ${{ github.ref_name }}
run: |
set -euo pipefail
RELEASE_BRANCH_REF="refs/remotes/origin/${WORKFLOW_REF_NAME}"
git fetch --no-tags origin "+refs/heads/${WORKFLOW_REF_NAME}:refs/remotes/origin/${WORKFLOW_REF_NAME}"
if [[ "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then
BRANCH_SHA="$(git rev-parse "${RELEASE_BRANCH_REF}")"
if [[ "$(git rev-parse HEAD)" != "${BRANCH_SHA}" ]]; then
echo "Commit SHA mode only supports the current ${WORKFLOW_REF_NAME} HEAD. Use a release tag for older commits." >&2
exit 1
fi
else
git merge-base --is-ancestor HEAD "${RELEASE_BRANCH_REF}"
SELECTED_SHA="$(git rev-parse HEAD)"
git fetch --no-tags origin '+refs/heads/*:refs/remotes/origin/*'
git fetch --tags origin '+refs/tags/*:refs/tags/*'
if git tag --points-at "${SELECTED_SHA}" | grep -Eq '^v'; then
exit 0
fi
if git for-each-ref --format='%(refname:short)' --contains "${SELECTED_SHA}" refs/remotes/origin | grep -Eq '^origin/'; then
exit 0
fi
echo "Ref '${RELEASE_REF}' resolved to ${SELECTED_SHA}, but that commit is not reachable from an OpenClaw branch or release tag." >&2
echo "Secret-bearing release checks only run repository-owned branch/tag history, not arbitrary unreferenced commits." >&2
exit 1
- name: Capture selected inputs
id: inputs
env:
@@ -155,6 +158,7 @@ jobs:
live_and_e2e_release_checks:
needs: [resolve_target]
permissions:
actions: read
contents: read
packages: write
pull-requests: read
@@ -211,6 +215,70 @@ jobs:
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
package_acceptance_release_checks:
name: Run package acceptance
needs: [resolve_target]
permissions:
actions: read
contents: read
packages: write
pull-requests: read
uses: ./.github/workflows/package-acceptance.yml
with:
workflow_ref: ${{ github.ref_name }}
source: ref
package_ref: ${{ needs.resolve_target.outputs.ref }}
suite_profile: custom
docker_lanes: bundled-channel-deps-compat plugins-offline
telegram_mode: mock-openai
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
FAL_KEY: ${{ secrets.FAL_KEY }}
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
qa_lab_parity_release_checks:
name: Run QA Lab parity gate
needs: [resolve_target]
@@ -332,19 +400,27 @@ jobs:
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS: "3000"
run: |
set -euo pipefail
output_dir=".artifacts/qa-e2e/matrix-live-release-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
echo "output_dir=${output_dir}" >> "$GITHUB_OUTPUT"
pnpm openclaw qa matrix \
matrix_args=(
--repo-root . \
--output-dir "${output_dir}" \
--provider-mode live-frontier \
--model "${OPENCLAW_CI_OPENAI_MODEL}" \
--alt-model "${OPENCLAW_CI_OPENAI_MODEL}" \
--profile fast \
--fast
)
if pnpm openclaw qa matrix --help 2>/dev/null | grep -F -q -- "--fail-fast"; then
matrix_args+=(--fail-fast)
fi
pnpm openclaw qa matrix "${matrix_args[@]}"
- name: Upload Matrix QA artifacts
if: always()
@@ -438,3 +514,40 @@ jobs:
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: warn
summary:
name: Verify release checks
needs:
- install_smoke_release_checks
- cross_os_release_checks
- live_and_e2e_release_checks
- package_acceptance_release_checks
- qa_lab_parity_release_checks
- qa_live_matrix_release_checks
- qa_live_telegram_release_checks
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Verify release check results
shell: bash
run: |
set -euo pipefail
failed=0
for item in \
"install_smoke_release_checks=${{ needs.install_smoke_release_checks.result }}" \
"cross_os_release_checks=${{ needs.cross_os_release_checks.result }}" \
"live_and_e2e_release_checks=${{ needs.live_and_e2e_release_checks.result }}" \
"package_acceptance_release_checks=${{ needs.package_acceptance_release_checks.result }}" \
"qa_lab_parity_release_checks=${{ needs.qa_lab_parity_release_checks.result }}" \
"qa_live_matrix_release_checks=${{ needs.qa_live_matrix_release_checks.result }}" \
"qa_live_telegram_release_checks=${{ needs.qa_live_telegram_release_checks.result }}"
do
name="${item%%=*}"
result="${item#*=}"
if [[ "$result" != "success" && "$result" != "skipped" ]]; then
echo "::error::${name} ended with ${result}"
failed=1
fi
done
exit "$failed"

518
.github/workflows/package-acceptance.yml vendored Normal file
View File

@@ -0,0 +1,518 @@
name: Package Acceptance
on:
workflow_dispatch:
inputs:
workflow_ref:
description: Trusted repo ref for workflow scripts and Docker E2E harness
required: true
default: main
type: string
source:
description: Package candidate source
required: true
default: npm
type: choice
options:
- npm
- ref
- url
- artifact
package_ref:
description: Trusted package source ref when source=ref
required: true
default: main
type: string
package_spec:
description: Published package spec when source=npm
required: false
default: openclaw@beta
type: string
package_url:
description: HTTPS .tgz URL when source=url
required: false
default: ""
type: string
package_sha256:
description: Expected package SHA-256; required for source=url
required: false
default: ""
type: string
artifact_run_id:
description: GitHub Actions run id when source=artifact
required: false
default: ""
type: string
artifact_name:
description: Artifact name containing one .tgz when source=artifact
required: false
default: package-under-test
type: string
suite_profile:
description: Acceptance profile
required: true
default: package
type: choice
options:
- smoke
- package
- product
- full
- custom
docker_lanes:
description: Comma/space separated Docker lanes when suite_profile=custom
required: false
default: ""
type: string
telegram_mode:
description: Optional Telegram QA lane for the resolved package candidate
required: true
default: none
type: choice
options:
- none
- mock-openai
- live-frontier
workflow_call:
inputs:
workflow_ref:
description: Trusted repo ref for workflow scripts and Docker E2E harness
required: false
default: main
type: string
source:
description: "Package candidate source: npm, ref, url, or artifact"
required: true
type: string
package_ref:
description: Trusted package source ref when source=ref
required: false
default: main
type: string
package_spec:
description: Published package spec when source=npm
required: false
default: openclaw@beta
type: string
package_url:
description: HTTPS .tgz URL when source=url
required: false
default: ""
type: string
package_sha256:
description: Expected package SHA-256; required for source=url
required: false
default: ""
type: string
artifact_run_id:
description: GitHub Actions run id when source=artifact
required: false
default: ""
type: string
artifact_name:
description: Artifact name containing one .tgz when source=artifact
required: false
default: package-under-test
type: string
suite_profile:
description: "Acceptance profile: smoke, package, product, full, or custom"
required: false
default: package
type: string
docker_lanes:
description: Comma/space separated Docker lanes when suite_profile=custom
required: false
default: ""
type: string
telegram_mode:
description: Optional Telegram QA lane for the resolved package candidate
required: false
default: none
type: string
secrets:
OPENAI_API_KEY:
required: false
OPENAI_BASE_URL:
required: false
ANTHROPIC_API_KEY:
required: false
ANTHROPIC_API_KEY_OLD:
required: false
ANTHROPIC_API_TOKEN:
required: false
BYTEPLUS_API_KEY:
required: false
CEREBRAS_API_KEY:
required: false
DASHSCOPE_API_KEY:
required: false
GROQ_API_KEY:
required: false
KIMI_API_KEY:
required: false
MODELSTUDIO_API_KEY:
required: false
MOONSHOT_API_KEY:
required: false
MISTRAL_API_KEY:
required: false
MINIMAX_API_KEY:
required: false
OPENCODE_API_KEY:
required: false
OPENCODE_ZEN_API_KEY:
required: false
OPENCLAW_LIVE_BROWSER_CDP_URL:
required: false
OPENCLAW_LIVE_SETUP_TOKEN:
required: false
OPENCLAW_LIVE_SETUP_TOKEN_MODEL:
required: false
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE:
required: false
OPENCLAW_LIVE_SETUP_TOKEN_VALUE:
required: false
GEMINI_API_KEY:
required: false
GOOGLE_API_KEY:
required: false
OPENROUTER_API_KEY:
required: false
QWEN_API_KEY:
required: false
FAL_KEY:
required: false
RUNWAY_API_KEY:
required: false
DEEPGRAM_API_KEY:
required: false
TOGETHER_API_KEY:
required: false
VYDRA_API_KEY:
required: false
XAI_API_KEY:
required: false
ZAI_API_KEY:
required: false
Z_AI_API_KEY:
required: false
BYTEPLUS_ACCESS_KEY_ID:
required: false
BYTEPLUS_SECRET_ACCESS_KEY:
required: false
CLAUDE_CODE_OAUTH_TOKEN:
required: false
OPENCLAW_CODEX_AUTH_JSON:
required: false
OPENCLAW_CODEX_CONFIG_TOML:
required: false
OPENCLAW_CLAUDE_JSON:
required: false
OPENCLAW_CLAUDE_CREDENTIALS_JSON:
required: false
OPENCLAW_CLAUDE_SETTINGS_JSON:
required: false
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON:
required: false
OPENCLAW_GEMINI_SETTINGS_JSON:
required: false
FIREWORKS_API_KEY:
required: false
OPENCLAW_QA_CONVEX_SITE_URL:
required: false
OPENCLAW_QA_CONVEX_SECRET_CI:
required: false
permissions:
actions: read
contents: read
packages: write
pull-requests: read
concurrency:
group: package-acceptance-${{ github.run_id }}
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "24.x"
PNPM_VERSION: "10.33.0"
PACKAGE_ARTIFACT_NAME: package-under-test
jobs:
resolve_package:
name: Resolve package candidate
runs-on: ubuntu-24.04
timeout-minutes: 60
outputs:
docker_lanes: ${{ steps.profile.outputs.docker_lanes }}
include_live_suites: ${{ steps.profile.outputs.include_live_suites }}
include_openwebui: ${{ steps.profile.outputs.include_openwebui }}
include_release_path_suites: ${{ steps.profile.outputs.include_release_path_suites }}
package_artifact_name: ${{ steps.profile.outputs.package_artifact_name }}
package_sha256: ${{ steps.resolve.outputs.sha256 }}
package_version: ${{ steps.resolve.outputs.package_version }}
telegram_enabled: ${{ steps.profile.outputs.telegram_enabled }}
telegram_mode: ${{ steps.profile.outputs.telegram_mode }}
steps:
- name: Checkout package workflow ref
uses: actions/checkout@v6
with:
ref: ${{ inputs.workflow_ref }}
fetch-depth: 0
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
install-bun: ${{ inputs.source == 'ref' && 'true' || 'false' }}
install-deps: "false"
- name: Download package artifact input
if: inputs.source == 'artifact'
env:
GH_TOKEN: ${{ github.token }}
ARTIFACT_RUN_ID: ${{ inputs.artifact_run_id }}
ARTIFACT_NAME: ${{ inputs.artifact_name }}
shell: bash
run: |
set -euo pipefail
if [[ -z "${ARTIFACT_RUN_ID// }" ]]; then
echo "artifact_run_id is required when source=artifact." >&2
exit 1
fi
if [[ -z "${ARTIFACT_NAME// }" ]]; then
echo "artifact_name is required when source=artifact." >&2
exit 1
fi
mkdir -p .artifacts/package-candidate-input
gh run download "$ARTIFACT_RUN_ID" -n "$ARTIFACT_NAME" -D .artifacts/package-candidate-input
- name: Resolve package candidate
id: resolve
env:
SOURCE: ${{ inputs.source }}
PACKAGE_REF: ${{ inputs.package_ref }}
PACKAGE_SPEC: ${{ inputs.package_spec }}
PACKAGE_URL: ${{ inputs.package_url }}
PACKAGE_SHA256: ${{ inputs.package_sha256 }}
shell: bash
run: |
set -euo pipefail
artifact_dir=""
if [[ "$SOURCE" == "artifact" ]]; then
artifact_dir=".artifacts/package-candidate-input"
fi
node scripts/resolve-openclaw-package-candidate.mjs \
--source "$SOURCE" \
--package-ref "$PACKAGE_REF" \
--package-spec "$PACKAGE_SPEC" \
--package-url "$PACKAGE_URL" \
--package-sha256 "$PACKAGE_SHA256" \
--artifact-dir "${artifact_dir:-.}" \
--output-dir .artifacts/docker-e2e-package \
--output-name openclaw-current.tgz \
--metadata .artifacts/docker-e2e-package/package-candidate.json \
--github-output "$GITHUB_OUTPUT"
- name: Select acceptance profile
id: profile
env:
SOURCE: ${{ inputs.source }}
SUITE_PROFILE: ${{ inputs.suite_profile }}
CUSTOM_DOCKER_LANES: ${{ inputs.docker_lanes }}
TELEGRAM_MODE: ${{ inputs.telegram_mode }}
shell: bash
run: |
set -euo pipefail
include_release_path_suites=false
include_openwebui=false
include_live_suites=false
docker_lanes=""
case "$SUITE_PROFILE" in
smoke)
docker_lanes="npm-onboard-channel-agent gateway-network config-reload"
;;
package)
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch bundled-channel-deps-compat plugins-offline plugin-update"
;;
product)
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch bundled-channel-deps-compat plugins plugin-update mcp-channels cron-mcp-cleanup openai-web-search-minimal openwebui"
include_openwebui=true
;;
full)
include_release_path_suites=true
include_openwebui=true
;;
custom)
docker_lanes="$CUSTOM_DOCKER_LANES"
if [[ -z "${docker_lanes// }" ]]; then
echo "docker_lanes is required when suite_profile=custom." >&2
exit 1
fi
if [[ "$docker_lanes" == *"openwebui"* ]]; then
include_openwebui=true
fi
;;
*)
echo "Unknown suite_profile: $SUITE_PROFILE" >&2
exit 1
;;
esac
telegram_enabled=false
if [[ "$TELEGRAM_MODE" != "none" ]]; then
telegram_enabled=true
fi
{
echo "docker_lanes=$docker_lanes"
echo "include_release_path_suites=$include_release_path_suites"
echo "include_openwebui=$include_openwebui"
echo "include_live_suites=$include_live_suites"
echo "telegram_enabled=$telegram_enabled"
echo "telegram_mode=$TELEGRAM_MODE"
echo "package_artifact_name=${PACKAGE_ARTIFACT_NAME}"
} >> "$GITHUB_OUTPUT"
- name: Upload package-under-test artifact
uses: actions/upload-artifact@v7
with:
name: ${{ env.PACKAGE_ARTIFACT_NAME }}
path: |
.artifacts/docker-e2e-package/openclaw-current.tgz
.artifacts/docker-e2e-package/package-candidate.json
retention-days: 14
if-no-files-found: error
- name: Summarize package candidate
env:
PACKAGE_SHA256: ${{ steps.resolve.outputs.sha256 }}
PACKAGE_VERSION: ${{ steps.resolve.outputs.package_version }}
PACKAGE_REF: ${{ inputs.package_ref }}
SOURCE: ${{ inputs.source }}
SUITE_PROFILE: ${{ inputs.suite_profile }}
WORKFLOW_REF: ${{ inputs.workflow_ref }}
shell: bash
run: |
{
echo "## Package acceptance"
echo
echo "- Source: \`${SOURCE}\`"
echo "- Workflow ref: \`${WORKFLOW_REF}\`"
if [[ "${SOURCE}" == "ref" ]]; then
echo "- Package ref: \`${PACKAGE_REF}\`"
fi
echo "- Version: \`${PACKAGE_VERSION}\`"
echo "- SHA-256: \`${PACKAGE_SHA256}\`"
echo "- Profile: \`${SUITE_PROFILE}\`"
} >> "$GITHUB_STEP_SUMMARY"
docker_acceptance:
name: Docker product acceptance
needs: resolve_package
uses: ./.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
with:
ref: ${{ inputs.workflow_ref }}
include_repo_e2e: false
include_release_path_suites: ${{ needs.resolve_package.outputs.include_release_path_suites == 'true' }}
include_openwebui: ${{ needs.resolve_package.outputs.include_openwebui == 'true' }}
docker_lanes: ${{ needs.resolve_package.outputs.docker_lanes }}
package_artifact_name: ${{ needs.resolve_package.outputs.package_artifact_name }}
include_live_suites: ${{ needs.resolve_package.outputs.include_live_suites == 'true' }}
live_models_only: false
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
FAL_KEY: ${{ secrets.FAL_KEY }}
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
package_telegram:
name: Telegram package acceptance
needs: resolve_package
if: needs.resolve_package.outputs.telegram_enabled == 'true'
uses: ./.github/workflows/npm-telegram-beta-e2e.yml
with:
package_spec: ${{ inputs.package_spec }}
package_artifact_name: ${{ needs.resolve_package.outputs.package_artifact_name }}
package_label: openclaw@${{ needs.resolve_package.outputs.package_version }}
harness_ref: ${{ inputs.source == 'ref' && inputs.package_ref || inputs.workflow_ref }}
provider_mode: ${{ needs.resolve_package.outputs.telegram_mode }}
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
summary:
name: Verify package acceptance
needs: [resolve_package, docker_acceptance, package_telegram]
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Verify package acceptance results
env:
DOCKER_RESULT: ${{ needs.docker_acceptance.result }}
PACKAGE_TELEGRAM_RESULT: ${{ needs.package_telegram.result }}
RESOLVE_RESULT: ${{ needs.resolve_package.result }}
shell: bash
run: |
set -euo pipefail
failed=0
for item in \
"resolve_package=${RESOLVE_RESULT}" \
"docker_acceptance=${DOCKER_RESULT}" \
"package_telegram=${PACKAGE_TELEGRAM_RESULT}"
do
name="${item%%=*}"
result="${item#*=}"
if [[ "$result" != "success" && "$result" != "skipped" ]]; then
echo "::error::${name} ended with ${result}"
failed=1
fi
done
exit "$failed"

View File

@@ -18,6 +18,19 @@ on:
description: Optional comma-separated Discord scenario ids
required: false
type: string
matrix_profile:
description: Matrix QA profile for the live Matrix lane
required: false
default: all
type: choice
options:
- fast
- all
- transport
- media
- e2ee-smoke
- e2ee-deep
- e2ee-cli
permissions:
contents: read
@@ -199,6 +212,7 @@ jobs:
run_live_matrix:
name: Run Matrix live QA lane
needs: [authorize_actor, validate_selected_ref]
if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.matrix_profile == 'all') }}
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 60
environment: qa-live-shared
@@ -236,20 +250,29 @@ jobs:
shell: bash
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
INPUT_MATRIX_PROFILE: ${{ github.event_name == 'workflow_dispatch' && inputs.matrix_profile || 'fast' }}
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS: "3000"
run: |
set -euo pipefail
output_dir=".artifacts/qa-e2e/matrix-live-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
echo "output_dir=${output_dir}" >> "$GITHUB_OUTPUT"
pnpm openclaw qa matrix \
matrix_args=(
--repo-root . \
--output-dir "${output_dir}" \
--provider-mode live-frontier \
--model "${OPENCLAW_CI_OPENAI_MODEL}" \
--alt-model "${OPENCLAW_CI_OPENAI_MODEL}" \
--profile "${INPUT_MATRIX_PROFILE}" \
--fast
)
if pnpm openclaw qa matrix --help 2>/dev/null | grep -F -q -- "--fail-fast"; then
matrix_args+=(--fail-fast)
fi
pnpm openclaw qa matrix "${matrix_args[@]}"
- name: Upload Matrix QA artifacts
if: always()
@@ -260,6 +283,88 @@ jobs:
retention-days: 14
if-no-files-found: warn
run_live_matrix_sharded:
name: Run Matrix live QA lane (${{ matrix.profile }})
needs: [authorize_actor, validate_selected_ref]
if: ${{ github.event_name == 'workflow_dispatch' && inputs.matrix_profile == 'all' }}
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 60
environment: qa-live-shared
strategy:
fail-fast: false
matrix:
profile:
- transport
- media
- e2ee-smoke
- e2ee-deep
- e2ee-cli
steps:
- name: Checkout selected ref
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
install-bun: "true"
- name: Validate required QA credential env
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
shell: bash
run: |
set -euo pipefail
if [[ -z "${OPENAI_API_KEY:-}" ]]; then
echo "Missing required OPENAI_API_KEY." >&2
exit 1
fi
- name: Build private QA runtime
run: pnpm build
- name: Run Matrix live lane shard
id: run_lane
shell: bash
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS: "3000"
run: |
set -euo pipefail
output_dir=".artifacts/qa-e2e/matrix-live-${{ matrix.profile }}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
echo "output_dir=${output_dir}" >> "$GITHUB_OUTPUT"
matrix_args=(
--repo-root . \
--output-dir "${output_dir}" \
--provider-mode live-frontier \
--model "${OPENCLAW_CI_OPENAI_MODEL}" \
--alt-model "${OPENCLAW_CI_OPENAI_MODEL}" \
--profile "${{ matrix.profile }}" \
--fast
)
if pnpm openclaw qa matrix --help 2>/dev/null | grep -F -q -- "--fail-fast"; then
matrix_args+=(--fail-fast)
fi
pnpm openclaw qa matrix "${matrix_args[@]}"
- name: Upload Matrix QA shard artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: qa-live-matrix-${{ matrix.profile }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ steps.run_lane.outputs.output_dir }}
retention-days: 14
if-no-files-found: warn
run_live_telegram:
name: Run Telegram live QA lane with Convex leases
needs: [authorize_actor, validate_selected_ref]

View File

@@ -41,7 +41,7 @@ jobs:
days-before-pr-close: 3
stale-issue-label: stale
stale-pr-label: stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
exempt-pr-labels: maintainer,no-stale,bad-barnacle
operations-per-run: 2000
ascending: true
@@ -60,7 +60,7 @@ jobs:
close-issue-reason: not_planned
close-pr-message: |
Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.
- name: Mark stale assigned issues (primary)
id: assigned-issue-stale-primary
@@ -73,7 +73,7 @@ jobs:
days-before-pr-stale: -1
days-before-pr-close: -1
stale-issue-label: stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
operations-per-run: 2000
ascending: true
include-only-assigned: true
@@ -108,7 +108,7 @@ jobs:
Please add updates or it will be closed.
close-pr-message: |
Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.
- name: Check stale state cache
id: stale-state
@@ -145,7 +145,7 @@ jobs:
days-before-pr-close: 3
stale-issue-label: stale
stale-pr-label: stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
exempt-pr-labels: maintainer,no-stale,bad-barnacle
operations-per-run: 2000
ascending: true
@@ -164,7 +164,7 @@ jobs:
close-issue-reason: not_planned
close-pr-message: |
Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.
- name: Mark stale assigned issues (fallback)
if: (steps.assigned-issue-stale-primary.outcome == 'failure' || steps.stale-state.outputs.has_state == 'true') && steps.app-token-fallback.outputs.token != ''
@@ -176,7 +176,7 @@ jobs:
days-before-pr-stale: -1
days-before-pr-close: -1
stale-issue-label: stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
operations-per-run: 2000
ascending: true
include-only-assigned: true
@@ -210,7 +210,7 @@ jobs:
Please add updates or it will be closed.
close-pr-message: |
Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
If you believe this PR should be revived, post in #clawtributors on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.
lock-closed-issues:

View File

@@ -50,11 +50,14 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
- Prod sweep: `pnpm check`; tests: `pnpm test`, `pnpm test:changed`, `pnpm test:serial`, `pnpm test:coverage`.
- Extension tests: `pnpm test:extensions`, `pnpm test extensions`, `pnpm test extensions/<id>`.
- Targeted tests: `pnpm test <path-or-filter> [vitest args...]`; never raw `vitest`.
- Vitest flags only; no Jest flags like `--runInBand`. For serial runs use `pnpm test:serial` or `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test ...`.
- Typecheck: `tsgo` lanes only (`pnpm tsgo*`, `pnpm check:test-types`); do not add `tsc --noEmit`, `typecheck`, `check:types`.
- Formatting: use `oxfmt`, not Prettier. Prefer `pnpm format:check` / `pnpm format`; for targeted files use `pnpm exec oxfmt --check --threads=1 <files...>` or `pnpm exec oxfmt --write --threads=1 <files...>`.
- Linting: use repo wrappers (`pnpm lint:*`, `scripts/run-oxlint.mjs`); do not invoke generic JS formatters/lints unless a repo script uses them.
- Heavy checks: `OPENCLAW_LOCAL_CHECK=1`, mode `OPENCLAW_LOCAL_CHECK_MODE=throttled|full`; CI/shared use `OPENCLAW_LOCAL_CHECK=0`.
- Local first. Use repo `pnpm` lanes before Blacksmith/Testbox. Remote only for parity-only failures, secrets/services, or explicit ask.
- Blacksmith/Testbox is maintainer opt-in, not a repo-wide default. If Blacksmith access is available and `OPENCLAW_TESTBOX=1` is set, or a maintainer's personal AGENTS rules ask for it, use Testbox for broad, slow, Docker, live, E2E, full-suite, or CI-parity validation instead of running those heavy lanes locally. Use `OPENCLAW_LOCAL_CHECK_MODE=throttled|full` as the explicit local escape hatch.
- Testbox use: run from repo root, pre-warm early with `blacksmith testbox warmup ci-check-testbox.yml --ref main --idle-timeout 90`, reuse the returned `tbx_...` id for all `run`/`download` commands, and stop boxes you created before handoff. Timeout bins: `90` minutes default, `240` multi-hour, `720` all-day, `1440` overnight; anything above `1440` needs explicit approval and cleanup.
- Testbox full-suite profile: `blacksmith testbox run --id <ID> "env NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_TEST_PROJECTS_PARALLEL=6 OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test"`. For installable package proof, prefer the GitHub `Package Acceptance` workflow over ad hoc Testbox commands.
## GitHub / CI
@@ -128,6 +131,7 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
- Prefer injection; if module mocking, mock narrow local `*.runtime.ts`, not broad barrels or `openclaw/plugin-sdk/*`.
- Share fixtures/builders; delete duplicate assertions; assert behavior that can regress here.
- Do not edit baseline/inventory/ignore/snapshot/expected-failure files to silence checks without explicit approval.
- Do not run multiple independent `pnpm test`/Vitest commands concurrently in the same worktree. They can race on `node_modules/.experimental-vitest-cache` and fail with `ENOTEMPTY`. Use one grouped `pnpm test ...` invocation, run targeted lanes sequentially, or set distinct `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH` values when true parallel Vitest processes are needed.
- Test workers max 16. Memory pressure: `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`.
- Live: `OPENCLAW_LIVE_TEST=1 pnpm test:live`; verbose `OPENCLAW_LIVE_TEST_QUIET=0`.
- Guide: `docs/help/testing.md`.
@@ -136,7 +140,7 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
- Docs change with behavior/API. Use docs list/read_when hints; docs links per `docs/AGENTS.md`.
- Changelog user-facing only; pure test/internal usually no entry.
- Changelog placement: active version `### Changes`/`### Fixes`; every added entry must include at least one `Thanks @author` attribution, using credited GitHub username(s). Never add `Thanks @steipete` or `Thanks @codex`.
- Changelog placement: active version `### Changes`/`### Fixes`; every added entry must include at least one `Thanks @author` attribution, using credited GitHub username(s). Never add `Thanks @codex`, `Thanks @openclaw`, or `Thanks @steipete`.
- Changelog bullets are always single-line. No wrapping/continuation across multiple lines. Long entries stay on one long line so dedupe, PR-ref, and credit-audit tooling work and so the visual style stays uniform.
## Git

View File

@@ -4,11 +4,188 @@ Docs: https://docs.openclaw.ai
## Unreleased
## 2026.4.26
### Changes
- Channels/QQBot: add full group chat support (history tracking, @-mention gating, activation modes, per-group config, FIFO message queue with deliver debounce), C2C `stream_messages` streaming with a `StreamingController` lifecycle manager, unified `sendMedia` with chunked upload for large files, and refactor the engine into pipeline stages, focused outbound submodules, builtin slash-command modules, and explicit DI ports via `createEngineAdapters()`. (#70624) Thanks @cxyhhhhh.
- Channels/Yuanbao: register the Tencent Yuanbao external channel plugin (`openclaw-plugin-yuanbao`) in the official channel catalog, contract suites, and community plugin docs, with a new `docs/channels/yuanbao.md` quick-start guide for WebSocket bot DMs and group chats. (#72756) Thanks @loongfay.
- Control UI/Talk: add a generic browser realtime transport contract, Google Live browser Talk sessions with constrained ephemeral tokens, and a Gateway relay for backend-only realtime voice plugins. Thanks @VACInc.
- CLI/models: route provider-filtered model listing through an explicit source plan so user config, installed manifest rows, Provider Index previews, and scoped runtime fallbacks keep a stable authority order without adding another catalog cache. Thanks @shakkernerd.
- Providers: add Cerebras as a bundled plugin with onboarding, static model catalog, docs, and manifest-owned endpoint metadata.
- Memory/OpenAI-compatible: add optional `memorySearch.inputType`, `queryInputType`, and `documentInputType` config for asymmetric embedding endpoints, including direct query embeddings and provider batch indexing. Carries forward #63313 and #60727. Thanks @HOYALIM and @prospect1314521.
- Ollama/memory: add model-specific retrieval query prefixes for `nomic-embed-text`, `qwen3-embedding`, and `mxbai-embed-large` memory-search queries while leaving document batches unchanged. Carries forward #45013. Thanks @laolin5564.
- Plugins/providers: move pre-runtime model-id normalization, endpoint host metadata, OpenAI-compatible request-family hints, model-catalog aliases/suppressions, OpenAI stale Spark suppression, and reusable startup metadata snapshots into plugin manifests so core no longer carries bundled-provider routing tables or repeated manifest rebuilds. Thanks @shakkernerd.
- Plugins/config: deprecate direct plugin config load/write helpers in favor of passed runtime snapshots plus transactional mutation helpers with explicit restart follow-up policy, scanner guardrails, runtime warnings, and revision-based cache invalidation.
- Plugins/install: allow `OPENCLAW_PLUGIN_STAGE_DIR` to contain layered runtime-dependency roots, resolving read-only preinstalled deps before installing missing deps into the final writable root. Fixes #72396. Thanks @liorb-mountapps.
- Control UI: add a raw config pending-changes diff panel that parses JSON5, redacts sensitive values until reveal, and avoids fake raw-edit callbacks when opening the panel. Refs #39831; supersedes #48621 and #46654. Thanks @JiajunBernoulli and @BunsDev.
- Control UI: polish the quick settings dashboard grid so common cards align across desktop, tablet, and mobile layouts without wasting horizontal space. Thanks @BunsDev.
- Matrix/E2EE: add `openclaw matrix encryption setup` to enable Matrix encryption, bootstrap recovery, and print verification status from one setup flow. Thanks @gumadeiras.
- Agents/compaction: add an opt-in `agents.defaults.compaction.maxActiveTranscriptBytes` preflight trigger that runs normal local compaction when the active JSONL grows too large, requiring transcript rotation so successful compaction moves future turns onto a smaller successor file instead of raw byte-splitting history. Thanks @vincentkoc.
- CLI/migration: add `openclaw migrate` with plan, dry-run, JSON, pre-migration backup, onboarding detection, archive-only reports, a Claude Code/Desktop importer, and a Hermes importer for configuration, memory/plugin hints, model providers, MCP servers, skills, commands, and supported credentials. Thanks @vincentkoc and @NousResearch.
### Fixes
- Agents/LSP: terminate bundled stdio LSP process trees during runtime disposal and Gateway shutdown, so nested children such as `tsserver` do not survive stop or restart. Fixes #72357. Thanks @ai-hpc and @bittoby.
- Gateway/device tokens: stop echoing rotated bearer tokens from shared/admin `device.token.rotate` responses while preserving the same-device token handoff needed by token-only clients before reconnect. (#66773) Thanks @MoerAI.
- Control UI/Talk: keep Google Live browser sessions on the WebSocket transport instead of falling back to WebRTC, validate browser Google Live WebSocket endpoints, cap Gateway relay sessions per browser connection, and remove stale browser-native voice buttons that did not use the configured Talk/TTS provider. Thanks @BunsDev.
- Gateway/startup: reuse config snapshot plugin manifests for startup auto-enable, config validation, and plugin bootstrap planning, including authored source config and disabled setup-probe handling, so restrictive allowlists avoid duplicate manifest/config passes during boot. Thanks @shakkernerd.
- Agents/subagents: enforce `subagents.allowAgents` for explicit same-agent `sessions_spawn(agentId=...)` calls instead of auto-allowing requester self-targets. Fixes #72827. Thanks @oiGaDio.
- ACP/sessions_spawn: let explicit `sessions_spawn(runtime="acp")` bootstrap turns run while `acp.dispatch.enabled=false` still blocks automatic ACP thread dispatch. Fixes #63591. Thanks @moeedahmed.
- CLI/update: install npm global updates into a verified temporary prefix before swapping the package tree into place, preventing mixed old/new installs and stale packaged files from breaking `openclaw update` verification. Thanks @shakkernerd.
- Gateway: skip CLI startup self-respawn for foreground gateway runs so low-memory Linux/Node 24 hosts start through the same path as direct `dist/index.js` without hanging before logs. Fixes #72720. Thanks @sign-2025.
- Google Meet: route local Chrome joins through OpenClaw browser control, grant Meet media permissions, pin local Chrome audio defaults to `BlackHole 2ch`, and use the configured OpenClaw browser profile so joined agents no longer show `Permission needed` or use raw/default Chrome state. Thanks @DougButdorf and @oromeis.
- Plugins/discovery: follow symlinked plugin directories in global and workspace plugin roots while keeping broken links ignored and existing package safety checks in place. Fixes #36754; carries forward #72695 and #63206. Thanks @Quackstro, @ming1523, and @xsfX20.
- Plugins/install: skip test files and directories during install security scans while still force-scanning declared runtime entrypoints, so packaged test mocks no longer block plugin installs. Fixes #66840; carries forward #67050. Thanks @saurabhjain1592 and @Magicray1217.
- Plugins/install: allow exact package-manager peer links back to the trusted OpenClaw host package during install security scans while continuing to block spoofed or nested escaping `node_modules` symlinks. Carries forward #70819. Thanks @fgabelmannjr.
- Plugins/install: resolve plugin install destinations from the active profile state dir across CLI, ClawHub, marketplace, local path, and channel setup installs, so `openclaw --profile <name> plugins install ...` no longer writes into the default profile. Fixes #69960; carries forward #69971. Thanks @FrancisLyman and @Sanjays2402.
- Plugins/registry: suppress duplicate-plugin startup warnings when a tracked npm-installed plugin intentionally overrides the bundled plugin with the same id. Carries forward #48673. Thanks @abdushsk.
- Plugins/startup: reuse canonical realpath lookups throughout each plugin discovery pass, including package and manifest boundary checks, so Windows npm-global startups no longer repeat expensive path resolution for the same plugin roots. Fixes #65733. Thanks @welfo-beo.
- Gateway/proxy: pass `ALL_PROXY` / `all_proxy` into the global Undici env-proxy dispatcher and provider proxy-fetch helper while keeping SSRF trusted-proxy auto-upgrade on `HTTP_PROXY` / `HTTPS_PROXY` only, so gateway/provider calls honor all-proxy setups without weakening guarded fetches. Fixes #43821; carries forward #43919. Thanks @RickyTong1.
- Reply/link understanding: keep media and link preprocessing on stable runtime entrypoints and continue with raw message content if optional enrichment fails, so URL-bearing messages are no longer dropped after stale runtime chunk upgrades. Fixes #68466. Thanks @songshikang0111.
- Discord: persist routed model-picker overrides when the hidden `/model` dispatch succeeds but the bound thread session store is still stale, including LM Studio suffixed model ids. Carries forward #61473. Thanks @Nanako0129.
- Nodes/CLI: add `openclaw nodes remove --node <id|name|ip>` and `node.pair.remove` so stale gateway-owned node pairing records can be cleaned without hand-editing state files.
- Gateway: include the connecting client and fresh presence version in the initial `hello-ok` snapshot, so clients no longer need a follow-up event before seeing themselves online.
- Docker: install the CA certificate bundle in the slim runtime image so HTTPS calls from containerized gateways no longer fail TLS setup after the `bookworm-slim` base switch. Fixes #72787. Thanks @ryuhaneul.
- Providers/OpenRouter: remove retired Hunter Alpha and Healer Alpha static catalog rows and disable proxy reasoning injection for stale Hunter Alpha configs, so replies are not hidden when OpenRouter returns answer text in reasoning fields. Fixes #43942. Thanks @EvanDataForge.
- Providers/reasoning: let Groq and LM Studio declare provider-native reasoning effort values, so Qwen thinking models receive `none`/`default` or `off`/`on` instead of OpenAI-only `low`/`medium` values. Fixes #32638. Thanks @Aqu1bp, @mgoulart, @Norpps, and @BSTail.
- Local models: default custom providers with only `baseUrl` to the Chat Completions adapter and trust loopback model requests automatically, so local OpenAI-compatible proxies receive `/v1/chat/completions` without timing out. Fixes #40024. Thanks @parachuteshe.
- Channels/message tool: surface Discord, Slack, and Mattermost `user:`/`channel:` target syntax in the shared message target schema and Discord ambiguity errors, so DM sends by numeric id stop burning retries before finding `user:<id>`. Fixes #72401. Thanks @garyd9, @hclsys, and @praveen9354.
- Agents/tools: scope tool-loop detection history to the active run when available, so scheduled heartbeat cycles no longer inherit stale repeated-call counts from previous runs. Fixes #40144. Thanks @mattbrown319.
- Agents/subagents: preserve requester delivery for completion announces across different channel accounts, keep same-channel thread completions routed to the child thread, and fail closed instead of guessing a child binding when requester conversation signal is missing. Thanks @sfuminya and @suyua9.
- Agents/status: persist the post-compaction token estimate from auto-compaction when providers omit usage metadata, so `/status` and session lists keep showing fresh context usage after compaction. Fixes #67667; carries forward #72822. Thanks @Jimmy-xuzimo and @skylight-9.
- Control UI: show loading, reload, and retry states when a lazy dashboard panel cannot load after an upgrade, so the Logs tab no longer appears blank on stale browser bundles. Fixes #72450. Thanks @sobergou.
- Gateway/plugins: start the Gateway in degraded mode when a single plugin entry has invalid schema config, and let `openclaw doctor --fix` quarantine that plugin config instead of crash-looping every channel. Fixes #62976 and #70371. Thanks @Doraemon-Claw and @pksidekyk.
- Agents/plugins: skip malformed plugin tools with missing schema objects and report plugin diagnostics, so one broken tool no longer crashes Anthropic agent runs. Fixes #69423. Thanks @jmnickels.
- Agents/reasoning: recover fully wrapped unclosed `<think>` replies that would otherwise sanitize to empty text while keeping strict stripping for closed reasoning blocks and unclosed tails after visible text. Fixes #37696; supersedes #51915. Thanks @druide67 and @okuyam2y.
- Control UI/Gateway: bind WebChat handshakes to their active socket and reject post-close server registrations, so aborted connects no longer leave zombie clients or misleading duplicate WebSocket connection logs. Fixes #72753. Thanks @LumenFromTheFuture.
- Agents/fallback: split ambiguous provider failures into `empty_response`, `no_error_details`, and `unclassified`, and add flat fallback-step fields to structured fallback logs so primary-model failures stay visible when later fallbacks also fail. Fixes #71922; refs #71744. Thanks @andyk-ms and @nikolaykazakovvs-ux.
- Plugins/Windows: normalize Windows absolute paths before handing bundled plugin modules to Jiti, so Feishu/Lark message sending no longer fails with unsupported `c:` ESM loader URLs. Fixes #72783. Thanks @jackychen-png.
- CLI/doctor: run bundled plugin runtime-dependency repairs through the async npm installer with spinner/line progress and heartbeat updates, so long `openclaw doctor --fix` installs no longer look hung in TTY or piped output. Fixes #72775. Thanks @dfpalhano.
- Feishu/Windows: normalize bundled channel sidecar loads before Jiti evaluates them, so Feishu outbound sends no longer fail with raw `C:` ESM loader errors on Windows. Fixes #72783. Thanks @jackychen-png.
- Agents/tools: ignore volatile `exec` runtime metadata when comparing tool-loop outcomes, so enabled loop detection can stop repeated identical shell-command results instead of resetting on duration, PID, session, or cwd changes. Fixes #34574; supersedes #41502. Thanks @gucasbrg and @Zcg2021.
- Agents/fallback: classify internal live-session model switch conflicts as unknown fallback failures instead of provider overloads, preventing local vLLM endpoints from receiving misleading overloaded cooldowns. Refs #63229. Thanks @clawdia-lobster.
- Discord: let thread sessions inherit the parent channel's session-level `/model` override as a model-only fallback without enabling parent transcript inheritance. Fixes #72755. Thanks @solavrc.
- Gateway/plugins: skip stale configured channels whose matching plugin is no longer discoverable, point cleanup at `openclaw doctor --fix`, and keep unrelated channel typos fatal so one missing channel plugin no longer crash-loops the Gateway. Fixes #53311. Thanks @futhgar.
- Control UI: keep session-specific assistant identity loads authoritative after WebSocket connect, so non-main agent chat sessions do not show the main agent name in the header after bootstrap refreshes. Fixes #72776. Thanks @rockytian-top.
- Agents/Qwen: preserve exact custom `modelstudio` provider configs with foreign `api` owners so explicit OpenAI-compatible Model Studio endpoints no longer get normalized into the bundled Qwen plugin path. Fixes #64483. Thanks @FiredMosquito831.
- MCP/bundle-mcp: normalize CLI-native `type: "http"` MCP server entries to OpenClaw `transport: "streamable-http"` on save, repair existing configs with doctor, and keep embedded Pi from falling back to legacy SSE GET-first startup for those servers. Fixes #72757. Thanks @Studioscale.
- OpenCode: expose Anthropic Opus/Sonnet 4.x thinking levels for proxied Claude models, so `/think xhigh`, `/think adaptive`, and `/think max` validate consistently with the direct Anthropic provider. Fixes #72729. Thanks @haishmg and @aaajiao.
- Media-understanding/audio: migrate deprecated `{input}` placeholders in legacy `audio.transcription.command` configs to `{{MediaPath}}`, so custom audio transcribers no longer receive the literal placeholder after doctor repair. Fixes #72760. Thanks @krisfanue3-hash.
- Ollama/WSL2: warn when GPU-backed WSL2 installs combine CUDA visibility with an autostarting `ollama.service` using `Restart=always`, and document the systemd, `.wslconfig`, and keep-alive mitigation for crash loops. Carries forward #61022; fixes #61185. Thanks @yhyatt.
- Ollama/onboarding: de-dupe suggested bare local models against installed `:latest` tags and skip redundant pulls, so setup shows the installed model once and no longer says it is downloading an already available model. Fixes #68952. Thanks @tleyden.
- Memory-core/doctor: keep `doctor.memory.status` on the cached path by default and only run live embedding pings for explicit deep probes, preventing slow local embedding backends from blocking Gateway status checks. Fixes #71568. Thanks @apex-system.
- Memory/QMD: group same-source collections into one QMD search invocation when the installed QMD supports multiple `-c` filters, while keeping older QMD builds on the per-collection fallback. Fixes #72484; supersedes #72485 and #69583. Thanks @BsnizND and @zeroaltitude.
- Memory/QMD: accept QMD status vector-count variants such as `Vectors = 42`, `Vectors:42`, and `Vectors: 42 embedded`, so `memory status --deep` no longer reports embeddings unavailable for healthy QMD wrappers. Fixes #63652; carries forward #63678. Thanks @apoapostolov and @WarrenJones.
- Memory/QMD: skip QMD vector status probes and embedding maintenance in lexical `searchMode: "search"`, so BM25-only QMD setups on ARM do not trigger llama.cpp/Vulkan builds during status checks or embed cycles. Fixes #59234 and #67113. Thanks @PrinceOfEgypt, @Vksh07, @Snipe76, @NomLom, @t4r3e2q1-commits, and @dmak.
- Memory/QMD: report the live watcher dirty state in memory status, so changed QMD-backed memory files show as dirty until the queued sync finishes. Fixes #60244. Thanks @xinzf.
- Compaction: skip oversized pre-compaction checkpoint snapshots and prune duplicate long user turns from compaction input and rotated successor transcripts, preventing retry storms from being preserved across checkpoint cycles. Fixes #72780. Thanks @SweetSophia.
- Control UI/Cron: render cron job prompts and run summaries as sanitized markdown in the dashboard, with full-width block content, safer link clicks, and no duplicate error text when a failed run has no summary. Supersedes #48504. Thanks @garethdaine.
- Control UI/Gateway: preserve WebChat client version labels across localhost, 127.0.0.1, and IPv6 loopback aliases on the same port, avoiding misleading `vcontrol-ui` connection logs while investigating duplicate-message reports. Refs #72753 and #72742. Thanks @LumenFromTheFuture and @allesgutefy.
- Agents/reasoning: treat orphan closing reasoning tags with following answer text as a privacy boundary across delivery, history, streaming, and Control UI sanitizers so malformed local-model output cannot leak chain-of-thought text. Fixes #67092. Thanks @AnildoSilva.
- Memory-core: run one-shot memory CLI commands through transient builtin and QMD managers so `memory index`, `memory status --index`, and `memory search` no longer start long-lived file watchers that can hit macOS `EMFILE` limits. Fixes #59101; carries forward #49851. Thanks @mbear469210-coder and @maoyuanxue.
- Agents/ACP: ship the Claude ACP adapter with OpenClaw and require Claude result messages before idle can complete a prompt, preventing parent agents from waking early on long-running `sessions_spawn(runtime: "acp", agentId: "claude")` children. Fixes #72080. Thanks @siavash-saki and @iannwu.
- CLI/tasks: route `tasks --json`, `tasks list --json`, and `tasks audit --json` through a lean JSON path so read-only task inspection no longer loads unrelated plugin/runtime command graphs. Fixes #66238. Thanks @ChuckChambers.
- Memory-core: re-resolve the active runtime config whenever `memory_search` or `memory_get` executes, so provider changes made by `config.patch` stop leaving stale embedding backends behind in existing tool instances. Fixes #61098. Thanks @BradGroux and @Linux2010.
- WebChat: keep bare `/new` and `/reset` startup instructions out of visible chat history while preserving `/reset <note>` as user-visible transcript text. Fixes #72369. Thanks @collynes and @haishmg.
- Tasks/memory: checkpoint and truncate SQLite WAL sidecars on a timer and before close for task, Task Flow, proxy capture, and builtin memory databases, bounding long-running gateway `*.sqlite-wal` growth. Fixes #72774. Thanks @dfpalhano.
- CLI/doctor: remove dangling channel config, heartbeat targets, and channel model overrides when stale plugin repair removes a missing channel plugin, preventing Gateway boot loops after failed plugin reinstalls. Fixes #65293. Thanks @yidecode.
- Control UI/Gateway: cache, coalesce, stale-refresh, and invalidate effective tool inventory on channel registry changes while reusing the gateway-bound plugin registry and avoiding model/auth discovery, so chat runs no longer stall Control UI requests on repeated plugin/model setup. Fixes #72365; supersedes #72558. Thanks @Gabiii2398 and @1yihui.
- Channels/setup: treat bundled channel plugins as already bundled during `channels add` and onboarding, enabling them without writing redundant `plugins.load.paths` entries or path install records. Fixes #72740. Thanks @iCodePoet.
- WhatsApp: honor gateway `HTTPS_PROXY` / `HTTP_PROXY` env vars for QR-login WebSocket connections, while respecting `NO_PROXY`, so proxied networks no longer fall back to direct `mmg.whatsapp.net` connections that time out with 408. Fixes #72547; supersedes #72692. Thanks @mebusw and @SymbolStar.
- Bonjour: default mDNS advertisements to the system hostname when it is DNS-safe, avoiding `openclaw.local` probing conflicts and Gateway restart loops on hosts such as `Lobster` or `ubuntu`. Fixes #72355 and #72689; supersedes #72694. Thanks @mscheuerlein-bot, @gcusms, @moyuwuhen601, @pavan987, @zml-0912, @hhq365, and @SymbolStar.
- Agents/OpenAI-compatible: retry replay-safe empty `stop` turns once for `openai-completions` endpoints, so transient empty local backend responses no longer surface as “Agent couldn't generate a response” when a continuation succeeds, and restore `openclaw agent --model` for one-shot CLI runs. Fixes #72751. Thanks @moooV252.
- Git hooks: skip ignored staged paths when formatting and restaging pre-commit files, so merge commits no longer abort when `.gitignore` newly ignores staged merged content. Fixes #72744. Thanks @100yenadmin.
- Memory-core/dreaming: add a supported `dreaming.model` knob for Dream Diary narrative subagents, wired through phase config and the existing plugin subagent model-override trust gate. Refs #65963. Thanks @esqandil and @mjamiv.
- Agents/Anthropic: remove trailing assistant prefill payloads when extended thinking is enabled, so Opus 4.7/Sonnet 4.6 requests do not fail Anthropic's user-final-turn validation. Fixes #72739. Thanks @superandylin.
- Agents/vLLM/Qwen: add plugin-owned Qwen thinking controls for vLLM chat-template kwargs and DashScope-style top-level `enable_thinking` flags, including preserved thinking for agent loops. Fixes #72329. Thanks @stavrostzagadouris.
- Memory-core/dreaming: treat request-scoped narrative fallback as expected, skip session cleanup when no subagent run was created, and remove duplicate phase-level cleanup so fallback no longer emits warning noise. Fixes #67152. Thanks @jsompis.
- Agents/exec: apply configured `tools.exec.timeoutSec` to background, `yieldMs`, and node `system.run` commands when no per-call timeout is set, preventing auto-backgrounded and remote node commands from running indefinitely. Fixes #67600; supersedes #67603. Thanks @dlmpx and @kagura-agent.
- Config/doctor: stop masking unknown-key validation diagnostics such as `agents.defaults.llm`, and have `openclaw doctor --fix` remove the retired `agents.defaults.llm` timeout block. Thanks @aidiffuser.
- CLI/startup: keep the built pre-dispatch CLI graph free of package-level imports and extend packaged CLI smoke coverage to onboard and doctor help paths, preventing missing runtime dependencies such as tslog from killing onboarding before repair code can run. Fixes #63024. Thanks @hu19940121.
- CLI/plugins: preserve unversioned ClawHub install specs so `plugins update` can follow newer ClawHub releases instead of pinning to the initially resolved version. Fixes #63010; supersedes #58426. Thanks @kangsen1234 and @robinspt.
- Memory-core/subagents: tag plugin-created subagent sessions with their plugin owner so dreaming narrative cleanup can delete its own ephemeral sessions without granting broad admin session deletion. Fixes #72712. Thanks @BSG2000.
- Gateway/models: move local-provider pricing opt-outs, OpenRouter/LiteLLM aliases, and proxy passthrough pricing lookup into plugin manifest metadata so core no longer carries extension-specific pricing tables.
- CLI/update: honor `OPENCLAW_NO_AUTO_UPDATE=1` as a gateway startup kill-switch for configured background package auto-updates, so operators can hold a deliberate downgrade during incident recovery without editing config first. Fixes #72715. Thanks @Xivi08.
- Agents/Claude CLI: force live-session launches to include `--output-format stream-json` whenever OpenClaw adds `--input-format stream-json`, so new Claude CLI sessions no longer fail immediately while reusable sessions keep working. Fixes #72206. Thanks @kwangwonkoh and @Xivi08.
- CLI/plugins: accept ClawHub plugin API wildcard ranges such as `*` without rejecting compatible plugin installs, while still requiring a valid runtime API version. Fixes #56446; supersedes #56466. Thanks @darconada and @claygeo.
- CLI/plugins: add an explicit `npm:<package>` install prefix that skips ClawHub lookup for known npm packages while keeping bare package specs ClawHub-first. Fixes #55805; supersedes #54377. Thanks @Zeoy2020 and @vagusX.
- CLI/plugins: let config-gated bundled plugins install without persisting invalid placeholder config entries, so install/uninstall sweeps can cover plugins such as memory-lancedb before the user configures credentials. Thanks @vincentkoc.
- CLI/plugins: reject malformed ClawHub plugin specs with trailing `@` before registry lookup, so empty-version typos report as invalid specs instead of package-not-found errors. Fixes #56579; supersedes #56582. Thanks @Kansodata.
- Agents/sessions: acquire the session write lock only after cold bootstrap, plugin, and tool setup so fallback runs are not blocked by stalled pre-model startup work.
- Browser/plugins: auto-start the bundled browser plugin when root `browser` config is present, including restrictive plugin allowlists, and ignore stale persisted plugin registries whose package paths no longer exist.
- Browser: circuit-break repeated managed Chrome launch failures per profile so browser requests stop spawning Chromium indefinitely when CDP cannot start. Fixes #64271. Thanks @TheophilusChinomona.
- Gateway/models: skip external OpenRouter and LiteLLM pricing refreshes for local/self-hosted model endpoints so startup does not wait on remote pricing catalogs for local-only Ollama, vLLM, and compatible providers.
- CLI/plugins: stop security-blocked plugin installs from retrying as hook packs, so normal plugin packages report the scanner failure without a misleading "not a valid hook pack" follow-up. Fixes #61175; supersedes #64102. Thanks @KonsultDigital and @ziyincody.
- Agents/Anthropic: strip stale trailing assistant prefill turns from outbound replay so context-engine short circuits cannot send unsupported assistant-prefill payloads to provider APIs. Fixes #72556. Thanks @Veda-openclaw.
- Agents/Google: strip stale trailing assistant/model prefill turns from Gemini outbound replay so Google Generative AI requests end with a user turn or function response. Follow-up to #72556. Thanks @Veda-openclaw.
- Control UI/Dreaming: require explicit confirmation before applying restart-impacting Dreaming mode changes, with restart warning copy and loading feedback. Fixes #63804. (#63807) Thanks @bbddbb1.
- CLI/agent: mark Gateway-to-embedded fallback runs with `meta.transport: "embedded"` and `meta.fallbackFrom: "gateway"` in JSON output, and make the terminal diagnostic explicit so scripts and operators can distinguish fallback runs from Gateway runs. Fixes #71416. Thanks @amknight.
- Agents/tools: normalize `null` or missing tool-call arguments to `{}` for parameterless object schemas before Pi validation, so empty-argument tools run instead of failing argument validation. Fixes #72587. Thanks @amknight.
- Agents/subagents: clear active embedded-run state before terminal lifecycle events so post-completion cleanup no longer treats finished child runs as still active and skips archive or announcement bookkeeping. (#70187) Thanks @amknight.
- CLI/update: keep the automatic post-update completion refresh on the core-command tree so it no longer stages bundled plugin runtime deps before the Gateway restart path, avoiding `.24` update hangs and 1006 disconnect cascades. Fixes #72665. Thanks @sakalaboator and @He-Pin.
- Control UI: make explicit Reload Config actions discard stale local config edits while passive refreshes and failed-save recovery keep pending drafts intact. Fixes #40352; carries forward #40443. Thanks @realmikechong-dotcom.
- Agents/Bedrock: stop heartbeat runs from persisting blank user transcript turns and repair existing blank user text messages before replay, preventing AWS Bedrock `ContentBlock` blank-text validation failures. Fixes #72640 and #72622. Thanks @goldzulu.
- Agents/LM Studio: promote standalone bracketed local-model tool requests into registered tool calls and hide unsupported bracket blocks from visible replies, so MemPalace MCP lookups do not print raw `[tool]` JSON scaffolding in chat. Fixes #66178. Thanks @detroit357.
- Local models: warn when an assistant reply looks like a tool call but the provider emitted plain text instead of a structured tool invocation, making fake/non-executed tool calls visible in logs. Fixes #51332. Thanks @emilclaw.
- Local models: accept persisted non-secret local auth markers for private-LAN custom OpenAI-compatible providers, so LAN Ollama configs no longer fail with missing auth when `ollama-local` is saved as the key. Fixes #49736. Thanks @charles-zh.
- TUI/local models: treat visible gateway client labels such as `openclaw-tui` as the current requester session for session-aware tools, so Ollama tool calls no longer fail by resolving the UI label as a session id. Fixes #66391. Thanks @kickingzebra.
- Local models: route self-hosted OpenAI-compatible model discovery through the guarded fetch path pinned to the configured host, covering vLLM and SGLang setup without reopening local/LAN SSRF probes. Supersedes #46359. Thanks @cdxiaodong.
- Local models: classify terminated, reset, closed, timeout, and aborted model-call failures and attach a process memory snapshot to the diagnostic event, making LM Studio/Ollama RAM-pressure failures easier to prove from stability bundles. Refs #65551. Thanks @BigWiLLi111.
- Local models: pass configured provider request timeouts through OpenAI SDK transports and the model idle watchdog so long-running local or custom OpenAI-compatible streams use one timeout knob instead of hitting the SDK's 10-minute default or the 120s idle default. Fixes #63663. Thanks @aidiffuser.
- LM Studio: trust configured LM Studio loopback, LAN, and tailnet endpoints for guarded model requests by default, preserving explicit private-network opt-outs. Refs #60994. Thanks @tnowakow.
- Docker/setup: route Docker onboarding defaults for host-side LM Studio and Ollama through `host.docker.internal` and add the Linux host-gateway mapping to the bundled Compose file, so containerized gateways can reach local providers without using container loopback. Fixes #68684; supersedes #68702. Thanks @safrano9999 and @skolez.
- Agents/LM Studio: strip prior-turn Gemma 4 reasoning from OpenAI-compatible replay while preserving active tool-call continuation reasoning. Fixes #68704. Thanks @chip-snomo and @Kailigithub.
- LM Studio: allow interactive onboarding to leave the API key blank for unauthenticated local servers, using local synthetic auth while clearing stale LM Studio auth profiles. Fixes #66937. Thanks @olamedia.
- Plugins/startup/registry: reuse a Gateway `PluginLookUpTable` and one manifest registry pass across startup plugin IDs, plugin loading, deferred channel reloads, model pricing, read-only channel defaults, capability/provider/media resolution, manifest contracts, extractors, web fallback discovery, owner maps, and cold provider-discovery caches, with new startup-trace timing/count metrics for installed-index, manifest, startup-plan, and owner-map work. Thanks @shakkernerd and @mcaxtr.
- Mattermost: keep direct-message replies top-level by suppressing reply roots for DM delivery while preserving channel and group thread roots, and derive inbound chat kind from the trusted channel lookup instead of the websocket event channel type. Carries forward #60115, #55186, #72305, and #72659; refs #59758, #59981, #59791, and #57565. Thanks @vincentkoc, @jwchmodx, and @hnykda.
- Docker: pre-create `/home/node/.openclaw` with node ownership and private permissions so first-run Docker Compose named volumes no longer fail startup with EACCES. (#48072, #63959; fixes #61279) Thanks @timoxue and @jeanibarz.
- CLI/Gateway: treat local restart probe policy closes for connect, exact `device required`, pairing, and auth failures as Gateway reachability proof without accepting empty, broad standalone token/password/scope/role, or pair-substring 1008 close reasons. Fixes #48771; carries forward #48801; related #63491. Thanks @MarsDoge and @genoooool.
- Feishu: send outgoing interactive reply payloads as native cards with clickable buttons while preserving text, media, and document-comment fallbacks. Fixes #13175 and #58298; carries forward #47891. Thanks @Horacehxw.
- Process/Windows: decode command stdout and stderr from raw bytes with console-codepage awareness, while preserving valid UTF-8 output and multibyte characters split across chunks. Fixes #50519. Thanks @iready, @kevinten10, @zhangyongjie1997, @knightplat-blip, @heiqishi666, and @slepybear.
- Bonjour/Windows: hide the bundled mDNS advertiser's Windows ARP shell probe so Gateway startup no longer flashes command-prompt windows. Fixes #70238. Thanks @alexandre-leng, @PratikRai0101, @infinitypacific, and @tomerpeled.
- Agents/bootstrap: dedupe hook-injected bootstrap context files by workspace-relative path and store normalized resolved paths so duplicate relative and absolute hook paths no longer depend on the process cwd. (#59344; fixes #59319; related #56721, #56725, and #57587) Thanks @koen666.
- Agents/bootstrap: refresh cached workspace bootstrap snapshots on long-lived main-session turns when `AGENTS.md`, `SOUL.md`, `MEMORY.md`, or `TOOLS.md` change on disk, while preserving unchanged snapshot identity through the workspace file cache. (#64871; related #43901, #26497, #28594, #30896) Thanks @aimqwest and @mikejuyoon.
- macOS Gateway: detect installed-but-unloaded LaunchAgent split-brain states during status, doctor, and restart, and re-bootstrap launchd supervision before falling back to unmanaged listener restarts. Fixes #67335, #53475, and #71060; refs #58890, #60885, and #70801. Thanks @ze1tgeist88, @dafacto, and @vishutdhar.
- Plugins/install: treat mirrored core logger dependencies as staged bundled runtime deps so packaged Gateway starts do not crash when the external plugin-runtime-deps root is missing `tslog`. Fixes #72228; supersedes #72493. Thanks @deepujain.
- Build/plugins: preserve active bundled runtime-dependency staging temp directories owned by live build processes so overlapping postbuild runs no longer delete each other's staged deps mid-prune. Supersedes #72220. Thanks @VACInc.
- Plugins/install: hide bundled runtime-dependency npm child windows on Windows across Gateway startup, postinstall, and packaged staging paths so Telegram/Anthropic dependency repair no longer flashes shell windows. Fixes #72315. Thanks @athuljayaram and @joshfeng.
- Agents/Windows: normalize lazy agent runtime imports before Node ESM loading so Windows drive-letter `subagent-registry` runtime paths no longer fail every agent task with `ERR_UNSUPPORTED_ESM_URL_SCHEME`. Fixes #72636; carries forward #72716. Thanks @Andyz-CData and @xialonglee.
- Plugins/Windows: normalize lazy plugin service override imports before Node ESM loading so drive-letter browser-control module paths no longer fail with `ERR_UNSUPPORTED_ESM_URL_SCHEME`. Fixes #72573; supersedes #72599 and #72582. Thanks @llzzww316, @feineryonah-byte, and @WuKongAI-CMU.
- Browser/plugins: load `playwright-core` through the browser runtime shim so packaged installs can run Playwright actions from staged plugin runtime deps after doctor/startup repair. Fixes #72168; supersedes #72238. Thanks @zdg1110 and @yetval.
- Plugins/install: stage bundled plugin runtime dependencies before Gateway startup, drain update restarts, and materialize plugin-owned root chunks in external mirrors so staged deps resolve under native ESM. Fixes #72058; supersedes #72084. Thanks @amnesia106 and @drvoss.
- TTS/SecretRef: resolve `messages.tts.providers.*.apiKey` from the active runtime snapshot so SecretRef-backed MiniMax and other TTS provider keys work in runtime reply/audio paths. Fixes #68690. Thanks @joshavant.
- Gateway/install: surface systemd user-bus recovery hints during Linux service activation and retry via the target user scope when `systemctl --user` reports no-medium bus failures, without letting stale `SUDO_USER` override `sudo -u` installs. Fixes #39673; refs #44417 and #63561. Thanks @Arbor4, @myrsu, @mssteuer, and @boyuaner.
- CLI/nodes: make unfiltered `openclaw nodes list` prefer the effective paired-node view used by `nodes status` while preserving pending rows, pairing-scope fallback, terminal-safe table rendering, and paired JSON metadata. Fixes #46871; carries forward #65772 through the ProjectClownfish #72619 repair. Thanks @skainguyen1412.
- CLI/startup: read generated startup metadata from the bundled `dist` layout before falling back to live help rendering, so root/browser help and channel-option bootstrap stay on the fast path. Thanks @vincentkoc.
- Feishu/Lark: stop treating broadcast-only `@all`/`@_all` messages as bot mentions while preserving direct bot mentions, including messages that also include `@all`. Fixes #37706. Thanks @JosepLee.
- CLI/help: treat positional `help` invocations like `openclaw channels help` as help paths for startup gating, avoiding model/auth warmup while preserving positional arguments such as `openclaw docs help`. Thanks @gumadeiras.
- Web search: route plugin-scoped web_search SecretRefs through the active runtime config snapshot so provider execution receives resolved credentials across app/runtime paths, including `plugins.entries.brave.config.webSearch.apiKey`. Fixes #68690. Thanks @VACInc.
- Voice Call: allow SecretRef-backed Twilio auth tokens and call-specific OpenAI/ElevenLabs TTS API keys through the plugin config surface. Fixes #68690. Thanks @joshavant.
- Google Meet/Voice Call: clean stale chrome-node realtime bridges before rejoining, expose bridge inspection, tolerate transient node input pull failures, default Chrome command-pair audio to 24 kHz PCM16 while preserving legacy 8 kHz G.711 mu-law pairs, handle Gemini Live interruptions/VAD and function-response names correctly, route stateful `google_meet` tools through the gateway runtime, support `realtime.agentId`, and send non-blocking consult continuations before long tool-backed answers finish. Fixes #72371, #72525, #72523, #72440, and #72425; (#72372, #72524, #72381, #72441, #72189, #72426) Thanks @BsnizND and @VACInc.
- Discord/media: keep incidental Markdown image badges in final replies as text unless a channel opts into Markdown-image media extraction, while preserving Telegram Markdown-image media replies and explicit `MEDIA:` attachments. Fixes #72642. Thanks @solavrc and @Bartok9.
- Matrix/E2EE: stabilize recovery and broken-device QA flows while avoiding Matrix device-cleanup sync races that could leave shutdown-time crypto work running. Thanks @gumadeiras.
- Cron: apply `cron.maxConcurrentRuns` to the nested isolated-agent lane, start isolated execution timeouts only after the runner enters that lane, keep legacy flat `jobs.json` rows loadable, invalidate stale pending runtime slots after schedule edits, and preserve due slots for formatting-only rewrites. Fixes #72707, #27996, #71607, and #41783; carries forward #71651. Thanks @kagura-agent, @xialonglee, @fagnersouza666, @ayanesakura, and @Hurray0.
- Cron/delivery: classify isolated successes, quiet `NO_REPLY` turns, model/provider failures, execution denials, `--no-deliver` traces, skipped-job alerts, and verified delivery outcomes correctly so cron history, retries, and failure counters reflect what actually happened. Fixes #72732, #50170, #43604, #68452, #60846, #72210, and #67172; follow-up to #54188; carries forward #43631, #68453, #72219, and #67186. Thanks @zNatix, @pixeldyn, @ChickenEggRoll, @SPFAdvisors, @anyech, @slideshow-dingo, @hatemclawbot-collab, @xydigit-sj, @oc-gh-dr, @hclsys, and @1yihui.
- Cron/routing: preserve direct Telegram thread/account IDs, explicit Discord `user:`/`channel:` delivery targets, and `session:<id>` failure-destination routing so reminders, cron announcements, and failure alerts keep the intended recipient kind across direct and group chats. Fixes #44270; refs #62777; carries forward #44325, #44351, #44412, #72657, #68535, and #62798. Thanks @RunMintOn, @arkyu2077, @0xsline, @vincentkoc, @slideshow-dingo, @likewen-tech, and @neeravmakwana.
- Subagents: keep the delegated task only in the subagent system prompt and send a short initial kickoff message, avoiding duplicate task tokens while preserving multiline task formatting. Fixes #72019; carries forward #72053. Thanks @Wizongod and @ly85206559.
- Onboarding/GitHub Copilot: add manifest-owned `--github-copilot-token` support for non-interactive setup, including env fallback, tokenRef storage in ref mode, saved-profile reuse, and current Copilot default-model wiring. Refs #50002 and supersedes #50003. Thanks @scottgl9.
- Gateway/install: add a validated `--wrapper`/`OPENCLAW_WRAPPER` service install path that persists executable LaunchAgent/systemd wrappers across forced reinstalls, updates, and doctor repairs instead of falling back to raw node/bun `ProgramArguments`. Fixes #69400. (#72445) Thanks @willtmc.
- Plugins: fail plugin registration when loader-owned acceptance gates reject missing hook names or memory-only capability registration from non-memory plugins, surfacing the issue through plugin status and doctor instead of silently dropping the registration. Fixes #72459. Thanks @amknight.
- macOS Gateway: write launchd services with a state-dir `WorkingDirectory`, use a durable state-dir temp path instead of freezing macOS session `TMPDIR`, create that temp directory before bootstrap, and label abort-shaped launchd exits as `SIGABRT/abort` in status output. Fixes #53679 and #70223; refs #71848. Thanks @dlturock, @stammi922, and @palladius.
- Control UI/update: make `Update now` require a real gateway process replacement, report skipped/error update outcomes with stable reasons, and verify the running gateway version after restart so global installs cannot silently keep old code in memory. Fixes #62492; addresses #64892 and #63562. Thanks @IAMSamuelRodda.
- Exec approvals: accept runtime-owned `source: "allow-always"` and `commandText` allowlist metadata in gateway and node approval-set payloads so Control UI round-trips no longer fail with `unexpected property 'source'`. Fixes #60000; carries forward #60064. Thanks @sd1471123, @sharkqwy, and @luoyanglang.
- Exec/node: skip approval-plan preparation for full-trust `host=node` runs so interpreter and script commands no longer fail with `SYSTEM_RUN_DENIED: approval cannot safely bind` when effective policy is `security=full` and `ask=off`. Fixes #48457 and duplicate #69251. Thanks @ajtran303, @jaserNo1, @Blakeshannon, @lesliefag, and @AvIsBeastMC.
- Exec/node: synthesize a local approval plan when a paired node advertises `system.run` without `system.run.prepare`, unblocking approval-required `host=node` exec on current macOS companion nodes while preserving remote prepare for node hosts that support it. Fixes #37591 and duplicate #66839; carries forward #69725. Thanks @soloclz.
- Memory/QMD: prefer QMD's `--mask` collection pattern flag so root memory indexing stays scoped to `MEMORY.md` instead of widening to every markdown file in the workspace. Fixes #65480; supersedes #65481 and #66259. Thanks @ccage-simp, @Bortlesboat, @seank-com, and @crazyscience.
- Memory/doctor: treat the specific `gateway timeout after ...` gateway memory probe result as inconclusive instead of reporting embeddings not ready, while preserving warnings for explicit failures. Fixes #44426; carries forward #46576 with the Greptile review feedback applied. Thanks Cengiz (@ghost).
- Gateway/startup: defer QMD, core request handlers, setup wizard, CLI outbound senders, plugin HTTP routes, chat/session projection, node session runtime validation, embedded-run activity reads, MCP loopback server imports, channel runtime helpers, HTTP/canvas/plugin auth helpers, isolated cron imports, and hook dispatch parsing until their request or shutdown paths, while making plain `gateway status` use a parse-only config snapshot so no-plugin boots and status reads avoid broad runtime fanout. Thanks @vincentkoc.
- Lobster/Gateway: memoize repeated Ajv schema compilation before loading the embedded Lobster runtime so scheduled workflows and `llm.invoke` loops stop growing gateway heap on content-identical schemas. Fixes #71148. Thanks @cmi525, @vsolaz, and @vincentkoc.
- Codex harness: normalize cached input tokens before session/context accounting so prompt cache reads are not double-counted in `/status`, `session_status`, or persisted `sessionEntry.totalTokens`. Fixes #69298. Thanks @richardmqq.
- Hooks/session-memory: use the host local timezone for memory filenames, fallback timestamp slugs, and markdown headers instead of UTC dates. Fixes #46703. (#46721) Thanks @Astro-Han.
- Gateway health: preserve live runtime-backed channel/account state in `gateway.health` snapshots and cached refreshes while keeping raw probe payloads on sensitive/admin paths only. (#39921, #42586, #46527, #52770, #42543) Thanks @FAL1989, @rstar327, @0xble, and @ajayr.
- Feishu: extract quoted/replied interactive-card text across schema 1.0, schema 2.0, i18n, template-variable, and post-format fallback shapes without carrying broad generated/config churn from related parser experiments. (#38776, #60383, #42218, #45936) Thanks @lishuaigit, @lskun, @just2gooo, and @Br1an67.
- Telegram/agents: hide raw failed write/edit warning messages in Telegram when the assistant already explicitly acknowledges the failed action, while keeping warnings when the reply claims success or omits the failure; #39406 remains the broader configurable delivery-policy follow-up. Fixes #51065; covers #39631. Thanks @Bartok9 and @Bortlesboat.
- Exec approvals: accept a symlinked `OPENCLAW_HOME` as the trusted approvals root while still rejecting symlinked `.openclaw` path components below it. (#64663) Thanks @FunJim.
- Logging: add top-level `hostname`, flattened `message`, and available `agent_id`, `session_id`, and `channel` fields to file-log JSONL records for multi-agent filtering without removing existing structured log arguments. Fixes #51075. Thanks @stevengonsalvez.
- ACP: route server logs to stderr before Gateway config/bootstrap work so ACP stdout remains JSON-RPC only for IDE integrations. Fixes #49060. Thanks @Hollychou924.
@@ -16,38 +193,38 @@ Docs: https://docs.openclaw.ai
- Diagnostics/OTEL: capture privacy-safe model-call request payload bytes, streamed response bytes, first-response latency, and total duration in diagnostic events, plugin hooks, stability snapshots, and OTEL model-call spans/metrics without logging raw model content. Fixes #33832. Thanks @wwh830.
- Logging: write validated diagnostic trace context as top-level `traceId`, `spanId`, `parentSpanId`, and `traceFlags` fields in file-log JSONL records so traced requests and model calls are easier to correlate in log processors. Refs #40353. Thanks @liangruochong44-ui.
- Logging/sessions: apply configured redaction patterns to persisted session transcript text and accept escaped character classes in safe custom redaction regexes, so transcript JSONL no longer keeps matching sensitive text in the clear. Fixes #42982. Thanks @panpan0000.
- Providers/Ollama: honor `/api/show` capabilities when registering local models so non-tool Ollama models no longer receive the agent tool surface, and keep native Ollama thinking opt-in instead of enabling it by default. Fixes #64710 and duplicate #65343. Thanks @yuan-b, @netherby, @xilopaint, and @Diyforfun2026.
- Providers/Ollama: expose native Ollama thinking effort levels so `/think max` is accepted for reasoning-capable Ollama models and maps to Ollama's highest supported `think` effort. Fixes #71584. Thanks @g0st1n.
- Agents/Ollama: validate explicit `--thinking max` against catalog-discovered Ollama reasoning metadata so local agent runs accept the same native thinking levels shown in the model catalog. Fixes #71584. Thanks @g0st1n.
- Agents/sessions: let `sessions_spawn runtime="subagent"` ignore ACP-only `streamTo` and `resumeSessionId` fields while keeping ACP passthrough and documenting `streamTo` as ACP-only. Fixes #43556 and #63120; covers #56326, #61724, #64714, and #67248; carries forward #68397, #65282, #58686, #56342, and #40102. Thanks @skernelx, @damselem, @Br1an67, @Mintalix, @IsaacAPerez, @vvitovec, @Sanjays2402, @shenkq97, and @1034378361.
- Providers/Ollama: honor `/api/show` capabilities, custom Modelfile `PARAMETER num_ctx`, configured provider/model context defaults, whitelisted native params such as `temperature`, `top_p`, and `think`, and native thinking effort levels so local models get accurate tools, context, and thinking behavior without forcing full-context VRAM use. Fixes #64710, duplicate #65343, #68344, #44550, #52206, #49684, #68662, #48010, #71584, and #44786; supersedes #69464; carries forward #44955. Thanks @yuan-b, @netherby, @xilopaint, @Diyforfun2026, @neeravmakwana, @taitruong, @armi0024, @LokiCode404, @zhouZcong, @dshenster-byte, @tangzhi, @pandego, @maweibin, @Adam-Researchh, @EmpireCreator, @g0st1n, and @voltwake.
- Image tool/media: honor `tools.media.image.timeoutSeconds` and matching per-model image timeouts in explicit image analysis, including the MiniMax VLM fallback path, so slow local vision models are not capped by hardcoded 30s/60s aborts. Fixes #67889; supersedes #67929. Thanks @AllenT22 and @alchip.
- Providers/Ollama: strip custom provider prefixes before native chat/embedding requests, skip ambient localhost discovery unless config/auth opts in, handle custom remote `api: "ollama"` providers, accept OpenAI SDK-style `baseURL`, scope synthetic local auth and embedding bearer headers to declared host boundaries, resolve custom-named local providers for subagents, add provider-scoped model request timeouts, preserve explicit input modalities, and document `params.keep_alive` plus local/LAN/cloud/multi-host/web-search/embedding/thinking setup recipes. Fixes #72353, #56939, #62533, #43945, #64541, #68796, and #39690; supersedes #57116, #62549, #69261, #69857, #65143, and #66511; refs #43945; carries forward #43224 and #39785. Thanks @maximus-dss, @hclsys, @IanxDev, @tsukhani, @issacthekaylon, @Julien-BKK, @Linux2010, @hyspacex, @maxramsay, @Meli73, @LittleJakub, @Juankcba, @uninhibite-scholar, @yfge, @Skrblik, and @Mriris.
- Providers/Ollama: move memory embeddings to `/api/embed` with batched `input`, route local web search through Ollama's signed daemon proxy while keeping cloud auth scoped, treat Ollama memory embeddings as key-optional in doctor, and keep model usage visible by estimating native transcript usage when `/api/chat` omits counters. Fixes #39983, #69132, and #46584; carries forward #39112. Thanks @sskkcc, @LiudengZhang, @yoon1012, @hyspacex, @fengly78, and @TylonHH.
- Agents/Ollama: parse stringified native tool-call arguments, retry native empty/thinking-only turns, accept already-prefixed LLM task model overrides, apply provider-owned replay normalization for Cloud models, validate explicit `--thinking max`, show resolved thinking defaults in Control UI, and include configured provider models in `models list --provider`. Fixes #69735, #50052, #71697, #71584, #72407, and #65207; supersedes #69910; carries forward #66552 and #61223. Thanks @rongshuzhao, @yfge, @L3G, @ralphy-maplebots, @Hollychou924, @ismael-81, @g0st1n, @NotecAG, and @drzeast-png.
- Providers/PDF/Ollama: add bounded network timeouts for Ollama model pulls and native Anthropic/Gemini PDF analysis requests so unresponsive provider endpoints no longer hang sessions indefinitely. Fixes #54142; supersedes #54144 and #54145. Thanks @jinduwang1001-max and @arkyu2077.
- Docker/QA: add observability coverage to the normal Docker aggregate so QA-lab OTEL and Prometheus diagnostics run inside Docker. Thanks @vincentkoc.
- Auto-reply: poison inbound message dedupe after replay-unsafe provider/runtime failures so retries stay safe before visible progress but cannot duplicate messages after block output, tool side effects, or session progress. Fixes #69303; keeps #58549 and #64606 as duplicate validation. Thanks @martingarramon, @NikolaFC, and @zeroth-blip.
- Agents/model fallback: keep auto-persisted fallback model overrides selected across turns until `/new` or reset clears them, avoiding repeated probes of a known-bad primary while `/status` shows the selected and active models. Thanks @kibedu.
- Agents/model fallback: jump directly to a known later live-session model redirect instead of walking unrelated fallback candidates, while preserving the already-landed live-session/fallback loop guard. Fixes #57471; related loop family already closed via #58496. Thanks @yuxiaoyang2007-prog.
- Gateway/Bonjour: keep @homebridge/ciao cancellation handlers registered across advertiser restarts so late probing cancellations cannot crash Linux and other mDNS-churned gateways. Thanks @codex.
- Plugins/startup: load the default `memory-core` slot during Gateway startup when permitted so active-memory recall can call `memory_search` and `memory_get` without requiring an explicit `plugins.slots.memory` entry, while preserving `plugins.slots.memory: "none"`. Thanks @codex.
- Gateway/Bonjour: keep @homebridge/ciao cancellation handlers registered across advertiser restarts so late probing cancellations cannot crash Linux and other mDNS-churned gateways.
- Plugins/startup: load the default `memory-core` slot during Gateway startup when permitted so active-memory recall can call `memory_search` and `memory_get` without requiring an explicit `plugins.slots.memory` entry, while preserving `plugins.slots.memory: "none"`.
- Plugins/CLI: prefer native require for compiled bundled plugin JavaScript before jiti so read-only config, status, device, and node commands avoid unnecessary transform overhead on slow hosts. Fixes #62842. Thanks @Effet.
- Plugins/compat: inventory doctor-side deprecation migrations separately from runtime plugin compatibility so release sweeps preserve needed repairs while enforcing dated removal windows. Thanks @vincentkoc.
- Plugins/compat: add missing dated compatibility records for legacy extension-api, memory registration, provider hook/type aliases, runtime aliases, channel SDK helpers, and approval/test utility shims. Thanks @vincentkoc.
- Plugins/CLI: refresh the persisted registry after managed plugin files are removed so ClawHub uninstall cannot leave stale `plugins list` entries. Thanks @codex.
- Plugins/CLI: make plugin install and uninstall config writes conflict-aware, clear stale denylist entries on explicit reinstall/removal, and delete managed plugin files only after config/index commit succeeds. Thanks @codex.
- Plugins: fail `plugins update` when tracked plugin or hook updates error, keep bundled runtime-dependency repair behind restrictive allowlists, and reject package installs with unloadable extension entries. Thanks @codex.
- Plugins/compat/CLI: inventory doctor-side deprecation migrations separately from runtime plugin compatibility, add dated records for legacy extension-api, memory registration, provider hook/type aliases, runtime aliases, channel SDK helpers, and approval/test utility shims, refresh the persisted registry after managed plugin removals, make plugin install/uninstall writes conflict-aware, clear stale denylists, and fail tracked plugin/hook updates or unloadable package installs instead of leaving stale state. Thanks @vincentkoc.
- WebChat/Control UI: support non-video file attachments in chat uploads while preserving the existing image attachment path and MIME-sniff fallback for generic image uploads. (#70947) Thanks @IAMSamuelRodda.
- Skills/memory: restore Chokidar v5 hot reloads by watching concrete skill and memory roots with filters, including SKILL.md removals and deleted skill folders without broad workspace recursion. Fixes #27404, #33585, and #41606. Thanks @shelvenzhou, @08820048, and @rocke2020.
- Gateway/chat: keep duplicate attachment-backed `chat.send` retries with the same idempotency key on the documented in-flight path so aborts still target the real active run. Fixes #70139. Thanks @Feelw00.
- Plugins: share package entrypoint resolution between install and discovery, reject mismatched `runtimeExtensions`, and cache bundled runtime-dependency manifest reads during scans. Thanks @codex.
- Gateway/session rows: report the same config-resolved thinking default that runtime sessions use, including global and per-agent defaults, so Control UI and TUI default labels stay aligned. (#71779, #70981, #71033, #70302) Thanks @chen-zhang-cs-code, @SymbolStar, and @cholaolu-boop.
- Plugins: share package entrypoint resolution between install and discovery, reject mismatched `runtimeExtensions`, and cache bundled runtime-dependency manifest reads during scans.
- WhatsApp/Web: keep quiet but healthy linked-device sessions connected by basing the watchdog on WhatsApp Web transport activity, while retaining a longer app-silence cap so frame activity cannot mask a stuck session forever. Fixes #70678; carries forward the focused #71466 approach and keeps #63939 as related configurable-timeout follow-up. Thanks @vincentkoc and @oromeis.
- Discord/gateway: count failed health-monitor restart attempts toward cooldown and hourly caps, and evict stale account lifecycle state during channel reloads so repeated Discord gateway recovery cannot loop on old status. Fixes #38596. (#40413) Thanks @jellyAI-dev and @vashquez.
- Cron/context engine: run isolated cron jobs under run-scoped context-engine session keys so prior runs of the same job are not inherited unless the job is explicitly session-bound. (#72292) Thanks @jalehman.
- Control UI: localize command palette labels, categories, skill shortcuts, footer hints, and connect-command copy labels while preserving localized command palette search matching. (#61130, #61119) Thanks @rubensfox20.
## 2026.4.26
### Fixes
- Plugins/CLI: let flag-driven `openclaw channels add` install the selected channel plugin from its default source without opening an interactive prompt, fixing published npm Telegram setup in stdin-closed automation. Thanks @codex.
- Onboarding/setup: keep first-run config reads, plugin compatibility notices, and post-model sanity checks on cold metadata paths unless the user chooses to browse all models, avoiding full plugin/runtime catalog work between prompts. Thanks @shakkernerd.
- Onboarding/auth: run manifest-owned provider auth choices through scoped setup providers so selecting OpenAI Codex browser/device auth no longer loads every provider runtime before OAuth starts. Thanks @shakkernerd.
- Onboarding/auth: keep the post-auth default-model policy lookup on manifest/setup metadata so the next prompt appears without loading broad provider runtime. Thanks @shakkernerd.
- Onboarding/models: keep skip-auth and provider-scoped model picker prompts off the full global model catalog path, and cache provider catalog hook resolution so setup no longer stalls after auth on large plugin registries. Thanks @shakkernerd.
- Plugins/memory-lancedb: request float embedding responses from OpenAI-compatible servers so local providers that default SDK requests to base64 no longer return dimension-mismatched LanceDB vectors while preserving configured dimensions. Fixes #45982. (#59048, #46069, #45986) Thanks @deep-introspection, @xiaokhkh, @caicongyang, and @thiswind.
- Plugins/memory-lancedb: advance auto-capture cursors per session only after messages are processed or intentionally skipped, retry failed messages, survive compacted histories, and clear cursor state on session end. Fixes #71349; carries forward #42083. Thanks @as775116191.
- Plugins/memory-core: respect configured memory-search embedding concurrency during non-batch indexing so local Ollama embedding backends can serialize indexing instead of flooding the server. Fixes #66822. (#66931) Thanks @oliviareid-svg and @LyraInTheFlesh.
- Docker/update smoke: keep the package-derived update-channel fixture on package-shipped files and make its UI build stub create the asset the updater verifies. Thanks @vincentkoc.
- Gateway/models: repair legacy `models.providers.*.api = "openai"` config values to `openai-completions`, and skip providers with future stale API enum values during startup instead of bricking the gateway. Fixes #72477. (#72542) Thanks @JooyoungChoi14 and @obviyus.
- Gateway/skills: redact `apiKey` and secret-named `env` values from the `skills.update` RPC response to prevent leaking credentials into WebSocket traffic, client logs, or session transcripts. Config is still written to disk in full; only the response payload is redacted. (#69998) Thanks @Ziy1-Tan.
- Plugins/CLI: let flag-driven `openclaw channels add` install the selected channel plugin from its default source without opening an interactive prompt, fixing published npm Telegram setup in stdin-closed automation.
- Onboarding/setup: keep first-run config reads, plugin compatibility notices, OpenAI Codex auth, post-auth default-model policy lookup, skip-auth, provider-scoped model pickers, and post-model sanity checks on cold manifest/setup metadata unless the user chooses to browse all models, avoiding full plugin/provider runtime loads between prompts. Thanks @shakkernerd.
- Gateway/Bonjour: suppress known @homebridge/ciao cancellation and network assertion failures through scoped process handlers so malformed mDNS packets or restricted VPS networking disable/restart Bonjour instead of crashing the gateway. Fixes #67578. Thanks @zenassist26-create.
- Discord: keep late clicks on already-resolved exec approval buttons quiet when elevated mode auto-resolved the request, while still surfacing real approval submission failures. Fixes #66906. Thanks @rlerikse.
- Telegram: send a fresh final message for long-lived preview-streamed replies so the visible Telegram timestamp reflects completion time instead of the preview creation time. Thanks @rubencu.
@@ -147,7 +324,7 @@ Docs: https://docs.openclaw.ai
- CLI/update: run package post-update doctor with `--fix` so package updates repair config migrations before restart. Thanks @shakkernerd.
- CLI/update: retry failed npm global updates with `--omit=optional` and ignore the superseded first failure when the fallback succeeds. Thanks @shakkernerd.
- Plugins/uninstall: migrate and reset `plugins.slots.contextEngine` alongside memory slots when plugin ids change or selected plugins are removed. Thanks @shakkernerd.
- Agents/Discord: keep raw `Agent failed before reply` runner failures out of Discord group/channel chats and show detailed runner errors in direct chats only when `/verbose` is enabled. Thanks @codex.
- Agents/Discord: keep raw `Agent failed before reply` runner failures out of Discord group/channel chats and show detailed runner errors in direct chats only when `/verbose` is enabled.
- UI/Windows: quote resolved pnpm `.cmd` launcher paths before spawning UI install/build/test commands so Node installs under `C:\Program Files` no longer fail as `C:\Program`. Fixes #45275. Thanks @Kobevictor, @stoppieboy, and @iubns.
- Codex/agent: translate `--thinking minimal` to `low` for modern Codex models (gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.2) at request build time so the first turn is accepted instead of paying a wasted call + retry-with-low fallback. Older Codex models still receive `minimal` directly. Fixes #71946. Thanks @hclsys.
- Plugins/uninstall: remove tracked plugin files from their recorded managed extensions root even when the current state directory points somewhere else, so `openclaw plugins uninstall --force` does not leave the plugin discoverable. Thanks @shakkernerd.
@@ -249,7 +426,7 @@ Docs: https://docs.openclaw.ai
- macOS/remote SSH: keep discovered gateway hosts in `gateway.remote.sshTarget` while pinning SSH transport URLs to the local loopback tunnel, so browser automation does not regress into blocked non-loopback `ws://` endpoints. Fixes #67336.
- Gateway/proxy: bootstrap env proxy dispatching from direct Gateway startup so provider and plugin network requests honor `HTTPS_PROXY`/`HTTP_PROXY` before the first embedded agent attempt runs. (#71833) Thanks @mjamiv.
- Plugins/runtime deps: verify clean npm installs actually place requested bundled runtime packages in the managed install root, reporting exact missing specs instead of a false successful repair. (#71883) Thanks @Solvely-Colin.
- Plugins/discovery: ignore stale `plugins.load.paths` aliases that point back at packaged bundled plugin directories and have doctor remove them, keeping bundled plugins on the runtime-deps staging path. Thanks @codex.
- Plugins/discovery: ignore stale `plugins.load.paths` aliases that point back at packaged bundled plugin directories and have doctor remove them, keeping bundled plugins on the runtime-deps staging path.
- Models/LM Studio: preserve `@iq*` quant suffixes in model refs and provider matching so `/model lmstudio/...@iq3_xxs` keeps the exact LM Studio variant. Fixes #71474. (#71486) Thanks @Bartok9, @XinwuC, and @Sanjays2402.
- Matrix/cron: preserve the live Matrix delivery target when creating implicit announce reminder jobs so mixed-case room IDs are not reconstructed from lowercased session keys. Fixes #71798.
- Feishu: accept Schema 2.0 card action callbacks that report `context.open_chat_id` instead of legacy `context.chat_id`, so button callbacks no longer drop as malformed. Fixes #71670. Thanks @eddy1068.
@@ -371,6 +548,7 @@ Docs: https://docs.openclaw.ai
- CLI/models: make `openclaw models scan` fall back to public OpenRouter free-model metadata when no `OPENROUTER_API_KEY` is configured, avoid config secret resolution for explicit `--no-probe` scans, and apply the scan timeout to the OpenRouter catalog request.
- Feishu: keep streaming cards to one live card per turn, flush throttled card edits after meaningful text boundaries, and skip exact block/partial repeats so tool-heavy replies do not duplicate card output. Thanks @allan0509.
- Feishu: finish the streaming-card duplicate closeout by stripping leaked reasoning tags, preserving cross-block partial snapshots, enabling topic-thread streaming cards, omitting the generic `main` card header, surfacing transient tool/compaction status, and cleaning streaming state after close failures. Thanks @sesame437, @Vicky-v7, @maoku-family, @Pengxiao-Wang, and @Maple778.
- Telegram: keep final-only answers on the normal final-send path instead of creating synthetic draft previews, while preserving real partial preview finalization. Credited from #39213. Thanks @chalawbot.
- Telegram: recover incomplete partial-stream previews by falling back to a final send when an ambiguous final edit failure would otherwise retain a strict prefix of the answer. Fixes #71525. (#71554) Thanks @sahilsatralkar.
- Control UI/chat: collapse assistant token/model context details behind an explicit Context disclosure and show full dates in message footers, making historical transcript timing clear without noisy default metadata. (#71337) Thanks @BunsDev.
- OpenAI/Codex OAuth: explain `unsupported_country_region_territory` token-exchange failures with a proxy/region hint instead of surfacing a generic OAuth error. Fixes #51175. (#71501) Thanks @vincentkoc and @wulala-xjj.
@@ -837,12 +1015,12 @@ Docs: https://docs.openclaw.ai
### Fixes
- Dependencies: refresh workspace package pins and lockfile entries for AWS SDK, Anthropic SDK, ACP SDK, Matrix crypto, TypeBox, Vite, tsdown, Slack Bolt, CopilotKit AIMock, and related bundled plugin packages. Thanks @steipete.
- Gateway/env: import each missing expected login-shell env var independently, so an existing gateway token no longer prevents `env.shellEnv` from loading plugin credentials such as `TWILIO_*` from `.profile`. Thanks @steipete.
- macOS/Gateway pairing: silently accept same-host native app `metadata-upgrade` reconnects, so macOS patch-version changes update paired metadata instead of spamming security audit warnings and `pairing required` disconnects. Thanks @steipete.
- Dependencies: refresh workspace package pins and lockfile entries for AWS SDK, Anthropic SDK, ACP SDK, Matrix crypto, TypeBox, Vite, tsdown, Slack Bolt, CopilotKit AIMock, and related bundled plugin packages.
- Gateway/env: import each missing expected login-shell env var independently, so an existing gateway token no longer prevents `env.shellEnv` from loading plugin credentials such as `TWILIO_*` from `.profile`.
- macOS/Gateway pairing: silently accept same-host native app `metadata-upgrade` reconnects, so macOS patch-version changes update paired metadata instead of spamming security audit warnings and `pairing required` disconnects.
- CLI/Gateway: wait for one-shot gateway RPC clients to finish WebSocket teardown before the CLI process exits, reducing hangs where commands like `openclaw status` or `openclaw version` could finish their work but stay alive until an external timeout killed them (#70691). Thanks @Takhoffman.
- Thinking defaults/status: raise the implicit default thinking level for reasoning-capable models from legacy `off`/`low` fallback behavior to a safe provider-supported `medium` equivalent when no explicit config default is set, preserve configured-model reasoning metadata when runtime catalog loading is empty, and make `/status` report the same resolved default as runtime (#70601). Thanks @Takhoffman.
- Gateway/model pricing: extend OpenRouter and LiteLLM catalog fetch timeouts to 60 seconds, reducing noisy timeout warnings during slow upstream responses. Thanks @steipete.
- Gateway/model pricing: extend OpenRouter and LiteLLM catalog fetch timeouts to 60 seconds, reducing noisy timeout warnings during slow upstream responses.
- Agents/failover: classify bare undici transport failures (`terminated`, `UND_ERR_SOCKET`, `UND_ERR_CONNECT_TIMEOUT`, body/header timeouts, aborted streams) and pi-ai's openai-codex `Request failed` sentinel as `timeout`, so Cloudflare 502s with empty bodies and mid-response socket resets actually enter the configured fallback chain instead of surfacing as unclassified errors. Fixes #69368. (#69677) Thanks @sk7n4k3d.
- Providers/Anthropic Vertex: restore ADC-backed model discovery after the lightweight provider-discovery path by resolving emitted discovery entries, exposing synthetic auth on bootstrap discovery, and honoring copied env snapshots when probing the default GCP ADC path. Fixes #65715. (#65716) Thanks @feiskyer.
- Plugins/install: add newly installed plugin ids to an existing `plugins.allow` list before enabling them, so allowlisted configs load installed plugins after restart.
@@ -1728,7 +1906,7 @@ Docs: https://docs.openclaw.ai
- Providers/Ollama: allow Ollama models using the native `api: "ollama"` path to optionally display thinking output when `/think` is set to a non-off level. (#62712) Thanks @hoyyeva.
- Codex CLI: pass OpenClaw's system prompt through Codex's `model_instructions_file` config override so fresh Codex CLI sessions receive the same prompt guidance as Claude CLI sessions.
- Auth/profiles: persist explicit auth-profile upserts directly and skip external CLI sync for local writes so profile changes are saved without stale external credential state.
- Agents/timeouts: make the LLM idle timeout inherit `agents.defaults.timeoutSeconds` when configured, disable the unconfigured idle watchdog for cron runs, and point idle-timeout errors at `agents.defaults.llm.idleTimeoutSeconds`. Thanks @drvoss.
- Agents/timeouts: make the LLM idle timeout inherit `agents.defaults.timeoutSeconds` when configured, disable the unconfigured idle watchdog for cron runs, and improve idle-timeout recovery guidance. Thanks @drvoss.
- Agents/failover: classify Z.ai vendor code `1311` as billing and `1113` as auth, including long wrapped `1311` payloads, so these errors stop falling through to generic failover handling. (#49552) Thanks @1bcMax.
- QQBot/media-tags: support HTML entity-encoded angle brackets (`&lt;`/`&gt;`), URL slashes in attributes, and self-closing media tags so upstream `<qqimg>` payloads are correctly parsed and normalized. (#60493) Thanks @ylc0919.
- Memory/dreaming: harden grounded backfill inputs, diary writes, status payloads, and diary action classification by preserving source-day labels, rejecting missing or symlinked targets cleanly, normalizing diary headings in gateway backfills, and tightening claim splitting plus diary source metadata. Thanks @mbelinky.
@@ -4607,7 +4785,7 @@ Docs: https://docs.openclaw.ai
- Slack/Allowlist channels: match channel IDs case-insensitively during channel allowlist resolution so lowercase config keys (for example `c0abc12345`) correctly match Slack runtime IDs (`C0ABC12345`) under `groupPolicy: "allowlist"`, preventing silent channel-event drops. (#26878) Thanks @lbo728.
- Discord/Typing indicator: prevent stuck typing indicators by sealing channel typing keepalive callbacks after idle/cleanup and ensuring Discord dispatch always marks typing idle even if preview-stream cleanup fails. (#26295) Thanks @ngutman.
- Channels/Typing indicator: guard typing keepalive start callbacks after idle/cleanup close so post-close ticks cannot re-trigger stale typing indicators. (#26325) Thanks @win4r.
- Followups/Typing indicator: ensure followup turns mark dispatch idle on every exit path (including `NO_REPLY`, empty payloads, and agent errors) so typing keepalive cleanup always runs and channel typing indicators do not get stuck after queued/silent followups. (#26881) Thanks @codexGW.
- Followups/Typing indicator: ensure followup turns mark dispatch idle on every exit path (including `NO_REPLY`, empty payloads, and agent errors) so typing keepalive cleanup always runs and channel typing indicators do not get stuck after queued/silent followups. (#26881)
- Voice-call/TTS tools: hide the `tts` tool when the message provider is `voice`, preventing voice-call runs from selecting self-playback TTS and falling into silent no-output loops. (#27025).
- Agents/Tools: normalize non-standard plugin tool results that omit `content` so embedded runs no longer crash with `Cannot read properties of undefined (reading 'filter')` after tool completion (including `tesseramemo_query`). (#27007).
- Agents/Tool-call dispatch: trim whitespace-padded tool names in both transcript repair and live streamed embedded-runner responses so exact-match tool lookup no longer fails with `Tool .. not found` for model outputs like `" read "`. (#27094) Thanks @openperf and @Sid-Qin.

View File

@@ -77,7 +77,7 @@ Welcome to the lobster tank! 🦞
- **Tengji (George) Zhang** - Chinese model APIs, cloud, pi
- GitHub: [@odysseus0](https://github.com/odysseus0) · X: [@odysseus0z](https://x.com/odysseus0z)
- **Sliverp** - Chinese Channel: QQ, WeChat, Wecom, Dingtalk, Feishu
- **Sliverp** - Chinese Channel: QQ, WeChat, Wecom, Yuanbao, Dingtalk, Feishu
- GitHub: [@sliverp](https://github.com/sliverp) · X: [@sliver01234](https://x.com/sliver01234)
- **Mason Huang** - Stability, Security, Speed

View File

@@ -72,10 +72,20 @@ RUN --mount=type=cache,id=openclaw-pnpm-store,target=/root/.local/share/pnpm/sto
NODE_OPTIONS=--max-old-space-size=2048 pnpm install --frozen-lockfile
# pnpm v10+ may append peer-resolution hashes to virtual-store folder names; do not hardcode `.pnpm/...`
# paths. Fail fast here if the Matrix native binding did not materialize after install.
RUN echo "==> Verifying critical native addons..." && \
# paths. Matrix's native downloader can hit transient release CDN errors while
# still exiting successfully, so retry the package downloader before failing.
RUN set -eux; \
echo "==> Verifying critical native addons..."; \
for attempt in 1 2 3 4 5; do \
if find /app/node_modules -name "matrix-sdk-crypto*.node" 2>/dev/null | grep -q .; then \
exit 0; \
fi; \
echo "matrix-sdk-crypto native addon missing; retrying download (${attempt}/5)"; \
node /app/node_modules/@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js || true; \
sleep $((attempt * 2)); \
done; \
find /app/node_modules -name "matrix-sdk-crypto*.node" 2>/dev/null | grep -q . || \
(echo "ERROR: matrix-sdk-crypto native addon missing (pnpm install may have silently failed on this arch)" >&2 && exit 1)
(echo "ERROR: matrix-sdk-crypto native addon missing after retries" >&2 && exit 1)
COPY . .
@@ -150,7 +160,8 @@ RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,shar
--mount=type=cache,id=openclaw-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
procps hostname curl git lsof openssl
ca-certificates procps hostname curl git lsof openssl && \
update-ca-certificates
RUN chown node:node /app
@@ -243,6 +254,11 @@ RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,shar
RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw \
&& chmod 755 /app/openclaw.mjs
# Pre-create the default state dir so first-run Docker named volumes mounted
# here inherit node ownership instead of starting as root-owned state.
RUN install -d -m 0700 -o node -g node /home/node/.openclaw && \
stat -c '%U:%G %a' /home/node/.openclaw | grep -qx 'node:node 700'
ENV NODE_ENV=production
# Security hardening: Run as non-root user

View File

@@ -2,6 +2,410 @@
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>OpenClaw</title>
<item>
<title>2026.4.25</title>
<pubDate>Mon, 27 Apr 2026 13:34:25 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026042590</sparkle:version>
<sparkle:shortVersionString>2026.4.25</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.4.25</h2>
<h3>Highlights</h3>
<ul>
<li>Voice replies get a full TTS upgrade: <code>/tts latest</code>, chat-scoped auto-TTS controls, personas, per-agent/per-account overrides, and new Azure Speech, Xiaomi, Local CLI, Inworld, Volcengine, and ElevenLabs v3 provider coverage. Thanks @leonchui, @zoujiejun, @solar2ain, @cshape, @xuruiray, @itsuzef, and @barronlroth.</li>
<li>Plugin startup and install paths move to the cold persisted registry, cutting broad manifest scans while making plugin update, repair, provider discovery, and install metadata more deterministic. Thanks @vincentkoc and @shakkernerd.</li>
<li>OpenTelemetry coverage expands across model calls, token usage, tool loops, harness runs, exec processes, outbound delivery, context assembly, and memory pressure with bounded low-cardinality attributes. Thanks @vincentkoc, @jlapenna, @Lidang-Jiang, and @oc-factus.</li>
<li>Browser automation gets safer tab URLs, iframe-aware role snapshots, CDP readiness tuning, headless one-shot launch, and deeper browser doctor probes for slow hosts. Thanks @beat843796 and @BenediktSchackenberg.</li>
<li>Control UI and setup flows add PWA/Web Push support, Crestodian first-run repair, TUI setup, context mode selection, and a shorter startup greeting. Thanks @eduardocruz, @SebTardif, and @kevinlin-openai.</li>
<li>Install/update hardening covers Windows, macOS, Linux, Docker, bundled plugin runtime deps, Node service restarts, LaunchAgent token rotation, and mixed-version gateway verification. Thanks @Kobevictor, @igormf, @abhinas90, @jsompis, @Solvely-Colin, and @gucasbrg.</li>
</ul>
<h3>Changes</h3>
<ul>
<li>TTS/WhatsApp: add <code>/tts latest</code> read-aloud support with duplicate suppression and <code>/tts chat on|off|default</code> session-scoped auto-TTS overrides, completing the on-demand voice-note UX for current-chat replies. Fixes #66032.</li>
<li>TTS/channels: resolve channel and account TTS overrides generically, enabling Feishu and QQBot accounts to deep-merge <code>channels.<channel>.accounts.<id>.tts</code> over global and per-agent TTS config. Thanks @sahilsatralkar.</li>
<li>TTS/agents: allow <code>agents.list[].tts</code> to override global <code>messages.tts</code> for per-agent voices, and make <code>/tts audio</code>, <code>/tts status</code>, and the <code>tts</code> agent tool honor the active voice/provider override while keeping shared provider credentials and preferences in the existing TTS config surface.</li>
<li>Providers/Azure Speech: add Azure Speech as a bundled TTS provider with Speech-resource auth, voice listing, SSML escaping, native Ogg/Opus voice-note output, and telephony output. (#51776) Thanks @leonchui.</li>
<li>Google Meet: add calendar-backed attendance export workflows, export manifests, dry-run previews, and tool parity for meeting records.</li>
<li>Control UI: add PWA install support and Web Push notifications for Gateway chat. (#44590) Thanks @eduardocruz.</li>
<li>Browser automation: add safe tab URLs in agent responses plus a CDP-native role snapshot fallback with iframe-aware refs, cursor-clickable detection, target attach preparation, and <code>openclaw browser doctor --deep</code> live snapshot probing.</li>
<li>CLI/image generation: expose generic <code>--background</code> on <code>openclaw infer image generate</code> and <code>openclaw infer image edit</code>, keep <code>--openai-background</code> as an OpenAI alias, and let fal image generation honor <code>--output-format png|jpeg</code>.</li>
<li>Browser/config: allow local managed Chrome launch discovery and post-launch CDP readiness timeouts to be raised for slower hosts such as Raspberry Pi. Fixes #66803. Thanks @beat843796.</li>
<li>Discord: allow <code>channels.discord.voice.model</code> to override the LLM used for voice channel responses while keeping STT and TTS on their existing media settings. (#64368) Thanks @mrdavey.</li>
<li>Browser/CLI: add <code>openclaw browser start --headless</code> as a one-shot local managed browser launch override without rewriting persisted browser config. Thanks @BenediktSchackenberg.</li>
<li>CLI/Crestodian/TUI: add the first-run setup helper, local planner fallback, full-TUI interactive Crestodian, startup progress indicators, context mode selector, and a shorter startup greeting. (#71720, #71760) Thanks @SebTardif and @kevinlin-openai.</li>
<li>Plugins: migrate the local plugin registry automatically during package install/update, keeping install metadata in the plugin index while indexing existing plugin manifests for the new cold registry path. Thanks @vincentkoc and @shakkernerd.</li>
<li>Plugins/doctor: make <code>openclaw doctor --fix</code> refresh the plugin index and cold registry index when needed without treating plugin install records as authored config. Thanks @vincentkoc and @shakkernerd.</li>
<li>Plugins/hooks: add before-agent-finalize hooks, cron <code>jobId</code> hook context, bounded native permission fingerprints, and Codex MCP hook relay support. (#71765, #71758, #71707) Thanks @vincentkoc and @pashpashpash.</li>
<li>Plugins/tokenjuice: bump the bundled tokenjuice runtime to 0.6.3. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: align model-call GenAI span attributes with OpenTelemetry stability opt-in semantics, keeping legacy <code>gen_ai.system</code> by default while emitting <code>gen_ai.provider.name</code> under <code>OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental</code>. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: support signal-specific OTLP endpoint overrides for traces, metrics, and logs via config or standard OTEL environment variables. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: emit bounded telemetry exporter health diagnostics for startup and log-export failures without exporting raw error text. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: export agent harness lifecycle telemetry as bounded <code>openclaw.harness.run</code> spans and <code>openclaw.harness.duration_ms</code> metrics so QA-lab, Codex, and future harnesses share one trace shape. Thanks @vincentkoc.</li>
<li>Diagnostics/trace: propagate W3C <code>traceparent</code> headers from trusted model-call trace context to provider transports while replacing caller-supplied traceparent values. Thanks @vincentkoc.</li>
<li>Diagnostics/Prometheus: add a bundled <code>diagnostics-prometheus</code> plugin with a protected gateway scrape route for low-cardinality diagnostics metrics. Thanks @vincentkoc.</li>
<li>Plugins/CLI: add <code>openclaw plugins registry</code> for explicit persisted-registry inspection and <code>--refresh</code> repair without making normal startup rescan plugin locations. Thanks @vincentkoc.</li>
<li>Plugins/CLI: make <code>openclaw plugins list</code> read the cold persisted registry snapshot by default, leaving module-aware diagnostics to <code>plugins doctor</code> and <code>plugins inspect</code>. Thanks @vincentkoc.</li>
<li>Plugins/startup: move gateway startup plugin planning onto the versioned cold registry index, with postinstall repair for older registry files that predate startup metadata. Thanks @vincentkoc.</li>
<li>Plugins/startup: normalize startup and provider plugin enablement through registry aliases so boot paths do not need the legacy manifest alias scan. Thanks @vincentkoc.</li>
<li>Providers/plugins: resolve provider ownership, provider discovery scopes, and catalog-hook provider ids from the cold plugin registry instead of rescanning manifests on those paths. Thanks @vincentkoc.</li>
<li>Plugins/registry: keep installed plugin index records focused on install/state/load paths and resolve plugin capabilities from manifests scoped to indexed plugins. Thanks @shakkernerd.</li>
<li>Plugins/registry: route cold manifest and capability lookups through the installed plugin index so setup, channels, config, secrets, doctor, and provider metadata paths avoid broad plugin-root scans before runtime execution. Thanks @shakkernerd.</li>
<li>CLI/models: speed up <code>models list --all --provider <id></code> for static manifest-backed providers by loading catalog rows through the installed plugin index instead of broad manifest scans or runtime suppression hooks. Thanks @shakkernerd.</li>
<li>CLI/models: use OpenClaw Provider Index preview rows as the final cold fallback for installable providers, while keeping user config, installed manifests, and refreshed cache rows above provider-index metadata. Thanks @vincentkoc.</li>
<li>Providers/plugins: keep onboarding and auth-choice setup lists on cold manifest/install metadata and add Provider Index install metadata for not-yet-installed provider plugins. Thanks @vincentkoc.</li>
<li>Providers/plugins: keep provider setup guidance and configure auth imports on cold manifest metadata, with a regression guard against static provider-runtime imports on setup/configure list paths. Thanks @vincentkoc.</li>
<li>CLI/capabilities: keep capability command registration from importing the models auth runtime until <code>model auth login</code> actually runs. Thanks @vincentkoc.</li>
<li>CLI/configure: keep web-search configure prompts on cold plugin registry metadata until the user chooses managed search setup. Thanks @vincentkoc.</li>
<li>Plugins/chat commands: refresh the persisted plugin registry after <code>/plugins enable</code> and <code>/plugins disable</code>, matching the CLI mutation path. Thanks @vincentkoc.</li>
<li>Plugins/compat: mark <code>OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY</code> as a deprecated break-glass switch and point operators at registry repair instead. Thanks @vincentkoc.</li>
<li>Plugins/compat: expand the central compatibility registry with dated owners, replacements, and maximum three-month removal targets for legacy SDK, manifest, setup, registry-migration, and agent-runtime surfaces. Thanks @vincentkoc.</li>
<li>Plugins/registry: ignore stale persisted registry reads when plugin policy no longer matches current config, and stamp generated registry files with a do-not-edit warning. Thanks @vincentkoc.</li>
<li>Config/plugins: keep plugin command-alias validation on cold manifest metadata instead of importing the runtime alias resolver. Thanks @vincentkoc.</li>
<li>Security/plugins: keep web-search credential presence checks on cold config, env, and manifest metadata instead of importing web-search provider runtime. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: surface provider request identifiers as bounded hashes on model-call diagnostics and span events, without exporting raw request IDs or metric labels. Thanks @Lidang-Jiang and @vincentkoc.</li>
<li>Plugins/diagnostics: add metadata-only <code>model_call_started</code> and <code>model_call_ended</code> hooks for provider/model call telemetry without exposing prompts, responses, headers, request bodies, or raw provider request IDs. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: emit bounded context assembly diagnostics and export <code>openclaw.context.assembled</code> spans with prompt/history sizes but no prompt, history, response, or session-key content. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: export existing tool-loop diagnostics as <code>openclaw.tool.loop</code> counters and spans without loop messages, session identifiers, params, or tool output. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: export diagnostic memory samples and pressure as bounded memory histograms, counters, and pressure spans to help spot leak regressions without session or payload data. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: add the GenAI <code>gen_ai.client.token.usage</code> histogram for input/output model usage while keeping session identifiers and aggregate cache counters out of the semantic metric. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: add a bounded <code>openclaw.agent</code> label to OpenClaw token metrics so per-agent Grafana dashboards can group usage without exporting session identifiers. Thanks @oc-factus.</li>
<li>Plugins/install: consolidate managed plugin install metadata into the state-managed plugin index at <code>plugins/installs.json</code>, replacing the temporary <code>plugins/installed-index.json</code> path and removing <code>plugins.installs</code> as an authored config surface. Thanks @vincentkoc and @shakkernerd.</li>
<li>Diagnostics/OTEL: add the GenAI <code>gen_ai.client.operation.duration</code> histogram for model-call latency in seconds with bounded provider/model/API and error attributes. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: add GenAI usage token attributes to model-usage spans, including cache read/write input token counts without session identifiers or prompt/response content. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: include bounded GenAI operation, provider, and request-model attributes on model-usage spans so token usage remains self-describing without diagnostic identifiers. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: keep model-usage span GenAI provider attributes aligned with the existing semantic-convention opt-in policy, using legacy <code>gen_ai.system</code> unless latest experimental GenAI conventions are enabled. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: keep <code>gen_ai.request.model</code> present on GenAI token usage metrics with a bounded <code>unknown</code> fallback when model usage events do not include a model. Thanks @vincentkoc.</li>
<li>Docs/OTEL: document the GenAI token and model-call duration metrics, model-usage span attributes, and <code>OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental</code> provider-attribute behavior. Thanks @vincentkoc.</li>
<li>Docs: refresh the MCP, model provider, doctor, troubleshooting, BlueBubbles, media generation, TTS, subagents, skills, cron/tasks, exec approvals, and voice-call guides with structured Steps, Tabs, and Accordion content.</li>
<li>Diagnostics/trace: add an internal traceparent propagation helper that only formats trusted dispatcher metadata, keeping plugin-emitted diagnostic traces out of outbound propagation by default. Thanks @vincentkoc.</li>
<li>Diagnostics/OTEL: add bounded outbound message delivery lifecycle diagnostics and export them as low-cardinality delivery spans/metrics without message body, recipient, room, or media-path data. (#71471) Thanks @vincentkoc and @jlapenna.</li>
<li>Diagnostics/OTEL: emit bounded exec-process diagnostics and export them as <code>openclaw.exec</code> spans without exposing command text, working directories, or container identifiers. (#71451) Thanks @vincentkoc and @jlapenna.</li>
<li>Diagnostics/OTEL: support <code>OPENCLAW_OTEL_PRELOADED=1</code> so the plugin can reuse an already-registered OpenTelemetry SDK while keeping OpenClaw diagnostic listeners wired. (#71450) Thanks @vincentkoc and @jlapenna.</li>
<li>Providers/Xiaomi: add MiMo TTS as a bundled speech provider with MP3/WAV output and voice-note Opus transcoding. Fixes #52376. (#55614) Thanks @zoujiejun.</li>
<li>Providers/ElevenLabs: include <code>eleven_v3</code> in the bundled TTS model catalog so model selection surfaces can offer ElevenLabs v3. (#68321) Thanks @itsuzef.</li>
<li>Providers/Local CLI TTS: add a bundled local command speech provider with file/stdout input, voice-note Opus conversion, and telephony PCM output. (#56239) Thanks @solar2ain.</li>
<li>Providers/Inworld: add Inworld as a bundled speech provider with streaming TTS synthesis, voice listing, voice-note output, and PCM telephony output. (#55972) Thanks @cshape.</li>
<li>Providers/Volcengine: add Volcengine/BytePlus Seed Speech as a bundled TTS provider with API-key auth, native Ogg/Opus voice-note output, and MP3 audio-file output. (#55641) Thanks @xuruiray.</li>
<li>Android/Talk Mode: expose Talk Mode in the Voice tab with runtime-owned voice capture modes and microphone foreground-service escalation. Thanks @alex-latitude.</li>
<li>Providers/LiteLLM: register <code>litellm</code> as an image-generation provider so <code>image_generate model=litellm/...</code> calls and <code>agents.defaults.imageGenerationModel.fallbacks</code> entries resolve through the LiteLLM proxy. Thanks @zqchris.</li>
<li>Providers/fal: add Seedance 2.0 reference-to-video models with multi-image, video, and audio reference input mapping plus model-specific capability limits for <code>video_generate</code>. Thanks @shivanker.</li>
<li>Codex harness: require Codex app-server <code>0.125.0</code> or newer and cover native MCP <code>PreToolUse</code>, <code>PostToolUse</code>, and <code>PermissionRequest</code> payloads through the OpenClaw hook relay.</li>
<li>Agents/Codex: teach prompts and <code>agents_list</code> to surface native Codex app-server availability so agents prefer <code>/codex ...</code> over Codex ACP unless ACP/acpx is explicit. Thanks @vincentkoc.</li>
<li>ACPX/Droid: add Factory Droid to the live ACP bind Docker matrix, including <code>.factory</code> settings staging, <code>FACTORY_API_KEY</code> forwarding, and the single-agent <code>test:docker:live-acp-bind:droid</code> recipe.</li>
<li>TTS/personas: add provider-aware TTS personas with deterministic provider binding merges, <code>/tts persona</code> controls, gateway/CLI persona state, Google Gemini <code>audio-profile-v1</code> prompt wrapping, and OpenAI instruction mapping. (#70748) Thanks @barronlroth.</li>
<li>Voice Wake: add trigger-based routing so macOS voice wake phrases can select a configured agent or session target, with Gateway routing APIs and node update events. (#30354) Thanks @longbiaochen.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Auto-reply: poison inbound message dedupe after replay-unsafe provider/runtime failures so retries stay safe before visible progress but cannot duplicate messages after block output, tool side effects, or session progress. Fixes #69303; keeps #58549 and #64606 as duplicate validation. Thanks @martingarramon, @NikolaFC, and @zeroth-blip.</li>
<li>Logging/sessions: apply configured redaction patterns to persisted session transcript text and accept escaped character classes in safe custom redaction regexes, so transcript JSONL no longer keeps matching sensitive text in the clear. Fixes #42982. Thanks @panpan0000.</li>
<li>Agents/OpenAI: keep Responses web search compatible with minimal thinking by raising <code>web_search</code> requests to the lowest supported reasoning effort instead of sending a rejected minimal payload.</li>
<li>Agents/tools: honor the <code>bundle-mcp</code> allowlist token when deciding whether bundled MCP tools are available, so restricted tool policies can still enable bundled MCP without exposing unrelated tools.</li>
<li>Agents/model fallback: jump directly to a known later live-session model redirect instead of walking unrelated fallback candidates, while preserving the already-landed live-session/fallback loop guard. Fixes #57471; related loop family already closed via #58496. Thanks @yuxiaoyang2007-prog.</li>
<li>Skills/memory: restore Chokidar v5 hot reloads by watching concrete skill and memory roots with filters, including SKILL.md removals and deleted skill folders without broad workspace recursion. Fixes #27404, #33585, and #41606. Thanks @shelvenzhou, @08820048, and @rocke2020.</li>
<li>Discord/gateway: count failed health-monitor restart attempts toward cooldown and hourly caps, and evict stale account lifecycle state during channel reloads so repeated Discord gateway recovery cannot loop on old status. Fixes #38596. (#40413) Thanks @jellyAI-dev and @vashquez.</li>
<li>Plugins/CLI: let flag-driven <code>openclaw channels add</code> install the selected channel plugin from its default source without opening an interactive prompt, fixing published npm Telegram setup in stdin-closed automation.</li>
<li>Plugins/startup: load the default <code>memory-core</code> slot during Gateway startup when permitted so active-memory recall can call <code>memory_search</code> and <code>memory_get</code> without requiring an explicit <code>plugins.slots.memory</code> entry, while preserving <code>plugins.slots.memory: "none"</code>.</li>
<li>Plugins/install: materialize plugin-owned root chunks in external bundled-runtime mirrors so staged plugin dependencies resolve under native ESM in packaged installs. Fixes #72058; supersedes #72084. Thanks @amnesia106 and @drvoss.</li>
<li>Plugins/CLI: prefer native require for compiled bundled plugin JavaScript before jiti so read-only config, status, device, and node commands avoid unnecessary transform overhead on slow hosts. Fixes #62842. Thanks @Effet.</li>
<li>Plugins/compat: inventory doctor-side deprecation migrations separately from runtime plugin compatibility so release sweeps preserve needed repairs while enforcing dated removal windows. Thanks @vincentkoc.</li>
<li>Plugins/compat: add missing dated compatibility records for legacy extension-api, memory registration, provider hook/type aliases, runtime aliases, channel SDK helpers, and approval/test utility shims. Thanks @vincentkoc.</li>
<li>Plugins/CLI: refresh the persisted registry after managed plugin files are removed so ClawHub uninstall cannot leave stale <code>plugins list</code> entries.</li>
<li>Plugins/CLI: make plugin install and uninstall config writes conflict-aware, clear stale denylist entries on explicit reinstall/removal, and delete managed plugin files only after config/index commit succeeds.</li>
<li>Plugins: fail <code>plugins update</code> when tracked plugin or hook updates error, keep bundled runtime-dependency repair behind restrictive allowlists, and reject package installs with unloadable extension entries.</li>
<li>Gateway/chat: keep duplicate attachment-backed <code>chat.send</code> retries with the same idempotency key on the documented in-flight path so aborts still target the real active run. Fixes #70139. Thanks @Feelw00.</li>
<li>Plugins: share package entrypoint resolution between install and discovery, reject mismatched <code>runtimeExtensions</code>, and cache bundled runtime-dependency manifest reads during scans.</li>
<li>WhatsApp/Web: keep quiet but healthy linked-device sessions connected by basing the watchdog on WhatsApp Web transport activity, while retaining a longer app-silence cap so frame activity cannot mask a stuck session forever. Fixes #70678; carries forward the focused #71466 approach and keeps #63939 as related configurable-timeout follow-up. Thanks @vincentkoc and @oromeis.</li>
<li>Onboarding/setup: keep first-run config reads, plugin compatibility notices, and post-model sanity checks on cold metadata paths unless the user chooses to browse all models, avoiding full plugin/runtime catalog work between prompts. Thanks @shakkernerd.</li>
<li>Onboarding/auth: run manifest-owned provider auth choices through scoped setup providers so selecting OpenAI Codex browser/device auth no longer loads every provider runtime before OAuth starts. Thanks @shakkernerd.</li>
<li>Onboarding/auth: keep the post-auth default-model policy lookup on manifest/setup metadata so the next prompt appears without loading broad provider runtime. Thanks @shakkernerd.</li>
<li>Onboarding/models: keep skip-auth and provider-scoped model picker prompts off the full global model catalog path, and cache provider catalog hook resolution so setup no longer stalls after auth on large plugin registries. Thanks @shakkernerd.</li>
<li>Gateway/Bonjour: suppress known @homebridge/ciao cancellation and network assertion failures through scoped process handlers so malformed mDNS packets or restricted VPS networking disable/restart Bonjour instead of crashing the gateway. Fixes #67578. Thanks @zenassist26-create.</li>
<li>Discord: keep late clicks on already-resolved exec approval buttons quiet when elevated mode auto-resolved the request, while still surfacing real approval submission failures. Fixes #66906. Thanks @rlerikse.</li>
<li>Agents/subagents: deliver completed yielded-subagent results back to no-thread requester routes via direct fallback when the dormant parent announce turn produces no visible reply, and add QA-lab coverage for the regression. Thanks @vincentkoc.</li>
<li>Gateway/Tailscale: let Tailscale-authenticated Control UI operator sessions with browser device identity skip the device-pairing round trip while still rejecting device-less and node-role connections. Refs #71986. Thanks @jokedul.</li>
<li>Doctor: honor <code>OPENCLAW_SERVICE_REPAIR_POLICY=external</code> by reporting gateway service health while skipping service install/start/restart/bootstrap, supervisor rewrites, and legacy service cleanup for externally managed environments. Thanks @shakkernerd.</li>
<li>CLI/update: run package post-update doctor with <code>--fix</code> so package updates repair config migrations before restart. Thanks @shakkernerd.</li>
<li>CLI/update: retry failed npm global updates with <code>--omit=optional</code> and ignore the superseded first failure when the fallback succeeds. Thanks @shakkernerd.</li>
<li>Plugins/uninstall: migrate and reset <code>plugins.slots.contextEngine</code> alongside memory slots when plugin ids change or selected plugins are removed. Thanks @shakkernerd.</li>
<li>Agents/Discord: keep raw <code>Agent failed before reply</code> runner failures out of Discord group/channel chats and show detailed runner errors in direct chats only when <code>/verbose</code> is enabled.</li>
<li>UI/Windows: quote resolved pnpm <code>.cmd</code> launcher paths before spawning UI install/build/test commands so Node installs under <code>C:\Program Files</code> no longer fail as <code>C:\Program</code>. Fixes #45275. Thanks @Kobevictor, @stoppieboy, and @iubns.</li>
<li>Codex/agent: translate <code>--thinking minimal</code> to <code>low</code> for modern Codex models (gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.2) at request build time so the first turn is accepted instead of paying a wasted call + retry-with-low fallback. Older Codex models still receive <code>minimal</code> directly. Fixes #71946. Thanks @hclsys.</li>
<li>Plugins/uninstall: remove tracked plugin files from their recorded managed extensions root even when the current state directory points somewhere else, so <code>openclaw plugins uninstall --force</code> does not leave the plugin discoverable. Thanks @shakkernerd.</li>
<li>Agents/runtime: add <code>agentRuntime.id</code> as the canonical config key, migrate legacy runtime-policy configs with <code>openclaw doctor --fix</code>, route canonical Anthropic models through <code>claude-cli</code> without passing CLI backend aliases to embedded harness selection, and load CLI backend owner plugins before channel startup. Fixes #71957. Thanks @WolvenRA.</li>
<li>CLI/update: guard Windows scheduled-task stops by state and timeout so auto-update restart cannot hang indefinitely on <code>schtasks /End</code> before stale-listener cleanup. Fixes #69970. Thanks @yangswld and @sherlock-huang.</li>
<li>Windows install/Lobster: execute <code>pnpm.exe</code> directly when <code>npm_execpath</code> points at the native pnpm binary, add an installed-package fallback for the Lobster embedded runtime, and include the Lobster runner regression test in Windows CI. Fixes #69456. Thanks @igormf.</li>
<li>Gateway/install: refresh loaded gateway service installs when the current service embeds stale gateway auth instead of returning already-installed, avoiding LaunchAgent token-mismatch loops after token rotation. Fixes #70752. Thanks @hyspacex.</li>
<li>Update: ignore bundled plugin <code>.openclaw-install-stage</code> directories during global install verification and packaged dist pruning so leftover runtime-dep staging files do not turn successful updates into <code>unexpected packaged dist file</code> failures. Fixes #71752. Thanks @waynegault.</li>
<li>CLI/update: fail package updates when post-update plugin sync fails and refresh legacy npm plugin install records before trusting unchanged artifacts, preventing successful updates from restarting with stale or failed plugin state. Thanks @vincentkoc and @shakkernerd.</li>
<li>Release/update: reject pre-populated bundled plugin <code>.openclaw-install-stage</code> directories, including mixed-case path variants, before package inventory generation so release tarballs cannot ship poisoned runtime-dependency staging debris. Fixes #71752. Thanks @hclsys.</li>
<li>Node runtime: keep node-host retry timers alive across Gateway restarts and exit on terminal credential pauses so supervised nodes do not become silent zombies. Fixes #69800. Thanks @meroli28.</li>
<li>Gateway/plugins: stop persisted WhatsApp auth state from activating bundled channel runtime-dependency repair during startup when <code>channels.whatsapp</code> is absent, avoiding npm/git stalls on packaged Linux installs. Fixes #71994. Thanks @xiao398008.</li>
<li>Gateway/device tokens: enforce caller-scope containment inside token rotation and revocation so pairing-only sessions cannot mutate higher-scope operator tokens. Fixes #71990. Thanks @coygeek.</li>
<li>Plugins/channels: keep security checks, thread-binding placement, provider summaries, health formatting, and message action labels on read-only or already-loaded channel metadata instead of importing full channel runtime. Thanks @shakkernerd.</li>
<li>Plugins/status: keep config-only channel labels and status security summaries from importing plugin runtime modules just to render metadata. Thanks @shakkernerd.</li>
<li>Sessions/channels: stop group-session metadata from loading bundled channel runtime just to classify <code>#channel</code> subjects, using only already-loaded channel capabilities on that path. Thanks @shakkernerd.</li>
<li>Plugins/channels: keep native command and native skill <code>auto</code> defaults on static channel metadata so config, audit, and command-list checks do not load channel runtime just to read those defaults. Thanks @shakkernerd.</li>
<li>CLI/channels: keep channel remove selection and all-channel capabilities summaries on read-only plugin metadata, loading channel runtime only for the selected mutation path. Thanks @shakkernerd.</li>
<li>CLI/models: keep Provider Index preview rows out of <code>models list --all --provider <id></code> when the owning provider plugin is disabled, preserving config authority for cold catalog fallbacks. Thanks @shakkernerd.</li>
<li>CLI/model runs: keep <code>openclaw infer model run</code> on explicit OpenRouter models from loading the full provider catalog or inheriting chat-agent silent-reply policy, restoring non-empty one-shot probe output. Fixes #68791. Thanks @limpredator.</li>
<li>Installer/macOS: rerun Homebrew install steps without the gum spinner when raw-mode ioctl failures occur, and avoid claiming <code>node@24</code> was installed when the Homebrew keg binary is missing. Fixes #70411. Thanks @1fanwang and @dad-io.</li>
<li>Installer: load nvm before Node.js detection so <code>curl | bash</code> installs respect nvm-managed Node instead of stale system Node. Fixes #49556. Thanks @heavenlxj.</li>
<li>Installer/Windows: route PowerShell install failures through a top-level handler so <code>iwr ... | iex</code> returns control to the current shell while direct script-file runs still exit non-zero. Fixes #38054. Thanks @PwrSrg.</li>
<li>CLI/Volta: respawn raw <code>openclaw</code> CLI runs through the named <code>node</code> shim when the current Node executable resolves to <code>volta-shim</code>, avoiding direct shim execution failures in non-interactive shells. Fixes #68672. Thanks @sanchezm86.</li>
<li>Installer: warn when multiple npm global roots contain OpenClaw installs, showing active Node/npm/openclaw plus each install path and version so stale version-manager installs are visible. Fixes #40839. Thanks @zhixianio.</li>
<li>Cron/tasks: recover completed cron task ledger records from durable run logs and job state before marking them <code>lost</code>, reducing false <code>backing session missing</code> audit errors for isolated cron runs and keeping offline CLI audit from treating its empty local cron active-job set as authoritative. Fixes #71963.</li>
<li>Docker: copy patched dependency files into runtime images so downstream <code>pnpm install</code> layers keep working. Fixes #69224. Thanks @gucasbrg.</li>
<li>Package: include patched dependency files in the published npm package so downstream installs can resolve <code>patchedDependencies</code>. (#69224) Thanks @gucasbrg and @vincentkoc.</li>
<li>Plugins/channels: treat malformed bundled channel plugin loaders that return <code>undefined</code> as unavailable instead of crashing config and help paths. Fixes #69044. Thanks @frankhli843 and @vincentkoc.</li>
<li>Scripts/watch: show corrupted dependency package-config recovery guidance when <code>gateway:watch</code> fails during watcher startup, without double-logging unrelated import failures. (#58780) Thanks @roytong9 and @vincentkoc.</li>
<li>Signal: read signal-cli RPC, health checks, and SSE events through Node's HTTP client so Node 24/25 fetch regressions do not break Signal sends or inbound events. Fixes #51716 and #53040. Thanks @Barukimang, @minupla, and @vincentkoc.</li>
<li>Skills/Docker: run npm-backed skill dependency installs with an OpenClaw-managed user prefix so non-root Docker images do not write to <code>/usr/local</code>. Fixes #59601. Thanks @chanjarster and @vincentkoc.</li>
<li>Agents/runtime: submit heartbeat, cron, and exec wakeups as transient runtime context instead of visible user prompts, keeping synthetic system work out of chat transcripts. Fixes #66496 and #66814. Thanks @jeades and @mandomaker.</li>
<li>Telegram: include native quote excerpts automatically for threaded replies and reply tags when the original Telegram text is available, without adding another config knob. Fixes #6975. Thanks @rex05ai.</li>
<li>Node/Linux: make <code>openclaw node install</code> enable and restart the <code>openclaw-node</code> systemd unit instead of the gateway unit on node-only VMs. Fixes #68287. Thanks @dlebee-agent.</li>
<li>Browser/CDP: retry transient raw-CDP WebSocket handshake failures before any browser command is sent, and reconnect stale persistent Playwright CDP sessions for safe tab-list reads without replaying mutating browser actions. Fixes #67728.</li>
<li>Gateway/Linux: retry <code>systemctl --user enable</code> after a second daemon reload when the freshly written gateway unit is not visible yet on migrated systemd installs. Fixes #65184. Thanks @liushuaiiu.</li>
<li>Telegram: preserve exact selected quote text when sending native quote replies, and retry with legacy replies if Telegram rejects quote parameters. (#71952) Thanks @rubencu.</li>
<li>Plugins/CLI: preserve manifest name, description, format, and source metadata in cold <code>openclaw plugins list</code> output without importing plugin runtime. Thanks @shakkernerd.</li>
<li>Security/audit: read channel exposure and plugin allowlist ownership from read-only plugin index metadata so cold audits do not depend on loaded channel runtime. Thanks @shakkernerd.</li>
<li>Plugins/chat: keep <code>/plugins list</code>, <code>/plugins enable</code>, and <code>/plugins disable</code> on the persisted plugin index path so chat plugin management does not load diagnostic/runtime plugin registries before execution. Thanks @shakkernerd.</li>
<li>Plugins/doctor: read workspace plugin status and legacy web-search ownership through installed-index manifest metadata instead of broad manifest registry scans. Thanks @shakkernerd.</li>
<li>CLI/agents: read channel provider status from read-only plugin index metadata for text <code>agents list</code> output instead of the loaded channel registry. Thanks @shakkernerd.</li>
<li>Logging: redact configured secret patterns at console and file-log sink exits so credentials that reach the logger are masked before terminal display or JSONL persistence. Fixes #67953. Thanks @Ziy1-Tan.</li>
<li>Gateway/services: refuse process and service mutations from an older OpenClaw binary when the config was last written by a newer version, preventing split-brain installs from stopping or rewriting newer gateway services. Fixes #57079.</li>
<li>Gateway: reserve <code>/healthz</code> and <code>/readyz</code> ahead of plugin, canvas, and Control UI HTTP stages so liveness/readiness probes still answer when a later route handler stalls. Fixes #69674. Thanks @Xike-Creek.</li>
<li>Logging: load <code>logging.file</code> and redaction settings directly from the active OpenClaw config path in bundled runtimes, so packaged gateways stop falling back to <code>/tmp/openclaw</code>. Fixes #59370, #67168, and #61295. Thanks @KeaneYan, @Pan9hu, and @zsjlovelike.</li>
<li>Logging: rotate file logs at <code>logging.maxFileBytes</code>, keep bounded numbered archives, and make long-lived rolling loggers follow the current-day file instead of suppressing diagnostics or writing stale dated files. Fixes #58583 and #62381. Thanks @jpeghead and @zhaoleink.</li>
<li>Agents/groups: treat clean empty assistant stops as silent <code>NO_REPLY</code> only for always-on groups where silent replies are allowed, while keeping direct and mention-gated sessions on the incomplete-turn retry path. Thanks @MagnaAI.</li>
<li>macOS/Node: keep native remote app nodes from advertising <code>browser.proxy</code>, start browser-capable CLI node services through the restored <code>openclaw node start</code> command, and show an actionable browser-control error when the local control service is missing. Fixes #66637.</li>
<li>Gateway/update: fail package updates when the restarted managed gateway reports the wrong version, including fallback restarts and JSON mode, avoiding false-success mixed-version restarts after macOS LaunchAgent updates. Fixes #71835. Thanks @abhinas90 and @jsompis.</li>
<li>Gateway/update: warn before package updates and bundled plugin runtime-dependency repairs when the target volume appears low on disk space, without blocking installs on best-effort filesystem checks. Fixes #71835. Thanks @abhinas90 and @jsompis.</li>
<li>Plugins/runtime deps: surface activated plugin load failures in health and fail package-update restart verification or doctor repair when bundled runtime deps still cannot load, avoiding false-success repairs. (#71883) Thanks @Solvely-Colin.</li>
<li>Gateway/Linux: include fnm <code>aliases/default/bin</code> in generated service PATHs and let doctor accept either modern fnm aliases or the legacy <code>current/bin</code> symlink, avoiding false PATH repair prompts. Fixes #68169. Thanks @richard-scott.</li>
<li>Installer/Linux: run apt installs with noninteractive dpkg and needrestart settings so fresh Ubuntu 24.04 <code>curl | bash</code> installs do not hang while installing Node.js, Git, or build tools. Fixes #41146. Thanks @iht76, @alexcarv318, @cs3gallery, @firofame, and @cgdusek.</li>
<li>Providers/Bedrock: defer the AWS SDK import until Bedrock discovery actually runs so plugin registration and setup stay lightweight on cold start. Fixes #71690. Thanks @jarvis-ai-gregmoser.</li>
<li>Installer/macOS: stop immediately when Homebrew <code>node@24</code> installation fails and avoid printing PATH advice for missing Homebrew Node installs. Fixes #70411. Thanks @1fanwang.</li>
<li>WhatsApp: remove ack reactions after a visible reply when <code>messages.removeAckAfterReply</code> is enabled, matching other reaction-capable channels. Fixes #26183. Thanks @MrUnforsaken.</li>
<li>Providers/Z.AI: map OpenClaw thinking controls to Z.AI's <code>thinking</code> payload and add opt-in preserved thinking replay via <code>params.preserveThinking</code>, so GLM 5.x can keep prior <code>reasoning_content</code> when requested. Fixes #58680. Thanks @xuanmingguo.</li>
<li>Channels/status: keep read-only channel lists on manifest and package metadata by default, loading setup runtime only for explicit fallback callers. Thanks @shakkernerd.</li>
<li>Plugins: scope setup and web-provider metadata manifest reads to explicit plugin ids when callers already know the owning plugin set. Thanks @vincentkoc.</li>
<li>Plugins/onboarding: defer onboarding install-record index writes until the guarded config commit so setup failures cannot leave the plugin index ahead of <code>openclaw.json</code>. Thanks @shakkernerd.</li>
<li>Plugins/registry: resolve web provider ownership from the installed plugin index instead of broad manifest scans on secret, tool, and pricing paths. Thanks @shakkernerd.</li>
<li>Config/providers: accept <code>video</code> and <code>audio</code> in configured model <code>input</code> values and preserve them in provider catalog entries. Fixes #20721. Thanks @alvinttang.</li>
<li>Models/auth: honor the parent <code>--agent</code> flag for auth write commands (<code>add</code>, <code>login</code>, <code>setup-token</code>, <code>paste-token</code>, and the GitHub Copilot shortcut) so OAuth/API-key/token results are written to the requested agent store instead of the default agent. Fixes #71864. (#71933) Thanks @balric-seo.</li>
<li>TTS: strip model-emitted TTS directives from streamed block text before channel delivery, including directives split across adjacent blocks, while preserving the accumulated raw reply for final-mode synthesis. Fixes #38937.</li>
<li>TTS: keep explicit <code>provider=...</code> directive keys scoped to that provider and warn on unsupported keys instead of letting another speech provider consume overlapping keys. Fixes #60131.</li>
<li>TTS/Feishu: normalize final-mode streamed TTS-only audio before delivery so generated voice-note files use the same safe media path and native voice routing as normal final replies. Fixes #71920.</li>
<li>Feishu: transcribe inbound voice-note audio with the shared media audio path before agent dispatch and keep raw Feishu <code>file_key</code> payloads out of message text. Fixes #67120 and #61876.</li>
<li>Tasks: terminalize async Gateway agent task records from the Gateway run result while preserving aborted, failed, and cancelled outcomes instead of leaving completed runs stuck as active or lost. (#71905) Thanks @likewen-tech.</li>
<li>WhatsApp: let authorized group voice-note transcripts satisfy mention gating before reply dispatch, while keeping unmentioned transcripts in pending group history. Fixes #44908.</li>
<li>Media understanding: carry channel voice-note preflight state into attachment selection so WhatsApp, Feishu, Telegram, and Discord do not transcribe the same inbound audio twice. Fixes #70580.</li>
<li>TTS/BlueBubbles: deliver compatible auto-TTS audio as iMessage voice memo bubbles instead of plain MP3/CAF file attachments. Fixes #16848.</li>
<li>TTS: resolve voice-note and voice-memo routing from channel plugin capabilities instead of speech-core-owned channel id lists.</li>
<li>ACP: send subagent and async-task completion wakes to external ACP harnesses as plain prompts instead of OpenClaw internal runtime-context envelopes, while keeping those envelopes out of ACP transcripts.</li>
<li>TTS/status: show configured TTS model, voice, and sanitized custom endpoint in <code>/status</code>, preserve OpenAI-compatible TTS instructions on custom endpoints, and retry empty Microsoft/Edge TTS output once. Addresses #46602, #47232, and #43936. Thanks @leekuangtao, @Huntterxx, and @rex993.</li>
<li>Agents/Gateway: steer agent-driven config edits and restarts through the owner-only <code>gateway</code> tool, document <code>config.schema.lookup</code> as the field-doc source, and warn against using <code>gateway stop && gateway start</code> as a restart substitute on macOS. Fixes #71929. Thanks @ygc3817922006-sketch.</li>
<li>Media understanding/audio: inject a deterministic transcript placeholder for too-small voice notes so agents do not hallucinate transcription or provider failures. Fixes #48944. Thanks @eulicesl.</li>
<li>Providers/vLLM: send Nemotron 3 chat-template kwargs when thinking is off and honor configured <code>params.chat_template_kwargs</code> for OpenAI-compatible completions, so vLLM/Nemotron replies stay visible instead of becoming thinking-only. Fixes #71891. Thanks @jmystaki-create and @dennis-lynch.</li>
<li>Channels/replies: strip copied inbound metadata blocks from user-facing assistant replies and model replay history, so Discord/vLLM sessions do not leak <code>Conversation info</code> / <code>UNTRUSTED ... message body</code> envelopes after a model echoes them. Fixes #71847. Thanks @jmystaki-create.</li>
<li>Subagents/memory: keep inter-session completion wakes out of memory and dreaming session exports, and strip internal runtime-context blocks from realtime Control UI chat events.</li>
<li>Agents/Claude: treat zero-token empty <code>stop</code> turns as failed provider output, retry once, repair replay, and allow configured model fallback instead of preserving them as successful silent replies. Fixes #71880. Thanks @MagnaAI.</li>
<li>Tasks: normalize task lifecycle timestamps at create, update, and restore time, and report retained lost tasks as audit warnings until their cleanup window expires. (#71871) Thanks @likewen-tech.</li>
<li>Diagnostics/OTEL: treat normal early model stream cleanup as a completed model call instead of exporting a misleading <code>StreamAbandoned</code> error span. Thanks @vincentkoc.</li>
<li>Gateway/pairing: stop corrupt or unreadable device/node pairing stores from being treated as empty state, preserving <code>paired.json</code> for repair instead of overwriting approved pairings. Fixes #71873. Thanks @iret77.</li>
<li>ACP: keep <code>/acp</code> management commands, plus local <code>/status</code> and <code>/unfocus</code>, on the Gateway path inside ACP-bound threads so they are not consumed as ACP prompt text. Fixes #66298. Thanks @kindomLee.</li>
<li>ACPX: stop probing ACP agents during normal Gateway startup; the embedded backend now registers without spawning Codex/ACP child processes unless <code>OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE=1</code> is explicitly set.</li>
<li>CLI/image edit: accept <code>--size</code>, <code>--aspect-ratio</code>, and <code>--resolution</code> on <code>openclaw infer image edit</code> and report all supported edit flags from <code>capability inspect image.edit</code>. Thanks @Pinghuachiu.</li>
<li>ACP: wait for the configured runtime backend to become healthy before startup identity reconciliation, avoiding transient acpx warnings during Gateway boot. Fixes #40566.</li>
<li>Channels/ACP bindings: time out configured binding readiness checks instead of letting Discord preflight hang forever when an ACP target never settles. Fixes #68776.</li>
<li>Control UI: hide the chat loading skeleton during background history reloads when existing messages or active stream content are already visible, avoiding reload flashes on high-latency local gateways. Fixes #71844. Thanks @WolvenRA.</li>
<li>Control UI: keep locally optimistic chat messages visible when a history reload temporarily returns empty, avoiding lost first-turn messages on high-latency gateways. Fixes #71878. Thanks @WolvenRA.</li>
<li>Control UI: keep chat history limits based on visible messages after filtering heartbeat and control-only transcript rows, so recent hidden entries no longer make older visible replies disappear. Thanks @WolvenRA.</li>
<li>Agents/images: scrub old <code>[media attached: ...]</code>, <code>[Image: source: ...]</code>, and <code>media://inbound/...</code> markers from pruned model replay context so stale media refs are not rehydrated as fresh prompt images. Fixes #71868. Thanks @jmeadlock.</li>
<li>Docker/Bonjour: disable Bonjour/mDNS advertising by default for bundled Compose gateways on bridge networking, while keeping host/macvlan opt-in with <code>OPENCLAW_DISABLE_BONJOUR=0</code>. Fixes #71879. Thanks @gbballpack.</li>
<li>CLI/status: label the OpenClaw Serve/Funnel setting as <code>Tailscale exposure</code> and show daemon state separately when available, so <code>gateway.tailscale.mode: "off"</code> no longer reads like the Tailscale daemon is stopped. Fixes #71790. Thanks @pesvobodak.</li>
<li>Plugins/Bonjour: stop ciao mDNS watchdog failures from looping forever when the advertiser stays stuck in <code>probing</code> or <code>announcing</code>; Bonjour now disables itself for the current Gateway process after repeated failed restarts while the Gateway keeps running. Fixes #69011. Thanks @siddharthaagarwalofficial-ux, @FiredMosquito831, and @spikefcz.</li>
<li>Gateway/Fly.io: seed Control UI allowed origins from the actual runtime bind and port so CLI-driven non-loopback starts do not crash before config exists. Fixes #71823.</li>
<li>macOS/remote SSH: keep discovered gateway hosts in <code>gateway.remote.sshTarget</code> while pinning SSH transport URLs to the local loopback tunnel, so browser automation does not regress into blocked non-loopback <code>ws://</code> endpoints. Fixes #67336.</li>
<li>Gateway/proxy: bootstrap env proxy dispatching from direct Gateway startup so provider and plugin network requests honor <code>HTTPS_PROXY</code>/<code>HTTP_PROXY</code> before the first embedded agent attempt runs. (#71833) Thanks @mjamiv.</li>
<li>Plugins/runtime deps: verify clean npm installs actually place requested bundled runtime packages in the managed install root, reporting exact missing specs instead of a false successful repair. (#71883) Thanks @Solvely-Colin.</li>
<li>Plugins/discovery: ignore stale <code>plugins.load.paths</code> aliases that point back at packaged bundled plugin directories and have doctor remove them, keeping bundled plugins on the runtime-deps staging path.</li>
<li>Models/LM Studio: preserve <code>@iq*</code> quant suffixes in model refs and provider matching so <code>/model lmstudio/...@iq3_xxs</code> keeps the exact LM Studio variant. Fixes #71474. (#71486) Thanks @Bartok9, @XinwuC, and @Sanjays2402.</li>
<li>Matrix/cron: preserve the live Matrix delivery target when creating implicit announce reminder jobs so mixed-case room IDs are not reconstructed from lowercased session keys. Fixes #71798.</li>
<li>Feishu: accept Schema 2.0 card action callbacks that report <code>context.open_chat_id</code> instead of legacy <code>context.chat_id</code>, so button callbacks no longer drop as malformed. Fixes #71670. Thanks @eddy1068.</li>
<li>Feishu: keep synthetic card-action and bot-menu ids out of platform reply targets, using the real card callback message id when Feishu provides one and plain-sending otherwise. Fixes #71673. Thanks @eddy1068.</li>
<li>Plugins/QQ Bot: prefer an installed QQ Bot plugin that declares it replaces the bundled <code>qqbot</code> channel, preventing duplicate <code>qqbot_channel_api</code> and <code>qqbot_remind</code> tool registration noise. Fixes #63102.</li>
<li>Browser automation: keep stable tab ids and labels attached when Chromium replaces the raw target after form submissions or other action-triggered navigations, and return the replacement <code>targetId</code> from <code>/act</code> when the match is provable. Fixes #46137.</li>
<li>QQ Bot: make <code>qqbot_remind</code> schedule, list, and remove Gateway cron jobs directly for owner-authorized senders instead of returning <code>cronParams</code> and relying on a follow-up generic <code>cron</code> tool call. Fixes #70865. (#70937) Thanks @GaosCode.</li>
<li>Agents/ACP: hide <code>sessions_spawn</code> ACP runtime options unless an ACP backend is loaded, and make <code>/acp doctor</code> call out <code>plugins.allow</code> blocking bundled <code>acpx</code>. Thanks @vincentkoc.</li>
<li>Agents/Codex: keep ACP prompt/skill routing hidden unless an ACP runtime backend is available, and warn in doctor when enabled Codex plugin configs still route <code>openai-codex/*</code> models through PI. Thanks @vincentkoc.</li>
<li>Media delivery: avoid sending generated image attachments twice when the assistant reply already includes explicit <code>MEDIA:</code> lines for the same turn, and reject unsafe remote <code>MEDIA:</code> URLs before delivery. Thanks @pashpashpash.</li>
<li>Codex harness: ignore retryable app-server error notifications after Codex recovers, and preserve the real nested error message for terminal app-server failures instead of replacing it with a generic failure. Thanks @pashpashpash.</li>
<li>Agents/Codex: prepare native Codex sub-agent session metadata without a nested Gateway session patch and add a focused Docker smoke for the app-server sub-agent path. Thanks @vincentkoc.</li>
<li>Agents/subagents: keep queued subagent announces session-only when the requester has no external channel target, avoiding ambiguous multi-channel delivery failures. Fixes #59201. Thanks @larrylhollan.</li>
<li>Image understanding: preserve configured provider-prefixed vision model metadata when callers request the model without the provider prefix, so custom image models keep their <code>input: ["text", "image"]</code> capability. Fixes #33185. Thanks @Kobe9312 and @vincentkoc.</li>
<li>Plugins/install: restore the previous plugin index records if a concurrent config write conflict interrupts install, update, or uninstall metadata commits. Thanks @shakkernerd.</li>
<li>Plugins/install: reject native plugin archives that do not include a valid <code>openclaw.plugin.json</code>, preventing manifestless archives from writing install records that later show missing-manifest diagnostics. Thanks @shakkernerd.</li>
<li>Plugins/uninstall: remove tracked managed plugin install directories even when the persisted install path differs from the default id-derived target, while still refusing deletes outside the managed extensions root. Thanks @shakkernerd.</li>
<li>Plugins/update: restore previous plugin index records if core update or channel setup hits a concurrent config write conflict after plugin metadata changes. Thanks @shakkernerd.</li>
<li>Plugins/onboarding: defer channel/provider plugin install records until the owning config write commits, keeping setup failures from advancing the plugin index ahead of <code>openclaw.json</code>. Thanks @shakkernerd.</li>
<li>Plugins/config: route configure and agent setup writes with pending plugin install records through the plugin index commit helper so provider onboarding metadata is not stripped by plain config writes. Thanks @shakkernerd.</li>
<li>Plugins/channels: merge pending channel plugin install records with the existing plugin index before config writes, preserving unrelated tracked installs during channel setup, resolve, remove, and capability repair flows. Thanks @shakkernerd.</li>
<li>Plugins/config: defer shipped <code>plugins.installs</code> index migration during config writes until the guarded config commit window and roll it back if the config write fails before commit. Thanks @shakkernerd.</li>
<li>Sessions: keep embedded runtime context out of the visible user prompt by sending it as a hidden next-turn custom message, and teach doctor to repair affected 2026.4.24 transcripts with duplicated prompt-rewrite branches. Fixes #71761.</li>
<li>Gateway/subagents: keep direct-loopback backend RPCs authenticated with the shared gateway token/password off stale CLI paired-device scope baselines, so internal calls no longer hit <code>scope-upgrade</code> pairing prompts while remote, browser, node, device-token, and explicit-device paths still require normal pairing approval. Fixes #63548.</li>
<li>Providers/Azure OpenAI: give deployment-scoped image generation requests a longer 600s default timeout so slow <code>gpt-image-2</code> generations can complete without a per-call <code>timeoutMs</code>. Fixes #71705. Thanks @voytas75.</li>
<li>Gateway/plugins: link source-checkout bundled runtime dependency caches instead of recursively copying <code>node_modules</code> on the gateway main thread, preventing local status, node, and skill probes from timing out during startup cache restores.</li>
<li>Skills/remote nodes: only expose remote macOS skill bins for connected nodes, clear stale bin matches when node probes fail, and include probe command, timeout, bin count, and connection state in timeout logs.</li>
<li>Skills/remote nodes: recognize <code>system.which</code> object-map responses when probing connected macOS nodes, so Linux gateways can expose macOS-only skills such as Apple Notes when the required binaries are installed remotely. Fixes #71877. Thanks @miguelarios.</li>
<li>CLI/gateway: keep diagnostic probes from creating first-time read-only device pairings, while still reusing cached device tokens for detailed read probes. Fixes #71766. Thanks @SunboZ.</li>
<li>CLI/plugins: keep <code>message</code> startup, <code>channels logs</code>, <code>agents delete</code>, and <code>agents set-identity</code> off broad plugin preloading; message delivery still loads plugins when the action actually runs.</li>
<li>Image understanding: resolve configured image models such as local LM Studio vision entries before reporting <code>Unknown model</code> when the discovery registry has not registered that provider. Fixes #66486. Thanks @zhanggpcsu.</li>
<li>QQ Bot: ignore self-echoed bot messages using the outbound ref-index marker, preventing mirrored replies from re-entering the agent loop while still allowing users to quote bot replies. Fixes #71912. Thanks @wangyc6003.</li>
<li>Sessions: separate reset freshness from session-store <code>updatedAt</code>, so heartbeat, cron, exec, and gateway bookkeeping no longer prevent configured daily/idle resets from rolling long-running channel sessions. Fixes #68315, #63732, #63820, and #69083. Thanks @maxatv, @longhairedsi, @bradfreels, and @akessel56.</li>
<li>Sessions: clear queued system-event notices during <code>/new</code>, <code>/reset</code>, gateway <code>sessions.reset</code>, and daily/idle rollover so stale background updates cannot leak into the first prompt of the fresh session. Fixes #66864. Thanks @opeyio, @Magicray1217, and @cedillarack.</li>
<li>CLI/agents: keep <code>agents bind</code>, <code>agents unbind</code>, and <code>agents bindings</code> on setup-safe channel metadata paths so they do not preload bundled plugin runtimes or stage runtime dependencies. Fixes #71743.</li>
<li>Plugins/registry: preserve explicit disabled plugin records during registry migration without persisting every unused bundled plugin discovered on disk. Thanks @shakkernerd.</li>
<li>Windows/native: keep CLI startup and bundled provider plugin loading off Windows ESM raw-path failure paths, fixing native onboarding/install smoke on Node 24.</li>
<li>Plugins/doctor: read bundled channel doctor capabilities through the same packaged plugin directory resolver used by plugin loading, so published installs keep Matrix DM allowlist repairs on <code>channels.matrix.dm.*</code> instead of writing invalid top-level <code>dmPolicy</code> keys. Fixes #71757.</li>
<li>Plugins/Windows: keep bundled plugin Jiti loaders off the native import path on Windows so channel plugins such as Telegram no longer crash with <code>ERR_UNSUPPORTED_ESM_URL_SCHEME</code> on <code>C:\...</code> paths. Fixes #71749. Thanks @smeyer9.</li>
<li>Providers/Ollama: use Ollama's current <code>/api/web_search</code> endpoint and honor <code>https://ollama.com</code> model-provider base URLs for Ollama Web Search. Fixes #71741. Thanks @madhvidua.</li>
<li>Memory/Ollama: serialize Ollama memory embedding batches and add an inline batch timeout override, with longer defaults for local/self-hosted embedding providers.</li>
<li>Sessions/usage: exclude compaction checkpoint transcript snapshots from usage totals and session discovery, while keeping old checkpoint files removable.</li>
<li>CLI/agents: keep <code>openclaw agents list --json</code> on the config-only path by default, avoiding bundled plugin loading unless callers request <code>--bindings</code>. Fixes #71739. Thanks @kaloster.</li>
<li>Plugins/install: force plugin dependency installs to stay project-local even when inherited npm config requests global installs, so successful installs still materialize the plugin's staged <code>node_modules</code>.</li>
<li>Providers/Google: transcode Gemini TTS PCM to Opus for voice-note targets so WhatsApp and other native voice-note replies can play as voice messages.</li>
<li>TTS/WhatsApp: mark non-Opus provider output as voice-note intent so channel delivery transcodes MP3/WebM replies to Ogg/Opus PTT audio.</li>
<li>Plugins/runtime deps: reuse existing external bundled-plugin stage roots when mirrored plugin roots are inspected again, avoiding second-generation <code>openclaw-unknown-*</code> stages and repeated first-turn restaging. Fixes #71599.</li>
<li>iOS/macOS Talk Mode: allow <code>talk.speechLocale</code> to set the speech recognition locale for non-English voice conversations. Fixes #44688.</li>
<li>Plugins/providers: honor explicit plugin candidate lists instead of reading a persisted registry snapshot from local state, keeping candidate-scoped provider discovery hermetic.</li>
<li>Plugins/doctor: keep bundled plugin runtime-dependency repairs inside the managed OpenClaw stage even when user npm prefix/global config points npm at <code>$HOME/node_modules</code>. Fixes #71730.</li>
<li>ACP/sessions_spawn: reject normal OpenClaw config agent ids when callers explicitly request <code>runtime="acp"</code>, while allowing agents configured with <code>runtime.type="acp"</code> to resolve to their ACP harness id. Fixes #63914.</li>
<li>ACP/sessions_spawn: apply <code>runTimeoutSeconds</code> to ACP child turns and dispatch those turns on the background subagent lane, so quota-stalled ACP harnesses do not occupy the main agent lane indefinitely. Fixes #68823.</li>
<li>ACP/oneshot: reconcile runtime session identity before closing completed oneshot ACP runs, so finished <code>sessions.json</code> entries do not stay stuck with <code>acp.identity.state="pending"</code>.</li>
<li>ACPX: bundle <code>acpx@0.6.1</code> so unsupported generic model overrides fail clearly instead of silently falling back to the target adapter default.</li>
<li>ACP/models: document that non-Codex ACP model overrides require adapter support for ACP <code>models</code> plus <code>session/set_model</code>, so unsupported harnesses fail clearly instead of silently falling back to their defaults.</li>
<li>Plugins/Voice Call: treat missing provider credentials as setup-incomplete during Gateway startup and log the missing keys as a warning instead of a runtime startup error, while keeping explicit command/tool errors when used.</li>
<li>Android/Talk Mode: prevent duplicate TTS playback when fast or repeated final chat events arrive while Talk Mode is waiting for its own response. Fixes #46546.</li>
<li>Tooling/check:changed: pass parent heavy-check lock markers to lint lanes so <code>pnpm check:changed</code> no longer waits on its own <code>lint:extensions</code> child.</li>
<li>CLI/completion: dedupe provider auth flags before registering <code>openclaw onboard</code> options, so completion-cache refresh during update no longer fails when stale core fallback flags overlap plugin manifest flags. Fixes #71667.</li>
<li>Diagnostics/trace: report live context usage from the current prompt snapshot instead of provider turn totals, avoiding false near-full context spikes on cached or tool-heavy runs.</li>
<li>Providers/Google: honor <code>models.providers.google.request.allowPrivateNetwork</code> for Gemini TTS and telephony TTS, matching Google image generation and media understanding. (#71723) Thanks @ro-hansolo.</li>
<li>Providers/MiniMax: register <code>minimax-portal</code> for music and video generation, preserving OAuth auth and regional MiniMax base URLs across the shared <code>music_generate</code> and <code>video_generate</code> tools. (#63241) Thanks @tars90percent.</li>
<li>Providers/onboarding: keep Runway and Alibaba Model Studio out of the text-inference setup picker by scoping their video-generation auth choices to the media setup flow. (#65856) Thanks @Jah-yee.</li>
<li>Plugins/Bonjour: stop the gateway from crash-looping on <code>CIAO PROBING CANCELLED</code> when the mDNS watchdog cancels a stuck probe. Restores the rejection-handler wiring dropped during the bonjour plugin migration and shares unhandled-rejection state across module instances so plugin-staged copies of <code>openclaw/plugin-sdk/runtime</code> register into the same handler set the host consults. Especially affects Docker on macOS, where mDNS probing reliably hits the watchdog. Thanks @troyhitch.</li>
<li>Google Meet: report pinned Chrome nodes as offline or missing capabilities in setup/join diagnostics, keep inaccessible nodes out of auto-selection, and preflight local BlackHole/SoX requirements before agents try local Chrome.</li>
<li>Providers/MiniMax: route <code>image-01</code> requests to the dedicated image generation endpoint while preserving CN endpoint selection. Fixes #61149. Thanks @mushuiyu886.</li>
<li>Plugins/startup: remove ownerless bundled runtime-dependency install locks after a short grace window and include lock owner details when startup times out waiting for a plugin runtime-deps lock.</li>
<li>Plugins/install: anchor bundled runtime-dependency npm installs with an OpenClaw-owned package manifest so Linux updates cannot accidentally write to a parent <code>$HOME/node_modules</code> tree. Fixes #71730.</li>
<li>Plugins/install: pass onboarding plugin config into plugin index writes so local plugin installs outside default discovery roots keep their install records. Thanks @shakkernerd.</li>
<li>Plugins/install: migrate shipped <code>plugins.installs</code> config records into the plugin index while stripping them from runtime config and future writes. Thanks @shakkernerd.</li>
<li>Plugins/install: durably remove shipped <code>plugins.installs</code> from <code>openclaw.json</code> after its records are copied into the plugin index, while rolling back the index write if config cleanup fails. Thanks @shakkernerd.</li>
<li>Plugins/install: keep migrated plugin install records in the plugin index even when the plugin manifest is missing or invalid, so update, uninstall, inspect, and audit can still recover broken installs. Thanks @shakkernerd.</li>
<li>Plugins/security: keep plugin audit JSON check ids stable while reporting plugin index install-record findings with updated wording. Thanks @shakkernerd.</li>
<li>CLI/config: reject direct <code>plugins.installs</code> edits with guidance to use <code>openclaw plugins install</code>, <code>openclaw plugins update</code>, or <code>openclaw plugins uninstall</code> instead. Thanks @shakkernerd.</li>
<li>Live tests/voice: accept common STT variants for OpenClaw and ElevenLabs brand names so provider smoke tests fail on real regressions rather than equivalent transcripts.</li>
<li>Agents/replies: forward sanitized underlying agent failure details on external channels instead of replacing unknown failures with a generic retry message.</li>
<li>CLI/MCP: translate OpenClaw <code>mcp.servers.*.transport</code> entries into Claude/Gemini CLI <code>type</code> fields so streamable HTTP MCP servers load in CLI backend sessions. (#71724) Thanks @Blockchain-Oracle.</li>
<li>Browser/CDP: honor configured remote and <code>attachOnly</code> CDP HTTP/WebSocket timeouts when opening tabs through raw CDP or <code>/json/new</code> fallback. (#54238) Thanks @FuncWei.</li>
<li>WhatsApp/TTS: send visible text separately from PTT voice-note audio instead of relying on hidden voice-note captions. Fixes #51081.</li>
<li>Browser/client: avoid telling agents to restart OpenClaw for dispatcher timeouts on external browser profiles such as <code>attachOnly</code>, remote CDP, and existing-session. (#40815) Thanks @0xsline.</li>
<li>Agents/TTS: preserve <code>[[audio_as_voice]]</code> directives on trusted text tool-result <code>MEDIA:</code> payloads so generated audio still delivers as a voice note. (#46535) Thanks @azade-c.</li>
<li>Agents/TTS: keep queued tool media when an assistant ends with <code>NO_REPLY</code> on non-block delivery paths, so media-only generated audio replies still send. (#60025) Thanks @bradlind1.</li>
<li>Telegram/STT: frame inbound voice-note transcripts as machine-generated, untrusted text in agent context while preserving raw transcript mention detection. Closes #33360. Thanks @smartchainark.</li>
<li>Subagents/browser: show an actionable <code>/tools</code> notice when browser automation is configured but filtered out by the active tool profile, and document that coding-profile agents should use <code>tools.alsoAllow: ["browser"]</code> rather than subagent allowlists alone.</li>
<li>Control UI/Quick Settings: persist the assistant avatar override to browser local storage (mirroring the user avatar) so uploaded image data URLs no longer fail config validation with "Too big: expected string to have <=200 characters". Also lift the gateway-side <code>ui.assistant.avatar</code> length cap to match the user avatar size budget for non-UI clients writing the field directly. Thanks @BunsDev.</li>
<li>Plugin SDK: share diagnostic event subscriptions across duplicate source/dist module graphs so legacy root SDK imports still receive runtime diagnostic events.</li>
<li>Agents/Bedrock: prevent empty assistant stream-error turns from poisoning Converse replay by persisting, repairing, and replaying a non-empty fallback block. Fixes #71572. (#71627) Thanks @openperf.</li>
<li>Agents/Anthropic/Bedrock: strip thinking blocks with missing, empty, or blank replay signatures before provider conversion, falling back to non-empty omitted-reasoning text when needed so corrupted signed-thinking history no longer poisons subsequent turns. Fixes #45010. (#70054) Thanks @castaples.</li>
<li>Agents/Anthropic/Bedrock: preserve stripped thinking-only assistant replay turns with non-empty omitted-reasoning text so provider adapters keep strict user/assistant turn shape. Thanks @wujiaming88.</li>
<li>ACP/Codex: pass <code>sessions_spawn(runtime="acp")</code> model and thinking overrides into Codex ACP startup, normalize <code>openai-codex/*</code> refs and slash reasoning suffixes, and recognize managed Codex ACP wrapper commands without blocking current <code>gpt-5.5</code> sessions. Fixes #40393. (#71643) Thanks @91wan.</li>
<li>Browser/CDP: make readiness diagnostics use the same discovery-first fallback as reachability for bare <code>ws://</code> Browserless and Browserbase CDP URLs. Fixes #69532.</li>
<li>Browser/CDP: explain that loopback Browserless or other externally managed CDP services need <code>attachOnly: true</code> and matching Browserless <code>EXTERNAL</code> endpoint when reporting local port ownership conflicts, and fall back to the configured bare WebSocket root when a discovered Browserless endpoint rejects CDP. Fixes #49815.</li>
<li>Gateway/reload: preserve indefinite <code>gateway.reload.deferralTimeoutMs: 0</code> semantics for channel hot reload deferrals so active agent runs are not interrupted by a forced channel restart. (#71637) Thanks @Poo-Squirry.</li>
<li>Agents/tool results: cap persisted Pi tool-result details and strip hidden diagnostics before provider conversion, preventing large debug payloads from bloating session transcripts. (#71637) Thanks @Poo-Squirry.</li>
<li>ACP/OpenCode: update the bundled acpx runtime to 0.6.0 and cover the OpenCode ACP bind path in Docker live tests.</li>
<li>Providers/OpenCode Go: add DeepSeek V4 Pro and DeepSeek V4 Flash to the Go catalog while the bundled Pi registry catches up. Fixes #71587.</li>
<li>Providers/OpenCode Go: route DeepSeek V4 Pro/Flash through the OpenAI-compatible Go endpoint and suppress invalid <code>reasoning_effort: "off"</code> payloads, fixing tool-enabled requests for <code>opencode-go/deepseek-v4-flash</code>. Fixes #71683.</li>
<li>Plugins/model defaults: run Skill Workshop review, Active Memory recall, and session-memory slug generation on the configured agent default model instead of the hardcoded OpenAI SDK fallback when hook context lacks model metadata. Fixes #71659.</li>
<li>Providers/Venice: fill the required DeepSeek V4 <code>reasoning_content</code> placeholder for <code>venice/deepseek-v4-pro</code> and <code>venice/deepseek-v4-flash</code> replay turns without sending native DeepSeek <code>thinking</code> controls that Venice rejects. Fixes #71628.</li>
<li>Browser/existing-session: support per-profile Chrome MCP command/args, map <code>cdpUrl</code> to <code>--browserUrl</code> or <code>--wsEndpoint</code>, and avoid combining endpoint flags with <code>--userDataDir</code>. Fixes #47879, #48037, and #62706. Thanks @puneet1409, @zhehao, and @madkow1001.</li>
<li>Media/plugins: bound MIME sniffing and ZIP archive preflight before handing untrusted files to <code>file-type</code> or <code>jszip</code>, reducing parser CPU and memory exposure for attachments and ClawHub plugin archives. Thanks @vincentkoc.</li>
<li>Memory-host SDK: use trusted env-proxy mode for remote embedding and batch HTTP calls only when Undici will proxy that target, preserving SSRF DNS pinning for <code>ALL_PROXY</code>-only and <code>NO_PROXY</code> bypass cases. Fixes #52162. (#71506) Thanks @DhtIsCoding.</li>
<li>Gateway/dashboard: render Control UI and WebSocket links with <code>https://</code>/<code>wss://</code> when <code>gateway.tls.enabled=true</code>, including <code>openclaw gateway status</code>. Fixes #71494. (#71499) Thanks @deepkilo.</li>
<li>Agents/OpenAI-compatible: default proxy/local completions tool requests to <code>tool_choice: "auto"</code> when tools are present, so providers enter native tool-calling mode instead of replying with plain-text tool directives. (#71472) Thanks @Speed-maker.</li>
<li>OpenAI image generation: use <code>gpt-5.5</code> for the Codex OAuth responses transport instead of the retired <code>gpt-5.4</code> model, fixing 500s from ChatGPT Codex image generation. Fixes #71513. Thanks @baolongl.</li>
<li>OpenAI image generation: route transparent-background default-model requests to <code>gpt-image-1.5</code>, document the expected <code>image_generate</code> call shape, and keep Azure/custom OpenAI-compatible deployment names untouched.</li>
<li>Google video generation: download direct MLDev Veo <code>video.uri</code> results instead of passing them through the Files API path, fixing 404s after successful generation/polling. Fixes #71200. Thanks @panhaishan.</li>
<li>Google video generation: fall back to the REST <code>predictLongRunning</code> Veo endpoint for text-only SDK 404s while keeping reference image/video generation on the SDK path. Fixes #62309 and #63008. (#62343) Thanks @leoleedev.</li>
<li>MiniMax music generation: switch the bundled default model from the unsupported <code>music-2.5+</code> id to the current <code>music-2.6</code> API model. Fixes #64870 and addresses the music default from #62315. Thanks @noahclanman and @edwardzheng1.</li>
<li>Cron: record jobs interrupted by a gateway restart as failed at their original <code>runningAtMs</code>, skip unsafe startup replay, and disable interrupted one-shot jobs so they show a visible failure instead of silently disappearing or duplicating work. Fixes #59056, #61343, #63657, and #59301. Thanks @ponchoooPenguin, @daemic24, @myradon, and @hikiwibot.</li>
<li>Cron tool: recover flat top-level schedule shorthand such as <code>cron</code>, <code>tz</code>, and <code>staggerMs</code> before gateway validation, so model-generated cron add/update calls preserve cron jitter settings. Thanks @tyxben.</li>
<li>Cron: hydrate flat legacy job rows with top-level <code>cron</code>, <code>tz</code>, <code>session</code>, and <code>message</code> fields into canonical schedule, target, and payload objects before startup recomputes run times. Fixes #43351.</li>
<li>Agents/replies: let pending group chat history trigger bare mentioned turns without treating metadata-only inbound context as user input. Fixes #71489. (#71520) Thanks @SymbolStar.</li>
<li>Google media generation: strip a configured trailing <code>/v1beta</code> from Google music/video provider base URLs before calling the Google GenAI SDK, preventing doubled <code>/v1beta/v1beta</code> paths. Fixes #63240. (#63258) Thanks @Hybirdss.</li>
<li>Discord: restore direct-message voice-note preflight transcription and classify URL-only Ogg/Opus voice attachments as audio while skipping partial attachments without usable URLs. Fixes #61314 and #64803.</li>
<li>Plugins/build: copy bundled plugin skill trees into <code>dist-runtime</code>, broaden Windows symlink-copy fallbacks, and fingerprint runtime dependencies from <code>lstat</code> so symlink-like directory entries cannot crash staging.</li>
<li>Google Chat: preserve reply text when a typing indicator message is deleted or can no longer be updated, so media captions and first text chunks are resent instead of silently disappearing. (#71498) Thanks @colin-lgtm.</li>
<li>Cron: tolerate malformed legacy job rows in startup, main-session system-event payloads, and human-readable <code>cron list</code> output so missing <code>state</code>, <code>payload.text</code>, or display fields no longer crash the scheduler or CLI. Fixes #66016, #65916, #64137, #57872, #59968, #63813, #52804, and #43163. (#71509) Thanks @vincentkoc.</li>
<li>CLI/models: make <code>openclaw models scan</code> fall back to public OpenRouter free-model metadata when no <code>OPENROUTER_API_KEY</code> is configured, avoid config secret resolution for explicit <code>--no-probe</code> scans, and apply the scan timeout to the OpenRouter catalog request.</li>
<li>Feishu: keep streaming cards to one live card per turn, flush throttled card edits after meaningful text boundaries, and skip exact block/partial repeats so tool-heavy replies do not duplicate card output. Thanks @allan0509.</li>
<li>Feishu: finish the streaming-card duplicate closeout by stripping leaked reasoning tags, preserving cross-block partial snapshots, enabling topic-thread streaming cards, omitting the generic <code>main</code> card header, surfacing transient tool/compaction status, and cleaning streaming state after close failures. Thanks @sesame437, @Vicky-v7, @maoku-family, @Pengxiao-Wang, and @Maple778.</li>
<li>Telegram: recover incomplete partial-stream previews by falling back to a final send when an ambiguous final edit failure would otherwise retain a strict prefix of the answer. Fixes #71525. (#71554) Thanks @sahilsatralkar.</li>
<li>Control UI/chat: collapse assistant token/model context details behind an explicit Context disclosure and show full dates in message footers, making historical transcript timing clear without noisy default metadata. (#71337) Thanks @BunsDev.</li>
<li>OpenAI/Codex OAuth: explain <code>unsupported_country_region_territory</code> token-exchange failures with a proxy/region hint instead of surfacing a generic OAuth error. Fixes #51175. (#71501) Thanks @vincentkoc and @wulala-xjj.</li>
<li>Browser/Linux: fall back to headless mode for local managed profiles on hosts without a display server, while preserving explicit per-profile headed overrides and reporting the headless source. (#60953) Thanks @rrpsantos.</li>
<li>Telegram: remove the startup persisted-offset <code>getUpdates</code> preflight so polling restarts do not self-conflict before the runner starts. Fixes #69304. (#69779) Thanks @chinar-amrutkar.</li>
<li>Telegram: keep the polling stall watchdog active even when grammY reports the runner as not running while its task is still pending, so a rebuilt transport cannot leave <code>getUpdates</code> silent until a manual gateway restart. Fixes #69064. Thanks @LDLoeb.</li>
<li>Subagents: fall back to direct completion delivery when the parent announce turn finishes without a visible payload, so child results still reach channel-backed requester sessions.</li>
<li>Subagents: tell parent agents to use <code>sessions_yield</code> while waiting for child completion events, preventing GPT-5 fast runs from ending silently after spawning workers.</li>
<li>Browser/Playwright: ignore benign already-handled route races during guarded navigation so browser-page tasks no longer fail when Playwright tears down a route mid-flight. (#68708) Thanks @Steady-ai.</li>
<li>Browser/CLI: lazy-load browser command groups and plugin runtime services so <code>openclaw browser --help</code> can render without loading the full browser automation stack. Fixes #65400. (#65460, #66640) Thanks @pandego and @Tianworld.</li>
<li>Browser/CLI: serve precomputed <code>openclaw browser --help</code> text from CLI startup metadata, avoiding the full plugin/config startup path for the common help invocation.</li>
<li>Browser/downloads: seed managed Chrome profiles with OpenClaw download prefs and capture unmanaged click-triggered downloads under the guarded downloads directory, while explicit download waiters still own their target file. (#64558) Thanks @Pearcekieser.</li>
<li>Browser/Chrome: stop passing redundant <code>--disable-setuid-sandbox</code> when <code>browser.noSandbox</code> is enabled; <code>--no-sandbox</code> remains the effective sandbox opt-out. (#67939) Thanks @sebykrueger.</li>
<li>Browser/client: stop telling agents to permanently avoid the browser after transient timeout or cancellation failures; keep the no-retry hint for persistent unavailable/rate-limit cases. (#46505) Thanks @jriff.</li>
<li>Browser/aria snapshots: bind <code>format=aria</code> <code>axN</code> refs to live DOM nodes through backend DOM ids when Playwright is available, so follow-up browser actions can use those refs without timing out. (#62434) Thanks @MrKipler.</li>
<li>Telegram: prevent duplicate in-process long pollers for the same bot token and add clearer <code>getUpdates</code> conflict diagnostics for external duplicate pollers. Fixes #56230. Thanks @Co-Messi.</li>
<li>Browser/Linux: detect Chromium-based installs under <code>/opt/google</code>, <code>/opt/brave.com</code>, <code>/usr/lib/chromium</code>, and <code>/usr/lib/chromium-browser</code> before asking users to set <code>browser.executablePath</code>. (#48563) Thanks @lupuletic.</li>
<li>Sessions/browser: close tracked browser tabs when idle, daily, <code>/new</code>, or <code>/reset</code> session rollover archives the previous transcript, preventing tabs from leaking past the old session. Thanks @jakozloski.</li>
<li>Sessions/forking: fall back to transcript-estimated parent token counts when cached totals are stale or missing, so oversized thread forks start fresh instead of cloning the full parent transcript. Thanks @jalehman.</li>
<li>OpenAI/Codex: send Codex Responses system prompts through top-level <code>instructions</code> while preserving the existing native Codex payload controls.</li>
<li>MCP/CLI: retire bundled MCP runtimes at the end of one-shot <code>openclaw agent</code> and <code>openclaw infer model run</code> gateway/local executions, so repeated scripted runs do not accumulate stdio MCP child processes. Fixes #71457. Thanks @spartoviMD.</li>
<li>OpenAI/Codex image generation: canonicalize legacy <code>openai-codex.baseUrl</code> values such as <code>https://chatgpt.com/backend-api</code> to the Codex Responses backend before calling <code>gpt-image-2</code>, matching the chat transport. Fixes #71460. Thanks @GodsBoy.</li>
<li>Control UI: make <code>/usage</code> use the fresh context snapshot for context percentage, and include cache-write tokens in the Usage overview cache-hit denominator. Fixes #47885. Thanks @imwyvern and @Ante042.</li>
<li>GitHub Copilot: preserve encrypted Responses reasoning item IDs during replay so Copilot can validate encrypted reasoning payloads across requests. (#71448) Thanks @a410979729-sys.</li>
<li>GitHub Copilot: never rewrite connection-bound reasoning item IDs regardless of whether <code>encrypted_content</code> is present, fixing a 400 "Encrypted content item_id did not match" error with <code>gpt-5.3-codex</code> and future Codex models that fall through to the forward-compat catch-all with <code>reasoning: false</code>. Also recognize Codex-named models as reasoning-capable so they inherit the correct capability flags. Refs #68735. Thanks @InvalidPandaa.</li>
<li>Agents/replies: recover final-answer text when streamed assistant chunks contain only whitespace, preventing completed turns from surfacing as empty-payload errors. Fixes #71454. (#71467) Thanks @Sanjays2402.</li>
<li>Feishu/TTS: transcode voice-intent MP3 and other audio replies to Ogg/Opus before sending native Feishu audio bubbles, while keeping ordinary MP3 attachments as files. Fixes #61249 and #37868. Thanks @sg1416-zg and @ycjlb2023-peteryi.</li>
<li>WhatsApp/TTS: transcode MP3/WebM audio, including Microsoft Edge TTS output, to Ogg/Opus before sending PTT voice notes.</li>
<li>QQBot/TTS: honor plain <code>audioAsVoice</code> replies by synthesizing TTS to native QQ voice messages, and mark inbound voice-only messages as audio media without exposing raw voice paths to generic media context.</li>
<li>Providers/SenseAudio: add bundled SenseAudio batch audio transcription through <code>tools.media.audio</code> with <code>SENSEAUDIO_API_KEY</code> auth. (#66943) Thanks @Fl0rencess720.</li>
<li>Providers/MiniMax: let TTS use MiniMax portal OAuth and Token Plan credentials before falling back to <code>MINIMAX_API_KEY</code>, and include current TTS HD model ids. Fixes #55017. Thanks @zx15210404690-hash.</li>
<li>Telegram/webhook: acknowledge validated webhook updates before running bot middleware, keeping slow agent turns from tripping Telegram delivery retries while preserving per-chat processing lanes. Fixes #71392. Thanks @joelforsberg46-source.</li>
<li>MCP/config reload: hot-apply <code>mcp.*</code> changes by disposing cached session MCP runtimes, and dispose bundled MCP runtimes during gateway shutdown so removed <code>mcp.servers</code> entries reap child processes promptly. Fixes #60656. Thanks @xieyuanqing.</li>
<li>Active Memory: keep silent recall sub-agent billing/auth failures out of shared auth-profile cooldown state, so a Claude CLI extra-usage rejection cannot disable normal Claude-backed turns. Fixes #71284. (#71539) Thanks @vishutdhar and @obviyus.</li>
<li>Auth/Claude CLI: sync refreshed Claude CLI OAuth credentials into the managed auth profile so long-running Claude CLI runs stop falling back to stale OpenClaw snapshots. (#70902) Thanks @starvex.</li>
<li>Sessions: make <code>sessions_spawn(mode="session")</code> errors name usable alternatives when the current channel cannot bind subagent threads. Fixes #67400. (#67790) Thanks @stainlu.</li>
<li>Agents/Claude CLI: pass the OpenClaw system prompt through Claude's prompt-file flag so Windows runs avoid argv length failures without changing system prompt semantics. Fixes #69158. (#69211) Thanks @skylee-01, @cassioanorte, @Syu0, and @Stache73.</li>
<li>Agents/CLI sessions: bind <code>google-gemini-cli</code> session auth-epoch to the Google account identity in <code>~/.gemini/oauth_creds.json</code>, so Gemini-backed agents resume their conversation after gateway restart instead of minting a fresh session, and stale bindings are invalidated when the authenticated Google account changes. Fixes #70973. (#71076) Thanks @openperf.</li>
<li>Slack: stop treating user mentions in assistant-authored message edit blocks as sender attribution, preventing edited bot messages from spoofing a mentioned DM user. (#71700) Thanks @vincentkoc.</li>
<li>Codex: consume unauthorized bound conversation inbound claims before they can fall through to other claim handlers or enqueue Codex turns. (#71702) Thanks @vincentkoc.</li>
<li>Codex media understanding: require approval-checked app-server image turns while explicitly declining tool, file, permission, and elicitation approval requests for the bounded image worker. (#71703) Thanks @vincentkoc.</li>
<li>Agents/Claude CLI: allow large live <code>stream-json</code> JSONL lines up to the existing per-turn raw limit, preventing large Telegram, WebChat, MCP, and image turns from aborting on the old stdout buffer cap. Fixes #71793, #71080, and #70766. (#71897) Thanks @chacher86, @shivamgrover21, and @tpjordan.</li>
<li>Agents/Claude CLI: unwrap nested Claude result envelopes in CLI JSON output so delegated agent responses surface as final text instead of raw result JSON. (#66819) Thanks @mraleko.</li>
<li>Agents/Claude CLI: apply the configured 1M context window override to eligible Claude CLI Opus and Sonnet models when <code>context1m</code> is enabled. (#70863) Thanks @bidadh.</li>
<li>Models/status: report fresh Claude CLI native auth instead of stale stored <code>anthropic:claude-cli</code> profile expiry when local credentials are current. Fixes #71256. (#71332) Thanks @matthiasjanke and @neeravmakwana.</li>
<li>CLI backends: compact OpenClaw transcripts after over-budget CLI turns and reseed fresh CLI sessions from the compacted transcript instead of stale external resume state. Fixes #68329. (#71916) Thanks @obviyus.</li>
<li>Telegram: keep default tool progress messages visible when answer preview streaming is disabled. (#71825) Thanks @VACInc.</li>
<li>Configure/models: clear deselected model fallbacks when updating the model picker allowlist, including provider-scoped setup flows. (#71596) Thanks @rubencu.</li>
<li>Agents/streaming: strip namespaced <code><antml:thinking></code> reasoning tags from streamed assistant replies before user-visible text is emitted. (#69288) Thanks @xialonglee.</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.4.25/OpenClaw-2026.4.25.zip" length="48125363" type="application/octet-stream" sparkle:edSignature="RnQ01wCFgupauUdwOFan+XPGZhBJi/w3sgJYA5EaasbeGrduDHBGw1e9Zj2Lqb4ud8e6Q+tRcJVfxh5KKSEIDg=="/>
</item>
<item>
<title>2026.4.24</title>
<pubDate>Sat, 25 Apr 2026 19:34:45 +0000</pubDate>
@@ -251,117 +655,5 @@
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.4.22/OpenClaw-2026.4.22.zip" length="47883836" type="application/octet-stream" sparkle:edSignature="kzJ2j2sWX4H+ZIc4dXEFORYr9tk3w1txpjCJ38cdSFz6yWHU0M6Sx9zN0DB7JGIpv1QC+D+jFbWBkl4SJqW2AA=="/>
</item>
<item>
<title>2026.4.20</title>
<pubDate>Tue, 21 Apr 2026 19:53:52 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026042090</sparkle:version>
<sparkle:shortVersionString>2026.4.20</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.4.20</h2>
<h3>Changes</h3>
<ul>
<li>Onboard/wizard: restyle the setup security disclaimer with a single yellow warning banner, section headings and bulleted checklists, and un-dim the note body so key guidance is easy to scan; add a loading spinner during the initial model catalog load so the wizard no longer goes blank while it runs; add an "API key" placeholder to provider API key prompts. (#69553) Thanks @Patrick-Erichsen.</li>
<li>Agents/prompts: strengthen the default system prompt and OpenAI GPT-5 overlay with clearer completion bias, live-state checks, weak-result recovery, and verification-before-final guidance.</li>
<li>Models/costs: support tiered model pricing from cached catalogs and configured models, and include bundled Moonshot Kimi K2.6/K2.5 cost estimates for token-usage reports. (#67605) Thanks @sliverp.</li>
<li>Sessions/Maintenance: enforce the built-in entry cap and age prune by default, and prune oversized stores at load time so accumulated cron/executor session backlogs cannot OOM the gateway before the write path runs. (#69404) Thanks @bobrenze-bot.</li>
<li>Plugins/tests: reuse plugin loader alias and Jiti config resolution across repeated same-context loads, reducing import-heavy test overhead. (#69316) Thanks @amknight.</li>
<li>Cron: split runtime execution state into <code>jobs-state.json</code> so <code>jobs.json</code> stays stable for git-tracked job definitions. (#63105) Thanks @Feelw00.</li>
<li>Agents/compaction: send opt-in start and completion notices during context compaction. (#67830) Thanks @feniix.</li>
<li>Moonshot/Kimi: default bundled Moonshot setup, web search, and media-understanding surfaces to <code>kimi-k2.6</code> while keeping <code>kimi-k2.5</code> available for compatibility. (#69477) Thanks @scoootscooob.</li>
<li>Moonshot/Kimi: allow <code>thinking.keep = "all"</code> on <code>moonshot/kimi-k2.6</code>, and strip it for other Moonshot models or requests where pinned <code>tool_choice</code> disables thinking. (#68816) Thanks @aniaan.</li>
<li>BlueBubbles/groups: forward per-group <code>systemPrompt</code> config into inbound context <code>GroupSystemPrompt</code> so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports <code>"*"</code> wildcard fallback matching the existing <code>requireMention</code> pattern. Closes #60665. (#69198) Thanks @omarshahine.</li>
<li>Plugins/tasks: add a detached runtime registration contract so plugin executors can own detached task lifecycle and cancellation without reaching into core task internals. (#68915) Thanks @mbelinky.</li>
<li>Terminal/logging: optimize <code>sanitizeForLog()</code> by replacing the iterative control-character stripping loop with a single regex pass while preserving the existing ANSI-first sanitization behavior. (#67205) Thanks @bulutmuf.</li>
<li>QA/CI: make <code>openclaw qa suite</code> and <code>openclaw qa telegram</code> fail by default when scenarios fail, add <code>--allow-failures</code> for artifact-only runs, and tighten live-lane defaults for CI automation. (#69122) Thanks @joshavant.</li>
<li>Mattermost: stream thinking, tool activity, and partial reply text into a single draft preview post that finalizes in place when safe. (#47838) thanks @ninjaa.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Exec/YOLO: stop rejecting gateway-host exec in <code>security=full</code> plus <code>ask=off</code> mode via the Python/Node script preflight hardening path, so promptless YOLO exec once again runs direct interpreter stdin and heredoc forms such as <code>node <<'NODE' ... NODE</code>.</li>
<li>OpenAI Codex: normalize legacy <code>openai-completions</code> transport overrides on default OpenAI/Codex and GitHub Copilot-compatible hosts back to the native Codex Responses transport while leaving custom proxies untouched. (#45304, #42194) Thanks @dyss1992 and @DeadlySilent.</li>
<li>Anthropic/plugins: scope Anthropic <code>api: "anthropic-messages"</code> defaulting to Anthropic-owned providers, so <code>openai-codex</code> and other providers without an explicit <code>api</code> no longer get rewritten to the wrong transport. Fixes #64534.</li>
<li>fix(qqbot): add SSRF guard to direct-upload URL paths in uploadC2CMedia and uploadGroupMedia [AI-assisted]. (#69595) Thanks @pgondhi987.</li>
<li>fix(gateway): enforce allowRequestSessionKey gate on template-rendered mapping sessionKeys. (#69381) Thanks @pgondhi987.</li>
<li>Browser/Chrome MCP: surface <code>DevToolsActivePort</code> attach failures as browser-connectivity errors instead of a generic "waiting for tabs" timeout, and point signed-out fallbacks toward the managed <code>openclaw</code> profile.</li>
<li>Webchat/images: treat inline image attachments as media for empty-turn gating while still ignoring metadata-only blank turns. (#69474) Thanks @Jaswir.</li>
<li>Discord/think: only show <code>adaptive</code> in <code>/think</code> autocomplete for provider/model pairs that actually support provider-managed adaptive thinking, so GPT/OpenAI models no longer advertise an Anthropic-only option.</li>
<li>Thinking: only expose <code>max</code> for models that explicitly support provider max reasoning, and remap stored <code>max</code> settings to the largest supported thinking mode when users switch to another model.</li>
<li>Gateway/usage: bound the cost usage cache with FIFO eviction so date/range lookups cannot grow unbounded. (#68842) Thanks @Feelw00.</li>
<li>OpenAI/Responses: resolve <code>/think</code> levels against each GPT model's supported reasoning efforts so <code>/think off</code> no longer becomes high reasoning or sends unsupported <code>reasoning.effort: "none"</code> payloads.</li>
<li>Lobster/TaskFlow: allow managed approval resumes to use <code>approvalId</code> without a resume token, and persist that id in approval wait state. (#69559) Thanks @kirkluokun.</li>
<li>Plugins/startup: install bundled runtime dependencies into each plugin's own runtime directory, reuse source-checkout repair caches after rebuilds, and log only packages that were actually installed so repeated Gateway starts stay quiet once deps are present.</li>
<li>Plugins/startup: ignore pnpm's <code>npm_execpath</code> when repairing bundled plugin runtime dependencies and skip workspace-only package specs so npm-only install flags or local workspace links do not break packaged plugin startup.</li>
<li>MCP: block interpreter-startup env keys such as <code>NODE_OPTIONS</code> for stdio servers while preserving ordinary credential and proxy env vars. (#69540) Thanks @drobison00.</li>
<li>Agents/shell: ignore non-interactive placeholder shells like <code>/usr/bin/false</code> and <code>/sbin/nologin</code>, falling back to <code>sh</code> so service-user exec runs no longer exit immediately. (#69308) Thanks @sk7n4k3d.</li>
<li>Setup/TUI: relaunch the setup hatch TUI in a fresh process while preserving the configured gateway target and auth source, so onboarding recovers terminal state cleanly without exposing gateway secrets on command-line args. (#69524) Thanks @shakkernerd.</li>
<li>Codex: avoid re-exposing the image-generation tool on native vision turns with inbound images, and keep bare image-model overrides on the configured image provider. (#65061) Thanks @zhulijin1991.</li>
<li>Sessions/reset: clear auto-sourced model, provider, and auth-profile overrides on <code>/new</code> and <code>/reset</code> while preserving explicit user selections, so channel sessions stop staying pinned to runtime fallback choices. (#69419) Thanks @sk7n4k3d.</li>
<li>Sessions/costs: snapshot <code>estimatedCostUsd</code> like token counters so repeated persist paths no longer compound the same run cost by up to dozens of times. (#69403) Thanks @MrMiaigi.</li>
<li>OpenAI Codex: route ChatGPT/Codex OAuth Responses requests through the <code>/backend-api/codex</code> endpoint so <code>openai-codex/gpt-5.4</code> no longer hits the removed <code>/backend-api/responses</code> alias. (#69336) Thanks @mzogithub.</li>
<li>OpenAI/Responses: omit disabled reasoning payloads when <code>/think off</code> is active, so GPT reasoning models no longer receive unsupported <code>reasoning.effort: "none"</code> requests. (#61982) Thanks @a-tokyo.</li>
<li>Gateway/pairing: treat loopback shared-secret node-host, TUI, and gateway clients as local for pairing decisions, so trusted local tools no longer reconnect as remote clients and fail with <code>pairing required</code>. (#69431) Thanks @SARAMALI15792.</li>
<li>Active Memory: degrade gracefully when memory recall fails during prompt building, logging a warning and letting the reply continue without memory context instead of failing the whole turn. (#69485) Thanks @Magicray1217.</li>
<li>Ollama: add provider-policy defaults for <code>baseUrl</code> and <code>models</code> so implicit local discovery can run before config validation rejects a minimal Ollama provider config. (#69370) Thanks @PratikRai0101.</li>
<li>Agents/model selection: clear transient auto-failover session overrides before each turn so recovered primary models are retried immediately without emitting user-override reset warnings. (#69365) Thanks @hitesh-github99.</li>
<li>Auto-reply: apply silent <code>NO_REPLY</code> policy per conversation type, so direct chats get a helpful rewritten reply while groups and internal deliveries can remain quiet. (#68644) Thanks @Takhoffman.</li>
<li>Telegram/status reactions: honor <code>messages.removeAckAfterReply</code> when lifecycle status reactions are enabled, clearing or restoring the reaction after success/error using the configured hold timings. (#68067) Thanks @poiskgit.</li>
<li>Web search/plugins: resolve plugin-scoped SecretRef API keys for bundled Exa, Firecrawl, Gemini, Kimi, Perplexity, Tavily, and Grok web-search providers when they are selected through the shared web-search config. (#68424) Thanks @afurm.</li>
<li>Telegram/polling: raise the default polling watchdog threshold from 90s to 120s and add configurable <code>channels.telegram.pollingStallThresholdMs</code> (also per-account) so long-running Telegram work gets more room before polling is treated as stalled. (#57737) Thanks @Vitalcheffe.</li>
<li>Telegram/polling: bound the persisted-offset confirmation <code>getUpdates</code> probe with a client-side timeout so a zombie socket cannot hang polling recovery before the runner watchdog starts. (#50368) Thanks @boticlaw.</li>
<li>Agents/Pi runner: retry silent <code>stopReason=error</code> turns with no output when no side effects ran, so non-frontier providers that briefly return empty error turns get another chance instead of ending the session early. (#68310) Thanks @Chased1k.</li>
<li>Plugins/memory: preserve the active memory capability when read-only snapshot plugin loads run, so status and provider discovery paths no longer wipe memory public artifacts. (#69219) Thanks @zeroaltitude.</li>
<li>Plugins: keep only the highest-precedence manifest when distinct discovered plugins share an id, so lower-precedence global or workspace duplicates no longer load beside bundled or config-selected plugins. (#41626) Thanks @Tortes.</li>
<li>fix(security): block MINIMAX_API_HOST workspace env injection and remove env-driven URL routing [AI-assisted]. (#67300) Thanks @pgondhi987.</li>
<li>Cron/delivery: treat explicit <code>delivery.mode: "none"</code> runs as not requested even if the runner reports <code>delivered: false</code>, so no-delivery cron jobs no longer persist false delivery failures or errors. (#69285) Thanks @matsuri1987.</li>
<li>Plugins/install: repair active and default-enabled bundled plugin runtime dependencies before import in packaged installs, so bundled Discord, WhatsApp, Slack, Telegram, and provider plugins work without putting their dependency trees in core.</li>
<li>BlueBubbles: raise the outbound <code>/api/v1/message/text</code> send timeout default from 10s to 30s, and add a configurable <code>channels.bluebubbles.sendTimeoutMs</code> (also per-account) so macOS 26 setups where Private API iMessage sends stall for 60+ seconds no longer silently lose messages at the 10s abort. Probes, chat lookups, and health checks keep the shorter 10s default. Fixes #67486. (#69193) Thanks @omarshahine.</li>
<li>Agents/bootstrap: budget truncation markers against per-file caps, preserve source content instead of silently wasting bootstrap bytes, and avoid marker-only output in tiny-budget truncation cases. (#69114) Thanks @BKF-Gitty.</li>
<li>Context engine/plugins: stop rejecting third-party context engines whose <code>info.id</code> differs from the registered plugin slot id. The strict-match contract added in 2026.4.14 broke <code>lossless-claw</code> and other plugins whose internal engine id does not equal the slot id they are registered under, producing repeated <code>info.id must match registered id</code> lane failures on every turn. Fixes #66601. (#66678) Thanks @GodsBoy.</li>
<li>Agents/compaction: rename embedded Pi compaction lifecycle events to <code>compaction_start</code> / <code>compaction_end</code> so OpenClaw stays aligned with <code>pi-coding-agent</code> 0.66.1 event naming. (#67713) Thanks @mpz4life.</li>
<li>Security/dotenv: block all <code>OPENCLAW_*</code> keys from untrusted workspace <code>.env</code> files so workspace-local env loading fails closed for new runtime-control variables instead of silently inheriting them. (#473)</li>
<li>Gateway/device pairing: restrict non-admin paired-device sessions (device-token auth) to their own pairing list, approve, and reject actions so a paired device cannot enumerate other devices or approve/reject pairing requests authored by another device. Admin and shared-secret operator sessions retain full visibility. (#69375) Thanks @eleqtrizit.</li>
<li>Agents/gateway tool: extend the agent-facing <code>gateway</code> tool's config mutation guard so model-driven <code>config.patch</code> and <code>config.apply</code> cannot rewrite operator-trusted paths (sandbox, plugin trust, gateway auth/TLS, hook routing and tokens, SSRF policy, MCP servers, workspace filesystem hardening) and cannot bypass the guard by editing per-agent sandbox, tools, or embedded-Pi overrides in place under <code>agents.list[]</code>. (#69377) Thanks @eleqtrizit.</li>
<li>Gateway/websocket broadcasts: require <code>operator.read</code> (or higher) for chat, agent, and tool-result event frames so pairing-scoped and node-role sessions no longer passively receive session chat content, and scope-gate unknown broadcast events by default. Plugin-defined <code>plugin.*</code> broadcasts are scoped to operator.write/admin, and status/transport events (<code>heartbeat</code>, <code>presence</code>, <code>tick</code>, etc.) remain unrestricted. Per-client sequence numbers preserve per-connection monotonicity. (#69373) Thanks @eleqtrizit.</li>
<li>Agents/compaction: always reload embedded Pi resources through an explicit loader and reapply reserve-token overrides so runs without extension factories no longer silently lose compaction settings before session start. (#67146) Thanks @ly85206559.</li>
<li>Memory-core/dreaming: normalize sweep timestamps and reuse hashed narrative session keys for fallback cleanup so Dreaming narrative sub-sessions stop leaking. (#67023) Thanks @chiyouYCH.</li>
<li>Gateway/startup: delay HTTP bind until websocket handlers are attached, so immediate post-startup websocket health/connect probes no longer hit the startup race window. (#43392) Thanks @dalefrieswthat.</li>
<li>Codex/app-server: release the session lane when a downstream consumer throws while draining the <code>turn/completed</code> notification, so follow-up messages after a Codex plugin reply stop queueing behind a stale lane lock. Fixes #67996. (#69072) Thanks @ayeshakhalid192007-dev.</li>
<li>Codex/app-server: default approval handling to <code>on-request</code> so Codex harness sessions do not start with overly permissive tool approvals. (#68721) Thanks @Lucenx9.</li>
<li>Cron/delivery: keep isolated cron chat delivery tools available, resolve <code>channel: "last"</code> targets from the gateway, show delivery previews in <code>cron list/show</code>, and avoid duplicate fallback sends after direct message-tool delivery. (#69587) Thanks @obviyus.</li>
<li>Cron/Telegram: key isolated direct-delivery dedupe to each cron execution instead of the reused session id, so recurring Telegram announce runs no longer report delivered while silently skipping later sends. (#69000) Thanks @obviyus.</li>
<li>Models/Kimi: default bundled Kimi thinking to off and normalize Anthropic-compatible <code>thinking</code> payloads so stale session <code>/think</code> state no longer silently re-enables reasoning on Kimi runs. (#68907) Thanks @frankekn.</li>
<li>Control UI/cron: keep the runtime-only <code>last</code> delivery sentinel from being materialized into persisted cron delivery and failure-alert channel configs when jobs are created or edited. (#68829) Thanks @tianhaocui.</li>
<li>OpenAI/Responses: strip orphaned reasoning blocks before outbound Responses API calls so compacted or restored histories no longer fail on standalone reasoning items. (#55787) Thanks @suboss87.</li>
<li>Cron/CLI: parse PowerShell-style <code>--tools</code> allow-lists the same way as comma-separated input, so <code>cron add</code> and <code>cron edit</code> no longer persist <code>exec read write</code> as one combined tool entry on Windows. (#68858) Thanks @chen-zhang-cs-code.</li>
<li>Browser/user-profile: let existing-session <code>profile="user"</code> tool calls auto-route to a connected browser node or use explicit <code>target="node"</code>, while still honoring explicit <code>target="host"</code> pinning. (#48677)</li>
<li>Discord/slash commands: tolerate partial Discord channel metadata in slash-command and model-picker flows so partial channel objects no longer crash when channel names, topics, or thread parent metadata are unavailable. (#68953) Thanks @dutifulbob.</li>
<li>BlueBubbles: consolidate outbound HTTP through a typed <code>BlueBubblesClient</code> that resolves the SSRF policy once at construction so image attachments stop getting blocked on localhost and reactions stop getting blocked on private-IP BB deployments. Fixes #34749 and #59722. (#68234) Thanks @omarshahine.</li>
<li>Cron/gateway: reject ambiguous announce delivery config at add/update time so invalid multi-channel or target-id provider settings fail early instead of persisting broken cron jobs. (#69015) Thanks @obviyus.</li>
<li>Cron/main-session delivery: preserve <code>heartbeat.target="last"</code> through deferred wake queuing, gateway wake forwarding, and same-target wake coalescing so queued cron replies still return to the last active chat. (#69021) Thanks @obviyus.</li>
<li>Cron/gateway: ignore disabled channels when announce delivery ambiguity is checked, and validate main-session delivery patches against the live cron service default agent so hot-reloaded agent config does not falsely reject valid updates. (#69040) Thanks @obviyus.</li>
<li>Matrix/allowlists: hot-reload <code>dm.allowFrom</code> and <code>groupAllowFrom</code> entries on inbound messages while keeping config removals authoritative, so Matrix allowlist changes no longer require a channel restart to add or revoke a sender. (#68546) Thanks @johnlanni.</li>
<li>BlueBubbles: always set <code>method</code> explicitly on outbound text sends (<code>"private-api"</code> when available, <code>"apple-script"</code> otherwise), and prefer Private API on macOS 26 even for plain text. Fixes silent delivery failure on macOS setups without Private API where an omitted <code>method</code> let BB Server fall back to version-dependent default behavior that silently drops the message (#64480), and the AppleScript <code>-1700</code> error on macOS 26 Tahoe plain text sends (#53159). (#69070) Thanks @xqing3.</li>
<li>Matrix/commands: recognize slash commands that are prefixed with the bot's Matrix mention, so room messages like <code>@bot:server /new</code> trigger the command path without requiring custom mention regexes. (#68570) Thanks @nightq and @johnlanni.</li>
<li>Gateway/pairing: return reason-specific <code>PAIRING_REQUIRED</code> details, remediation hints, and request ids so unapproved-device and scope-upgrade failures surface actionable recovery guidance in the CLI and Control UI. (#69227) Thanks @obviyus.</li>
<li>Agents/subagents: include requested role and runtime timing on subagent failure payloads so parent agents can correlate failed or timed-out child work. (#68726) Thanks @BKF-Gitty.</li>
<li>Gateway/sessions: reject stale agent-scoped sessions after an agent is removed from config while preserving legacy default-agent main-session aliases. (#65986) Thanks @bittoby.</li>
<li>Doctor/gateway: surface pending device pairing requests, scope-upgrade approval drift, and stale device-token mismatch repair steps so <code>openclaw doctor --fix</code> no longer leaves pairing/auth setup failures unexplained. (#69210) Thanks @obviyus.</li>
<li>Cron/isolated-agent: preserve explicit <code>delivery.mode: "none"</code> message targets for isolated runs without inheriting implicit <code>last</code> routing, so agent-initiated Telegram sends keep their authored destination while bare <code>mode:none</code> jobs stay targetless. (#69153) Thanks @obviyus.</li>
<li>Cron/isolated-agent: keep <code>delivery.mode: "none"</code> account-only or thread-only configs from inheriting a stale implicit recipient, so isolated runs only resolve message routing when the job authored an explicit <code>to</code> target. (#69163) Thanks @obviyus.</li>
<li>Gateway/TUI: retry session history while the local gateway is still finishing startup, so <code>openclaw tui</code> reconnects no longer fail on transient <code>chat.history unavailable during gateway startup</code> errors. (#69164) Thanks @shakkernerd.</li>
<li>BlueBubbles/reactions: fall back to <code>love</code> when an agent reacts with an emoji outside the iMessage tapback set (<code>love</code>/<code>like</code>/<code>dislike</code>/<code>laugh</code>/<code>emphasize</code>/<code>question</code>), so wider-vocabulary model reactions like <code>👀</code> still produce a visible tapback instead of failing the whole reaction request. Configured ack reactions still validate strictly via the new <code>normalizeBlueBubblesReactionInputStrict</code> path. (#64693) Thanks @zqchris.</li>
<li>BlueBubbles: prefer iMessage over SMS when both chats exist for the same handle, honor explicit <code>sms:</code> targets, and never silently downgrade iMessage-available recipients. (#61781) Thanks @rmartin.</li>
<li>Telegram/setup: require numeric <code>allowFrom</code> user IDs during setup instead of offering unsupported <code>@username</code> DM resolution, and point operators to <code>from.id</code>/<code>getUpdates</code> for discovery. (#69191) Thanks @obviyus.</li>
<li>GitHub Copilot/onboarding: default GitHub Copilot setup to <code>claude-opus-4.6</code> and keep the bundled default model list aligned, so new Copilot setups no longer start on the older <code>gpt-4o</code> default. (#69207) Thanks @obviyus.</li>
<li>Gateway/status: separate reachability, capability, and read-probe reporting so connect-only or scope-limited sessions no longer look fully healthy, and normalize SSH targets entered as <code>ssh user@host</code>. (#69215) Thanks @obviyus.</li>
<li>Slack: fix outbound replies failing with "unresolved SecretRef" for accounts configured via <code>file</code> or <code>exec</code> secret sources; the send path now tolerates the runtime snapshot retaining an unresolved channel SecretRef when a boot-resolved token override is already available. (#68954) Thanks @openperf.</li>
<li>Control UI/device pairing: explain scope and role approval upgrades during reconnects, and show requested versus approved access in the Control UI and <code>openclaw devices</code> so broader reconnects no longer look like lost pairings. (#69221) Thanks @obviyus.</li>
<li>Gateway/Control UI: surface pending scope, role, and device-metadata pairing approvals in auth errors and Control UI hints so broader reconnects no longer look like random auth breakage. (#69226) Thanks @obviyus.</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.4.20/OpenClaw-2026.4.20.zip" length="47535600" type="application/octet-stream" sparkle:edSignature="D7XcNGxmc10IIayYY91RZBoascFSnXyd4dg6cSpC3+PTIwVrWYs/FwSBc/1J+1P53LlnTHKDGQYMkWVNMnRSAQ=="/>
</item>
</channel>
</rss>

View File

@@ -82,7 +82,7 @@ public struct HelloOk: Codable, Sendable {
public let features: [String: AnyCodable]
public let snapshot: Snapshot
public let canvashosturl: String?
public let auth: [String: AnyCodable]?
public let auth: [String: AnyCodable]
public let policy: [String: AnyCodable]
public init(
@@ -92,7 +92,7 @@ public struct HelloOk: Codable, Sendable {
features: [String: AnyCodable],
snapshot: Snapshot,
canvashosturl: String?,
auth: [String: AnyCodable]?,
auth: [String: AnyCodable],
policy: [String: AnyCodable])
{
self.type = type
@@ -600,6 +600,7 @@ public struct AgentParams: Codable, Sendable {
public let extrasystemprompt: String?
public let bootstrapcontextmode: AnyCodable?
public let bootstrapcontextrunkind: AnyCodable?
public let acpturnsource: String?
public let internalevents: [[String: AnyCodable]]?
public let inputprovenance: [String: AnyCodable]?
public let voicewaketrigger: String?
@@ -635,6 +636,7 @@ public struct AgentParams: Codable, Sendable {
extrasystemprompt: String?,
bootstrapcontextmode: AnyCodable?,
bootstrapcontextrunkind: AnyCodable?,
acpturnsource: String?,
internalevents: [[String: AnyCodable]]?,
inputprovenance: [String: AnyCodable]?,
voicewaketrigger: String?,
@@ -669,6 +671,7 @@ public struct AgentParams: Codable, Sendable {
self.extrasystemprompt = extrasystemprompt
self.bootstrapcontextmode = bootstrapcontextmode
self.bootstrapcontextrunkind = bootstrapcontextrunkind
self.acpturnsource = acpturnsource
self.internalevents = internalevents
self.inputprovenance = inputprovenance
self.voicewaketrigger = voicewaketrigger
@@ -705,6 +708,7 @@ public struct AgentParams: Codable, Sendable {
case extrasystemprompt = "extraSystemPrompt"
case bootstrapcontextmode = "bootstrapContextMode"
case bootstrapcontextrunkind = "bootstrapContextRunKind"
case acpturnsource = "acpTurnSource"
case internalevents = "internalEvents"
case inputprovenance = "inputProvenance"
case voicewaketrigger = "voiceWakeTrigger"
@@ -893,6 +897,20 @@ public struct NodePairRejectParams: Codable, Sendable {
}
}
public struct NodePairRemoveParams: Codable, Sendable {
public let nodeid: String
public init(
nodeid: String)
{
self.nodeid = nodeid
}
private enum CodingKeys: String, CodingKey {
case nodeid = "nodeId"
}
}
public struct NodePairVerifyParams: Codable, Sendable {
public let nodeid: String
public let token: String
@@ -2375,33 +2393,93 @@ public struct TalkRealtimeSessionParams: Codable, Sendable {
}
}
public struct TalkRealtimeSessionResult: Codable, Sendable {
public let provider: String
public let clientsecret: String
public let model: String?
public let voice: String?
public let expiresat: Double?
public struct TalkRealtimeRelayAudioParams: Codable, Sendable {
public let relaysessionid: String
public let audiobase64: String
public let timestamp: Double?
public init(
provider: String,
clientsecret: String,
model: String?,
voice: String?,
expiresat: Double?)
relaysessionid: String,
audiobase64: String,
timestamp: Double?)
{
self.provider = provider
self.clientsecret = clientsecret
self.model = model
self.voice = voice
self.expiresat = expiresat
self.relaysessionid = relaysessionid
self.audiobase64 = audiobase64
self.timestamp = timestamp
}
private enum CodingKeys: String, CodingKey {
case provider
case clientsecret = "clientSecret"
case model
case voice
case expiresat = "expiresAt"
case relaysessionid = "relaySessionId"
case audiobase64 = "audioBase64"
case timestamp
}
}
public struct TalkRealtimeRelayMarkParams: Codable, Sendable {
public let relaysessionid: String
public let markname: String?
public init(
relaysessionid: String,
markname: String?)
{
self.relaysessionid = relaysessionid
self.markname = markname
}
private enum CodingKeys: String, CodingKey {
case relaysessionid = "relaySessionId"
case markname = "markName"
}
}
public struct TalkRealtimeRelayStopParams: Codable, Sendable {
public let relaysessionid: String
public init(
relaysessionid: String)
{
self.relaysessionid = relaysessionid
}
private enum CodingKeys: String, CodingKey {
case relaysessionid = "relaySessionId"
}
}
public struct TalkRealtimeRelayToolResultParams: Codable, Sendable {
public let relaysessionid: String
public let callid: String
public let result: AnyCodable
public init(
relaysessionid: String,
callid: String,
result: AnyCodable)
{
self.relaysessionid = relaysessionid
self.callid = callid
self.result = result
}
private enum CodingKeys: String, CodingKey {
case relaysessionid = "relaySessionId"
case callid = "callId"
case result
}
}
public struct TalkRealtimeRelayOkResult: Codable, Sendable {
public let ok: Bool
public init(
ok: Bool)
{
self.ok = ok
}
private enum CodingKeys: String, CodingKey {
case ok
}
}
@@ -4487,6 +4565,8 @@ public struct ChatEvent: Codable, Sendable {
}
}
public struct UpdateStatusParams: Codable, Sendable {}
public struct UpdateRunParams: Codable, Sendable {
public let sessionkey: String?
public let deliverycontext: [String: AnyCodable]?

View File

@@ -45,6 +45,7 @@ enum GatewayWebSocketTestSupport {
"stateVersion": { "presence": 0, "health": 0 },
"uptimeMs": 0
},
"auth": { "role": "operator", "scopes": [] },
"policy": { "maxPayload": 1, "maxBufferedBytes": 1, "tickIntervalMs": 30000 }
}
}

View File

@@ -23,7 +23,7 @@ struct MacGatewayChatTransportMappingTests {
features: [:],
snapshot: snapshot,
canvashosturl: nil,
auth: nil,
auth: [:],
policy: [:])
let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.snapshot(hello))

View File

@@ -663,7 +663,8 @@ public actor GatewayChannelActor {
} else if let tick = ok.policy["tickIntervalMs"]?.value as? Int {
self.tickIntervalMs = Double(tick)
}
if let auth = ok.auth, let identity {
let auth = ok.auth
if let identity {
if let deviceToken = auth["deviceToken"]?.value as? String {
let authRole = auth["role"]?.value as? String ?? role
let scopes = (auth["scopes"]?.value as? [ProtoAnyCodable])?

View File

@@ -82,7 +82,7 @@ public struct HelloOk: Codable, Sendable {
public let features: [String: AnyCodable]
public let snapshot: Snapshot
public let canvashosturl: String?
public let auth: [String: AnyCodable]?
public let auth: [String: AnyCodable]
public let policy: [String: AnyCodable]
public init(
@@ -92,7 +92,7 @@ public struct HelloOk: Codable, Sendable {
features: [String: AnyCodable],
snapshot: Snapshot,
canvashosturl: String?,
auth: [String: AnyCodable]?,
auth: [String: AnyCodable],
policy: [String: AnyCodable])
{
self.type = type
@@ -600,6 +600,7 @@ public struct AgentParams: Codable, Sendable {
public let extrasystemprompt: String?
public let bootstrapcontextmode: AnyCodable?
public let bootstrapcontextrunkind: AnyCodable?
public let acpturnsource: String?
public let internalevents: [[String: AnyCodable]]?
public let inputprovenance: [String: AnyCodable]?
public let voicewaketrigger: String?
@@ -635,6 +636,7 @@ public struct AgentParams: Codable, Sendable {
extrasystemprompt: String?,
bootstrapcontextmode: AnyCodable?,
bootstrapcontextrunkind: AnyCodable?,
acpturnsource: String?,
internalevents: [[String: AnyCodable]]?,
inputprovenance: [String: AnyCodable]?,
voicewaketrigger: String?,
@@ -669,6 +671,7 @@ public struct AgentParams: Codable, Sendable {
self.extrasystemprompt = extrasystemprompt
self.bootstrapcontextmode = bootstrapcontextmode
self.bootstrapcontextrunkind = bootstrapcontextrunkind
self.acpturnsource = acpturnsource
self.internalevents = internalevents
self.inputprovenance = inputprovenance
self.voicewaketrigger = voicewaketrigger
@@ -705,6 +708,7 @@ public struct AgentParams: Codable, Sendable {
case extrasystemprompt = "extraSystemPrompt"
case bootstrapcontextmode = "bootstrapContextMode"
case bootstrapcontextrunkind = "bootstrapContextRunKind"
case acpturnsource = "acpTurnSource"
case internalevents = "internalEvents"
case inputprovenance = "inputProvenance"
case voicewaketrigger = "voiceWakeTrigger"
@@ -893,6 +897,20 @@ public struct NodePairRejectParams: Codable, Sendable {
}
}
public struct NodePairRemoveParams: Codable, Sendable {
public let nodeid: String
public init(
nodeid: String)
{
self.nodeid = nodeid
}
private enum CodingKeys: String, CodingKey {
case nodeid = "nodeId"
}
}
public struct NodePairVerifyParams: Codable, Sendable {
public let nodeid: String
public let token: String
@@ -2375,33 +2393,93 @@ public struct TalkRealtimeSessionParams: Codable, Sendable {
}
}
public struct TalkRealtimeSessionResult: Codable, Sendable {
public let provider: String
public let clientsecret: String
public let model: String?
public let voice: String?
public let expiresat: Double?
public struct TalkRealtimeRelayAudioParams: Codable, Sendable {
public let relaysessionid: String
public let audiobase64: String
public let timestamp: Double?
public init(
provider: String,
clientsecret: String,
model: String?,
voice: String?,
expiresat: Double?)
relaysessionid: String,
audiobase64: String,
timestamp: Double?)
{
self.provider = provider
self.clientsecret = clientsecret
self.model = model
self.voice = voice
self.expiresat = expiresat
self.relaysessionid = relaysessionid
self.audiobase64 = audiobase64
self.timestamp = timestamp
}
private enum CodingKeys: String, CodingKey {
case provider
case clientsecret = "clientSecret"
case model
case voice
case expiresat = "expiresAt"
case relaysessionid = "relaySessionId"
case audiobase64 = "audioBase64"
case timestamp
}
}
public struct TalkRealtimeRelayMarkParams: Codable, Sendable {
public let relaysessionid: String
public let markname: String?
public init(
relaysessionid: String,
markname: String?)
{
self.relaysessionid = relaysessionid
self.markname = markname
}
private enum CodingKeys: String, CodingKey {
case relaysessionid = "relaySessionId"
case markname = "markName"
}
}
public struct TalkRealtimeRelayStopParams: Codable, Sendable {
public let relaysessionid: String
public init(
relaysessionid: String)
{
self.relaysessionid = relaysessionid
}
private enum CodingKeys: String, CodingKey {
case relaysessionid = "relaySessionId"
}
}
public struct TalkRealtimeRelayToolResultParams: Codable, Sendable {
public let relaysessionid: String
public let callid: String
public let result: AnyCodable
public init(
relaysessionid: String,
callid: String,
result: AnyCodable)
{
self.relaysessionid = relaysessionid
self.callid = callid
self.result = result
}
private enum CodingKeys: String, CodingKey {
case relaysessionid = "relaySessionId"
case callid = "callId"
case result
}
}
public struct TalkRealtimeRelayOkResult: Codable, Sendable {
public let ok: Bool
public init(
ok: Bool)
{
self.ok = ok
}
private enum CodingKeys: String, CodingKey {
case ok
}
}
@@ -4487,6 +4565,8 @@ public struct ChatEvent: Codable, Sendable {
}
}
public struct UpdateStatusParams: Codable, Sendable {}
public struct UpdateRunParams: Codable, Sendable {
public let sessionkey: String?
public let deliverycontext: [String: AnyCodable]?

View File

@@ -34,6 +34,11 @@ services:
# - /var/run/docker.sock:/var/run/docker.sock
# group_add:
# - "${DOCKER_GID:-999}"
# Let bundled local-model providers reach host-side LM Studio/Ollama via
# http://host.docker.internal:<port>. Docker Desktop usually provides this
# alias; the host-gateway mapping makes it work on Linux Docker Engine too.
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
- "${OPENCLAW_BRIDGE_PORT:-18790}:18790"

View File

@@ -1,4 +1,4 @@
4d1995e41b659e484afb5a48d6fca0558337123200a4a537f556ca38e8e829e7 config-baseline.json
3245c9a013c55ee8a24db52d5e88c42bc86e26f822d4a144fc7f37fc71e05fa8 config-baseline.core.json
7cd9c908f066c143eab2a201efbc9640f483ab28bba92ddeca1d18cc2b528bc3 config-baseline.channel.json
f9e0174988718959fe1923a54496ec5b9262721fe1e7306f32ccb1316d9d9c3f config-baseline.plugin.json
f84f0e7ad9e5334779bfc6dbeb849a64fd9feed99bef3ba23104a2f31d9a4a88 config-baseline.json
7dcb21e47ddd5de98e2af1ecbc41e11ac0c5742819c359e6d851fbc39c0226e9 config-baseline.core.json
6f7e255d4520ba18364771faca375c0971bb68472c4a0bd3e55c032407042067 config-baseline.channel.json
6938050627f0d120109d2045b4300aa8b508b35132542db434033ed0fe3e2b3a config-baseline.plugin.json

View File

@@ -1,2 +1,2 @@
21914ef8c5840e0defc36d571834dc28a92d6d5ca2d42a088c33b4de681e836a plugin-sdk-api-baseline.json
3f22e6af0dad3433d25d996802d7436a3cc0e68bc86ecaf813a22e2b4e5333eb plugin-sdk-api-baseline.jsonl
9a9970607c4ee25d8b493ea1bb1ecef939dc11330c16188251bd69eee7ab6b2d plugin-sdk-api-baseline.json
e37f1ec7f6a9b19466ab09935f713890f375cc6c8d67647dca701140a989f5f4 plugin-sdk-api-baseline.jsonl

View File

@@ -379,6 +379,10 @@
"source": "Plugin SDK",
"target": "插件 SDK"
},
{
"source": "Plugins",
"target": "插件"
},
{
"source": "Building plugins",
"target": "构建插件"
@@ -435,6 +439,38 @@
"source": "Setup",
"target": "设置"
},
{
"source": "Migrate",
"target": "迁移"
},
{
"source": "Migrating",
"target": "迁移"
},
{
"source": "Migrating from Hermes",
"target": "从 Hermes 迁移"
},
{
"source": "Migrating from Claude",
"target": "从 Claude 迁移"
},
{
"source": "Agent workspace",
"target": "Agent 工作区"
},
{
"source": "Migration",
"target": "迁移"
},
{
"source": "Hermes",
"target": "Hermes"
},
{
"source": "Archive-only",
"target": "仅归档"
},
{
"source": "Channel Plugin SDK",
"target": "渠道插件 SDK"
@@ -467,6 +503,18 @@
"source": "QA Refactor",
"target": "QA 重构"
},
{
"source": "Matrix QA",
"target": "Matrix QA"
},
{
"source": "QA overview",
"target": "QA overview"
},
{
"source": "QA channel",
"target": "QA channel"
},
{
"source": "Rich Output Protocol",
"target": "富输出协议"

View File

@@ -15,7 +15,7 @@ This document defines the canonical credential eligibility and resolution semant
The goal is to keep selection-time and runtime behavior aligned.
## Stable Probe Reason Codes
## Stable probe reason codes
- `ok`
- `excluded_by_auth_order`
@@ -25,7 +25,7 @@ The goal is to keep selection-time and runtime behavior aligned.
- `unresolved_ref`
- `no_model`
## Token Credentials
## Token credentials
Token credentials (`type: "token"`) support inline `token` and/or `tokenRef`.
@@ -44,7 +44,7 @@ Token credentials (`type: "token"`) support inline `token` and/or `tokenRef`.
2. For eligible profiles, token material may be resolved from inline value or `tokenRef`.
3. Unresolvable refs produce `unresolved_ref` in `models status --probe` output.
## Explicit Auth Order Filtering
## Explicit auth order filtering
- When `auth.order.<provider>` or the auth-store order override is set for a
provider, `models status --probe` only probes profile ids that remain in the
@@ -54,7 +54,7 @@ Token credentials (`type: "token"`) support inline `token` and/or `tokenRef`.
`reasonCode: excluded_by_auth_order` and the detail
`Excluded by auth.order for this provider.`
## Probe Target Resolution
## Probe target resolution
- Probe targets can come from auth profiles, environment credentials, or
`models.json`.

View File

@@ -3,7 +3,7 @@ summary: "Redirect to /gateway/authentication"
title: "Auth monitoring"
---
This page moved to [Authentication](/gateway/authentication). See [Authentication](/gateway/authentication) for auth monitoring documentation.
Auth monitoring lives under [Authentication](/gateway/authentication).
## Related

View File

@@ -3,7 +3,7 @@ summary: "Redirect to Task Flow"
title: "ClawFlow"
---
ClawFlow was renamed to [Task Flow](/automation/taskflow). See [Task Flow](/automation/taskflow) for the current documentation.
ClawFlow was renamed to [Task flow](/automation/taskflow).
## Related

View File

@@ -43,10 +43,13 @@ Cron is the Gateway's built-in scheduler. It persists jobs, wakes the agent at t
- Job definitions persist at `~/.openclaw/cron/jobs.json` so restarts do not lose schedules.
- Runtime execution state persists next to it in `~/.openclaw/cron/jobs-state.json`. If you track cron definitions in git, track `jobs.json` and gitignore `jobs-state.json`.
- After the split, older OpenClaw versions can read `jobs.json` but may treat jobs as fresh because runtime fields now live in `jobs-state.json`.
- When `jobs.json` is edited while the Gateway is running or stopped, OpenClaw compares the changed schedule fields with pending runtime slot metadata and clears stale `nextRunAtMs` values. Pure formatting or key-order-only rewrites preserve the pending slot.
- All cron executions create [background task](/automation/tasks) records.
- One-shot jobs (`--at`) auto-delete after success by default.
- Isolated cron runs best-effort close tracked browser tabs/processes for their `cron:<jobId>` session when the run completes, so detached browser automation does not leave orphaned processes behind.
- Isolated cron runs also guard against stale acknowledgement replies. If the first result is just an interim status update (`on it`, `pulling everything together`, and similar hints) and no descendant subagent run is still responsible for the final answer, OpenClaw re-prompts once for the actual result before delivery.
- Isolated cron runs prefer structured execution-denial metadata from the embedded run, then fall back to known final summary/output markers such as `SYSTEM_RUN_DENIED` and `INVALID_REQUEST`, so a blocked command is not reported as a green run.
- Isolated cron runs also treat run-level agent failures as job errors even when no reply payload is produced, so model/provider failures increment error counters and trigger failure notifications instead of clearing the job as successful.
<a id="maintenance"></a>
@@ -159,6 +162,7 @@ Failure notifications follow a separate destination path:
- `job.delivery.failureDestination` overrides that per job.
- If neither is set and the job already delivers via `announce`, failure notifications now fall back to that primary announce target.
- `delivery.failureDestination` is only supported on `sessionTarget="isolated"` jobs unless the primary delivery mode is `webhook`.
- `failureAlert.includeSkipped: true` opts a job or global cron alert policy into repeated skipped-run alerts. Skipped runs keep a separate consecutive skip counter, so they do not affect execution-error backoff.
## CLI examples
@@ -394,8 +398,12 @@ Model override note:
}
```
`maxConcurrentRuns` limits both scheduled cron dispatch and isolated agent-turn execution. Isolated cron agent turns use the queue's dedicated `cron-nested` execution lane internally, so raising this value lets independent cron LLM runs progress in parallel instead of only starting their outer cron wrappers. The shared non-cron `nested` lane is not widened by this setting.
The runtime state sidecar is derived from `cron.store`: a `.json` store such as `~/clawd/cron/jobs.json` uses `~/clawd/cron/jobs-state.json`, while a store path without a `.json` suffix appends `-state.json`.
If you hand-edit `jobs.json`, leave `jobs-state.json` out of source control. OpenClaw uses that sidecar for pending slots, active markers, last-run metadata, and the schedule identity that tells the scheduler when an externally edited job needs a fresh `nextRunAtMs`.
Disable cron: `cron.enabled: false` or `OPENCLAW_SKIP_CRON=1`.
<AccordionGroup>

View File

@@ -3,7 +3,7 @@ summary: "Redirect to /automation"
title: "Cron vs heartbeat"
---
This page moved to [Automation & Tasks](/automation). See [Automation & Tasks](/automation) for the decision guide comparing cron and heartbeat.
The decision guide for cron vs heartbeat lives under [Automation and tasks](/automation).
## Related

View File

@@ -33,21 +33,23 @@ openclaw hooks info session-memory
## Event types
| Event | When it fires |
| ------------------------ | ------------------------------------------------ |
| `command:new` | `/new` command issued |
| `command:reset` | `/reset` command issued |
| `command:stop` | `/stop` command issued |
| `command` | Any command event (general listener) |
| `session:compact:before` | Before compaction summarizes history |
| `session:compact:after` | After compaction completes |
| `session:patch` | When session properties are modified |
| `agent:bootstrap` | Before workspace bootstrap files are injected |
| `gateway:startup` | After channels start and hooks are loaded |
| `message:received` | Inbound message from any channel |
| `message:transcribed` | After audio transcription completes |
| `message:preprocessed` | After all media and link understanding completes |
| `message:sent` | Outbound message delivered |
| Event | When it fires |
| ------------------------ | ---------------------------------------------------------- |
| `command:new` | `/new` command issued |
| `command:reset` | `/reset` command issued |
| `command:stop` | `/stop` command issued |
| `command` | Any command event (general listener) |
| `session:compact:before` | Before compaction summarizes history |
| `session:compact:after` | After compaction completes |
| `session:patch` | When session properties are modified |
| `agent:bootstrap` | Before workspace bootstrap files are injected |
| `gateway:startup` | After channels start and hooks are loaded |
| `gateway:shutdown` | When gateway shutdown begins |
| `gateway:pre-restart` | Before an expected gateway restart |
| `message:received` | Inbound message from any channel |
| `message:transcribed` | After audio transcription completes |
| `message:preprocessed` | After media and link preprocessing completes or is skipped |
| `message:sent` | Outbound message delivered |
## Writing hooks
@@ -131,6 +133,8 @@ lifecycle, not an agent-finalization gate. Plugins that need to inspect a
natural final answer and ask the agent for one more pass should use the typed
plugin hook `before_agent_finalize` instead. See [Plugin hooks](/plugins/hooks).
**Gateway lifecycle events**: `gateway:shutdown` includes `reason` and `restartExpectedMs` and fires when gateway shutdown begins. `gateway:pre-restart` includes the same context but only fires when shutdown is part of an expected restart and a finite `restartExpectedMs` value is supplied. During shutdown, each lifecycle hook wait is best-effort and bounded so shutdown continues if a handler stalls.
## Hook discovery
Hooks are discovered from these directories, in order of increasing override precedence:

View File

@@ -11,7 +11,7 @@ Standing orders grant your agent **permanent operating authority** for defined p
This is the difference between telling your assistant "send the weekly report" every Friday vs. granting standing authority: "You own the weekly report. Compile it every Friday, send it, and only escalate if something looks wrong."
## Why Standing Orders?
## Why standing orders
**Without standing orders:**
@@ -44,7 +44,7 @@ The agent loads these instructions every session via the workspace bootstrap fil
Put standing orders in `AGENTS.md` to guarantee they're loaded every session. The workspace bootstrap automatically injects `AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md`, and `MEMORY.md` — but not arbitrary files in subdirectories.
</Tip>
## Anatomy of a Standing Order
## Anatomy of a standing order
```markdown
## Program: Weekly Status Report
@@ -54,7 +54,7 @@ Put standing orders in `AGENTS.md` to guarantee they're loaded every session. Th
**Approval gate:** None for standard reports. Flag anomalies for human review.
**Escalation:** If data source is unavailable or metrics look unusual (>2σ from norm)
### Execution Steps
### Execution steps
1. Pull metrics from configured sources
2. Compare to prior week and targets
@@ -62,14 +62,14 @@ Put standing orders in `AGENTS.md` to guarantee they're loaded every session. Th
4. Deliver summary via configured channel
5. Log completion to Agent/Logs/
### What NOT to Do
### What NOT to do
- Do not send reports to external parties
- Do not modify source data
- Do not skip delivery if metrics look bad — report accurately
```
## Standing Orders + Cron Jobs
## Standing orders plus cron jobs
Standing orders define **what** the agent is authorized to do. [Cron jobs](/automation/cron-jobs) define **when** it happens. They work together:
@@ -97,7 +97,7 @@ openclaw cron add \
## Examples
### Example 1: Content & Social Media (Weekly Cycle)
### Example 1: content and social media (weekly cycle)
```markdown
## Program: Content & Social Media
@@ -106,13 +106,13 @@ openclaw cron add \
**Approval gate:** All posts require owner review for first 30 days, then standing approval
**Trigger:** Weekly cycle (Monday review → mid-week drafts → Friday brief)
### Weekly Cycle
### Weekly cycle
- **Monday:** Review platform metrics and audience engagement
- **TuesdayThursday:** Draft social posts, create blog content
- **Friday:** Compile weekly marketing brief → deliver to owner
### Content Rules
### Content rules
- Voice must match the brand (see SOUL.md or brand voice guide)
- Never identify as AI in public-facing content
@@ -120,7 +120,7 @@ openclaw cron add \
- Focus on value to audience, not self-promotion
```
### Example 2: Finance Operations (Event-Triggered)
### Example 2: finance operations (event-triggered)
```markdown
## Program: Financial Processing
@@ -129,7 +129,7 @@ openclaw cron add \
**Approval gate:** None for analysis. Recommendations require owner approval.
**Trigger:** New data file detected OR scheduled monthly cycle
### When New Data Arrives
### When new data arrives
1. Detect new file in designated input directory
2. Parse and categorize all transactions
@@ -138,7 +138,7 @@ openclaw cron add \
5. Generate report in designated output directory
6. Deliver summary to owner via configured channel
### Escalation Rules
### Escalation rules
- Single item > $500: immediate alert
- Category > budget by 20%: flag in report
@@ -146,7 +146,7 @@ openclaw cron add \
- Failed processing after 2 retries: report failure, do not guess
```
### Example 3: Monitoring & Alerts (Continuous)
### Example 3: monitoring and alerts (continuous)
```markdown
## Program: System Monitoring
@@ -162,7 +162,7 @@ openclaw cron add \
- Pending tasks not stale (>24 hours)
- Delivery channels operational
### Response Matrix
### Response matrix
| Condition | Action | Escalate? |
| ---------------- | ------------------------ | ------------------------ |
@@ -172,7 +172,7 @@ openclaw cron add \
| Channel offline | Log and retry next cycle | If offline > 2 hours |
```
## The Execute-Verify-Report Pattern
## Execute-verify-report pattern
Standing orders work best when combined with strict execution discipline. Every task in a standing order should follow this loop:
@@ -181,7 +181,7 @@ Standing orders work best when combined with strict execution discipline. Every
3. **Report** — Tell the owner what was done and what was verified
```markdown
### Execution Rules
### Execution rules
- Every task follows Execute-Verify-Report. No exceptions.
- "I'll do that" is not execution. Do it, then report.
@@ -193,7 +193,7 @@ Standing orders work best when combined with strict execution discipline. Every
This pattern prevents the most common agent failure mode: acknowledging a task without completing it.
## Multi-Program Architecture
## Multi-program architecture
For agents managing multiple concerns, organize standing orders as separate programs with clear boundaries:
@@ -222,7 +222,7 @@ Each program should have:
- Its own **approval gates** (some programs need more oversight than others)
- Clear **boundaries** (the agent should know where one program ends and another begins)
## Best Practices
## Best practices
### Do
@@ -243,8 +243,8 @@ Each program should have:
## Related
- [Automation & Tasks](/automation) all automation mechanisms at a glance
- [Cron Jobs](/automation/cron-jobs) schedule enforcement for standing orders
- [Hooks](/automation/hooks) event-driven scripts for agent lifecycle events
- [Webhooks](/automation/cron-jobs#webhooks) inbound HTTP event triggers
- [Agent Workspace](/concepts/agent-workspace) where standing orders live, including the full list of auto-injected bootstrap files (AGENTS.md, SOUL.md, etc.)
- [Automation and tasks](/automation): all automation mechanisms at a glance.
- [Cron jobs](/automation/cron-jobs): schedule enforcement for standing orders.
- [Hooks](/automation/hooks): event-driven scripts for agent lifecycle events.
- [Webhooks](/automation/cron-jobs#webhooks): inbound HTTP event triggers.
- [Agent workspace](/concepts/agent-workspace): where standing orders live, including the full list of auto-injected bootstrap files (`AGENTS.md`, `SOUL.md`, etc.).

View File

@@ -116,6 +116,9 @@ Example: three independent cron jobs that together form a "morning ops" routine.
## Durable state and revision tracking
Each flow persists its own state and tracks revisions so progress survives gateway restarts. Revision tracking enables conflict detection when multiple sources attempt to advance the same flow concurrently.
The flow registry uses SQLite with bounded write-ahead-log maintenance, including
periodic and shutdown checkpoints, so long-running gateways do not retain
unbounded `registry.sqlite-wal` sidecar files.
## Cancel behavior

View File

@@ -9,7 +9,7 @@ sidebarTitle: "Background tasks"
---
<Note>
Looking for scheduling? See [Automation & Tasks](/automation) for choosing the right mechanism. This page covers **tracking** background work, not scheduling it.
Looking for scheduling? See [Automation and tasks](/automation) for choosing the right mechanism. This page is the activity ledger for background work, not the scheduler.
</Note>
Background tasks track work that runs **outside your main conversation session**: ACP runs, subagent spawns, isolated cron job executions, and CLI-initiated operations.
@@ -305,6 +305,8 @@ $OPENCLAW_STATE_DIR/tasks/runs.sqlite
```
The registry loads into memory at gateway start and syncs writes to SQLite for durability across restarts.
The Gateway keeps the SQLite write-ahead log bounded by using SQLite's default
autocheckpoint threshold plus periodic and shutdown `TRUNCATE` checkpoints.
### Automatic maintenance

View File

@@ -585,6 +585,7 @@ Default slash command settings:
Thread behavior:
- Discord threads route as channel sessions and inherit parent channel config unless overridden.
- Thread sessions inherit the parent channel's session-level `/model` selection as a model-only fallback; thread-local `/model` selections still take precedence and parent transcript history is not copied unless transcript inheritance is enabled.
- `channels.discord.thread.inheritParent` (default `false`) opts new auto-threads into seeding from the parent transcript. Per-account overrides live under `channels.discord.accounts.<id>.thread.inheritParent`.
- Message-tool reactions can resolve `user:<id>` DM targets.
- `guilds.<guild>.channels.<channel>.requireMention: false` is preserved during reply-stage activation fallback.

View File

@@ -16,7 +16,9 @@ Feishu/Lark is an all-in-one collaboration platform where teams chat, share docu
## Quick start
> **Requires OpenClaw 2026.4.25 or above.** Run `openclaw --version` to check. Upgrade with `openclaw update`.
<Note>
Requires OpenClaw 2026.4.25 or above. Run `openclaw --version` to check. Upgrade with `openclaw update`.
</Note>
<Steps>
<Step title="Run the channel setup wizard">
@@ -70,6 +72,7 @@ Default: `allowlist`
- `true` — require @mention (default)
- `false` — respond without @mention
- Per-group override: `channels.feishu.groups.<chat_id>.requireMention`
- Broadcast-only `@all` and `@_all` are not treated as bot mentions. A message that mentions both `@all` and the bot directly still counts as a bot mention.
---
@@ -169,7 +172,9 @@ openclaw pairing list feishu
| `/reset` | Reset the current session |
| `/model` | Show or switch the AI model |
> Feishu/Lark does not support native slash-command menus, so send these as plain text messages.
<Note>
Feishu/Lark does not support native slash-command menus, so send these as plain text messages.
</Note>
---

View File

@@ -7,7 +7,9 @@ title: "Group messages"
Goal: let Clawd sit in WhatsApp groups, wake up only when pinged, and keep that thread separate from the personal DM session.
Note: `agents.list[].groupChat.mentionPatterns` is now used by Telegram/Discord/Slack/iMessage as well; this doc focuses on WhatsApp-specific behavior. For multi-agent setups, set `agents.list[].groupChat.mentionPatterns` per agent (or use `messages.groupChat.mentionPatterns` as a global fallback).
<Note>
`agents.list[].groupChat.mentionPatterns` is also used by Telegram, Discord, Slack, and iMessage. This doc focuses on WhatsApp-specific behavior. For multi-agent setups, set `agents.list[].groupChat.mentionPatterns` per agent, or use `messages.groupChat.mentionPatterns` as a global fallback.
</Note>
## Current implementation (2025-12-03)

View File

@@ -6,7 +6,7 @@ read_when:
title: "Matrix migration"
---
This page covers upgrades from the previous public `matrix` plugin to the current implementation.
Upgrade from the previous public `matrix` plugin to the current implementation.
For most users, the upgrade is in place:
@@ -361,9 +361,10 @@ openclaw matrix verify status
If the device is still unverified after that, finish verification from your Matrix client by comparing the SAS emoji or decimal codes and confirming that they match.
## Related pages
## Related
- [Matrix](/channels/matrix)
- [Doctor](/gateway/doctor)
- [Migrating](/install/migrating)
- [Plugins](/tools/plugin)
- [Matrix](/channels/matrix): channel setup and config.
- [Matrix push rules](/channels/matrix-push-rules): notification routing.
- [Doctor](/gateway/doctor): health check and automatic migration trigger.
- [Migration guide](/install/migrating): all migration paths (machine moves, cross-system imports).
- [Plugins](/tools/plugin): plugin install and registration.

View File

@@ -132,6 +132,8 @@ New user-defined `override` rules are inserted ahead of default suppress rules,
If you run Synapse behind a reverse proxy or workers, make sure `/_matrix/client/.../pushrules/` reaches Synapse correctly. Push delivery is handled by the main process or `synapse.app.pusher` / configured pusher workers — ensure those are healthy.
The rule uses the `event_property_is` push-rule condition (MSC3758, push rule v1.10), which was added to Synapse in 2023. Older Synapse releases accept the `PUT pushrules/...` call but silently never match the condition — upgrade Synapse if no notification arrives on a finalized preview edit.
</Accordion>
<Accordion title="Tuwunel">

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,9 @@ teams login
teams status # verify you're logged in and see your tenant info
```
> **Note:** The Teams CLI is currently in preview. Commands and flags may change between releases.
<Note>
The Teams CLI is currently in preview. Commands and flags may change between releases.
</Note>
**2. Start a tunnel** (Teams can't reach localhost)
@@ -55,7 +57,9 @@ devtunnel host my-openclaw-bot
# Your endpoint: https://<tunnel-id>.devtunnels.ms/api/messages
```
> **Note:** `--allow-anonymous` is required because Teams can't authenticate with devtunnels. Each incoming bot request is still validated by the Teams SDK automatically.
<Note>
`--allow-anonymous` is required because Teams cannot authenticate with devtunnels. Each incoming bot request is still validated by the Teams SDK automatically.
</Note>
Alternatives: `ngrok http 3978` or `tailscale funnel 3978` (but these may change URLs each session).
@@ -110,9 +114,11 @@ teams app doctor <teamsAppId>
This runs diagnostics across bot registration, AAD app config, manifest validity, and SSO setup.
For production deployments, consider using [federated authentication](#federated-authentication-certificate--managed-identity) (certificate or managed identity) instead of client secrets.
For production deployments, consider using [federated authentication](/channels/msteams#federated-authentication-certificate-plus-managed-identity) (certificate or managed identity) instead of client secrets.
Note: group chats are blocked by default (`channels.msteams.groupPolicy: "allowlist"`). To allow group replies, set `channels.msteams.groupAllowFrom` (or use `groupPolicy: "open"` to allow any member, mention-gated).
<Note>
Group chats are blocked by default (`channels.msteams.groupPolicy: "allowlist"`). To allow group replies, set `channels.msteams.groupAllowFrom`, or use `groupPolicy: "open"` to allow any member (mention-gated).
</Note>
## Goals
@@ -217,7 +223,9 @@ If you can't use the Teams CLI, you can set up the bot manually through the Azur
| **Type of App** | **Single Tenant** (recommended - see note below) |
| **Creation type** | **Create new Microsoft App ID** |
> **Deprecation notice:** Creation of new multi-tenant bots was deprecated after 2025-07-31. Use **Single Tenant** for new bots.
<Warning>
Creation of new multi-tenant bots was deprecated after 2025-07-31. Use **Single Tenant** for new bots.
</Warning>
3. Click **Review + create****Create** (wait ~1-2 minutes)
@@ -275,7 +283,7 @@ The Teams channel starts automatically when the plugin is available and `msteams
</details>
## Federated Authentication (Certificate + Managed Identity)
## Federated authentication (certificate plus managed identity)
> Added in 2026.3.24
@@ -417,7 +425,7 @@ For AKS deployments using workload identity:
**Default behavior:** When `authType` is not set, OpenClaw defaults to client secret authentication. Existing configurations continue to work without changes.
## Local Development (Tunneling)
## Local development (tunneling)
Teams can't reach `localhost`. Use a persistent dev tunnel so your URL stays the same across sessions:
@@ -487,7 +495,7 @@ The action is gated by `channels.msteams.actions.memberInfo` (default: enabled w
- In other words, allowlists gate who can trigger the agent; only specific supplemental context paths are filtered today.
- DM history can be limited with `channels.msteams.dmHistoryLimit` (user turns). Per-user overrides: `channels.msteams.dms["<user_id>"].historyLimit`.
## Current Teams RSC Permissions (Manifest)
## Current Teams RSC permissions (manifest)
These are the **existing resourceSpecific permissions** in our Teams app manifest. They only apply inside the team/chat where the app is installed.
@@ -511,7 +519,7 @@ To add RSC permissions via the Teams CLI:
teams app rsc add <teamsAppId> ChannelMessage.Read.Group --type Application
```
## Example Teams Manifest (redacted)
## Example Teams manifest (redacted)
Minimal, valid example with the required fields. Replace IDs and URLs.
@@ -643,7 +651,7 @@ If you need images/files in **channels** or want to fetch **message history**, y
**Additional permission for user mentions:** User @mentions work out of the box for users in the conversation. However, if you want to dynamically search and mention users who are **not in the current conversation**, add `User.Read.All` (Application) permission and grant admin consent.
## Known Limitations
## Known limitations
### Webhook timeouts
@@ -706,7 +714,7 @@ Key settings (see `/gateway/configuration` for shared channel patterns):
- `agent:<agentId>:msteams:channel:<conversationId>`
- `agent:<agentId>:msteams:group:<conversationId>`
## Reply Style: Threads vs Posts
## Reply style: threads vs posts
Teams recently introduced two channel UI styles over the same underlying data model:
@@ -833,7 +841,7 @@ OpenClaw sends Teams polls as Adaptive Cards (there is no native Teams poll API)
- The gateway must stay online to record votes.
- Polls do not auto-post result summaries yet (inspect the store file if needed).
## Presentation Cards
## Presentation cards
Send semantic presentation payloads to Teams users or conversations using the `message` tool or CLI. OpenClaw renders them as Teams Adaptive Cards from the generic presentation contract.
@@ -914,7 +922,9 @@ openclaw message send --channel msteams --target "conversation:19:abc...@thread.
}
```
Note: Without the `user:` prefix, names default to group/team resolution. Always use `user:` when targeting people by display name.
<Note>
Without the `user:` prefix, names default to group or team resolution. Always use `user:` when targeting people by display name.
</Note>
## Proactive messaging
@@ -947,7 +957,7 @@ https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?gr
- Channel ID = path segment after `/channel/` (URL-decoded)
- **Ignore** the `groupId` query parameter
## Private Channels
## Private channels
Bots have limited support in private channels:

View File

@@ -52,8 +52,9 @@ Account scoping behavior:
Treat these as sensitive (they gate access to your assistant).
Important: this store is for DM access. Group authorization is separate.
Approving a DM pairing code does not automatically allow that sender to run group commands or control the bot in groups. For group access, configure the channel's explicit group allowlists (for example `groupAllowFrom`, `groups`, or per-group/per-topic overrides depending on the channel).
<Note>
This store is for DM access. Group authorization is separate. Approving a DM pairing code does not automatically allow that sender to run group commands or control the bot in groups. For group access, configure the channel's explicit group allowlists (for example `groupAllowFrom`, `groups`, or per-group or per-topic overrides depending on the channel).
</Note>
## 2) Node device pairing (iOS/Android/macOS/headless nodes)
@@ -100,11 +101,9 @@ If the same device retries with different auth details (for example different
role/scopes/public key), the previous pending request is superseded and a new
`requestId` is created.
Important: an already paired device does not get broader access silently. If it
reconnects asking for more scopes or a broader role, OpenClaw keeps the
existing approval as-is and creates a fresh pending upgrade request. Use
`openclaw devices list` to compare the currently approved access with the newly
requested access before you approve.
<Note>
An already paired device does not get broader access silently. If it reconnects asking for more scopes or a broader role, OpenClaw keeps the existing approval as-is and creates a fresh pending upgrade request. Use `openclaw devices list` to compare the currently approved access with the newly requested access before you approve.
</Note>
### Optional trusted-CIDR node auto-approve
@@ -137,7 +136,7 @@ Stored under `~/.openclaw/devices/`:
### Notes
- The legacy `node.pair.*` API (CLI: `openclaw nodes pending|approve|reject|rename`) is a
- The legacy `node.pair.*` API (CLI: `openclaw nodes pending|approve|reject|remove|rename`) is a
separate gateway-owned pairing store. WS nodes still require device pairing.
- The pairing record is the durable source of truth for approved roles. Active
device tokens stay bounded to that approved role set; a stray token entry

View File

@@ -7,27 +7,16 @@ read_when:
- You are iterating on end-to-end QA automation
---
`qa-channel` is a bundled synthetic message transport for automated OpenClaw QA.
`qa-channel` is a bundled synthetic message transport for automated OpenClaw QA. It is not a production channel — it exists to exercise the same channel plugin boundary used by real transports while keeping state deterministic and fully inspectable.
It is not a production channel. It exists to exercise the same channel plugin
boundary used by real transports while keeping state deterministic and fully
inspectable.
## What it does today
## What it does
- Slack-class target grammar:
- `dm:<user>`
- `channel:<room>`
- `thread:<room>/<thread>`
- HTTP-backed synthetic bus for:
- inbound message injection
- outbound transcript capture
- thread creation
- reactions
- edits
- deletes
- search and read actions
- Bundled host-side self-check runner that writes a Markdown report
- HTTP-backed synthetic bus for inbound message injection, outbound transcript capture, thread creation, reactions, edits, deletes, and search/read actions.
- Host-side self-check runner that writes a Markdown report to `.artifacts/qa-e2e/`.
## Config
@@ -45,68 +34,53 @@ inspectable.
}
```
Supported account keys:
Account keys:
- `baseUrl`
- `botUserId`
- `botDisplayName`
- `pollTimeoutMs`
- `allowFrom`
- `defaultTo`
- `actions.messages`
- `actions.reactions`
- `actions.search`
- `actions.threads`
- `enabled` — master toggle for this account.
- `name` — optional display label.
- `baseUrl` — synthetic bus URL.
- `botUserId` — Matrix-style bot user id used in target grammar.
- `botDisplayName` — display name for outbound messages.
- `pollTimeoutMs` — long-poll wait window. Integer between 100 and 30000.
- `allowFrom` — sender allowlist (user ids or `"*"`).
- `defaultTo` — fallback target when none is supplied.
- `actions.messages` / `actions.reactions` / `actions.search` / `actions.threads` — per-action tool gating.
## Runner
Multi-account keys at the top level:
Current vertical slice:
- `accounts` — record of named per-account overrides keyed by account id.
- `defaultAccount` — preferred account id when multiple are configured.
## Runners
Host-side self-check (writes a Markdown report under `.artifacts/qa-e2e/`):
```bash
pnpm qa:e2e
```
This now routes through the bundled `qa-lab` extension. It starts the in-repo
QA bus, boots the bundled `qa-channel` runtime slice, runs a deterministic
self-check, and writes a Markdown report under `.artifacts/qa-e2e/`.
This routes through `qa-lab`, starts the in-repo QA bus, boots the bundled `qa-channel` runtime slice, and runs a deterministic self-check.
Private debugger UI:
```bash
pnpm qa:lab:up
```
That one command builds the QA site, starts the Docker-backed gateway + QA Lab
stack, and prints the QA Lab URL. From that site you can pick scenarios, choose
the model lane, launch individual runs, and watch results live.
Full repo-backed QA suite:
Full repo-backed scenario suite:
```bash
pnpm openclaw qa suite
```
That launches the private QA debugger at a local URL, separate from the
shipped Control UI bundle.
Runs scenarios in parallel against the QA gateway lane. See [QA overview](/concepts/qa-e2e-automation) for scenarios, profiles, and provider modes.
## Scope
Docker-backed QA site (gateway + QA Lab debugger UI in one stack):
Current scope is intentionally narrow:
```bash
pnpm qa:lab:up
```
- bus + plugin transport
- threaded routing grammar
- channel-owned message actions
- Markdown reporting
- Docker-backed QA site with run controls
Follow-up work will add:
- provider/model matrix execution
- richer scenario discovery
- OpenClaw-native orchestration later
Builds the QA site, starts the Docker-backed gateway + QA Lab stack, and prints the QA Lab URL. From there you can pick scenarios, choose the model lane, launch individual runs, and watch results live. The QA Lab debugger is separate from the shipped Control UI bundle.
## Related
- [QA overview](/concepts/qa-e2e-automation) — overall stack, transport adapters, scenario authoring
- [Matrix QA](/concepts/qa-matrix) — example live-transport runner that drives a real channel
- [Pairing](/channels/pairing)
- [Groups](/channels/groups)
- [Channels overview](/channels)

View File

@@ -152,7 +152,9 @@ openclaw channels status --probe
- Approve code on the server: `openclaw pairing approve signal <PAIRING_CODE>`.
- Save the bot number as a contact on your phone to avoid "Unknown contact".
Important: registering a phone number account with `signal-cli` can de-authenticate the main Signal app session for that number. Prefer a dedicated bot number, or use QR link mode if you need to keep your existing phone app setup.
<Warning>
Registering a phone number account with `signal-cli` can de-authenticate the main Signal app session for that number. Prefer a dedicated bot number, or use QR link mode if you need to keep your existing phone app setup.
</Warning>
Upstream references:

View File

@@ -530,7 +530,9 @@ Manual reply tags are supported:
- `[[reply_to_current]]`
- `[[reply_to:<id>]]`
Note: `replyToMode="off"` disables **all** reply threading in Slack, including explicit `[[reply_to_*]]` tags. This differs from Telegram, where explicit tags are still honored in `"off"` mode — Slack threads hide messages from the channel while Telegram replies stay visible inline.
<Note>
`replyToMode="off"` disables **all** reply threading in Slack, including explicit `[[reply_to_*]]` tags. This differs from Telegram, where explicit tags are still honored in `"off"` mode. Slack threads hide messages from the channel while Telegram replies stay visible inline.
</Note>
## Ack reactions

View File

@@ -31,11 +31,12 @@ Healthy baseline:
### WhatsApp failure signatures
| Symptom | Fastest check | Fix |
| ------------------------------- | --------------------------------------------------- | ------------------------------------------------------- |
| Connected but no DM replies | `openclaw pairing list whatsapp` | Approve sender or switch DM policy/allowlist. |
| Group messages ignored | Check `requireMention` + mention patterns in config | Mention the bot or relax mention policy for that group. |
| Random disconnect/relogin loops | `openclaw channels status --probe` + logs | Re-login and verify credentials directory is healthy. |
| Symptom | Fastest check | Fix |
| ------------------------------- | --------------------------------------------------- | -------------------------------------------------------- |
| Connected but no DM replies | `openclaw pairing list whatsapp` | Approve sender or switch DM policy/allowlist. |
| Group messages ignored | Check `requireMention` + mention patterns in config | Mention the bot or relax mention policy for that group. |
| QR login times out with 408 | Check gateway `HTTPS_PROXY` / `HTTP_PROXY` env | Set a reachable proxy; use `NO_PROXY` only for bypasses. |
| Random disconnect/relogin loops | `openclaw channels status --probe` + logs | Re-login and verify credentials directory is healthy. |
Full troubleshooting: [WhatsApp troubleshooting](/channels/whatsapp#troubleshooting)

View File

@@ -526,6 +526,13 @@ Behavior notes:
</Accordion>
<Accordion title="QR login times out behind a proxy">
Symptom: `openclaw channels login --channel whatsapp` fails before showing a usable QR code with `status=408 Request Time-out` or a TLS socket disconnect.
WhatsApp Web login uses the gateway host's standard proxy environment (`HTTPS_PROXY`, `HTTP_PROXY`, lowercase variants, and `NO_PROXY`). Verify the gateway process inherits the proxy env and that `NO_PROXY` does not match `mmg.whatsapp.net`.
</Accordion>
<Accordion title="No active listener when sending">
Outbound sends fail fast when no active gateway listener exists for the target account.
@@ -567,7 +574,9 @@ The effective `direct` map is determined first: if the account defines its own `
1. **Direct-specific system prompt** (`direct["<peerId>"].systemPrompt`): used when the specific peer entry exists in the map **and** its `systemPrompt` key is defined. If `systemPrompt` is an empty string (`""`), the wildcard is suppressed and no system prompt is applied.
2. **Direct wildcard system prompt** (`direct["*"].systemPrompt`): used when the specific peer entry is absent from the map entirely, or when it exists but defines no `systemPrompt` key.
Note: `dms` remains the lightweight per-DM history override bucket (`dms.<id>.historyLimit`); prompt overrides live under `direct`.
<Note>
`dms` remains the lightweight per-DM history override bucket (`dms.<id>.historyLimit`). Prompt overrides live under `direct`.
</Note>
**Difference from Telegram multi-account behavior:** In Telegram, root `groups` is intentionally suppressed for all accounts in a multi-account setup — even accounts that define no `groups` of their own — to prevent a bot from receiving group messages for groups it does not belong to. WhatsApp does not apply this guard: root `groups` and root `direct` are always inherited by accounts that define no account-level override, regardless of how many accounts are configured. In a multi-account WhatsApp setup, if you want per-account group or direct prompts, define the full map under each account explicitly rather than relying on root-level defaults.

416
docs/channels/yuanbao.md Normal file
View File

@@ -0,0 +1,416 @@
---
summary: "YuanBao bot overview, features, and configuration"
read_when:
- You want to connect a YuanBao bot
- You are configuring the YuanBao channel
title: YuanBao
---
# YuanBao
YuanBao is Tencent's AI assistant platform that supports bot integration via instant messaging. Bots can interact with users through direct messages and group chats.
**Status:** production-ready for bot DMs + group chats. WebSocket is the only supported connection mode.
---
## Quick start
> **Requires OpenClaw 2026.4.10 or above.** Run `openclaw --version` to check. Upgrade with `openclaw update`.
<Steps>
<Step title="Add the YuanBao channel with your credentials">
```bash
openclaw channels add --channel yuanbao --token "appKey:appSecret"
```
The `--token` value uses colon-separated `appKey:appSecret` format. You can obtain these from the YuanBao APP by creating a robot in your application settings.
</Step>
<Step title="After setup completes, restart the gateway to apply the changes">
```bash
openclaw gateway restart
```
</Step>
</Steps>
### Interactive setup (alternative)
You can also use the interactive wizard:
```bash
openclaw channels login --channel yuanbao
```
Follow the prompts to enter your App ID and App Secret.
---
## Access control
### Direct messages
Configure `dmPolicy` to control who can DM the bot:
- `"pairing"` — unknown users receive a pairing code; approve via CLI
- `"allowlist"` — only users listed in `allowFrom` can chat
- `"open"` — allow all users (default)
- `"disabled"` — disable all DMs
**Approve a pairing request:**
```bash
openclaw pairing list yuanbao
openclaw pairing approve yuanbao <CODE>
```
### Group chats
**Mention requirement** (`channels.yuanbao.requireMention`):
- `true` — require @mention (default)
- `false` — respond without @mention
Replying to the bot's message in a group chat is treated as an implicit mention.
---
## Configuration examples
### Basic setup with open DM policy
```json5
{
channels: {
yuanbao: {
appKey: "your_app_key",
appSecret: "your_app_secret",
dm: {
policy: "open",
},
},
},
}
```
### Restrict DMs to specific users
```json5
{
channels: {
yuanbao: {
appKey: "your_app_key",
appSecret: "your_app_secret",
dm: {
policy: "allowlist",
allowFrom: ["user_id_1", "user_id_2"],
},
},
},
}
```
### Disable @mention requirement in groups
```json5
{
channels: {
yuanbao: {
requireMention: false,
},
},
}
```
### Optimize outbound message delivery
```json5
{
channels: {
yuanbao: {
// Send each chunk immediately without buffering
outboundQueueStrategy: "immediate",
},
},
}
```
### Tune merge-text strategy
```json5
{
channels: {
yuanbao: {
outboundQueueStrategy: "merge-text",
minChars: 2800, // buffer until this many chars
maxChars: 3000, // force split above this limit
idleMs: 5000, // auto-flush after idle timeout (ms)
},
},
}
```
---
## Common commands
| Command | Description |
| ---------- | --------------------------- |
| `/help` | Show available commands |
| `/status` | Show bot status |
| `/new` | Start a new session |
| `/stop` | Stop the current run |
| `/restart` | Restart OpenClaw |
| `/compact` | Compact the session context |
> YuanBao supports native slash-command menus. Commands are synced to the platform automatically when the gateway starts.
---
## Troubleshooting
### Bot does not respond in group chats
1. Ensure the bot is added to the group
2. Ensure you @mention the bot (required by default)
3. Check logs: `openclaw logs --follow`
### Bot does not receive messages
1. Ensure the bot is created and approved in YuanBao APP
2. Ensure `appKey` and `appSecret` are correctly configured
3. Ensure the gateway is running: `openclaw gateway status`
4. Check logs: `openclaw logs --follow`
### Bot sends empty or fallback replies
1. Check if the AI model is returning valid content
2. The default fallback reply is: "暂时无法解答,你可以换个问题问问我哦"
3. Customize it via `channels.yuanbao.fallbackReply`
### App Secret leaked
1. Reset the App Secret in YuanBao APP
2. Update the value in your config
3. Restart the gateway: `openclaw gateway restart`
---
## Advanced configuration
### Multiple accounts
```json5
{
channels: {
yuanbao: {
defaultAccount: "main",
accounts: {
main: {
appKey: "key_xxx",
appSecret: "secret_xxx",
name: "Primary bot",
},
backup: {
appKey: "key_yyy",
appSecret: "secret_yyy",
name: "Backup bot",
enabled: false,
},
},
},
},
}
```
`defaultAccount` controls which account is used when outbound APIs do not specify an `accountId`.
### Message limits
- `maxChars` — single message max character count (default: `3000` chars)
- `mediaMaxMb` — media upload/download limit (default: `20` MB)
- `overflowPolicy` — behavior when message exceeds limit: `"split"` (default) or `"stop"`
### Streaming
YuanBao supports block-level streaming output. When enabled, the bot sends text in chunks as it generates.
```json5
{
channels: {
yuanbao: {
disableBlockStreaming: false, // block streaming enabled (default)
},
},
}
```
Set `disableBlockStreaming: true` to send the complete reply in one message.
### Group chat history context
Control how many historical messages are included in the AI context for group chats:
```json5
{
channels: {
yuanbao: {
historyLimit: 100, // default: 100, set 0 to disable
},
},
}
```
### Reply-to mode
Control how the bot quotes messages when replying in group chats:
```json5
{
channels: {
yuanbao: {
replyToMode: "first", // "off" | "first" | "all" (default: "first")
},
},
}
```
| Value | Behavior |
| --------- | -------------------------------------------------------- |
| `"off"` | No quote reply |
| `"first"` | Quote only the first reply per inbound message (default) |
| `"all"` | Quote every reply |
### Markdown hint injection
By default, the bot injects instructions in the system prompt to prevent the AI model from wrapping the entire reply in markdown code blocks.
```json5
{
channels: {
yuanbao: {
markdownHintEnabled: true, // default: true
},
},
}
```
### Debug mode
Enable unsanitized log output for specific bot IDs:
```json5
{
channels: {
yuanbao: {
debugBotIds: ["bot_user_id_1", "bot_user_id_2"],
},
},
}
```
### Multi-agent routing
Use `bindings` to route YuanBao DMs or groups to different agents.
```json5
{
agents: {
list: [
{ id: "main" },
{ id: "agent-a", workspace: "/home/user/agent-a" },
{ id: "agent-b", workspace: "/home/user/agent-b" },
],
},
bindings: [
{
agentId: "agent-a",
match: {
channel: "yuanbao",
peer: { kind: "direct", id: "user_xxx" },
},
},
{
agentId: "agent-b",
match: {
channel: "yuanbao",
peer: { kind: "group", id: "group_zzz" },
},
},
],
}
```
Routing fields:
- `match.channel`: `"yuanbao"`
- `match.peer.kind`: `"direct"` (DM) or `"group"` (group chat)
- `match.peer.id`: user ID or group code
---
## Configuration reference
Full configuration: [Gateway configuration](/gateway/configuration)
| Setting | Description | Default |
| ------------------------------------------ | ------------------------------------------------- | -------------------------------------- |
| `channels.yuanbao.enabled` | Enable/disable the channel | `true` |
| `channels.yuanbao.defaultAccount` | Default account for outbound routing | `default` |
| `channels.yuanbao.accounts.<id>.appKey` | App Key (used for signing and ticket generation) | — |
| `channels.yuanbao.accounts.<id>.appSecret` | App Secret (used for signing) | — |
| `channels.yuanbao.accounts.<id>.token` | Pre-signed token (skips automatic ticket signing) | — |
| `channels.yuanbao.accounts.<id>.name` | Account display name | — |
| `channels.yuanbao.accounts.<id>.enabled` | Enable/disable a specific account | `true` |
| `channels.yuanbao.dm.policy` | DM policy | `open` |
| `channels.yuanbao.dm.allowFrom` | DM allowlist (user ID list) | — |
| `channels.yuanbao.requireMention` | Require @mention in groups | `true` |
| `channels.yuanbao.overflowPolicy` | Long message handling (`split` or `stop`) | `split` |
| `channels.yuanbao.replyToMode` | Group reply-to strategy (`off`, `first`, `all`) | `first` |
| `channels.yuanbao.outboundQueueStrategy` | Outbound strategy (`merge-text` or `immediate`) | `merge-text` |
| `channels.yuanbao.minChars` | Merge-text: min chars to trigger send | `2800` |
| `channels.yuanbao.maxChars` | Merge-text: max chars per message | `3000` |
| `channels.yuanbao.idleMs` | Merge-text: idle timeout before auto-flush (ms) | `5000` |
| `channels.yuanbao.mediaMaxMb` | Media size limit (MB) | `20` |
| `channels.yuanbao.historyLimit` | Group chat history context entries | `100` |
| `channels.yuanbao.disableBlockStreaming` | Disable block-level streaming output | `false` |
| `channels.yuanbao.fallbackReply` | Fallback reply when AI returns no content | `暂时无法解答,你可以换个问题问问我哦` |
| `channels.yuanbao.markdownHintEnabled` | Inject markdown anti-wrapping instructions | `true` |
| `channels.yuanbao.debugBotIds` | Debug whitelist bot IDs (unsanitized logs) | `[]` |
---
## Supported message types
### Receive
- ✅ Text
- ✅ Images
- ✅ Files
- ✅ Audio / Voice
- ✅ Video
- ✅ Stickers / Custom emoji
- ✅ Custom elements (link cards, etc.)
### Send
- ✅ Text (with markdown support)
- ✅ Images
- ✅ Files
- ✅ Audio
- ✅ Video
- ✅ Stickers
### Threads and replies
- ✅ Quote replies (configurable via `replyToMode`)
- ❌ Thread replies (not supported by platform)
---
## Related
- [Channels Overview](/channels) — all supported channels
- [Pairing](/channels/pairing) — DM authentication and pairing flow
- [Groups](/channels/groups) — group chat behavior and mention gating
- [Channel Routing](/channels/channel-routing) — session routing for messages
- [Security](/gateway/security) — access model and hardening

View File

@@ -8,7 +8,9 @@ title: "Zalo personal"
Status: experimental. This integration automates a **personal Zalo account** via native `zca-js` inside OpenClaw.
> **Warning:** This is an unofficial integration and may result in account suspension/ban. Use at your own risk.
<Warning>
This is an unofficial integration and may result in account suspension or ban. Use at your own risk.
</Warning>
## Bundled plugin

File diff suppressed because one or more lines are too long

View File

@@ -26,6 +26,7 @@ Related:
- `-t, --to <dest>`: recipient used to derive the session key
- `--session-id <id>`: explicit session id
- `--agent <id>`: agent id; overrides routing bindings
- `--model <id>`: model override for this run (`provider/model` or model id)
- `--thinking <level>`: agent thinking level (`off`, `minimal`, `low`, `medium`, `high`, plus provider-supported custom levels such as `xhigh`, `adaptive`, or `max`)
- `--verbose <on|off>`: persist verbose level for the session
- `--channel <channel>`: delivery channel; omit to use the main session channel
@@ -42,6 +43,7 @@ Related:
```bash
openclaw agent --to +15555550123 --message "status update" --deliver
openclaw agent --agent ops --message "Summarize logs"
openclaw agent --agent ops --model openai/gpt-5.4 --message "Summarize logs"
openclaw agent --session-id 1234 --message "Summarize inbox" --thinking medium
openclaw agent --to +15555550123 --message "Trace logs" --verbose on --json
openclaw agent --agent ops --message "Generate report" --deliver --reply-channel slack --reply-to "#reports"
@@ -55,6 +57,7 @@ openclaw agent --agent ops --message "Run locally" --local
- Each `openclaw agent` invocation is treated as a one-shot run. Bundled or user-configured MCP servers opened for that run are retired after the reply, even when the command uses the Gateway path, so stdio MCP child processes do not stay alive between scripted invocations.
- `--channel`, `--reply-channel`, and `--reply-account` affect reply delivery, not session routing.
- `--json` keeps stdout reserved for the JSON response. Gateway, plugin, and embedded-fallback diagnostics are routed to stderr so scripts can parse stdout directly.
- Embedded fallback JSON includes `meta.transport: "embedded"` and `meta.fallbackFrom: "gateway"` so scripts can distinguish fallback runs from Gateway runs.
- When this command triggers `models.json` regeneration, SecretRef-managed provider credentials are persisted as non-secret markers (for example env var names, `secretref-env:ENV_VAR_NAME`, or `secretref-managed`), not resolved secret plaintext.
- Marker writes are source-authoritative: OpenClaw persists markers from the active source config snapshot, not from resolved runtime secret values.

View File

@@ -11,9 +11,9 @@ Manage isolated agents (workspaces + auth + routing).
Related:
- Multi-agent routing: [Multi-Agent Routing](/concepts/multi-agent)
- Agent workspace: [Agent workspace](/concepts/agent-workspace)
- Skill visibility config: [Skills config](/tools/skills-config)
- [Multi-agent routing](/concepts/multi-agent)
- [Agent workspace](/concepts/agent-workspace)
- [Skills config](/tools/skills-config): skill visibility configuration.
## Examples
@@ -34,10 +34,7 @@ openclaw agents delete work
Use routing bindings to pin inbound channel traffic to a specific agent.
If you also want different visible skills per agent, configure
`agents.defaults.skills` and `agents.list[].skills` in `openclaw.json`. See
[Skills config](/tools/skills-config) and
[Configuration Reference](/gateway/config-agents#agents-defaults-skills).
If you also want different visible skills per agent, configure `agents.defaults.skills` and `agents.list[].skills` in `openclaw.json`. See [Skills config](/tools/skills-config) and [Configuration reference](/gateway/config-agents#agents-defaults-skills).
List bindings:

View File

@@ -85,8 +85,8 @@ Notes:
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:
When `plugins.allow` is present, list the bundled browser plugin explicitly
unless the config already has a root `browser` block:
```json5
{
@@ -96,8 +96,9 @@ explicitly:
}
```
`browser.enabled=true` does not restore the CLI subcommand when the plugin
allowlist excludes `browser`.
An explicit root `browser` block, for example `browser.enabled=true` or
`browser.profiles.<name>`, also activates the bundled browser plugin under a
restrictive plugin allowlist.
Related: [Browser tool](/tools/browser#missing-browser-command-or-tool)

View File

@@ -12,7 +12,7 @@ Manage chat channel accounts and their runtime status on the Gateway.
Related docs:
- Channel guides: [Channels](/channels/index)
- Channel guides: [Channels](/channels)
- Gateway configuration: [Configuration](/gateway/configuration)
## Common commands
@@ -47,7 +47,9 @@ openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY"
openclaw channels remove --channel telegram --delete
```
Tip: `openclaw channels add --help` shows per-channel flags (token, private key, app token, signal-cli paths, etc).
<Tip>
`openclaw channels add --help` shows per-channel flags (token, private key, app token, signal-cli paths, etc).
</Tip>
Common non-interactive add surfaces include:
@@ -81,17 +83,15 @@ Routing behavior stays consistent:
If your config was already in a mixed state (named accounts present and top-level single-account values still set), run `openclaw doctor --fix` to move account-scoped values into the promoted account chosen for that channel. Most channels promote into `accounts.default`; Matrix can preserve an existing named/default target instead.
## Login / logout (interactive)
## Login and logout (interactive)
```bash
openclaw channels login --channel whatsapp
openclaw channels logout --channel whatsapp
```
Notes:
- `channels login` supports `--verbose`.
- `channels login` / `logout` can infer the channel when only one supported login target is configured.
- `channels login` and `logout` can infer the channel when only one supported login target is configured.
## Troubleshooting

View File

@@ -9,23 +9,15 @@ title: "Configure"
Interactive prompt to set up credentials, devices, and agent defaults.
Note: The **Model** section now includes a multi-select for the
`agents.defaults.models` allowlist (what shows up in `/model` and the model picker).
Provider-scoped setup choices merge their selected models into the existing
allowlist instead of replacing unrelated providers already in the config.
Re-running provider auth from configure preserves an existing
`agents.defaults.model.primary`; use `openclaw models auth login --provider <id> --set-default`
or `openclaw models set <model>` when you intentionally want to change the default model.
<Note>
The **Model** section includes a multi-select for the `agents.defaults.models` allowlist (what shows up in `/model` and the model picker). Provider-scoped setup choices merge their selected models into the existing allowlist instead of replacing unrelated providers already in the config. Re-running provider auth from configure preserves an existing `agents.defaults.model.primary`. Use `openclaw models auth login --provider <id> --set-default` or `openclaw models set <model>` when you intentionally want to change the default model.
</Note>
When configure starts from a provider auth choice, the default-model and
allowlist pickers prefer that provider automatically. For paired providers such
as Volcengine/BytePlus, the same preference also matches their coding-plan
variants (`volcengine-plan/*`, `byteplus-plan/*`). If the preferred-provider
filter would produce an empty list, configure falls back to the unfiltered
catalog instead of showing a blank picker.
When configure starts from a provider auth choice, the default-model and allowlist pickers prefer that provider automatically. For paired providers such as Volcengine and BytePlus, the same preference also matches their coding-plan variants (`volcengine-plan/*`, `byteplus-plan/*`). If the preferred-provider filter would produce an empty list, configure falls back to the unfiltered catalog instead of showing a blank picker.
Tip: `openclaw config` without a subcommand opens the same wizard. Use
`openclaw config get|set|unset` for non-interactive edits.
<Tip>
`openclaw config` without a subcommand opens the same wizard. Use `openclaw config get|set|unset` for non-interactive edits.
</Tip>
For web search, `openclaw configure --section web` lets you choose a provider
and configure its credentials. Some providers also show provider-specific

View File

@@ -129,7 +129,7 @@ Discovery is not audited. Only applied operations and writes are logged.
`openclaw onboard --modern` starts Crestodian as the modern onboarding preview.
Plain `openclaw onboard` still runs classic onboarding.
## Setup Bootstrap
## Setup bootstrap
`setup` is the chat-first onboarding bootstrap. It writes only through typed
config operations and asks for approval first.

View File

@@ -2,7 +2,7 @@
summary: "CLI reference for `openclaw cron` (schedule and run background jobs)"
read_when:
- You want scheduled jobs and wakeups
- Youre debugging cron execution and logs
- You are debugging cron execution and logs
title: "Cron"
---
@@ -10,86 +10,142 @@ title: "Cron"
Manage cron jobs for the Gateway scheduler.
Related:
<Tip>
Run `openclaw cron --help` for the full command surface. See [Cron jobs](/automation/cron-jobs) for the conceptual guide.
</Tip>
- Cron jobs: [Cron jobs](/automation/cron-jobs)
## Sessions
Tip: run `openclaw cron --help` for the full command surface.
`--session` accepts `main`, `isolated`, `current`, or `session:<id>`.
Note: `openclaw cron list` and `openclaw cron show <job-id>` preview the
resolved delivery route. For `channel: "last"`, the preview shows whether the
route resolved from the main/current session or will fail closed.
<AccordionGroup>
<Accordion title="Session keys">
- `main` binds to the agent's main session.
- `isolated` creates a fresh transcript and session id for each run.
- `current` binds to the active session at creation time.
- `session:<id>` pins to an explicit persistent session key.
</Accordion>
<Accordion title="Isolated session semantics">
Isolated runs reset ambient conversation context. Channel and group routing, send/queue policy, elevation, origin, and ACP runtime binding are reset for the new run. Safe preferences and explicit user-selected model or auth overrides can carry across runs.
</Accordion>
</AccordionGroup>
Note: isolated `cron add` jobs default to `--announce` delivery. Use `--no-deliver` to keep
output internal. `--deliver` remains as a deprecated alias for `--announce`.
## Delivery
Note: isolated cron chat delivery is shared. `--announce` is runner fallback
delivery for the final reply; `--no-deliver` disables that fallback but does
not remove the agent's `message` tool when a chat route is available.
`openclaw cron list` and `openclaw cron show <job-id>` preview the resolved delivery route. For `channel: "last"`, the preview shows whether the route resolved from the main or current session, or will fail closed.
Note: one-shot (`--at`) jobs delete after success by default. Use `--keep-after-run` to keep them.
<Note>
Isolated `cron add` jobs default to `--announce` delivery. Use `--no-deliver` to keep output internal. `--deliver` remains as a deprecated alias for `--announce`.
</Note>
Note: `--session` supports `main`, `isolated`, `current`, and `session:<id>`.
Use `current` to bind to the active session at creation time, or `session:<id>` for
an explicit persistent session key.
### Delivery ownership
Note: `--session isolated` creates a fresh transcript/session id for each run.
Safe preferences and explicit user-selected model/auth overrides can carry, but
ambient conversation context does not: channel/group routing, send/queue policy,
elevation, origin, and ACP runtime binding are reset for the new isolated run.
Isolated cron chat delivery is shared between the agent and the runner:
Note: for one-shot CLI jobs, offset-less `--at` datetimes are treated as UTC unless you also pass
`--tz <iana>`, which interprets that local wall-clock time in the given timezone.
- The agent can send directly using the `message` tool when a chat route is available.
- `announce` fallback-delivers the final reply only when the agent did not send directly to the resolved target.
- `webhook` posts the finished payload to a URL.
- `none` disables runner fallback delivery.
Note: recurring jobs now use exponential retry backoff after consecutive errors (30s → 1m → 5m → 15m → 60m), then return to normal schedule after the next successful run.
`--announce` is runner fallback delivery for the final reply. `--no-deliver` disables that fallback but does not remove the agent's `message` tool when a chat route is available.
Note: `openclaw cron run` now returns as soon as the manual run is queued for execution. Successful responses include `{ ok: true, enqueued: true, runId }`; use `openclaw cron runs --id <job-id>` to follow the eventual outcome.
Reminders created from an active chat preserve the live chat delivery target for fallback announce delivery. Internal session keys may be lowercase; do not use them as a source of truth for case-sensitive provider IDs such as Matrix room IDs.
Note: `openclaw cron run <job-id>` force-runs by default. Use `--due` to keep the
older "only run if due" behavior.
### Failure delivery
Note: isolated cron turns suppress stale acknowledgement-only replies. If the
first result is just an interim status update and no descendant subagent run is
responsible for the eventual answer, cron re-prompts once for the real result
before delivery.
Failure notifications resolve in this order:
Note: if an isolated cron run returns only the silent token (`NO_REPLY` /
`no_reply`), cron suppresses direct outbound delivery and the fallback queued
summary path as well, so nothing is posted back to chat.
1. `delivery.failureDestination` on the job.
2. Global `cron.failureDestination`.
3. The job's primary announce target (when no explicit failure destination is set).
Note: `cron add|edit --model ...` uses that selected allowed model for the job.
If the model is not allowed, cron warns and falls back to the job's agent/default
model selection instead. Configured fallback chains still apply, but a plain
model override with no explicit per-job fallback list no longer appends the
agent primary as a hidden extra retry target.
<Note>
Main-session jobs may only use `delivery.failureDestination` when primary delivery mode is `webhook`. Isolated jobs accept it in all modes.
</Note>
Note: isolated cron model precedence is Gmail-hook override first, then per-job
`--model`, then any user-selected stored cron-session model override, then the
normal agent/default selection.
Note: isolated cron runs treat run-level agent failures as job errors even when
no reply payload is produced, so model/provider failures still increment error
counters and trigger failure notifications.
Note: isolated cron fast mode follows the resolved live model selection. Model
config `params.fastMode` applies by default, but a stored session `fastMode`
override still wins over config.
## Scheduling
Note: if an isolated run throws `LiveSessionModelSwitchError`, cron persists the
switched provider/model (and switched auth profile override when present) for
the active run before retrying. The outer retry loop is bounded to 2 switch
retries after the initial attempt, then aborts instead of looping forever.
### One-shot jobs
Note: failure notifications use `delivery.failureDestination` first, then
global `cron.failureDestination`, and finally fall back to the job's primary
announce target when no explicit failure destination is configured.
`--at <datetime>` schedules a one-shot run. Offset-less datetimes are treated as UTC unless you also pass `--tz <iana>`, which interprets the wall-clock time in the given timezone.
Note: retention/pruning is controlled in config:
<Note>
One-shot jobs delete after success by default. Use `--keep-after-run` to preserve them.
</Note>
### Recurring jobs
Recurring jobs use exponential retry backoff after consecutive errors: 30s, 1m, 5m, 15m, 60m. The schedule returns to normal after the next successful run.
Skipped runs are tracked separately from execution errors. They do not affect retry backoff, but `openclaw cron edit <job-id> --failure-alert-include-skipped` can opt failure alerts into repeated skipped-run notifications.
Note: cron job definitions live in `jobs.json`, while pending runtime state lives in `jobs-state.json`. If `jobs.json` is edited externally, the Gateway reloads changed schedules and clears stale pending slots; formatting-only rewrites do not clear the pending slot.
### Manual runs
`openclaw cron run` returns as soon as the manual run is queued. Successful responses include `{ ok: true, enqueued: true, runId }`. Use `openclaw cron runs --id <job-id>` to follow the eventual outcome.
<Note>
`openclaw cron run <job-id>` force-runs by default. Use `--due` to keep the older "only run if due" behavior.
</Note>
## Models
`cron add|edit --model <ref>` selects an allowed model for the job.
<Warning>
If the model is not allowed, cron warns and falls back to the job's agent or default model selection. Configured fallback chains still apply, but a plain model override with no explicit per-job fallback list no longer appends the agent primary as a hidden extra retry target.
</Warning>
### Isolated cron model precedence
Isolated cron resolves the active model in this order:
1. Gmail-hook override.
2. Per-job `--model`.
3. Stored cron-session model override (when the user selected one).
4. Agent or default model selection.
### Fast mode
Isolated cron fast mode follows the resolved live model selection. Model config `params.fastMode` applies by default, but a stored session `fastMode` override still wins over config.
### Live model switch retries
If an isolated run throws `LiveSessionModelSwitchError`, cron persists the switched provider and model (and switched auth profile override when present) for the active run before retrying. The outer retry loop is bounded to two switch retries after the initial attempt, then aborts instead of looping forever.
## Run output and denials
### Stale acknowledgement suppression
Isolated cron turns suppress stale acknowledgement-only replies. If the first result is just an interim status update and no descendant subagent run is responsible for the eventual answer, cron re-prompts once for the real result before delivery.
### Silent token suppression
If an isolated cron run returns only the silent token (`NO_REPLY` or `no_reply`), cron suppresses both direct outbound delivery and the fallback queued summary path, so nothing is posted back to chat.
### Structured denials
Isolated cron runs prefer structured execution-denial metadata from the embedded run, then fall back to known denial markers in final output, such as `SYSTEM_RUN_DENIED`, `INVALID_REQUEST`, and approval-binding refusal phrases.
`cron list` and run history surface the denial reason instead of reporting a blocked command as `ok`.
## Retention
Retention and pruning are controlled in config:
- `cron.sessionRetention` (default `24h`) prunes completed isolated run sessions.
- `cron.runLog.maxBytes` + `cron.runLog.keepLines` prune `~/.openclaw/cron/runs/<jobId>.jsonl`.
- `cron.runLog.maxBytes` and `cron.runLog.keepLines` prune `~/.openclaw/cron/runs/<jobId>.jsonl`.
Upgrade note: if you have older cron jobs from before the current delivery/store format, run
`openclaw doctor --fix`. Doctor now normalizes legacy cron fields (`jobId`, `schedule.cron`,
top-level delivery fields including legacy `threadId`, payload `provider` delivery aliases) and migrates simple
`notify: true` webhook fallback jobs to explicit webhook delivery when `cron.webhook` is
configured.
## Migrating older jobs
<Note>
If you have cron jobs from before the current delivery and store format, run `openclaw doctor --fix`. Doctor normalizes legacy cron fields (`jobId`, `schedule.cron`, top-level delivery fields including legacy `threadId`, payload `provider` delivery aliases) and migrates simple `notify: true` webhook fallback jobs to explicit webhook delivery when `cron.webhook` is configured.
</Note>
## Common edits
@@ -131,21 +187,9 @@ openclaw cron add \
`--light-context` applies to isolated agent-turn jobs only. For cron runs, lightweight mode keeps bootstrap context empty instead of injecting the full workspace bootstrap set.
Delivery ownership note:
- Isolated cron chat delivery is shared. The agent can send directly with the
`message` tool when a chat route is available.
- `announce` fallback-delivers the final reply only when the agent did not send
directly to the resolved target. `webhook` posts the finished payload to a URL.
`none` disables runner fallback delivery.
- Reminders created from an active chat preserve the live chat delivery target
for fallback announce delivery. Internal session keys may be lowercase; do not
use them as a source of truth for case-sensitive provider IDs such as Matrix
room IDs.
## Common admin commands
Manual run:
Manual run and inspection:
```bash
openclaw cron list
@@ -155,10 +199,9 @@ openclaw cron run <job-id> --due
openclaw cron runs --id <job-id> --limit 50
```
`cron runs` entries include delivery diagnostics with the intended cron target,
the resolved target, message-tool sends, fallback use, and delivered state.
`cron runs` entries include delivery diagnostics with the intended cron target, the resolved target, message-tool sends, fallback use, and delivered state.
Agent/session retargeting:
Agent and session retargeting:
```bash
openclaw cron edit <job-id> --agent ops
@@ -176,14 +219,6 @@ openclaw cron edit <job-id> --no-best-effort-deliver
openclaw cron edit <job-id> --no-deliver
```
Failure-delivery note:
- `delivery.failureDestination` is supported for isolated jobs.
- Main-session jobs may only use `delivery.failureDestination` when primary
delivery mode is `webhook`.
- If you do not set any failure destination and the job already announces to a
channel, failure notifications reuse that same announce target.
## Related
- [CLI reference](/cli)

View File

@@ -55,10 +55,9 @@ is omitted or `--latest` is passed, OpenClaw only prints the selected pending
request and exits; rerun approval with the exact request ID after verifying
the details.
Note: if a device retries pairing with changed auth details (role/scopes/public
key), OpenClaw supersedes the previous pending entry and issues a new
`requestId`. Run `openclaw devices list` right before approval to use the
current ID.
<Note>
If a device retries pairing with changed auth details (role, scopes, or public key), OpenClaw supersedes the previous pending entry and issues a new `requestId`. Run `openclaw devices list` right before approval to use the current ID.
</Note>
If the device is already paired and asks for broader scopes or a broader role,
OpenClaw keeps the existing approval in place and creates a new pending upgrade
@@ -103,7 +102,10 @@ caller already has.
openclaw devices rotate --device <deviceId> --role operator --scope operator.read --scope operator.write
```
Returns the new token payload as JSON.
Returns rotation metadata as JSON. If the caller is rotating its own token while
authenticated with that device token, the response also includes the replacement
token so the client can persist it before reconnecting. Shared/admin rotations
do not echo the bearer token.
### `openclaw devices revoke --device <id> --role <role>`
@@ -128,8 +130,9 @@ Returns the revoke result as JSON.
- `--timeout <ms>`: RPC timeout.
- `--json`: JSON output (recommended for scripting).
Note: when you set `--url`, the CLI does not fall back to config or environment credentials.
Pass `--token` or `--password` explicitly. Missing explicit credentials is an error.
<Warning>
When you set `--url`, the CLI does not fall back to config or environment credentials. Pass `--token` or `--password` explicitly. Missing explicit credentials is an error.
</Warning>
## Notes

View File

@@ -43,7 +43,9 @@ Notes:
- `--fix` (alias for `--repair`) writes a backup to `~/.openclaw/openclaw.json.bak` and drops unknown config keys, listing each removal.
- State integrity checks now detect orphan transcript files in the sessions directory and can archive them as `.deleted.<timestamp>` to reclaim space safely.
- Doctor also scans `~/.openclaw/cron/jobs.json` (or `cron.store`) for legacy cron job shapes and can rewrite them in place before the scheduler has to auto-normalize them at runtime.
- Doctor repairs missing bundled plugin runtime dependencies without writing into packaged global installs. For root-owned npm installs or hardened systemd units, set `OPENCLAW_PLUGIN_STAGE_DIR` to a writable directory such as `/var/lib/openclaw/plugin-runtime-deps`.
- Doctor repairs missing bundled plugin runtime dependencies without writing into packaged global installs. For root-owned npm installs or hardened systemd units, set `OPENCLAW_PLUGIN_STAGE_DIR` to a writable directory such as `/var/lib/openclaw/plugin-runtime-deps`; it can also be a path-list such as `/opt/openclaw/plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps`, where earlier roots are read-only lookup layers and the final root is the repair target.
- Doctor repairs stale plugin config by removing missing plugin ids from `plugins.allow`/`plugins.entries`, plus matching dangling channel config, heartbeat targets, and channel model overrides when plugin discovery is healthy.
- Doctor quarantines invalid plugin config by disabling the affected `plugins.entries.<id>` entry and removing its invalid `config` payload. Gateway startup already skips only that bad plugin so other plugins and channels can keep running.
- Set `OPENCLAW_SERVICE_REPAIR_POLICY=external` when another supervisor owns the gateway lifecycle. Doctor still reports gateway/service health and applies non-service repairs, but skips service install/start/restart/bootstrap and legacy service cleanup.
- Doctor auto-migrates legacy flat Talk config (`talk.voiceId`, `talk.modelId`, and friends) into `talk.provider` + `talk.providers.<provider>`.
- Repeat `doctor --fix` runs no longer report/apply Talk normalization when the only difference is object key order.

View File

@@ -110,8 +110,8 @@ Inline `--password` can be exposed in local process listings. Prefer `--password
### Startup profiling
- Set `OPENCLAW_GATEWAY_STARTUP_TRACE=1` to log phase timings during Gateway startup.
- Run `pnpm test:startup:gateway -- --runs 5 --warmup 1` to benchmark Gateway startup. The benchmark records first process output, `/healthz`, `/readyz`, and startup trace timings.
- Set `OPENCLAW_GATEWAY_STARTUP_TRACE=1` to log phase timings during Gateway startup, including per-phase `eventLoopMax` delay and plugin lookup-table timings for installed-index, manifest registry, startup planning, and owner-map work.
- Run `pnpm test:startup:gateway -- --runs 5 --warmup 1` to benchmark Gateway startup. The benchmark records first process output, `/healthz`, `/readyz`, startup trace timings, event-loop delay, and plugin lookup-table timing details.
## Query a running Gateway
@@ -422,21 +422,57 @@ openclaw gateway restart
openclaw gateway uninstall
```
### Install with a wrapper
Use `--wrapper` when the managed service must start through another executable, for example a
secrets manager shim or a run-as helper. The wrapper receives the normal Gateway args and is
responsible for eventually exec'ing `openclaw` or Node with those args.
```bash
cat > ~/.local/bin/openclaw-doppler <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
exec doppler run --project my-project --config production -- openclaw "$@"
EOF
chmod +x ~/.local/bin/openclaw-doppler
openclaw gateway install --wrapper ~/.local/bin/openclaw-doppler --force
openclaw gateway restart
```
You can also set the wrapper through the environment. `gateway install` validates that the path is
an executable file, writes the wrapper into service `ProgramArguments`, and persists
`OPENCLAW_WRAPPER` in the service environment for later forced reinstalls, updates, and doctor
repairs.
```bash
OPENCLAW_WRAPPER="$HOME/.local/bin/openclaw-doppler" openclaw gateway install --force
openclaw doctor
```
To remove a persisted wrapper, clear `OPENCLAW_WRAPPER` while reinstalling:
```bash
OPENCLAW_WRAPPER= openclaw gateway install --force
openclaw gateway restart
```
<AccordionGroup>
<Accordion title="Command options">
- `gateway status`: `--url`, `--token`, `--password`, `--timeout`, `--no-probe`, `--require-rpc`, `--deep`, `--json`
- `gateway install`: `--port`, `--runtime <node|bun>`, `--token`, `--force`, `--json`
- `gateway install`: `--port`, `--runtime <node|bun>`, `--token`, `--wrapper <path>`, `--force`, `--json`
- `gateway uninstall|start|stop|restart`: `--json`
</Accordion>
<Accordion title="Service install and lifecycle notes">
- `gateway install` supports `--port`, `--runtime`, `--token`, `--force`, `--json`.
<Accordion title="Lifecycle behavior">
- Use `gateway restart` to restart a managed service. Do not chain `gateway stop` and `gateway start` as a restart substitute; on macOS, `gateway stop` intentionally disables the LaunchAgent before stopping it.
- Lifecycle commands accept `--json` for scripting.
</Accordion>
<Accordion title="Auth and SecretRefs at install time">
- When token auth requires a token and `gateway.auth.token` is SecretRef-managed, `gateway install` validates that the SecretRef is resolvable but does not persist the resolved token into service environment metadata.
- If token auth requires a token and the configured token SecretRef is unresolved, install fails closed instead of persisting fallback plaintext.
- For password auth on `gateway run`, prefer `OPENCLAW_GATEWAY_PASSWORD`, `--password-file`, or a SecretRef-backed `gateway.auth.password` over inline `--password`.
- In inferred auth mode, shell-only `OPENCLAW_GATEWAY_PASSWORD` does not relax install token requirements; use durable config (`gateway.auth.password` or config `env`) when installing a managed service.
- If both `gateway.auth.token` and `gateway.auth.password` are configured and `gateway.auth.mode` is unset, install is blocked until mode is set explicitly.
- Lifecycle commands accept `--json` for scripting.
</Accordion>
</AccordionGroup>

View File

@@ -17,7 +17,7 @@ Related:
- Hooks: [Hooks](/automation/hooks)
- Plugin hooks: [Plugin hooks](/plugins/hooks)
## List All Hooks
## List all hooks
```bash
openclaw hooks list
@@ -60,7 +60,7 @@ openclaw hooks list --json
Returns structured JSON for programmatic use.
## Get Hook Information
## Get hook information
```bash
openclaw hooks info <name>
@@ -100,7 +100,7 @@ Requirements:
Config: ✓ workspace.dir
```
## Check Hooks Eligibility
## Check hooks eligibility
```bash
openclaw hooks check
@@ -194,10 +194,11 @@ openclaw hooks disable command-logger
- `openclaw hooks list --json`, `info --json`, and `check --json` write structured JSON directly to stdout.
- Plugin-managed hooks cannot be enabled or disabled here; enable or disable the owning plugin instead.
## Install Hook Packs
## Install hook packs
```bash
openclaw plugins install <package> # ClawHub first, then npm
openclaw plugins install npm:<package> # npm only
openclaw plugins install <package> --pin # pin version
openclaw plugins install <path> # local path
```
@@ -248,7 +249,7 @@ openclaw plugins install -l ./my-hook-pack
Linked hook packs are treated as managed hooks from an operator-configured
directory, not as workspace hooks.
## Update Hook Packs
## Update hook packs
```bash
openclaw plugins update <id>
@@ -269,7 +270,7 @@ When a stored integrity hash exists and the fetched artifact hash changes,
OpenClaw prints a warning and asks for confirmation before proceeding. Use
global `--yes` to bypass prompts in CI/non-interactive runs.
## Bundled Hooks
## Bundled hooks
### session-memory

View File

@@ -381,6 +381,7 @@ Notes:
- `list` sorts server names.
- `show` without a name prints the full configured MCP server object.
- `set` expects one JSON object value on the command line.
- Use `transport: "streamable-http"` for Streamable HTTP MCP servers. `openclaw mcp set` also normalizes CLI-native `type: "http"` to the same canonical config shape for compatibility.
- `unset` fails if the named server does not exist.
Examples:
@@ -389,7 +390,7 @@ Examples:
openclaw mcp list
openclaw mcp show context7 --json
openclaw mcp set context7 '{"command":"uvx","args":["context7-mcp"]}'
openclaw mcp set docs '{"url":"https://mcp.example.com"}'
openclaw mcp set docs '{"url":"https://mcp.example.com","transport":"streamable-http"}'
openclaw mcp unset context7
```
@@ -404,7 +405,8 @@ Example config shape:
"args": ["context7-mcp"]
},
"docs": {
"url": "https://mcp.example.com"
"url": "https://mcp.example.com",
"transport": "streamable-http"
}
}
}
@@ -470,6 +472,8 @@ Sensitive values in `url` (userinfo) and `headers` are redacted in logs and stat
| `headers` | Optional key-value map of HTTP headers (for example auth tokens) |
| `connectionTimeoutMs` | Per-server connection timeout in ms (optional) |
OpenClaw config uses `transport: "streamable-http"` as the canonical spelling. CLI-native MCP `type: "http"` values are accepted when saved through `openclaw mcp set` and repaired by `openclaw doctor --fix` in existing config, but `transport` is what embedded Pi consumes directly.
Example:
```json

View File

@@ -51,7 +51,7 @@ openclaw memory index --agent main --verbose
`memory status`:
- `--deep`: probe vector + embedding availability.
- `--deep`: probe vector + embedding availability. Plain `memory status` stays fast and does not run a live embedding ping. QMD lexical `searchMode: "search"` skips semantic vector probes and embedding maintenance even with `--deep`.
- `--index`: run a reindex if the store is dirty (implies `--deep`).
- `--fix`: repair stale recall locks and normalize promotion metadata.
- `--json`: print JSON output.

170
docs/cli/migrate.md Normal file
View File

@@ -0,0 +1,170 @@
---
summary: "CLI reference for `openclaw migrate` (import state from another agent system)"
read_when:
- You want to migrate from Hermes or another agent system into OpenClaw
- You are adding a plugin-owned migration provider
title: "Migrate"
---
# `openclaw migrate`
Import state from another agent system through a plugin-owned migration provider. Bundled providers cover [Claude](/install/migrating-claude) and [Hermes](/install/migrating-hermes); third-party plugins can register additional providers.
<Tip>
For user-facing walkthroughs, see [Migrating from Claude](/install/migrating-claude) and [Migrating from Hermes](/install/migrating-hermes). The [migration hub](/install/migrating) lists all paths.
</Tip>
## Commands
```bash
openclaw migrate list
openclaw migrate claude --dry-run
openclaw migrate hermes --dry-run
openclaw migrate hermes
openclaw migrate apply claude --yes
openclaw migrate apply hermes --yes
openclaw migrate apply hermes --include-secrets --yes
openclaw onboard --flow import
openclaw onboard --import-from claude --import-source ~/.claude
openclaw onboard --import-from hermes --import-source ~/.hermes
```
<ParamField path="<provider>" type="string">
Name of a registered migration provider, for example `hermes`. Run `openclaw migrate list` to see installed providers.
</ParamField>
<ParamField path="--dry-run" type="boolean">
Build the plan and exit without changing state.
</ParamField>
<ParamField path="--from <path>" type="string">
Override the source state directory. Hermes defaults to `~/.hermes`.
</ParamField>
<ParamField path="--include-secrets" type="boolean">
Import supported credentials. Off by default.
</ParamField>
<ParamField path="--overwrite" type="boolean">
Allow apply to replace existing targets when the plan reports conflicts.
</ParamField>
<ParamField path="--yes" type="boolean">
Skip the confirmation prompt. Required in non-interactive mode.
</ParamField>
<ParamField path="--no-backup" type="boolean">
Skip the pre-apply backup. Requires `--force` when local OpenClaw state exists.
</ParamField>
<ParamField path="--force" type="boolean">
Required alongside `--no-backup` when apply would otherwise refuse to skip backup.
</ParamField>
<ParamField path="--json" type="boolean">
Print the plan or apply result as JSON. With `--json` and no `--yes`, apply prints the plan and does not mutate state.
</ParamField>
## Safety model
`openclaw migrate` is preview-first.
<AccordionGroup>
<Accordion title="Preview before apply">
The provider returns an itemized plan before anything changes, including conflicts, skipped items, and sensitive items. JSON plans, apply output, and migration reports redact nested secret-looking keys such as API keys, tokens, authorization headers, cookies, and passwords.
`openclaw migrate apply <provider>` previews the plan and prompts before changing state unless `--yes` is set. In non-interactive mode, apply requires `--yes`.
</Accordion>
<Accordion title="Backups">
Apply creates and verifies an OpenClaw backup before applying the migration. If no local OpenClaw state exists yet, the backup step is skipped and the migration can continue. To skip a backup when state exists, pass both `--no-backup` and `--force`.
</Accordion>
<Accordion title="Conflicts">
Apply refuses to continue when the plan has conflicts. Review the plan, then rerun with `--overwrite` if replacing existing targets is intentional. Providers may still write item-level backups for overwritten files in the migration report directory.
</Accordion>
<Accordion title="Secrets">
Secrets are never imported by default. Use `--include-secrets` to import supported credentials.
</Accordion>
</AccordionGroup>
## Claude provider
The bundled Claude provider detects Claude Code state at `~/.claude` by default. Use `--from <path>` to import a specific Claude Code home or project root.
<Tip>
For a user-facing walkthrough, see [Migrating from Claude](/install/migrating-claude).
</Tip>
### What Claude imports
- Project `CLAUDE.md` and `.claude/CLAUDE.md` into the OpenClaw agent workspace.
- User `~/.claude/CLAUDE.md` appended to workspace `USER.md`.
- MCP server definitions from project `.mcp.json`, Claude Code `~/.claude.json`, and Claude Desktop `claude_desktop_config.json`.
- Claude skill directories that include `SKILL.md`.
- Claude command Markdown files converted into OpenClaw skills with manual invocation only.
### Archive and manual-review state
Claude hooks, permissions, environment defaults, local memory, path-scoped rules, subagents, caches, plans, and project history are preserved in the migration report or reported as manual-review items. OpenClaw does not execute hooks, copy broad allowlists, or import OAuth/Desktop credential state automatically.
## Hermes provider
The bundled Hermes provider detects state at `~/.hermes` by default. Use `--from <path>` when Hermes lives elsewhere.
### What Hermes imports
- Default model configuration from `config.yaml`.
- Configured model providers and custom OpenAI-compatible endpoints from `providers` and `custom_providers`.
- MCP server definitions from `mcp_servers` or `mcp.servers`.
- `SOUL.md` and `AGENTS.md` into the OpenClaw agent workspace.
- `memories/MEMORY.md` and `memories/USER.md` appended to workspace memory files.
- Memory config defaults for OpenClaw file memory, plus archive or manual-review items for external memory providers such as Honcho.
- Skills that include a `SKILL.md` file under `skills/<name>/`.
- Per-skill config values from `skills.config`.
- Supported API keys from `.env`, only with `--include-secrets`.
### Supported `.env` keys
`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `GOOGLE_API_KEY`, `GEMINI_API_KEY`, `GROQ_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `DEEPSEEK_API_KEY`.
### Archive-only state
Hermes state that OpenClaw cannot safely interpret is copied into the migration report for manual review, but it is not loaded into live OpenClaw config or credentials. This preserves opaque or unsafe state without pretending OpenClaw can execute or trust it automatically:
- `plugins/`
- `sessions/`
- `logs/`
- `cron/`
- `mcp-tokens/`
- `auth.json`
- `state.db`
### After applying
```bash
openclaw doctor
```
## Plugin contract
Migration sources are plugins. A plugin declares its provider ids in `openclaw.plugin.json`:
```json
{
"contracts": {
"migrationProviders": ["hermes"]
}
}
```
At runtime the plugin calls `api.registerMigrationProvider(...)`. The provider implements `detect`, `plan`, and `apply`. Core owns CLI orchestration, backup policy, prompts, JSON output, and conflict preflight. Core passes the reviewed plan into `apply(ctx, plan)`, and providers may rebuild the plan only when that argument is absent for compatibility.
Provider plugins can use `openclaw/plugin-sdk/migration` for item construction and summary counts, plus `openclaw/plugin-sdk/migration-runtime` for conflict-aware file copies, archive-only report copies, and migration reports.
## Onboarding integration
Onboarding can offer migration when a provider detects a known source. Both `openclaw onboard --flow import` and `openclaw setup --wizard --import-from hermes` use the same plugin migration provider and still show a preview before applying.
<Note>
Onboarding imports require a fresh OpenClaw setup. Reset config, credentials, sessions, and the workspace first if you already have local state. Backup-plus-overwrite or merge imports are feature-gated for existing setups.
</Note>
## Related
- [Migrating from Hermes](/install/migrating-hermes): user-facing walkthrough.
- [Migrating from Claude](/install/migrating-claude): user-facing walkthrough.
- [Migrating](/install/migrating): move OpenClaw to a new machine.
- [Doctor](/gateway/doctor): health check after applying a migration.
- [Plugins](/tools/plugin): plugin install and registration.

View File

@@ -67,7 +67,7 @@ Notes:
stale removed-provider default.
- `models status` may show `marker(<value>)` in auth output for non-secret placeholders (for example `OPENAI_API_KEY`, `secretref-managed`, `minimax-oauth`, `oauth:chutes`, `ollama-local`) instead of masking them as secrets.
### `models scan`
### Models scan
`models scan` reads OpenRouter's public `:free` catalog and ranks candidates for
fallback use. The catalog itself is public, so metadata-only scans do not need
@@ -96,7 +96,7 @@ Options:
`--set-default` and `--set-image` require live probes; metadata-only scan
results are informational and are not applied to config.
### `models status`
### Models status
Options:

View File

@@ -29,6 +29,7 @@ openclaw nodes list --last-connected 24h
openclaw nodes pending
openclaw nodes approve <requestId>
openclaw nodes reject <requestId>
openclaw nodes remove --node <id|name|ip>
openclaw nodes rename --node <id|name|ip> --name <displayName>
openclaw nodes status
openclaw nodes status --connected
@@ -38,6 +39,7 @@ openclaw nodes status --last-connected 24h
`nodes list` prints pending/paired tables. Paired rows include the most recent connect age (Last Connect).
Use `--connected` to only show currently-connected nodes. Use `--last-connected <duration>` to
filter to nodes that connected within a duration (e.g. `24h`, `7d`).
Use `nodes remove --node <id|name|ip>` to delete a stale gateway-owned node pairing record.
Approval note:

View File

@@ -11,11 +11,23 @@ Interactive onboarding for local or remote Gateway setup.
## Related guides
- CLI onboarding hub: [Onboarding (CLI)](/start/wizard)
- Onboarding overview: [Onboarding Overview](/start/onboarding-overview)
- CLI onboarding reference: [CLI Setup Reference](/start/wizard-cli-reference)
- CLI automation: [CLI Automation](/start/wizard-cli-automation)
- macOS onboarding: [Onboarding (macOS App)](/start/onboarding)
<CardGroup cols={2}>
<Card title="CLI onboarding hub" href="/start/wizard" icon="rocket">
Walkthrough of the interactive CLI flow.
</Card>
<Card title="Onboarding overview" href="/start/onboarding-overview" icon="map">
How OpenClaw onboarding fits together.
</Card>
<Card title="CLI setup reference" href="/start/wizard-cli-reference" icon="book">
Outputs, internals, and per-step behavior.
</Card>
<Card title="CLI automation" href="/start/wizard-cli-automation" icon="terminal">
Non-interactive flags and scripted setups.
</Card>
<Card title="macOS app onboarding" href="/start/onboarding" icon="apple">
Onboarding flow for the macOS menu bar app.
</Card>
</CardGroup>
## Examples
@@ -24,10 +36,14 @@ openclaw onboard
openclaw onboard --modern
openclaw onboard --flow quickstart
openclaw onboard --flow manual
openclaw onboard --flow import
openclaw onboard --import-from hermes --import-source ~/.hermes
openclaw onboard --skip-bootstrap
openclaw onboard --mode remote --remote-url wss://gateway-host:18789
```
`--flow import` uses plugin-owned migration providers such as Hermes. It only runs against a fresh OpenClaw setup; if existing config, credentials, sessions, or workspace memory/identity files are present, reset or choose a fresh setup before importing.
`--modern` starts the Crestodian conversational onboarding preview. Without
`--modern`, `openclaw onboard` keeps the classic onboarding flow.
@@ -132,10 +148,11 @@ Interactive onboarding behavior with reference mode:
- Onboarding performs a fast preflight validation before saving the ref.
- If validation fails, onboarding shows the error and lets you retry.
Non-interactive Z.AI endpoint choices:
### Non-interactive Z.AI endpoint choices
Note: `--auth-choice zai-api-key` now auto-detects the best Z.AI endpoint for your key (prefers the general API with `zai/glm-5.1`).
If you specifically want the GLM Coding Plan endpoints, pick `zai-coding-global` or `zai-coding-cn`.
<Note>
`--auth-choice zai-api-key` auto-detects the best Z.AI endpoint for your key (prefers the general API with `zai/glm-5.1`). If you specifically want the GLM Coding Plan endpoints, pick `zai-coding-global` or `zai-coding-cn`.
</Note>
```bash
# Promptless endpoint selection
@@ -157,26 +174,34 @@ openclaw onboard --non-interactive \
--mistral-api-key "$MISTRAL_API_KEY"
```
Flow notes:
## Flow notes
- `quickstart`: minimal prompts, auto-generates a gateway token.
- `manual`: full prompts for port/bind/auth (alias of `advanced`).
- When an auth choice implies a preferred provider, onboarding prefilters the
default-model and allowlist pickers to that provider. For Volcengine and
BytePlus, this also matches the coding-plan variants
(`volcengine-plan/*`, `byteplus-plan/*`).
- If the preferred-provider filter yields no loaded models yet, onboarding
falls back to the unfiltered catalog instead of leaving the picker empty.
- In the web-search step, some providers can trigger provider-specific
follow-up prompts:
- **Grok** can offer optional `x_search` setup with the same `XAI_API_KEY`
and an `x_search` model choice.
- **Kimi** can ask for the Moonshot API region (`api.moonshot.ai` vs
`api.moonshot.cn`) and the default Kimi web-search model.
- Local onboarding DM scope behavior: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals).
- Fastest first chat: `openclaw dashboard` (Control UI, no channel setup).
- Custom Provider: connect any OpenAI or Anthropic compatible endpoint,
including hosted providers not listed. Use Unknown to auto-detect.
<AccordionGroup>
<Accordion title="Flow types">
- `quickstart`: minimal prompts, auto-generates a gateway token.
- `manual`: full prompts for port, bind, and auth (alias of `advanced`).
- `import`: runs a detected migration provider, previews the plan, then applies after confirmation.
</Accordion>
<Accordion title="Provider prefiltering">
When an auth choice implies a preferred provider, onboarding prefilters the default-model and allowlist pickers to that provider. For Volcengine and BytePlus, this also matches the coding-plan variants (`volcengine-plan/*`, `byteplus-plan/*`).
If the preferred-provider filter yields no loaded models yet, onboarding falls back to the unfiltered catalog instead of leaving the picker empty.
</Accordion>
<Accordion title="Web-search follow-ups">
Some web-search providers trigger provider-specific follow-up prompts:
- **Grok** can offer optional `x_search` setup with the same `XAI_API_KEY` and an `x_search` model choice.
- **Kimi** can ask for the Moonshot API region (`api.moonshot.ai` vs `api.moonshot.cn`) and the default Kimi web-search model.
</Accordion>
<Accordion title="Other behaviors">
- Local onboarding DM scope behavior: [CLI setup reference](/start/wizard-cli-reference#outputs-and-internals).
- Fastest first chat: `openclaw dashboard` (Control UI, no channel setup).
- Custom provider: connect any OpenAI or Anthropic compatible endpoint, including hosted providers not listed. Use Unknown to auto-detect.
- If Hermes state is detected, onboarding offers a migration flow. Use [Migrate](/cli/migrate) for dry-run plans, overwrite mode, reports, and exact mappings.
</Accordion>
</AccordionGroup>
## Common follow-up commands

View File

@@ -61,6 +61,7 @@ Native OpenClaw plugins must ship `openclaw.plugin.json` with an inline JSON Sch
```bash
openclaw plugins install <package> # ClawHub first, then npm
openclaw plugins install clawhub:<package> # ClawHub only
openclaw plugins install npm:<package> # npm only
openclaw plugins install <package> --force # overwrite existing install
openclaw plugins install <package> --pin # pin version
openclaw plugins install <package> --dangerously-force-unsafe-install
@@ -78,7 +79,7 @@ Bare package names are checked against ClawHub first, then npm. Treat plugin ins
<Accordion title="Config includes and invalid-config recovery">
If your `plugins` section is backed by a single-file `$include`, `plugins install/update/enable/disable/uninstall` write through to that included file and leave `openclaw.json` untouched. Root includes, include arrays, and includes with sibling overrides fail closed instead of flattening. See [Config includes](/gateway/configuration) for the supported shapes.
If config is invalid, `plugins install` normally fails closed and tells you to run `openclaw doctor --fix` first. The only documented exception is a narrow bundled-plugin recovery path for plugins that explicitly opt into `openclaw.install.allowInvalidConfigRecovery`.
If config is invalid during install, `plugins install` normally fails closed and tells you to run `openclaw doctor --fix` first. During Gateway startup, invalid config for one plugin is isolated to that plugin so other channels and plugins can keep running; `openclaw doctor --fix` can quarantine the invalid plugin entry. The only documented install-time exception is a narrow bundled-plugin recovery path for plugins that explicitly opt into `openclaw.install.allowInvalidConfigRecovery`.
</Accordion>
<Accordion title="--force and reinstall vs update">
@@ -101,6 +102,8 @@ Bare package names are checked against ClawHub first, then npm. Treat plugin ins
Npm specs are **registry-only** (package name + optional **exact version** or **dist-tag**). Git/URL/file specs and semver ranges are rejected. Dependency installs run project-local with `--ignore-scripts` for safety, even when your shell has global npm install settings.
Use `npm:<package>` when you want to skip ClawHub lookup and install directly from npm. Bare package specs still prefer ClawHub and only fall back to npm when ClawHub does not have that package or version.
Bare specs and `@latest` stay on the stable track. If npm resolves either of those to a prerelease, OpenClaw stops and asks you to opt in explicitly with a prerelease tag such as `@beta`/`@rc` or an exact prerelease version such as `@1.2.3-beta.4`.
If a bare install spec matches a bundled plugin id (for example `diffs`), OpenClaw installs the bundled plugin directly. To install an npm package with the same name, use an explicit scoped spec (for example `@scope/diffs`).
@@ -127,7 +130,15 @@ OpenClaw now also prefers ClawHub for bare npm-safe plugin specs. It only falls
openclaw plugins install openclaw-codex-app-server
```
Use `npm:` to force npm-only resolution, for example when ClawHub is unreachable or you know the package exists only on npm:
```bash
openclaw plugins install npm:openclaw-codex-app-server
openclaw plugins install npm:@scope/plugin-name@1.0.1
```
OpenClaw downloads the package archive from ClawHub, checks the advertised plugin API / minimum gateway compatibility, then installs it through the normal archive path. Recorded installs keep their ClawHub source metadata for later updates.
Unversioned ClawHub installs keep an unversioned recorded spec so `openclaw plugins update` can follow newer ClawHub releases; explicit version or tag selectors such as `clawhub:pkg@1.2.3` and `clawhub:pkg@beta` remain pinned to that selector.
#### Marketplace shorthand

View File

@@ -75,9 +75,11 @@ openclaw sandbox recreate --all --force # Skip confirmation
- `--browser`: Only recreate browser containers
- `--force`: Skip confirmation prompt
**Important:** Runtimes are automatically recreated when the agent is next used.
<Note>
Runtimes are automatically recreated when the agent is next used.
</Note>
## Use Cases
## Use cases
### After updating a Docker image
@@ -148,18 +150,19 @@ openclaw sandbox recreate --agent family
openclaw sandbox recreate --agent alfred
```
## Why is this needed?
## Why this is needed
**Problem:** When you update sandbox configuration:
When you update sandbox configuration:
- Existing runtimes continue running with old settings
- Runtimes are only pruned after 24h of inactivity
- Regularly-used agents keep old runtimes alive indefinitely
- Existing runtimes continue running with old settings.
- Runtimes are only pruned after 24h of inactivity.
- Regularly-used agents keep old runtimes alive indefinitely.
**Solution:** Use `openclaw sandbox recreate` to force removal of old runtimes. They'll be recreated automatically with current settings when next needed.
Use `openclaw sandbox recreate` to force removal of old runtimes. They are recreated automatically with current settings when next needed.
Tip: prefer `openclaw sandbox recreate` over manual backend-specific cleanup.
It uses the Gateways runtime registry and avoids mismatches when scope/session keys change.
<Tip>
Prefer `openclaw sandbox recreate` over manual backend-specific cleanup. It uses the Gateway's runtime registry and avoids mismatches when scope or session keys change.
</Tip>
## Configuration
@@ -193,4 +196,4 @@ Sandbox settings live in `~/.openclaw/openclaw.json` under `agents.defaults.sand
- [CLI reference](/cli)
- [Sandboxing](/gateway/sandboxing)
- [Agent workspace](/concepts/agent-workspace)
- [Doctor](/gateway/doctor) checks sandbox setup
- [Doctor](/gateway/doctor): checks sandbox setup.

View File

@@ -21,6 +21,7 @@ Related:
openclaw setup
openclaw setup --workspace ~/.openclaw/workspace
openclaw setup --wizard
openclaw setup --wizard --import-from hermes --import-source ~/.hermes
openclaw setup --non-interactive --mode remote --remote-url wss://gateway-host:18789 --remote-token <token>
```
@@ -30,6 +31,9 @@ openclaw setup --non-interactive --mode remote --remote-url wss://gateway-host:1
- `--wizard`: run onboarding
- `--non-interactive`: run onboarding without prompts
- `--mode <local|remote>`: onboarding mode
- `--import-from <provider>`: migration provider to run during onboarding
- `--import-source <path>`: source agent home for `--import-from`
- `--import-secrets`: import supported secrets during onboarding migration
- `--remote-url <url>`: remote Gateway WebSocket URL
- `--remote-token <token>`: remote Gateway token
@@ -42,7 +46,8 @@ openclaw setup --wizard
Notes:
- Plain `openclaw setup` initializes config + workspace without the full onboarding flow.
- Onboarding auto-runs when any onboarding flags are present (`--wizard`, `--non-interactive`, `--mode`, `--remote-url`, `--remote-token`).
- Onboarding auto-runs when any onboarding flags are present (`--wizard`, `--non-interactive`, `--mode`, `--import-from`, `--import-source`, `--import-secrets`, `--remote-url`, `--remote-token`).
- If Hermes state is detected, interactive onboarding can offer migration automatically. Import onboarding requires a fresh setup; use [Migrate](/cli/migrate) for dry-run plans, backups, and overwrite mode outside onboarding.
## Related

View File

@@ -40,9 +40,11 @@ openclaw --update
`postUpdate.plugins.integrityDrifts` when npm plugin artifact drift is
detected during post-update plugin sync.
- `--timeout <seconds>`: per-step timeout (default is 1800s).
- `--yes`: skip confirmation prompts (for example downgrade confirmation)
- `--yes`: skip confirmation prompts (for example downgrade confirmation).
Note: downgrades require confirmation because older versions can break configuration.
<Warning>
Downgrades require confirmation because older versions can break configuration.
</Warning>
## `update status`
@@ -83,43 +85,68 @@ install method aligned:
The Gateway core auto-updater (when enabled via config) reuses this same update path.
For package-manager installs, `openclaw update` resolves the target package
version before invoking the package manager. Even when the installed version
version before invoking the package manager. npm global installs use a staged
install: OpenClaw installs the new package into a temporary npm prefix, verifies
the packaged `dist` inventory there, then swaps that clean package tree into the
real global prefix. If verification fails, post-update doctor, plugin sync, and
restart work do not run from the suspect tree. Even when the installed version
already matches the target, the command refreshes the global package install,
then runs plugin sync, completion refresh, and restart work. This keeps packaged
sidecars and channel-owned plugin records aligned with the installed OpenClaw
build.
then runs plugin sync, a core-command completion refresh, and restart work. This
keeps packaged sidecars and channel-owned plugin records aligned with the
installed OpenClaw build while leaving full plugin-command completion rebuilds to
explicit `openclaw completion --write-state` runs.
## Git checkout flow
Channels:
### Channel selection
- `stable`: checkout the latest non-beta tag, then build + doctor.
- `beta`: prefer the latest `-beta` tag, but fall back to the latest stable tag
when beta is missing or older.
- `dev`: checkout `main`, then fetch + rebase.
- `stable`: checkout the latest non-beta tag, then build and doctor.
- `beta`: prefer the latest `-beta` tag, but fall back to the latest stable tag when beta is missing or older.
- `dev`: checkout `main`, then fetch and rebase.
High-level:
### Update steps
1. Requires a clean worktree (no uncommitted changes).
2. Switches to the selected channel (tag or branch).
3. Fetches upstream (dev only).
4. Dev only: preflight lint + TypeScript build in a temp worktree; if the tip fails, walks back up to 10 commits to find the newest clean build.
5. Rebases onto the selected commit (dev only).
6. Installs deps with the repo package manager. For pnpm checkouts, the updater bootstraps `pnpm` on demand (via `corepack` first, then a temporary `npm install pnpm@10` fallback) instead of running `npm run build` inside a pnpm workspace.
7. Builds + builds the Control UI.
8. Runs `openclaw doctor` as the final “safe update” check.
9. Syncs plugins to the active channel (dev uses bundled plugins; stable/beta uses npm) and updates npm-installed plugins.
<Steps>
<Step title="Verify clean worktree">
Requires no uncommitted changes.
</Step>
<Step title="Switch channel">
Switches to the selected channel (tag or branch).
</Step>
<Step title="Fetch upstream">
Dev only.
</Step>
<Step title="Preflight build (dev only)">
Runs lint and TypeScript build in a temp worktree. If the tip fails, walks back up to 10 commits to find the newest clean build.
</Step>
<Step title="Rebase">
Rebases onto the selected commit (dev only).
</Step>
<Step title="Install dependencies">
Uses the repo package manager. For pnpm checkouts, the updater bootstraps `pnpm` on demand (via `corepack` first, then a temporary `npm install pnpm@10` fallback) instead of running `npm run build` inside a pnpm workspace.
</Step>
<Step title="Build Control UI">
Builds the gateway and the Control UI.
</Step>
<Step title="Run doctor">
`openclaw doctor` runs as the final safe-update check.
</Step>
<Step title="Sync plugins">
Syncs plugins to the active channel. Dev uses bundled plugins; stable and beta use npm. Updates npm-installed plugins.
</Step>
</Steps>
If an exact pinned npm plugin update resolves to an artifact whose integrity
differs from the stored install record, `openclaw update` aborts that plugin
artifact update instead of installing it. Reinstall or update the plugin
explicitly only after verifying that you trust the new artifact.
<Warning>
If an exact pinned npm plugin update resolves to an artifact whose integrity differs from the stored install record, `openclaw update` aborts that plugin artifact update instead of installing it. Reinstall or update the plugin explicitly only after verifying that you trust the new artifact.
</Warning>
Post-update plugin sync failures fail the update result and stop restart
follow-up work. Fix the plugin install/update error, then rerun
`openclaw update`.
<Note>
Post-update plugin sync failures fail the update result and stop restart follow-up work. Fix the plugin install or update error, then rerun `openclaw update`.
If pnpm bootstrap still fails, the updater now stops early with a package-manager-specific error instead of trying `npm run build` inside the checkout.
When the updated Gateway starts, enabled bundled plugin runtime dependencies are staged before plugin activation. Update-triggered restarts drain any active runtime-dependency staging before closing the Gateway, so service-manager restarts do not interrupt an in-flight npm install.
If pnpm bootstrap still fails, the updater stops early with a package-manager-specific error instead of trying `npm run build` inside the checkout.
</Note>
## `--update` shorthand

View File

@@ -162,7 +162,8 @@ surfaces, while Codex native hooks remain a separate lower-level Codex mechanism
- `agent.wait` default: 30s (just the wait). `timeoutMs` param overrides.
- Agent runtime: `agents.defaults.timeoutSeconds` default 172800s (48 hours); enforced in `runEmbeddedPiAgent` abort timer.
- LLM idle timeout: `agents.defaults.llm.idleTimeoutSeconds` aborts a model request when no response chunks arrive before the idle window. Set it explicitly for slow local models or reasoning/tool-call providers; set it to 0 to disable. If it is not set, OpenClaw uses `agents.defaults.timeoutSeconds` when configured, otherwise 120s. Cron-triggered runs with no explicit LLM or agent timeout disable the idle watchdog and rely on the cron outer timeout.
- Model idle timeout: OpenClaw aborts a model request when no response chunks arrive before the idle window. `models.providers.<id>.timeoutSeconds` extends this idle watchdog for slow local/self-hosted providers; otherwise OpenClaw uses `agents.defaults.timeoutSeconds` when configured, capped at 120s by default. Cron-triggered runs with no explicit model or agent timeout disable the idle watchdog and rely on the cron outer timeout.
- Provider HTTP request timeout: `models.providers.<id>.timeoutSeconds` applies to that provider's model HTTP fetches, including connect, headers, body, SDK request timeout, total guarded-fetch abort handling, and model stream idle watchdog. Use this for slow local/self-hosted providers such as Ollama before raising the whole agent runtime timeout.
## Where things can end early

View File

@@ -6,9 +6,7 @@ read_when:
title: "Compaction"
---
Every model has a context window -- the maximum number of tokens it can process.
When a conversation approaches that limit, OpenClaw **compacts** older messages
into a summary so the chat can continue.
Every model has a context window: the maximum number of tokens it can process. When a conversation approaches that limit, OpenClaw **compacts** older messages into a summary so the chat can continue.
## How it works
@@ -16,33 +14,54 @@ into a summary so the chat can continue.
2. The summary is saved in the session transcript.
3. Recent messages are kept intact.
When OpenClaw splits history into compaction chunks, it keeps assistant tool
calls paired with their matching `toolResult` entries. If a split point lands
inside a tool block, OpenClaw moves the boundary so the pair stays together and
the current unsummarized tail is preserved.
When OpenClaw splits history into compaction chunks, it keeps assistant tool calls paired with their matching `toolResult` entries. If a split point lands inside a tool block, OpenClaw moves the boundary so the pair stays together and the current unsummarized tail is preserved.
The full conversation history stays on disk. Compaction only changes what the
model sees on the next turn.
The full conversation history stays on disk. Compaction only changes what the model sees on the next turn.
## Auto-compaction
Auto-compaction is on by default. It runs when the session nears the context
limit, or when the model returns a context-overflow error (in which case
OpenClaw compacts and retries). Typical overflow signatures include
`request_too_large`, `context length exceeded`, `input exceeds the maximum
number of tokens`, `input token count exceeds the maximum number of input
tokens`, `input is too long for the model`, and `ollama error: context length
exceeded`.
Auto-compaction is on by default. It runs when the session nears the context limit, or when the model returns a context-overflow error (in which case OpenClaw compacts and retries).
You will see:
- `🧹 Auto-compaction complete` in verbose mode.
- `/status` showing `🧹 Compactions: <count>`.
<Info>
Before compacting, OpenClaw automatically reminds the agent to save important
notes to [memory](/concepts/memory) files. This prevents context loss.
Before compacting, OpenClaw automatically reminds the agent to save important notes to [memory](/concepts/memory) files. This prevents context loss.
</Info>
Use the `agents.defaults.compaction` setting in your `openclaw.json` to configure compaction behavior (mode, target tokens, etc.).
Compaction summarization preserves opaque identifiers by default (`identifierPolicy: "strict"`). You can override this with `identifierPolicy: "off"` or provide custom text with `identifierPolicy: "custom"` and `identifierInstructions`.
<AccordionGroup>
<Accordion title="Recognized overflow signatures">
OpenClaw detects context overflow from these provider error patterns:
You can optionally specify a different model for compaction summarization via `agents.defaults.compaction.model`. This is useful when your primary model is a local or small model and you want compaction summaries produced by a more capable model. The override accepts any `provider/model-id` string:
- `request_too_large`
- `context length exceeded`
- `input exceeds the maximum number of tokens`
- `input token count exceeds the maximum number of input tokens`
- `input is too long for the model`
- `ollama error: context length exceeded`
</Accordion>
</AccordionGroup>
## Manual compaction
Type `/compact` in any chat to force a compaction. Add instructions to guide the summary:
```
/compact Focus on the API design decisions
```
When `agents.defaults.compaction.keepRecentTokens` is set, manual compaction honors that Pi cut-point and keeps the recent tail in rebuilt context. Without an explicit keep budget, manual compaction behaves as a hard checkpoint and continues from the new summary alone.
## Configuration
Configure compaction under `agents.defaults.compaction` in your `openclaw.json`. The most common knobs are listed below; for the full reference, see [Session management deep dive](/reference/session-management-compaction).
### Using a different model
By default, compaction uses the agent's primary model. Set `agents.defaults.compaction.model` to delegate summarization to a more capable or specialized model. The override accepts any `provider/model-id` string:
```json
{
@@ -56,7 +75,7 @@ You can optionally specify a different model for compaction summarization via `a
}
```
This also works with local models, for example a second Ollama model dedicated to summarization or a fine-tuned compaction specialist:
This works with local models too, for example a second Ollama model dedicated to summarization:
```json
{
@@ -70,75 +89,34 @@ This also works with local models, for example a second Ollama model dedicated t
}
```
When unset, compaction uses the agents primary model.
When unset, compaction uses the agent's primary model.
## Pluggable compaction providers
### Identifier preservation
Plugins can register a custom compaction provider via `registerCompactionProvider()` on the plugin API. When a provider is registered and configured, OpenClaw delegates summarization to it instead of the built-in LLM pipeline.
Compaction summarization preserves opaque identifiers by default (`identifierPolicy: "strict"`). Override with `identifierPolicy: "off"` to disable, or `identifierPolicy: "custom"` plus `identifierInstructions` for custom guidance.
To use a registered provider, set the provider id in your config:
### Active transcript byte guard
```json
{
"agents": {
"defaults": {
"compaction": {
"provider": "my-provider"
}
}
}
}
```
When `agents.defaults.compaction.maxActiveTranscriptBytes` is set, OpenClaw triggers normal local compaction before a run if the active JSONL reaches that size. This is useful for long-running sessions where provider-side context management may keep model context healthy while the local transcript keeps growing. It does not split raw JSONL bytes; it asks the normal compaction pipeline to create a semantic summary.
Setting a `provider` automatically forces `mode: "safeguard"`. Providers receive the same compaction instructions and identifier-preservation policy as the built-in path, and OpenClaw still preserves recent-turn and split-turn suffix context after provider output. If the provider fails or returns an empty result, OpenClaw falls back to built-in LLM summarization.
<Warning>
The byte guard requires `truncateAfterCompaction: true`. Without transcript rotation, the active file would not shrink and the guard remains inactive.
</Warning>
## Auto-compaction (default on)
### Successor transcripts
When a session nears or exceeds the models context window, OpenClaw triggers auto-compaction and may retry the original request using the compacted context.
When `agents.defaults.compaction.truncateAfterCompaction` is enabled, OpenClaw does not rewrite the existing transcript in place. It creates a new active successor transcript from the compaction summary, preserved state, and unsummarized tail, then keeps the previous JSONL as the archived checkpoint source.
Successor transcripts also drop exact duplicate long user turns that arrive
inside a short retry window, so channel retry storms are not carried into the
next active transcript after compaction.
Youll see:
Pre-compaction checkpoints are retained only while they stay below OpenClaw's
checkpoint size cap; oversized active transcripts still compact, but OpenClaw
skips the large debug snapshot instead of doubling disk usage.
- `🧹 Auto-compaction complete` in verbose mode
- `/status` showing `🧹 Compactions: <count>`
### Compaction notices
Before compaction, OpenClaw can run a **silent memory flush** turn to store
durable notes to disk. See [Memory](/concepts/memory) for details and config.
## Manual compaction
Type `/compact` in any chat to force a compaction. Add instructions to guide
the summary:
```
/compact Focus on the API design decisions
```
When `agents.defaults.compaction.keepRecentTokens` is set, manual compaction
honors that Pi cut-point and keeps the recent tail in rebuilt context. Without
an explicit keep budget, manual compaction behaves as a hard checkpoint and
continues from the new summary alone.
## Using a different model
By default, compaction uses your agent's primary model. You can use a more
capable model for better summaries:
```json5
{
agents: {
defaults: {
compaction: {
model: "openrouter/anthropic/claude-sonnet-4-6",
},
},
},
}
```
## Compaction notices
By default, compaction runs silently. To show brief notices when compaction
starts and when it completes, enable `notifyUser`:
By default, compaction runs silently. Set `notifyUser` to show brief status messages when compaction starts and completes:
```json5
{
@@ -152,8 +130,33 @@ starts and when it completes, enable `notifyUser`:
}
```
When enabled, the user sees short status messages around each compaction run
(for example, "Compacting context..." and "Compaction complete").
### Memory flush
Before compaction, OpenClaw can run a **silent memory flush** turn to store durable notes to disk. See [Memory](/concepts/memory) for details and config.
## Pluggable compaction providers
Plugins can register a custom compaction provider via `registerCompactionProvider()` on the plugin API. When a provider is registered and configured, OpenClaw delegates summarization to it instead of the built-in LLM pipeline.
To use a registered provider, set its id in your config:
```json
{
"agents": {
"defaults": {
"compaction": {
"provider": "my-provider"
}
}
}
}
```
Setting a `provider` automatically forces `mode: "safeguard"`. Providers receive the same compaction instructions and identifier-preservation policy as the built-in path, and OpenClaw still preserves recent-turn and split-turn suffix context after provider output.
<Note>
If the provider fails or returns an empty result, OpenClaw falls back to built-in LLM summarization.
</Note>
## Compaction vs pruning
@@ -163,28 +166,21 @@ When enabled, the user sees short status messages around each compaction run
| **Saved?** | Yes (in session transcript) | No (in-memory only, per request) |
| **Scope** | Entire conversation | Tool results only |
[Session pruning](/concepts/session-pruning) is a lighter-weight complement that
trims tool output without summarizing.
[Session pruning](/concepts/session-pruning) is a lighter-weight complement that trims tool output without summarizing.
## Troubleshooting
**Compacting too often?** The model's context window may be small, or tool
outputs may be large. Try enabling
[session pruning](/concepts/session-pruning).
**Compacting too often?** The model's context window may be small, or tool outputs may be large. Try enabling [session pruning](/concepts/session-pruning).
**Context feels stale after compaction?** Use `/compact Focus on <topic>` to
guide the summary, or enable the [memory flush](/concepts/memory) so notes
survive.
**Context feels stale after compaction?** Use `/compact Focus on <topic>` to guide the summary, or enable the [memory flush](/concepts/memory) so notes survive.
**Need a clean slate?** `/new` starts a fresh session without compacting.
For advanced configuration (reserve tokens, identifier preservation, custom
context engines, OpenAI server-side compaction), see the
[Session Management Deep Dive](/reference/session-management-compaction).
For advanced configuration (reserve tokens, identifier preservation, custom context engines, OpenAI server-side compaction), see the [Session management deep dive](/reference/session-management-compaction).
## Related
- [Session](/concepts/session) session management and lifecycle
- [Session Pruning](/concepts/session-pruning) trimming tool results
- [Context](/concepts/context) how context is built for agent turns
- [Hooks](/automation/hooks) compaction lifecycle hooks (before_compaction, after_compaction)
- [Session](/concepts/session): session management and lifecycle.
- [Session pruning](/concepts/session-pruning): trimming tool results.
- [Context](/concepts/context): how context is built for agent turns.
- [Hooks](/automation/hooks): compaction lifecycle hooks (`before_compaction`, `after_compaction`).

View File

@@ -194,6 +194,10 @@ Required members:
Prepended to the system prompt.
</ParamField>
`compact` returns a `CompactResult`. When compaction rotates the active
transcript, `result.sessionId` and `result.sessionFile` identify the successor
session that the next retry or turn must use.
Optional members:
| Member | Kind | Purpose |

View File

@@ -70,11 +70,15 @@ The delegate operates **autonomously** on a schedule, executing standing orders
This tier combines Tier 2 permissions with [Cron Jobs](/automation/cron-jobs) and [Standing Orders](/automation/standing-orders).
> **Security warning**: Tier 3 requires careful configuration of hard blocks — actions the agent must never take regardless of instruction. Complete the prerequisites below before granting any identity provider permissions.
<Warning>
Tier 3 requires careful configuration of hard blocks: actions the agent must never take regardless of instruction. Complete the prerequisites below before granting any identity provider permissions.
</Warning>
## Prerequisites: isolation and hardening
> **Do this first.** Before you grant any credentials or identity provider access, lock down the delegate's boundaries. The steps in this section define what the agent **cannot** do — establish these constraints before giving it the ability to do anything.
<Note>
**Do this first.** Before you grant any credentials or identity provider access, lock down the delegate's boundaries. The steps in this section define what the agent **cannot** do. Establish these constraints before giving it the ability to do anything.
</Note>
### Hard blocks (non-negotiable)
@@ -180,7 +184,9 @@ New-ApplicationAccessPolicy `
-AccessRight RestrictAccess
```
> **Security warning**: without an application access policy, `Mail.Read` application permission grants access to **every mailbox in the tenant**. Always create the access policy before the application reads any mail. Test by confirming the app returns `403` for mailboxes outside the security group.
<Warning>
Without an application access policy, `Mail.Read` application permission grants access to **every mailbox in the tenant**. Always create the access policy before the application reads any mail. Test by confirming the app returns `403` for mailboxes outside the security group.
</Warning>
#### Google Workspace
@@ -196,7 +202,9 @@ https://www.googleapis.com/auth/calendar # Tier 2
The service account impersonates the delegate user (not the principal), preserving the "on behalf of" model.
> **Security warning**: domain-wide delegation allows the service account to impersonate **any user in the entire domain**. Restrict the scopes to the minimum required, and limit the service account's client ID to only the scopes listed above in the Admin Console (Security > API controls > Domain-wide delegation). A leaked service account key with broad scopes grants full access to every mailbox and calendar in the organization. Rotate keys on a schedule and monitor the Admin Console audit log for unexpected impersonation events.
<Warning>
Domain-wide delegation allows the service account to impersonate **any user in the entire domain**. Restrict the scopes to the minimum required, and limit the service account's client ID to only the scopes listed above in the Admin Console (Security > API controls > Domain-wide delegation). A leaked service account key with broad scopes grants full access to every mailbox and calendar in the organization. Rotate keys on a schedule and monitor the Admin Console audit log for unexpected impersonation events.
</Warning>
### 3. Bind the delegate to channels

View File

@@ -72,7 +72,7 @@ Dreaming can ingest redacted session transcripts into the dreaming corpus. When
## Dream Diary
Dreaming also keeps a narrative **Dream Diary** in `DREAMS.md`. After each phase has enough material, `memory-core` runs a best-effort background subagent turn (using the default runtime model) and appends a short diary entry.
Dreaming also keeps a narrative **Dream Diary** in `DREAMS.md`. After each phase has enough material, `memory-core` runs a best-effort background subagent turn and appends a short diary entry. It uses the default runtime model unless `dreaming.model` is configured.
<Note>
This diary is for human reading in the Dreams UI, not a promotion source. Dreaming-generated diary/report artifacts are excluded from short-term promotion. Only grounded memory snippets are eligible to promote into `MEMORY.md`.
@@ -112,9 +112,10 @@ When enabled, `memory-core` auto-manages one cron job for a full dreaming sweep.
Default cadence behavior:
| Setting | Default |
| -------------------- | ----------- |
| `dreaming.frequency` | `0 3 * * *` |
| Setting | Default |
| -------------------- | ------------- |
| `dreaming.frequency` | `0 3 * * *` |
| `dreaming.model` | default model |
## Quick start
@@ -210,6 +211,13 @@ All settings live under `plugins.entries.memory-core.config.dreaming`.
<ParamField path="frequency" type="string" default="0 3 * * *">
Cron cadence for the full dreaming sweep.
</ParamField>
<ParamField path="model" type="string">
Optional Dream Diary subagent model override. Use a canonical `provider/model` value when also setting a subagent `allowedModels` allowlist.
</ParamField>
<Warning>
`dreaming.model` requires `plugins.entries.memory-core.subagent.allowModelOverride: true`. To restrict it, also set `plugins.entries.memory-core.subagent.allowedModels`.
</Warning>
<Note>
Phase policy, thresholds, and storage behavior are internal implementation details (not user-facing config). See [Memory configuration reference](/reference/memory-config#dreaming) for the full key list.

View File

@@ -78,6 +78,8 @@ OpenClaw indexes `MEMORY.md` and `memory/*.md` into chunks (~400 tokens with
80-token overlap) and stores them in a per-agent SQLite database.
- **Index location:** `~/.openclaw/memory/<agentId>.sqlite`
- **Storage maintenance:** SQLite WAL sidecars are bounded with periodic and
shutdown checkpoints.
- **File watching:** changes to memory files trigger a debounced reindex (1.5s).
- **Auto-reindex:** when the embedding provider, model, or chunking config
changes, the entire index is rebuilt automatically.

View File

@@ -43,7 +43,7 @@ OpenClaw creates a self-contained QMD home under
`~/.openclaw/agents/<agentId>/qmd/` and manages the sidecar lifecycle
automatically -- collections, updates, and embedding runs are handled for you.
It prefers current QMD collection and MCP query shapes, but still falls back to
legacy `--mask` collection flags and older MCP tool names when needed.
alternate collection pattern flags and older MCP tool names when needed.
Boot-time reconciliation also recreates stale managed collections back to their
canonical patterns when an older QMD collection with the same name is still
present.
@@ -51,13 +51,18 @@ present.
## How the sidecar works
- OpenClaw creates collections from your workspace memory files and any
configured `memory.qmd.paths`, then runs `qmd update` + `qmd embed` on boot
and periodically (default every 5 minutes).
configured `memory.qmd.paths`, then runs `qmd update` on boot and
periodically (default every 5 minutes). Semantic modes also run `qmd embed`.
- The default workspace collection tracks `MEMORY.md` plus the `memory/`
tree. Lowercase `memory.md` is not indexed as a root memory file.
- Boot refresh runs in the background so chat startup is not blocked.
- Searches use the configured `searchMode` (default: `search`; also supports
`vsearch` and `query`). If a mode fails, OpenClaw retries with `qmd query`.
`vsearch` and `query`). `search` is BM25-only, so OpenClaw skips semantic
vector readiness probes and embedding maintenance in that mode. If a mode
fails, OpenClaw retries with `qmd query`.
- With QMD releases that advertise multi-collection filters, OpenClaw groups
same-source collections into one QMD search invocation. Older QMD releases
keep the compatible per-collection fallback.
- If QMD fails entirely, OpenClaw falls back to the builtin SQLite engine.
<Info>
@@ -65,6 +70,37 @@ The first search may be slow -- QMD auto-downloads GGUF models (~2 GB) for
reranking and query expansion on the first `qmd query` run.
</Info>
## Search performance and compatibility
OpenClaw keeps the QMD search path compatible with both current and older QMD
installs.
On startup, OpenClaw checks the installed QMD help text once per manager. If the
binary advertises support for multiple collection filters, OpenClaw searches all
same-source collections with one command:
```bash
qmd search "router notes" --json -n 10 -c memory-root-main -c memory-dir-main
```
This avoids starting one QMD subprocess for every durable-memory collection.
Session transcript collections stay in their own source group, so mixed
`memory` + `sessions` searches still give the result diversifier input from both
sources.
Older QMD builds only accept one collection filter. When OpenClaw detects one
of those builds, it keeps the compatibility path and searches each collection
separately before merging and deduplicating results.
To inspect the installed contract manually, run:
```bash
qmd --help | grep -i collection
```
Current QMD help says collection filters can target one or more collections.
Older help usually describes a single collection.
## Model overrides
QMD model environment variables pass through unchanged from the gateway
@@ -161,9 +197,37 @@ with no extra dependencies.
runs as a service, create a symlink:
`sudo ln -s ~/.bun/bin/qmd /usr/local/bin/qmd`.
If `qmd --version` works in your shell but OpenClaw still reports
`spawn qmd ENOENT`, the gateway process likely has a different `PATH` than your
interactive shell. Pin the binary explicitly:
```json5
{
memory: {
backend: "qmd",
qmd: {
command: "/absolute/path/to/qmd",
},
},
}
```
Use `command -v qmd` in the environment where QMD is installed, then recheck
with `openclaw memory status --deep`.
**First search very slow?** QMD downloads GGUF models on first use. Pre-warm
with `qmd query "test"` using the same XDG dirs OpenClaw uses.
**Many QMD subprocesses during search?** Update QMD if possible. OpenClaw uses
one process for same-source multi-collection searches only when the installed
QMD advertises support for multiple `-c` filters; otherwise it keeps the older
per-collection fallback for correctness.
**BM25-only QMD still trying to build llama.cpp?** Set
`memory.qmd.searchMode = "search"`. OpenClaw treats that mode as lexical-only,
does not run QMD vector status probes or embedding maintenance, and leaves
semantic readiness checks to `vsearch` or `query` setups.
**Search times out?** Increase `memory.qmd.limits.timeoutMs` (default: 4000ms).
Set to `120000` for slower hardware.

View File

@@ -32,6 +32,11 @@ explicitly:
For local embeddings with no API key, install the optional `node-llama-cpp`
runtime package next to OpenClaw and use `provider: "local"`.
Some OpenAI-compatible embedding endpoints require asymmetric labels such as
`input_type: "query"` for searches and `input_type: "document"` or `"passage"`
for indexed chunks. Configure those with `memorySearch.queryInputType` and
`memorySearch.documentInputType`; see the [Memory configuration reference](/reference/memory-config#provider-specific-config).
## Supported providers
| Provider | ID | Needs API key | Notes |

View File

@@ -7,18 +7,18 @@ read_when:
---
OpenClaw remembers things by writing **plain Markdown files** in your agent's
workspace. The model only "remembers" what gets saved to disk -- there is no
workspace. The model only "remembers" what gets saved to disk there is no
hidden state.
## How it works
Your agent has three memory-related files:
- **`MEMORY.md`** -- long-term memory. Durable facts, preferences, and
- **`MEMORY.md`** long-term memory. Durable facts, preferences, and
decisions. Loaded at the start of every DM session.
- **`memory/YYYY-MM-DD.md`** -- daily notes. Running context and observations.
- **`memory/YYYY-MM-DD.md`** daily notes. Running context and observations.
Today and yesterday's notes are loaded automatically.
- **`DREAMS.md`** (optional) -- Dream Diary and dreaming sweep
- **`DREAMS.md`** (optional) Dream Diary and dreaming sweep
summaries for human review, including grounded historical backfill entries.
These files live in the agent workspace (default `~/.openclaw/workspace`).
@@ -32,9 +32,9 @@ prefer TypeScript." It will write it to the appropriate file.
The agent has two tools for working with memory:
- **`memory_search`** -- finds relevant notes using semantic search, even when
- **`memory_search`** finds relevant notes using semantic search, even when
the wording differs from the original.
- **`memory_get`** -- reads a specific memory file or line range.
- **`memory_get`** reads a specific memory file or line range.
Both tools are provided by the active memory plugin (default: `memory-core`).
@@ -61,7 +61,7 @@ See [Memory Wiki](/plugins/memory-wiki).
## Memory search
When an embedding provider is configured, `memory_search` uses **hybrid
search** -- combining vector similarity (semantic meaning) with keyword matching
search** combining vector similarity (semantic meaning) with keyword matching
(exact terms like IDs and code symbols). This works out of the box once you have
an API key for any supported provider.
@@ -104,7 +104,7 @@ dashboards, bridge mode, and Obsidian-friendly workflows.
Before [compaction](/concepts/compaction) summarizes your conversation, OpenClaw
runs a silent turn that reminds the agent to save important context to memory
files. This is on by default -- you do not need to configure anything.
files. This is on by default you do not need to configure anything.
<Tip>
The memory flush prevents context loss during compaction. If your agent has
@@ -176,16 +176,14 @@ openclaw memory index --force # Rebuild the index
## Further reading
- [Builtin Memory Engine](/concepts/memory-builtin) -- default SQLite backend
- [QMD Memory Engine](/concepts/memory-qmd) -- advanced local-first sidecar
- [Honcho Memory](/concepts/memory-honcho) -- AI-native cross-session memory
- [Memory Wiki](/plugins/memory-wiki) -- compiled knowledge vault and wiki-native tools
- [Memory Search](/concepts/memory-search) -- search pipeline, providers, and
tuning
- [Dreaming](/concepts/dreaming) -- background promotion
from short-term recall to long-term memory
- [Memory configuration reference](/reference/memory-config) -- all config knobs
- [Compaction](/concepts/compaction) -- how compaction interacts with memory
- [Builtin memory engine](/concepts/memory-builtin): default SQLite backend.
- [QMD memory engine](/concepts/memory-qmd): advanced local-first sidecar.
- [Honcho memory](/concepts/memory-honcho): AI-native cross-session memory.
- [Memory Wiki](/plugins/memory-wiki): compiled knowledge vault and wiki-native tools.
- [Memory search](/concepts/memory-search): search pipeline, providers, and tuning.
- [Dreaming](/concepts/dreaming): background promotion from short-term recall to long-term memory.
- [Memory configuration reference](/reference/memory-config): all config knobs.
- [Compaction](/concepts/compaction): how compaction interacts with memory.
## Related

View File

@@ -7,8 +7,7 @@ read_when:
title: "Messages"
---
This page ties together how OpenClaw handles inbound messages, sessions, queueing,
streaming, and reasoning visibility.
OpenClaw handles inbound messages through a pipeline of session resolution, queueing, streaming, tool execution, and reasoning visibility. This page maps the path from inbound message to reply.
## Message flow (high level)

View File

@@ -33,7 +33,7 @@ For a normal text run, OpenClaw evaluates candidates in this order:
If that provider is exhausted with a failover-worthy error, move to the next model candidate.
</Step>
<Step title="Persist fallback override">
Persist the selected fallback override before the retry starts so other session readers see the same provider/model the runner is about to use.
Persist the selected fallback override before the retry starts so other session readers see the same provider/model the runner is about to use. The persisted model override is marked `modelOverrideSource: "auto"`.
</Step>
<Step title="Roll back narrowly on failure">
If the fallback candidate fails, roll back only the fallback-owned session override fields when they still match that failed candidate.
@@ -47,6 +47,7 @@ This is intentionally narrower than "save and restore the whole session". The re
- `providerOverride`
- `modelOverride`
- `modelOverrideSource`
- `authProfileOverride`
- `authProfileOverrideSource`
- `authProfileOverrideCompactionCount`
@@ -202,7 +203,7 @@ Defaults:
## Model fallback
If all profiles for a provider fail, OpenClaw moves to the next model in `agents.defaults.model.fallbacks`. This applies to auth failures, rate limits, and timeouts that exhausted profile rotation (other errors do not advance fallback).
If all profiles for a provider fail, OpenClaw moves to the next model in `agents.defaults.model.fallbacks`. This applies to auth failures, rate limits, and timeouts that exhausted profile rotation (other errors do not advance fallback). Provider errors that do not expose enough detail are still labeled precisely in fallback state: `empty_response` means the provider returned no usable message or status, `no_error_details` means the provider explicitly returned `Unknown error (no error details in response)`, and `unclassified` means OpenClaw preserved the raw preview but no classifier matched it yet.
Overloaded and rate-limit errors are handled more aggressively than billing cooldowns. By default, OpenClaw allows one same-provider auth-profile retry, then switches to the next configured model fallback without waiting. Provider-busy signals such as `ModelNotReadyException` land in that overloaded bucket. Tune this with `auth.cooldowns.overloadedProfileRotations`, `auth.cooldowns.overloadedBackoffMs`, and `auth.cooldowns.rateLimitedProfileRotations`.
@@ -264,6 +265,8 @@ That means fallback retries have to coordinate with live model switching:
- Only explicit user-driven model changes mark a pending live switch. That includes `/model`, `session_status(model=...)`, and `sessions.patch`.
- System-driven model changes such as fallback rotation, heartbeat overrides, or compaction never mark a pending live switch on their own.
- Before a fallback retry starts, the reply runner persists the selected fallback override fields to the session entry.
- Auto fallback overrides remain selected on subsequent turns so OpenClaw does not probe a known-bad primary on every message. `/new`, `/reset`, and `sessions.reset` clear auto-sourced overrides and return the session to the configured default.
- `/status` shows the selected model and, when fallback state differs, the active fallback model and reason.
- Live-session reconciliation prefers persisted session overrides over stale runtime model fields.
- If a live-switch error points at a later candidate in the active fallback chain, OpenClaw jumps directly to that selected model instead of walking unrelated candidates first.
- If the fallback attempt fails, the runner rolls back only the override fields it wrote, and only if they still match that failed candidate.
@@ -299,6 +302,8 @@ The persisted fallback override closes that window, and the narrow rollback keep
- optional status/code
- human-readable error summary
Structured `model_fallback_decision` logs also include flat `fallbackStep*` fields when a candidate fails, is skipped, or a later fallback succeeds. These fields make the attempted transition explicit (`fallbackStepFromModel`, `fallbackStepToModel`, `fallbackStepFromFailureReason`, `fallbackStepFromFailureDetail`, `fallbackStepFinalOutcome`) so log and diagnostic exporters can reconstruct the primary failure even when the terminal fallback also fails.
When every candidate fails, OpenClaw throws `FallbackSummaryError`. The outer reply runner can use that to build a more specific message such as "all models are temporarily rate-limited" and include the soonest cooldown expiry when one is known.
That cooldown summary is model-aware:

View File

@@ -16,7 +16,7 @@ Reference for **LLM/model providers** (not chat channels like WhatsApp/Telegram)
- Model refs use `provider/model` (example: `opencode/claude-opus-4-6`).
- `agents.defaults.models` acts as an allowlist when set.
- CLI helpers: `openclaw onboard`, `openclaw models list`, `openclaw models set <provider/model>`.
- `models.providers.*.models[].contextWindow` is native model metadata; `contextTokens` is the effective runtime cap.
- `models.providers.*.contextWindow` / `contextTokens` / `maxTokens` set provider-level defaults; `models.providers.*.models[].contextWindow` / `contextTokens` / `maxTokens` override them per model.
- Fallback rules, cooldown probes, and session-override persistence: [Model failover](/concepts/model-failover).
</Accordion>
<Accordion title="OpenAI provider/runtime split">
@@ -317,7 +317,7 @@ See [/providers/kilocode](/providers/kilocode) for setup details.
Uses the xAI Responses path. `/fast` or `params.fastMode: true` rewrites `grok-3`, `grok-3-mini`, `grok-4`, and `grok-4-0709` to their `*-fast` variants. `tool_stream` defaults on; disable via `agents.defaults.models["xai/<model>"].params.tool_stream=false`.
</Accordion>
<Accordion title="Cerebras">
GLM models use `zai-glm-4.7` / `zai-glm-4.6`; OpenAI-compatible base URL is `https://api.cerebras.ai/v1`.
Ships as the bundled `cerebras` provider plugin. GLM uses `zai-glm-4.7`; OpenAI-compatible base URL is `https://api.cerebras.ai/v1`.
</Accordion>
</AccordionGroup>
@@ -367,7 +367,7 @@ Kimi K2 model IDs:
}
```
### Kimi Coding
### Kimi coding
Kimi Coding uses Moonshot AI's Anthropic-compatible endpoint:
@@ -625,6 +625,7 @@ Example (OpenAIcompatible):
baseUrl: "http://localhost:1234/v1",
apiKey: "${LM_API_TOKEN}",
api: "openai-completions",
timeoutSeconds: 300,
models: [
{
id: "my-local-model",
@@ -659,7 +660,8 @@ Example (OpenAIcompatible):
- For `api: "openai-completions"` on non-native endpoints (any non-empty `baseUrl` whose host is not `api.openai.com`), OpenClaw forces `compat.supportsDeveloperRole: false` to avoid provider 400 errors for unsupported `developer` roles.
- Proxy-style OpenAI-compatible routes also skip native OpenAI-only request shaping: no `service_tier`, no Responses `store`, no Completions `store`, no prompt-cache hints, no OpenAI reasoning-compat payload shaping, and no hidden OpenClaw attribution headers.
- For OpenAI-compatible Completions proxies that need vendor-specific fields, set `agents.defaults.models["provider/model"].params.extra_body` (or `extraBody`) to merge extra JSON into the outbound request body.
- For vLLM chat-template controls, set `agents.defaults.models["provider/model"].params.chat_template_kwargs`. OpenClaw automatically sends `enable_thinking: false` and `force_nonempty_content: true` for `vllm/nemotron-3-*` when the session thinking level is off.
- For vLLM chat-template controls, set `agents.defaults.models["provider/model"].params.chat_template_kwargs`. The bundled vLLM plugin automatically sends `enable_thinking: false` and `force_nonempty_content: true` for `vllm/nemotron-3-*` when the session thinking level is off.
- For slow local models or remote LAN/tailnet hosts, set `models.providers.<id>.timeoutSeconds`. This extends provider model HTTP request handling, including connect, headers, body streaming, and the total guarded-fetch abort, without increasing the whole agent runtime timeout.
- If `baseUrl` is empty/omitted, OpenClaw keeps the default OpenAI behavior (which resolves to `api.openai.com`).
- For safety, an explicit `compat.supportsDeveloperRole: true` is still overridden on non-native `openai-completions` endpoints.
</Accordion>

View File

@@ -1,10 +1,11 @@
---
summary: "Private QA automation shape for qa-lab, qa-channel, seeded scenarios, and protocol reports"
summary: "QA stack overview: qa-lab, qa-channel, repo-backed scenarios, live transport lanes, transport adapters, and reporting."
read_when:
- Extending qa-lab or qa-channel
- Understanding how the QA stack fits together
- Extending qa-lab, qa-channel, or a transport adapter
- Adding repo-backed QA scenarios
- Building higher-realism QA automation around the Gateway dashboard
title: "QA E2E automation"
title: "QA overview"
---
The private QA stack is meant to exercise OpenClaw in a more realistic,
@@ -16,9 +17,37 @@ Current pieces:
reaction, edit, and delete surfaces.
- `extensions/qa-lab`: debugger UI and QA bus for observing the transcript,
injecting inbound messages, and exporting a Markdown report.
- `extensions/qa-matrix`, future runner plugins: live-transport adapters that
drive a real channel inside a child QA gateway.
- `qa/`: repo-backed seed assets for the kickoff task and baseline QA
scenarios.
## Command surface
Every QA flow runs under `pnpm openclaw qa <subcommand>`. Many have `pnpm qa:*`
script aliases; both forms are supported.
| Command | Purpose |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `qa run` | Bundled QA self-check; writes a Markdown report. |
| `qa suite` | Run repo-backed scenarios against the QA gateway lane. Aliases: `pnpm openclaw qa suite --runner multipass` for a disposable Linux VM. |
| `qa coverage` | Print the markdown scenario-coverage inventory (`--json` for machine output). |
| `qa parity-report` | Compare two `qa-suite-summary.json` files and write the agentic parity-gate report. |
| `qa character-eval` | Run the character QA scenario across multiple live models with a judged report. See [Reporting](#reporting). |
| `qa manual` | Run a one-off prompt against the selected provider/model lane. |
| `qa ui` | Start the QA debugger UI and local QA bus (alias: `pnpm qa:lab:ui`). |
| `qa docker-build-image` | Build the prebaked QA Docker image. |
| `qa docker-scaffold` | Write a docker-compose scaffold for the QA dashboard + gateway lane. |
| `qa up` | Build the QA site, start the Docker-backed stack, print the URL (alias: `pnpm qa:lab:up`; `:fast` variant adds `--use-prebuilt-image --bind-ui-dist --skip-ui-build`). |
| `qa aimock` | Start only the AIMock provider server. |
| `qa mock-openai` | Start only the scenario-aware `mock-openai` provider server. |
| `qa credentials doctor` / `add` / `list` / `remove` | Manage the shared Convex credential pool. |
| `qa matrix` | Live transport lane against a disposable Tuwunel homeserver. See [Matrix QA](/concepts/qa-matrix). |
| `qa telegram` | Live transport lane against a real private Telegram group. |
| `qa discord` | Live transport lane against a real private Discord guild channel. |
## Operator flow
The current QA operator flow is a two-pane QA site:
- Left: Gateway dashboard (Control UI) with the agent.
@@ -65,50 +94,27 @@ model calls must not export `StreamAbandoned` on successful turns; raw diagnosti
`openclaw.content.*` attributes must stay out of the trace. It writes
`otel-smoke-summary.json` next to the QA suite artifacts.
The normal Docker aggregate and release-path core chunk also run an
observability lane. It reuses the shared package-installed functional Docker
image, mounts the QA harness files read-only, runs the OTEL trace smoke inside
the container, then runs the `docker-prometheus-smoke` QA scenario with the
`diagnostics-prometheus` plugin enabled. Set
`OPENCLAW_DOCKER_OBSERVABILITY_LOOPS=<count>` to repeat both checks inside one
Docker run while preserving per-loop artifacts under
`.artifacts/docker-observability/...`.
Observability QA stays source-checkout only. The npm tarball intentionally omits
QA Lab, so package Docker release lanes do not run `qa` commands. Use
`pnpm qa:otel:smoke` from a built source checkout when changing diagnostics
instrumentation.
For a transport-real Matrix smoke lane, run:
```bash
pnpm openclaw qa matrix
pnpm openclaw qa matrix --profile fast --fail-fast
```
That lane provisions a disposable Tuwunel homeserver in Docker, registers
temporary driver, SUT, and observer users, creates one private room, then runs
the real Matrix plugin inside a QA gateway child. The live transport lane keeps
the child config scoped to the transport under test, so Matrix runs without
`qa-channel` in the child config. It writes the structured report artifacts and
a combined stdout/stderr log into the selected Matrix QA output directory. To
capture the outer `scripts/run-node.mjs` build/launcher output too, set
`OPENCLAW_RUN_NODE_OUTPUT_LOG=<path>` to a repo-local log file.
Matrix progress is printed by default. `OPENCLAW_QA_MATRIX_TIMEOUT_MS` bounds
the full run, and `OPENCLAW_QA_MATRIX_CLEANUP_TIMEOUT_MS` bounds cleanup so a
stuck Docker teardown reports the exact recovery command instead of hanging.
The full CLI reference, profile/scenario catalog, env vars, and artifact layout for this lane live in [Matrix QA](/concepts/qa-matrix). At a glance: it provisions a disposable Tuwunel homeserver in Docker, registers temporary driver/SUT/observer users, runs the real Matrix plugin inside a child QA gateway scoped to that transport (no `qa-channel`), then writes a Markdown report, JSON summary, observed-events artifact, and combined output log under `.artifacts/qa-e2e/matrix-<timestamp>/`.
For a transport-real Telegram smoke lane, run:
For transport-real Telegram and Discord smoke lanes:
```bash
pnpm openclaw qa telegram
pnpm openclaw qa discord
```
That lane targets one real private Telegram group instead of provisioning a
disposable server. It requires `OPENCLAW_QA_TELEGRAM_GROUP_ID`,
`OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN`, and
`OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`, plus two distinct bots in the same
private group. The SUT bot must have a Telegram username, and bot-to-bot
observation works best when both bots have Bot-to-Bot Communication Mode
enabled in `@BotFather`.
The command exits non-zero when any scenario fails. Use `--allow-failures` when
you want artifacts without a failing exit code.
The Telegram report and summary include per-reply RTT from the driver message
send request to the observed SUT reply, starting with the canary.
Both target a pre-existing real channel with two bots (driver + SUT). Required env vars, scenario lists, output artifacts, and the Convex credential pool are documented in [Telegram and Discord QA reference](#telegram-and-discord-qa-reference) below.
Before using pooled live credentials, run:
@@ -116,32 +122,11 @@ Before using pooled live credentials, run:
pnpm openclaw qa credentials doctor
```
The doctor checks Convex broker env, validates endpoint settings, and verifies
admin/list reachability when the maintainer secret is present. It reports only
set/missing status for secrets.
The doctor checks Convex broker env, validates endpoint settings, and verifies admin/list reachability when the maintainer secret is present. It reports only set/missing status for secrets.
For a transport-real Discord smoke lane, run:
## Live transport coverage
```bash
pnpm openclaw qa discord
```
That lane targets one real private Discord guild channel with two bots: a
driver bot controlled by the harness and a SUT bot started by the child
OpenClaw gateway through the bundled Discord plugin. It requires
`OPENCLAW_QA_DISCORD_GUILD_ID`, `OPENCLAW_QA_DISCORD_CHANNEL_ID`,
`OPENCLAW_QA_DISCORD_DRIVER_BOT_TOKEN`, `OPENCLAW_QA_DISCORD_SUT_BOT_TOKEN`,
and `OPENCLAW_QA_DISCORD_SUT_APPLICATION_ID` when using env credentials.
The lane verifies channel mention handling and checks that the SUT bot has
registered the native `/help` command with Discord.
The command exits non-zero when any scenario fails. Use `--allow-failures` when
you want artifacts without a failing exit code.
Live transport lanes now share one smaller contract instead of each inventing
their own scenario list shape:
`qa-channel` remains the broad synthetic product-behavior suite and is not part
of the live transport coverage matrix.
Live transport lanes share one contract instead of each inventing their own scenario list shape. `qa-channel` is the broad synthetic product-behavior suite and is not part of the live transport coverage matrix.
| Lane | Canary | Mention gating | Allowlist block | Top-level reply | Restart resume | Thread follow-up | Thread isolation | Reaction observation | Help command | Native command registration |
| -------- | ------ | -------------- | --------------- | --------------- | -------------- | ---------------- | ---------------- | -------------------- | ------------ | --------------------------- |
@@ -174,6 +159,106 @@ guest: env-based provider keys, the QA live provider config path, and
`CODEX_HOME` when present. Keep `--output-dir` under the repo root so the guest
can write back through the mounted workspace.
## Telegram and Discord QA reference
Matrix has a [dedicated page](/concepts/qa-matrix) because of its scenario count and Docker-backed homeserver provisioning. Telegram and Discord are smaller — a handful of scenarios each, no profile system, against pre-existing real channels — so their reference lives here.
### Shared CLI flags
Both lanes register through `extensions/qa-lab/src/live-transports/shared/live-transport-cli.ts` and accept the same flags:
| Flag | Default | Description |
| ------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `--scenario <id>` | — | Run only this scenario. Repeatable. |
| `--output-dir <path>` | `<repo>/.artifacts/qa-e2e/{telegram,discord}-<timestamp>` | Where reports/summary/observed messages and the output log are written. Relative paths resolve against `--repo-root`. |
| `--repo-root <path>` | `process.cwd()` | Repository root when invoking from a neutral cwd. |
| `--sut-account <id>` | `sut` | Temporary account id inside the QA gateway config. |
| `--provider-mode <mode>` | `live-frontier` | `mock-openai` or `live-frontier` (legacy `live-openai` still works). |
| `--model <ref>` / `--alt-model <ref>` | provider default | Primary/alternate model refs. |
| `--fast` | off | Provider fast mode where supported. |
| `--credential-source <env\|convex>` | `env` | See [Convex credential pool](#convex-credential-pool). |
| `--credential-role <maintainer\|ci>` | `ci` in CI, `maintainer` otherwise | Role used when `--credential-source convex`. |
Both exit non-zero on any failed scenario. `--allow-failures` writes artifacts without setting a failing exit code.
### Telegram QA
```bash
pnpm openclaw qa telegram
```
Targets one real private Telegram group with two distinct bots (driver + SUT). The SUT bot must have a Telegram username; bot-to-bot observation works best when both bots have **Bot-to-Bot Communication Mode** enabled in `@BotFather`.
Required env when `--credential-source env`:
- `OPENCLAW_QA_TELEGRAM_GROUP_ID` — numeric chat id (string).
- `OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN`
- `OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`
Optional:
- `OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT=1` keeps message bodies in observed-message artifacts (default redacts).
Scenarios (`extensions/qa-lab/src/live-transports/telegram/telegram-live.runtime.ts:44`):
- `telegram-canary`
- `telegram-mention-gating`
- `telegram-mentioned-message-reply`
- `telegram-help-command`
- `telegram-commands-command`
- `telegram-tools-compact-command`
- `telegram-whoami-command`
- `telegram-context-command`
Output artifacts:
- `telegram-qa-report.md`
- `telegram-qa-summary.json` — includes per-reply RTT (driver send → observed SUT reply) starting with the canary.
- `telegram-qa-observed-messages.json` — bodies redacted unless `OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT=1`.
### Discord QA
```bash
pnpm openclaw qa discord
```
Targets one real private Discord guild channel with two bots: a driver bot controlled by the harness and a SUT bot started by the child OpenClaw gateway through the bundled Discord plugin. Verifies channel mention handling and that the SUT bot has registered the native `/help` command with Discord.
Required env when `--credential-source env`:
- `OPENCLAW_QA_DISCORD_GUILD_ID`
- `OPENCLAW_QA_DISCORD_CHANNEL_ID`
- `OPENCLAW_QA_DISCORD_DRIVER_BOT_TOKEN`
- `OPENCLAW_QA_DISCORD_SUT_BOT_TOKEN`
- `OPENCLAW_QA_DISCORD_SUT_APPLICATION_ID` — must match the SUT bot user id returned by Discord (the lane fails fast otherwise).
Optional:
- `OPENCLAW_QA_DISCORD_CAPTURE_CONTENT=1` keeps message bodies in observed-message artifacts.
Scenarios (`extensions/qa-lab/src/live-transports/discord/discord-live.runtime.ts:36`):
- `discord-canary`
- `discord-mention-gating`
- `discord-native-help-command-registration`
Output artifacts:
- `discord-qa-report.md`
- `discord-qa-summary.json`
- `discord-qa-observed-messages.json` — bodies redacted unless `OPENCLAW_QA_DISCORD_CAPTURE_CONTENT=1`.
### Convex credential pool
Both Telegram and Discord lanes can lease credentials from a shared Convex pool instead of reading the env vars above. Pass `--credential-source convex` (or set `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`); QA Lab acquires an exclusive lease, heartbeats it for the duration of the run, and releases it on shutdown. Pool kinds are `"telegram"` and `"discord"`.
Payload shapes the broker validates on `admin/add`:
- Telegram (`kind: "telegram"`): `{ groupId: string, driverToken: string, sutToken: string }``groupId` must be a numeric chat-id string.
- Discord (`kind: "discord"`): `{ guildId: string, channelId: string, driverBotToken: string, sutBotToken: string, sutApplicationId: string }`.
Operational env vars and the Convex broker endpoint contract live in [Testing → Shared Telegram credentials via Convex](/help/testing#shared-telegram-credentials-via-convex-v1) (the section name predates Discord support; the broker semantics are identical for both kinds).
## Repo-backed seeds
Seed assets live in `qa/`:
@@ -233,19 +318,79 @@ provider names.
## Transport adapters
`qa-lab` owns a generic transport seam for markdown QA scenarios.
`qa-channel` is the first adapter on that seam, but the design target is wider:
future real or synthetic channels should plug into the same suite runner
instead of adding a transport-specific QA runner.
`qa-lab` owns a generic transport seam for markdown QA scenarios. `qa-channel` is the first adapter on that seam, but the design target is wider: future real or synthetic channels should plug into the same suite runner instead of adding a transport-specific QA runner.
At the architecture level, the split is:
- `qa-lab` owns generic scenario execution, worker concurrency, artifact writing, and reporting.
- the transport adapter owns gateway config, readiness, inbound and outbound observation, transport actions, and normalized transport state.
- markdown scenario files under `qa/scenarios/` define the test run; `qa-lab` provides the reusable runtime surface that executes them.
- The transport adapter owns gateway config, readiness, inbound and outbound observation, transport actions, and normalized transport state.
- Markdown scenario files under `qa/scenarios/` define the test run; `qa-lab` provides the reusable runtime surface that executes them.
Maintainer-facing adoption guidance for new channel adapters lives in
[Testing](/help/testing#adding-a-channel-to-qa).
### Adding a channel
Adding a channel to the markdown QA system requires exactly two things:
1. A transport adapter for the channel.
2. A scenario pack that exercises the channel contract.
Do not add a new top-level QA command root when the shared `qa-lab` host can own the flow.
`qa-lab` owns the shared host mechanics:
- the `openclaw qa` command root
- suite startup and teardown
- worker concurrency
- artifact writing
- report generation
- scenario execution
- compatibility aliases for older `qa-channel` scenarios
Runner plugins own the transport contract:
- how `openclaw qa <runner>` is mounted beneath the shared `qa` root
- how the gateway is configured for that transport
- how readiness is checked
- how inbound events are injected
- how outbound messages are observed
- how transcripts and normalized transport state are exposed
- how transport-backed actions are executed
- how transport-specific reset or cleanup is handled
The minimum adoption bar for a new channel:
1. Keep `qa-lab` as the owner of the shared `qa` root.
2. Implement the transport runner on the shared `qa-lab` host seam.
3. Keep transport-specific mechanics inside the runner plugin or channel harness.
4. Mount the runner as `openclaw qa <runner>` instead of registering a competing root command. Runner plugins should declare `qaRunners` in `openclaw.plugin.json` and export a matching `qaRunnerCliRegistrations` array from `runtime-api.ts`. Keep `runtime-api.ts` light; lazy CLI and runner execution should stay behind separate entrypoints.
5. Author or adapt markdown scenarios under the themed `qa/scenarios/` directories.
6. Use the generic scenario helpers for new scenarios.
7. Keep existing compatibility aliases working unless the repo is doing an intentional migration.
The decision rule is strict:
- If behavior can be expressed once in `qa-lab`, put it in `qa-lab`.
- If behavior depends on one channel transport, keep it in that runner plugin or plugin harness.
- If a scenario needs a new capability that more than one channel can use, add a generic helper instead of a channel-specific branch in `suite.ts`.
- If a behavior is only meaningful for one transport, keep the scenario transport-specific and make that explicit in the scenario contract.
### Scenario helper names
Preferred generic helpers for new scenarios:
- `waitForTransportReady`
- `waitForChannelReady`
- `injectInboundMessage`
- `injectOutboundMessage`
- `waitForTransportOutboundMessage`
- `waitForChannelOutboundMessage`
- `waitForNoTransportOutbound`
- `getTransportSnapshot`
- `readTransportMessage`
- `readTransportTranscript`
- `formatTransportTranscript`
- `resetTransport`
Compatibility aliases remain available for existing scenarios — `waitForQaChannelReady`, `waitForOutboundMessage`, `waitForNoOutbound`, `formatConversationTranscript`, `resetBus` — but new scenario authoring should use the generic names. The aliases exist to avoid a flag-day migration, not as the model going forward.
## Reporting
@@ -257,6 +402,8 @@ The report should answer:
- What stayed blocked
- What follow-up scenarios are worth adding
For the inventory of available scenarios — useful when sizing follow-up work or wiring a new transport — run `pnpm openclaw qa coverage` (add `--json` for machine-readable output).
For character and style checks, run the same scenario across multiple live model
refs and write a judged Markdown report:
@@ -312,6 +459,7 @@ When no `--judge-model` is passed, the judges default to
## Related docs
- [Testing](/help/testing)
- [Matrix QA](/concepts/qa-matrix)
- [QA Channel](/channels/qa-channel)
- [Testing](/help/testing)
- [Dashboard](/web/dashboard)

136
docs/concepts/qa-matrix.md Normal file
View File

@@ -0,0 +1,136 @@
---
summary: "Maintainer reference for the Docker-backed Matrix live QA lane: CLI, profiles, env vars, scenarios, and output artifacts."
read_when:
- Running pnpm openclaw qa matrix locally
- Adding or selecting Matrix QA scenarios
- Triaging Matrix QA failures, timeouts, or stuck cleanup
title: "Matrix QA"
---
The Matrix QA lane runs the bundled `@openclaw/matrix` plugin against a disposable Tuwunel homeserver in Docker, with temporary driver, SUT, and observer accounts plus seeded rooms. It is the live transport-real coverage for Matrix.
This is maintainer-only tooling. Packaged OpenClaw releases intentionally omit `qa-lab`, so `openclaw qa` is only available from a source checkout. Source checkouts load the bundled runner directly — no plugin install step is needed.
For broader QA framework context, see [QA overview](/concepts/qa-e2e-automation).
## Quick start
```bash
pnpm openclaw qa matrix --profile fast --fail-fast
```
Plain `pnpm openclaw qa matrix` runs `--profile all` and does not stop on first failure. Use `--profile fast --fail-fast` for a release gate; shard the catalog with `--profile transport|media|e2ee-smoke|e2ee-deep|e2ee-cli` when running the full inventory in parallel.
## What the lane does
1. Provisions a disposable Tuwunel homeserver in Docker (default image `ghcr.io/matrix-construct/tuwunel:v1.5.1`, server name `matrix-qa.test`, port `28008`).
2. Registers three temporary users — `driver` (sends inbound traffic), `sut` (the OpenClaw Matrix account under test), `observer` (third-party traffic capture).
3. Seeds rooms required by the selected scenarios (main, threading, media, restart, secondary, allowlist, E2EE, verification DM, etc.).
4. Starts a child OpenClaw gateway with the real Matrix plugin scoped to the SUT account; `qa-channel` is not loaded in the child.
5. Runs scenarios in sequence, observing events through the driver/observer Matrix clients.
6. Tears down the homeserver, writes report and summary artifacts, then exits.
## CLI
```text
pnpm openclaw qa matrix [options]
```
### Common flags
| Flag | Default | Description |
| --------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `--profile <profile>` | `all` | Scenario profile. See [Profiles](#profiles). |
| `--fail-fast` | off | Stop after the first failed check or scenario. |
| `--scenario <id>` | — | Run only this scenario. Repeatable. See [Scenarios](#scenarios). |
| `--output-dir <path>` | `<repo>/.artifacts/qa-e2e/matrix-<timestamp>` | Where reports, summary, observed events, and the output log are written. Relative paths resolve against `--repo-root`. |
| `--repo-root <path>` | `process.cwd()` | Repository root when invoking from a neutral working directory. |
| `--sut-account <id>` | `sut` | Matrix account id inside the QA gateway config. |
### Provider flags
The lane uses a real Matrix transport but the model provider is configurable:
| Flag | Default | Description |
| ------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `--provider-mode <mode>` | `live-frontier` | `mock-openai` for deterministic mock dispatch or `live-frontier` for live frontier providers. The legacy alias `live-openai` still works. |
| `--model <ref>` | provider default | Primary `provider/model` ref. |
| `--alt-model <ref>` | provider default | Alternate `provider/model` ref where scenarios switch mid-run. |
| `--fast` | off | Enable provider fast mode where supported. |
Matrix QA does not accept `--credential-source` or `--credential-role`. The lane provisions disposable users locally; there is no shared credential pool to lease against.
## Profiles
The selected profile decides which scenarios run.
| Profile | Use it for |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `all` (default) | Full catalog. Slow but exhaustive. |
| `fast` | Release-gate subset that exercises the live transport contract: canary, mention gating, allowlist block, reply shape, restart resume, thread follow-up, thread isolation, reaction observation. |
| `transport` | Transport-level threading, DM, room, autojoin, mention/allowlist scenarios. |
| `media` | Image, audio, video, PDF, EPUB attachment coverage. |
| `e2ee-smoke` | Minimum E2EE coverage — basic encrypted reply, thread follow-up, bootstrap success. |
| `e2ee-deep` | Exhaustive E2EE state-loss, backup, key, and recovery scenarios. |
| `e2ee-cli` | `openclaw matrix encryption setup` and `verify *` CLI scenarios driven through the QA harness. |
The exact mapping lives in `extensions/qa-matrix/src/runners/contract/scenario-catalog.ts`.
## Scenarios
The full scenario id list is the `MatrixQaScenarioId` union in `extensions/qa-matrix/src/runners/contract/scenario-catalog.ts:15`. Categories include:
- threading — `matrix-thread-*`, `matrix-subagent-thread-spawn`
- top-level / DM / room — `matrix-top-level-reply-shape`, `matrix-room-*`, `matrix-dm-*`
- media — `matrix-media-type-coverage`, `matrix-room-image-understanding-attachment`, `matrix-attachment-only-ignored`, `matrix-unsupported-media-safe`
- routing — `matrix-room-autojoin-invite`, `matrix-secondary-room-*`
- reactions — `matrix-reaction-*`
- restart and replay — `matrix-restart-*`, `matrix-stale-sync-replay-dedupe`, `matrix-room-membership-loss`, `matrix-homeserver-restart-resume`, `matrix-initial-catchup-then-incremental`
- mention gating and allowlists — `matrix-mention-*`, `matrix-allowlist-*`, `matrix-multi-actor-ordering`, `matrix-inbound-edit-*`, `matrix-mxid-prefixed-command-block`, `matrix-observer-allowlist-override`
- E2EE — `matrix-e2ee-*` (basic reply, thread follow-up, bootstrap, recovery key lifecycle, state-loss variants, server backup behavior, device hygiene, SAS / QR / DM verification, restart, artifact redaction)
- E2EE CLI — `matrix-e2ee-cli-*` (encryption setup, idempotent setup, bootstrap failure, recovery-key lifecycle, multi-account, gateway-reply round-trip, self-verification)
Pass `--scenario <id>` (repeatable) to run a hand-picked set; combine with `--profile all` to ignore profile gating.
## Environment variables
| Variable | Default | Effect |
| --------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OPENCLAW_QA_MATRIX_TIMEOUT_MS` | `1800000` (30 min) | Hard upper bound on the entire run. |
| `OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS` | `8000` | Quiet window for negative no-reply assertions. Clamped to `≤` the run timeout. |
| `OPENCLAW_QA_MATRIX_CLEANUP_TIMEOUT_MS` | `90000` | Bound for Docker teardown. Failure surfaces include the recovery `docker compose ... down --remove-orphans` command. |
| `OPENCLAW_QA_MATRIX_TUWUNEL_IMAGE` | `ghcr.io/matrix-construct/tuwunel:v1.5.1` | Override the homeserver image when validating against a different Tuwunel version. |
| `OPENCLAW_QA_MATRIX_PROGRESS` | on | `0` silences `[matrix-qa] ...` progress lines on stderr. `1` forces them on. |
| `OPENCLAW_QA_MATRIX_CAPTURE_CONTENT` | redacted | `1` keeps message body and `formatted_body` in `matrix-qa-observed-events.json`. Default redacts to keep CI artifacts safe. |
| `OPENCLAW_QA_MATRIX_DISABLE_FORCE_EXIT` | off | `1` skips the deterministic `process.exit` after artifact write. The default forces exit because matrix-js-sdk's native crypto handles can keep the event loop alive past artifact completion. |
| `OPENCLAW_RUN_NODE_OUTPUT_LOG` | unset | When set by an outer launcher (e.g. `scripts/run-node.mjs`), Matrix QA reuses that log path instead of starting its own tee. |
## Output artifacts
Written to `--output-dir`:
- `matrix-qa-report.md` — Markdown protocol report (what passed, failed, was skipped, and why).
- `matrix-qa-summary.json` — Structured summary suitable for CI parsing and dashboards.
- `matrix-qa-observed-events.json` — Observed Matrix events from the driver and observer clients. Bodies are redacted unless `OPENCLAW_QA_MATRIX_CAPTURE_CONTENT=1`.
- `matrix-qa-output.log` — Combined stdout/stderr from the run. If `OPENCLAW_RUN_NODE_OUTPUT_LOG` is set, the outer launcher's log is reused instead.
The default output dir is `<repo>/.artifacts/qa-e2e/matrix-<timestamp>` so successive runs do not overwrite each other.
## Triage tips
- **Run hangs near the end:** `matrix-js-sdk` native crypto handles can outlive the harness. The default forces a clean `process.exit` after artifact write; if you have unset `OPENCLAW_QA_MATRIX_DISABLE_FORCE_EXIT=1`, expect the process to linger.
- **Cleanup error:** look for the printed recovery command (a `docker compose ... down --remove-orphans` invocation) and run it manually to release the homeserver port.
- **Flaky negative-assertion windows in CI:** lower `OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS` (default 8 s) when CI is fast; raise it on slow shared runners.
- **Need redacted bodies for a bug report:** rerun with `OPENCLAW_QA_MATRIX_CAPTURE_CONTENT=1` and attach `matrix-qa-observed-events.json`. Treat the resulting artifact as sensitive.
- **Different Tuwunel version:** point `OPENCLAW_QA_MATRIX_TUWUNEL_IMAGE` at the version under test. The lane checks in only the pinned default image.
## Live transport contract
Matrix is one of three live transport lanes (Matrix, Telegram, Discord) that share a single contract checklist defined in [QA overview → Live transport coverage](/concepts/qa-e2e-automation#live-transport-coverage). `qa-channel` remains the broad synthetic suite and is intentionally not part of that matrix.
## Related
- [QA overview](/concepts/qa-e2e-automation) — overall QA stack and live transport contract
- [QA Channel](/channels/qa-channel) — synthetic channel adapter for repo-backed scenarios
- [Testing](/help/testing) — running tests and adding QA coverage
- [Matrix](/channels/matrix) — the channel plugin under test

View File

@@ -77,7 +77,7 @@ Defaults: `debounceMs: 1000`, `cap: 20`, `drop: summarize`.
- Applies to auto-reply agent runs across all inbound channels that use the gateway reply pipeline (WhatsApp web, Telegram, Slack, Discord, Signal, iMessage, webchat, etc.).
- Default lane (`main`) is process-wide for inbound + main heartbeats; set `agents.defaults.maxConcurrent` to allow multiple sessions in parallel.
- Additional lanes may exist (e.g. `cron`, `subagent`) so background jobs can run in parallel without blocking inbound replies. These detached runs are tracked as [background tasks](/automation/tasks).
- Additional lanes may exist (e.g. `cron`, `cron-nested`, `nested`, `subagent`) so background jobs can run in parallel without blocking inbound replies. Isolated cron agent turns hold a `cron` slot while their inner agent execution uses `cron-nested`; both use `cron.maxConcurrentRuns`. Shared non-cron `nested` flows keep their own lane behavior. These detached runs are tracked as [background tasks](/automation/tasks).
- Per-session lanes guarantee that only one agent run touches a given session at a time.
- No external dependencies or background worker threads; pure TypeScript + promises.

View File

@@ -22,6 +22,27 @@ orchestrate sub-agents.
| `subagents` | List, steer, or kill spawned sub-agents for this session |
| `session_status` | Show a `/status`-style card and optionally set a per-session model override |
These tools are still subject to the active tool profile and allow/deny
policy. `tools.profile: "coding"` includes the full session orchestration
set, including `sessions_spawn`, `sessions_yield`, and `subagents`.
`tools.profile: "messaging"` includes cross-session messaging tools
(`sessions_list`, `sessions_history`, `sessions_send`, `session_status`) but
does not include sub-agent spawning. To keep a messaging profile and still
allow native delegation, add:
```json5
{
tools: {
profile: "messaging",
alsoAllow: ["sessions_spawn", "sessions_yield", "subagents"],
},
}
```
Group, provider, sandbox, and per-agent policies can still remove those tools
after the profile stage. Use `/tools` from the affected session to inspect the
effective tool list.
## Listing and reading sessions
`sessions_list` returns sessions with their key, agentId, kind, channel, model,
@@ -82,7 +103,9 @@ agents alternate messages (up to 5 turns). The target agent can reply
or another visible session. It reports usage, time, model/runtime state, and
linked background-task context when present. Like `/status`, it can backfill
sparse token/cache counters from the latest transcript usage entry, and
`model=default` clears a per-session override.
`model=default` clears a per-session override. Use `sessionKey="current"` for
the caller's current session; visible client labels such as `openclaw-tui` are
not session keys.
`sessions_yield` intentionally ends the current turn so the next message can be
the follow-up event you are waiting for. Use it after spawning sub-agents when

View File

@@ -118,6 +118,8 @@ to `"enforce"` for automatic cleanup:
}
```
For production-sized `maxEntries` limits, Gateway runtime writes use a small high-water buffer and clean back down to the configured cap in batches. This avoids running full store cleanup on every isolated cron session. `openclaw sessions cleanup --enforce` applies the cap immediately.
Preview with `openclaw sessions cleanup --dry-run`.
## Inspecting sessions

View File

@@ -116,12 +116,9 @@ heartbeats are disabled for the default agent or
files concise — especially `MEMORY.md`, which can grow over time and lead to
unexpectedly high context usage and more frequent compaction.
> **Note:** `memory/*.md` daily files are **not** part of the normal bootstrap
> Project Context. On ordinary turns they are accessed on demand via the
> `memory_search` and `memory_get` tools, so they do not count against the
> context window unless the model explicitly reads them. Bare `/new` and
> `/reset` turns are the exception: the runtime can prepend recent daily memory
> as a one-shot startup-context block for that first turn.
<Note>
`memory/*.md` daily files are **not** part of the normal bootstrap Project Context. On ordinary turns they are accessed on demand via the `memory_search` and `memory_get` tools, so they do not count against the context window unless the model explicitly reads them. Bare `/new` and `/reset` turns are the exception: the runtime can prepend recent daily memory as a one-shot startup-context block for that first turn.
</Note>
Large files are truncated with a marker. The max per-file size is controlled by
`agents.defaults.bootstrapMaxChars` (default: 12000). Total injected bootstrap

View File

@@ -52,6 +52,10 @@
]
},
"redirects": [
{
"source": "/install/migrating-matrix",
"destination": "/channels/matrix-migration"
},
{
"source": "/help/gpt54-codex-agentic-parity",
"destination": "/help/gpt55-codex-agentic-parity"
@@ -340,6 +344,14 @@
"source": "/bonjour",
"destination": "/gateway/bonjour"
},
{
"source": "/gateway/config-tts",
"destination": "/tools/tts"
},
{
"source": "/gateway/observability",
"destination": "/gateway/opentelemetry"
},
{
"source": "/browser",
"destination": "/tools/browser"
@@ -975,6 +987,22 @@
"group": "Install overview",
"pages": ["install/index", "install/installer", "install/node"]
},
{
"group": "Maintenance",
"pages": [
"install/updating",
{
"group": "Migrating",
"pages": [
"install/migrating",
"install/migrating-claude",
"install/migrating-hermes"
]
},
"install/uninstall",
"install/development-channels"
]
},
{
"group": "Containers",
"pages": [
@@ -1007,16 +1035,6 @@
"install/render"
]
},
{
"group": "Maintenance",
"pages": [
"install/updating",
"install/migrating",
"install/migrating-matrix",
"install/uninstall",
"install/development-channels"
]
},
{
"group": "Advanced setup",
"pages": ["start/setup", "pi-dev"]
@@ -1043,6 +1061,7 @@
"channels/imessage",
"channels/bluebubbles",
"channels/matrix",
"channels/matrix-migration",
"channels/matrix-push-rules"
]
},
@@ -1102,7 +1121,8 @@
"concepts/oauth",
"start/bootstrapping",
"concepts/experimental-features",
"concepts/qa-e2e-automation"
"concepts/qa-e2e-automation",
"concepts/qa-matrix"
]
},
{
@@ -1545,6 +1565,7 @@
"cli/gateway",
"cli/health",
"cli/logs",
"cli/migrate",
"cli/onboard",
"cli/reset",
"cli/secrets",

View File

@@ -7,7 +7,7 @@ title: "Authentication"
---
<Note>
This page covers **model provider** authentication (API keys, OAuth, Claude CLI reuse, and Anthropic setup-token). For **gateway connection** authentication (token, password, trusted-proxy), see [Configuration](/gateway/configuration) and [Trusted Proxy Auth](/gateway/trusted-proxy-auth).
This page is the **model provider** authentication reference (API keys, OAuth, Claude CLI reuse, and Anthropic setup-token). For **gateway connection** authentication (token, password, trusted-proxy), see [Configuration](/gateway/configuration) and [Trusted Proxy Auth](/gateway/trusted-proxy-auth).
</Note>
OpenClaw supports OAuth and API keys for model providers. For always-on gateway

View File

@@ -17,7 +17,7 @@ Key parameters:
- `command` (required)
- `yieldMs` (default 10000): autobackground after this delay
- `background` (bool): background immediately
- `timeout` (seconds, default 1800): kill the process after this timeout
- `timeout` (seconds, default `tools.exec.timeoutSec`): kill the process after this timeout; set `timeout: 0` only to disable the exec process timeout for that call
- `elevated` (bool): run outside the sandbox if elevated mode is enabled/allowed (`gateway` by default, or `node` when the exec target is `node`)
- Need a real TTY? Set `pty: true`.
- `workdir`, `env`
@@ -26,6 +26,7 @@ Behavior:
- Foreground runs return output directly.
- When backgrounded (explicit or timeout), the tool returns `status: "running"` + `sessionId` and a short tail.
- Background and `yieldMs` runs inherit `tools.exec.timeoutSec` unless the call provides an explicit `timeout`.
- Output is kept in memory until the session is polled or cleared.
- If the `process` tool is disallowed, `exec` runs synchronously and ignores `yieldMs`/`background`.
- Spawned exec commands receive `OPENCLAW_SHELL=exec` for context-aware shell/profile rules.

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