Compare commits

..

598 Commits

Author SHA1 Message Date
pashpashpash
ae03b79b73 Fail fast for removed Codex import auth choice 2026-04-22 19:12:21 -07:00
pashpashpash
d507d5a766 Preserve removed Codex import auth choice 2026-04-22 19:07:02 -07:00
pashpashpash
a7a3a5f37b Fix legacy update compat sidecars 2026-04-22 19:07:02 -07:00
pashpashpash
2476992301 Remove stale Codex import auth choice 2026-04-22 19:06:28 -07:00
pashpashpash
1151e7d40b Remove Codex CLI auth import 2026-04-22 19:06:28 -07:00
Peter Steinberger
51ed22e608 feat(providers): add streaming stt providers 2026-04-23 03:05:53 +01:00
Peter Steinberger
5b68092351 ci: pass gateway watch artifacts across runners 2026-04-23 03:04:22 +01:00
Peter Steinberger
c4242890f4 ci: reuse runtime artifacts for gateway watch 2026-04-23 03:01:08 +01:00
Peter Steinberger
74dfeaae0d fix(qa): preserve image generation plugin allowlist 2026-04-23 02:55:22 +01:00
Peter Steinberger
e3e2626583 fix: update generated protocol models 2026-04-23 02:49:50 +01:00
Peter Steinberger
c9ea10b184 ci: rotate ci concurrency key 2026-04-23 02:47:42 +01:00
Gustavo Madeira Santana
c992a8e5d8 Harden diagnostic stability bundle imports 2026-04-22 21:47:23 -04:00
Peter Steinberger
1489febee9 test: cover docker MCP cleanup for subagents 2026-04-23 02:46:13 +01:00
Peter Steinberger
ccf2e77e8d fix: retire one-shot subagent MCP runtimes 2026-04-23 02:46:13 +01:00
Peter Steinberger
dcff528805 ci: rebalance extension shards 2026-04-23 02:43:02 +01:00
Peter Steinberger
2e90a2247e fix: harden Slack stream fallback delivery (#70370) (thanks @mvanhorn) 2026-04-23 02:42:48 +01:00
Matt Van Horn
e55b932632 fix(slack): fall back to chat.postMessage when stream finalize fails pre-flush
Address adversarial review finding on #70295: the prior swallow-on-benign
fix silently dropped short replies to Slack Connect users. The SDK's
ChatStreamer buffers text locally until buffer_size (256 default), so
short replies never trigger chat.startStream via append(). streamer.stop()
then issues startStream internally; on Slack Connect recipients this
throws user_not_found. With the prior fix that error was swallowed and
the dispatcher marked the turn delivered - user saw 'done' reaction but
no message.

SlackStreamSession now tracks delivered (true once any Slack API call
returned a response) and pendingText (accumulation of every append +
final-stop text). stopSlackStream:
  - swallows the benign code when delivered=true (prior append flushed;
    text is visible; same behavior as before)
  - throws a new SlackStreamNotDeliveredError carrying pendingText when
    delivered=false (nothing reached Slack)

dispatch.ts catches SlackStreamNotDeliveredError and posts pendingText
via a rename-bound chat.postMessage (to dodge the unicorn lint rule),
and flips streamFallbackDelivered so anyReplyDelivered stays correct.

Fixes #70295
2026-04-23 02:42:48 +01:00
Matt Van Horn
676ed34cbd fix(slack): treat Slack Connect finalize errors as benign in stopSlackStream
When Slack's chat.stopStream fails with user_not_found (Slack Connect DM
recipients), team_not_found (cross-workspace shared channels), or
missing_recipient_user_id (DM closed mid-stream), the text already
delivered via append() is still visible to the user. Swallow those
specific codes and mark the session stopped rather than surfacing a
spurious 'slack-stream: failed to stop stream' error in dispatch. Other
Slack API errors still propagate.

Fixes #70295
2026-04-23 02:42:48 +01:00
Peter Steinberger
688fc288af ci: trim duplicate android apk build 2026-04-23 02:38:01 +01:00
Peter Steinberger
5461195035 docs: document session mailbox discovery (#69839) 2026-04-23 02:33:55 +01:00
Peter Steinberger
b53bce9f47 fix(agents): filter session previews after visibility 2026-04-23 02:33:55 +01:00
dangoZhang
13882581b6 fix(agents): clean up sessions_list forwarding 2026-04-23 02:33:55 +01:00
dangoZhang
1a4c32e366 feat: expose mailbox session discovery in sessions_list 2026-04-23 02:33:55 +01:00
Peter Steinberger
dcc243c889 test: stabilize loopback port release check 2026-04-23 02:25:53 +01:00
Peter Steinberger
4ff720a837 fix(openai): harden realtime stt 2026-04-23 02:22:17 +01:00
Peter Steinberger
26bf916382 fix(gateway): resolve dynamic models during warmup 2026-04-23 02:20:11 +01:00
Peter Steinberger
1cbd5a9470 fix(codex): harden app-server approvals 2026-04-23 02:20:10 +01:00
Peter Steinberger
de95e414d1 style: format stale source files 2026-04-23 02:20:10 +01:00
Peter Steinberger
0ada97d513 fix: restore legacy update compat sidecars 2026-04-23 02:19:19 +01:00
Peter Steinberger
0f77fcac31 test: improve xai realtime stt live coverage 2026-04-23 02:06:07 +01:00
Peter Steinberger
6a1d6b7d89 ci: run docker smoke for scope changes 2026-04-23 01:58:58 +01:00
Peter Steinberger
b5cc7ea879 ci: expand docker smoke changed scope 2026-04-23 01:57:25 +01:00
Peter Steinberger
71ae0d737a fix: override vulnerable uuid dependency 2026-04-23 01:56:14 +01:00
dulingxiao
c4dea58712 fix(moonshot): preserve native Kimi tool_call IDs in openai-completions replay 2026-04-23 01:52:58 +01:00
Peter Steinberger
23a448986f fix(xai): declare websocket runtime dependency 2026-04-23 01:50:00 +01:00
Gustavo Madeira Santana
28818f9140 Improve gateway diagnostics export for support reports (#70324)
Merged via squash.

Prepared head SHA: 3d6ee85993
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-22 20:47:14 -04:00
Peter Steinberger
6b41ef311f fix: isolate external direct-message runtime policy 2026-04-23 01:39:56 +01:00
Peter Steinberger
67f09ea87a feat: add xai realtime transcription 2026-04-23 01:38:11 +01:00
Peter Steinberger
d4c171f594 ci: keep extension batch parallelism at two 2026-04-23 01:35:30 +01:00
Peter Steinberger
53f388fa83 docs(plugins): document npm update behavior 2026-04-23 01:29:32 +01:00
Peter Steinberger
67850c4fc8 ci: run extension batches three-wide 2026-04-23 01:29:20 +01:00
Peter Steinberger
87a64a33f1 fix(plugins): clarify installed plugin replacement 2026-04-23 01:25:29 +01:00
Peter Steinberger
fa43cbfcba fix: drop invalid Codex app-server service tiers 2026-04-23 01:24:25 +01:00
Peter Steinberger
9f358456db ci: skip duplicate extension fast on main 2026-04-23 01:23:23 +01:00
Peter Steinberger
0946e37523 fix(plugins): skip unchanged npm updates 2026-04-23 01:23:03 +01:00
Peter Steinberger
bf132d6fb9 test(qa-matrix): stabilize sync timeout cursor 2026-04-23 01:21:52 +01:00
Peter Steinberger
f72c97afca test(qa-matrix): stabilize sync timeout 2026-04-23 01:20:45 +01:00
Peter Steinberger
7724f7a923 test(opencode-go): lock pi catalog coverage 2026-04-23 01:17:13 +01:00
Peter Steinberger
d6eac07b06 ci: add fast bundled docker e2e 2026-04-23 01:09:35 +01:00
Peter Steinberger
012841816d feat: add xai speech-to-text support 2026-04-23 01:06:07 +01:00
Peter Steinberger
2bec189174 test(zalo): trim lifecycle reset imports 2026-04-23 01:02:57 +01:00
Peter Steinberger
4177b27e24 docs: note codex dynamic tool fingerprint fix (#69976) 2026-04-23 01:01:33 +01:00
chen-zhang-cs-code
5210b20523 fix(codex): ignore tool descriptions in thread fingerprint 2026-04-23 01:01:33 +01:00
Peter Steinberger
38c76b34f4 test(agents): stabilize context lookup warmup 2026-04-23 00:58:13 +01:00
Peter Steinberger
3d07eadec3 fix: restore model-level base url contract (#70340) 2026-04-23 00:52:32 +01:00
Peter Steinberger
dbab0f7aad fix: restore codex permission approval targets (#70340) (thanks @Lucenx9) 2026-04-23 00:52:32 +01:00
Lucenx9
08a81740ae fix(codex): restore sanitized permission approval detail 2026-04-23 00:52:32 +01:00
Lucenx9
dc13cd68ed fix(codex): clarify permission approvals 2026-04-23 00:52:32 +01:00
Peter Steinberger
5a5aa3a178 fix(config): tolerate missing channel metadata during auto-enable 2026-04-23 00:50:34 +01:00
Peter Steinberger
53e822f407 fix: keep cli reply runs streaming 2026-04-23 00:49:43 +01:00
Peter Steinberger
c4e5ca8625 fix(agents): expose configured MCP tools in Pi profiles 2026-04-23 00:47:37 +01:00
Peter Steinberger
bba63d4e78 test(codex): await event projector setup 2026-04-23 00:46:04 +01:00
Peter Steinberger
f437d96ae2 fix(config): avoid false reload restarts 2026-04-23 00:44:54 +01:00
Peter Steinberger
c65b232463 fix(amazon-bedrock-mantle): align runtime deps 2026-04-23 00:43:12 +01:00
Peter Steinberger
d50181e209 test(docker): speed bundled dependency e2e 2026-04-23 00:35:17 +01:00
pashpashpash
ff02563c7c feat(codex): add guardian app-server mode (#70090)
Reworks the Codex app-server Guardian change into the final landing shape:

- keep YOLO as the default local app-server mode
- add explicit `appServer.mode: "guardian"`
- remove the legacy `OPENCLAW_CODEX_APP_SERVER_GUARDIAN` shortcut
- document Guardian configuration and behavior
- add Guardian event projection and Docker live probes for approved/ask-back decisions

Co-authored-by: pashpashpash <nik@vault77.ai>
2026-04-23 00:25:43 +01:00
Vincent Koc
34e45ecfcc feat(codex): add llm lifecycle hooks (#70312)
* feat(codex): add llm lifecycle hooks

* fix(codex): close llm hook lifecycle gaps

* fix(codex): dedupe llm hook context

* fix(codex): preserve abort and error hook state
2026-04-22 16:19:59 -07:00
Vincent Koc
a5128777ee feat(codex): add tool hook parity (#70307)
* feat(codex): add tool hook parity

* fix(codex): stabilize tool hook parity

* fix(codex): tighten transcript hook typing

* fix(codex): preserve mirrored transcript idempotency

* fix(codex): normalize tool hook context
2026-04-22 16:18:10 -07:00
Peter Steinberger
da9700903c ci: skip no-op changed-scope fanout 2026-04-23 00:16:01 +01:00
Vincent Koc
44965bf63c fix(diffs): refresh live tool config 2026-04-22 16:14:23 -07:00
Peter Steinberger
1019b663ce chore: format extension runtime deps 2026-04-23 00:12:47 +01:00
Vincent Koc
d686e6f876 fix(hooks): avoid stale active-memory startup fallback 2026-04-22 16:10:01 -07:00
wirjo
18507ed85f feat(amazon-bedrock-mantle): add Claude Opus 4.7 via per-model Anthropic Messages API override (#68730)
* feat(amazon-bedrock-mantle): add Claude Opus 4.7 via Anthropic auth

* fix(amazon-bedrock-mantle): keep Opus 4.7 transport-safe

* fix(amazon-bedrock-mantle): restore anthropic base url helper

* fix(auto-reply): apply runtime auth to conversation labels

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-22 16:09:39 -07:00
KateWilkins
f342da5fcc feat: add xai media providers
Add xAI image generation and text-to-speech provider support with docs, live tests, and guarded provider HTTP handling.\n\nThanks @KateWilkins.
2026-04-23 00:07:39 +01:00
Vincent Koc
386a0884d7 fix(hooks): avoid stale lancedb startup fallback 2026-04-22 16:06:55 -07:00
Peter Steinberger
ed0ffa472b docs: clarify codex compaction docs (#69612) (thanks @91wan) 2026-04-23 00:05:47 +01:00
91wan
dee8150bab docs(codex): narrow compaction claims 2026-04-23 00:05:47 +01:00
Peter Steinberger
bee491f439 test(telegram): cover debounce topic keys at seam 2026-04-22 23:57:08 +01:00
Peter Steinberger
9b1f1036ac fix(channels): isolate bundled load failures 2026-04-22 23:56:14 +01:00
Vincent Koc
e8b56a9928 feat(codex): add prompt and compaction hooks (#70313)
* feat(codex): add prompt and compaction hooks

* fix(codex): clean prompt and compaction hook tests
2026-04-22 15:56:08 -07:00
Peter Steinberger
ac8495adaa fix(config): write through single-file includes 2026-04-22 23:53:56 +01:00
wirjo
2a15a3bb53 fix(amazon-bedrock): add known model context windows to discovery (#65952)
* fix(amazon-bedrock): add known model context windows to discovery

Bedrock's ListFoundationModels API does not expose token limits. Discovery
was hardcoding contextWindow: 32000 for every model, causing Claude (1M),
Nova (300K), and other models to hit premature 'Context limit exceeded'
errors and unnecessary session resets.

Adds a lookup table of known context windows for Bedrock models:
- Anthropic Claude: 200K-1M
- Amazon Nova: 128K-1M
- Meta Llama: 128K
- Mistral: 32K-128K
- DeepSeek: 128K
- Cohere: 128K
- AI21 Jamba: 256K

Inference profile prefixes (us., eu., ap., global.) are stripped before
lookup, so us.anthropic.claude-opus-4-6-v1 correctly resolves to 1M.

Also raises the default fallback from 32K to 128K for unknown models —
most modern models have at least 128K context.

Single file change, no type system modifications.

Complementary to #65030 (provenance flag for warning on unknown models).

Fixes #64919
Related: #64250

* add KNOWN_MAX_TOKENS map and expand model coverage

- Add KNOWN_MAX_TOKENS lookup table with Bedrock-optimized values that
  balance response quality against quota burndown (5x rate for Claude 3.7+)
- Add missing models to KNOWN_CONTEXT_WINDOWS: Opus 4.7 (1M), Opus 4.1/4.5,
  Sonnet 4, Claude 3/3.5 Haiku, DeepSeek V3/V3.2, Google Gemma 3
- Refactor prefix-stripping into shared resolveKnownValue() helper
- Fix: use !== undefined instead of truthy check for table lookups
- Wire resolveKnownMaxTokens into toModelDefinition and resolveInferenceProfiles

Quota burndown context: Bedrock reserves input_tokens + max_tokens from
TPM at request start. For Claude 3.7+, output burns at 5x. The values
in KNOWN_MAX_TOKENS are intentionally conservative (8-16K for Claude)
to maximize concurrent throughput while still allowing useful responses.
Thinking budget is added separately by the runtime.

* remove KNOWN_MAX_TOKENS — maxTokens should be handled upstream

Remove the KNOWN_MAX_TOKENS map. Hardcoding maxTokens values in
discovery is the wrong layer to solve this — any explicit value
still gets reserved against Bedrock's TPM quota at request start.

The correct fix is upstream in pi's Bedrock provider: omit maxTokens
from inferenceConfig when not explicitly set, letting the model use
its internal default. This avoids quota waste entirely.

See: badlogic/pi-mono#3399 and badlogic/pi-mono#3400

Keep the expanded KNOWN_CONTEXT_WINDOWS (context windows ARE the
right thing to set in discovery — they affect compaction thresholds
and session management, not API-level quota reservation).

* docs: clarify why hardcoded context windows are needed

Bedrock's ListFoundationModels and GetFoundationModel APIs return no
token limit information — there is no Bedrock API to discover context
windows or max output tokens programmatically. Note that this table
should become a fallback if AWS adds token metadata in the future.

* fix: add au and apac to inference profile prefix regex

Add missing geo prefixes discovered by querying inference profiles
across multiple regions:
- au. (Australia/NZ, used in ap-southeast-2/4/6)
- apac. (Asia-Pacific, used for older models in ap-northeast-1)

Both resolveKnownContextWindow and resolveBaseModelId now handle
all known prefixes: us, eu, ap, apac, au, jp, global.

* test: port au. prefix test from #65449 by @alickgithub2, add apac. coverage

Port the Australia/NZ inference profile test from PR #65449
(credit: @alickgithub2) and extend it to also cover the apac.
prefix discovered in ap-northeast-1.

* expand model coverage: Llama 4, MiniMax, NVIDIA, Mistral 3, GLM, Qwen

Cross-referenced KNOWN_CONTEXT_WINDOWS against live
list-foundation-models API. Added missing models:
- Llama 4 Maverick (1M) and Scout (512K)
- MiniMax M2/M2.1/M2.5 (1M)
- NVIDIA Nemotron Super/Nano variants (128K)
- Mistral Large 3 675B (128K)
- GLM 4.7/4.7-flash/5 (128K)
- Qwen3 Coder/32B/VL (128-256K)

Removed deprecated deepseek.v3-v1:0 and claude-opus-4-20250514
(not in active foundation models list).

* raise default context window from 128K to 200K

200K matches the floor for all current Claude models (the most
popular on Bedrock). Every other active model with a lower actual
limit is already in the explicit table. This ensures new Claude
models get a correct default without requiring a table update.

* test: update discovery test expectations for known context window values

* test: fix remaining contextWindow expectation (default 200K)

* fix(amazon-bedrock): keep conservative context fallback

* docs(changelog): note Bedrock context window fix

* fix(amazon-bedrock): normalize known context fallback

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-22 15:53:41 -07:00
wirjo
420c96e7aa fix(amazon-bedrock-mantle): refresh IAM bearer token via resolveConfigApiKey cache lookup (#68903)
* fix(amazon-bedrock-mantle): refresh IAM bearer token via resolveConfigApiKey cache lookup

The Mantle plugin generates a bearer token from IAM credentials at discovery
time and bakes it as a static string into the provider config. After the
token's cache TTL expires (~1hr), requests fail because resolveConfigApiKey
only handled the explicit AWS_BEARER_TOKEN_BEDROCK env var case.

Fix: expose getCachedIamToken() as a sync read from the existing iamTokenCache,
and wire it into resolveConfigApiKey as a fallback when no explicit env var is
set. The catalog.run still generates/refreshes the token on discovery; this
change ensures the cached token is served at auth resolution time.

Fixes #68900

* fix(amazon-bedrock-mantle): refresh runtime IAM bearer auth

* docs(changelog): note Mantle IAM refresh

* fix(agents): apply runtime auth in simple completion

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-22 15:52:24 -07:00
Devin Robison
2321d67263 fix(gateway): require auth for control ui bootstrap config (#70247)
* fix(gateway): require auth for control ui bootstrap config

* fix(ui): send auth on bootstrap fetch

* fix(ui): keep bootstrap auth same-origin

* fix(ui): refresh bootstrap after auth hello

* docs(changelog): note control ui bootstrap auth

* fix(ui): retry bootstrap auth with alternate shared secret on 401
2026-04-22 16:52:08 -06:00
Peter Steinberger
c87c9742ed fix(telegram): isolate direct chat sandbox sessions 2026-04-22 23:46:34 +01:00
Peter Steinberger
46fba1d814 docs(config): clarify symlinked config support 2026-04-22 23:45:03 +01:00
Devin Robison
95119017c8 fix(openshell): pin sandbox file reads (#69798)
* fix(openshell): pin sandbox file reads against parent symlink swaps

* docs(changelog): note openshell sandbox read pinning (#69798)

* fix(openshell): containment-check against literal root and self-contain file-identity helper

* test(openshell): spy on fsPromises.open for swap races, skip dev=0 test on win32

* fix(openshell): single-syscall fallback identity check + tighten sameFileIdentity types

* fix(openshell): re-fstat pinned handle after identity check for defense-in-depth

* fix(openshell): lstat leaf on platforms without O_NOFOLLOW to close windows symlink gap

* fix(openshell): expose test seam for O_NOFOLLOW availability instead of patching native constants
2026-04-22 16:44:25 -06:00
Val Alexander
12bbb371d0 feat(control-ui): personalize local user identity and tighten layouts
## Summary
- add browser-local operator identity in Control UI and route user name/avatar rendering through the shared chat/avatar path used by assistant and agent surfaces
- tighten Quick Settings, fallback chip, and mobile chat layout behavior so the personalized UI uses space better and avoids clipped controls
- guard oversized local avatar uploads before FileReader allocation, restore the fallback-chip keyboard focus ring, and add the changelog note for the user-visible Control UI work

## Testing
- pnpm test ui/src/ui/views/config-quick.test.ts ui/src/styles/components.test.ts
- pnpm check:changed
2026-04-22 17:38:58 -05:00
Peter Steinberger
5daa104e63 docs: note codex approval hardening (#70356) (thanks @Lucenx9) 2026-04-22 23:38:44 +01:00
Lucenx9
ec5015924c fix(codex): fail closed for unknown approvals 2026-04-22 23:38:44 +01:00
Peter Steinberger
4285958bcd test(codex): cover websocket token rotation (#70328) (thanks @Lucenx9) 2026-04-22 23:37:58 +01:00
Lucenx9
15f285c0cb fix(codex): scope stale shared-client cleanup 2026-04-22 23:37:58 +01:00
Lucenx9
0bc5ccc706 fix(codex): rotate shared app-server clients on auth changes 2026-04-22 23:37:58 +01:00
Peter Steinberger
f4c4e940a6 test(qa): stabilize lab catalog abort fixture 2026-04-22 23:36:34 +01:00
Peter Steinberger
2cd3164a0f feat(providers): share GPT-5 prompt overlay 2026-04-22 23:36:06 +01:00
Peter Steinberger
7b2c9a6fa3 fix(config): recover critical config clobbers 2026-04-22 23:35:48 +01:00
Peter Steinberger
1d7be63228 ci: rebalance extension test shards 2026-04-22 23:29:34 +01:00
Peter Steinberger
22814c1add docs(config): document safe model config merges 2026-04-22 23:23:54 +01:00
Peter Steinberger
f7e668d0ec chore: record extension runtime deps 2026-04-22 23:19:20 +01:00
Peter Steinberger
c2ac1e3ef4 feat: expose OpenClaw tools to ACPX 2026-04-22 23:19:20 +01:00
Peter Steinberger
87f8e82347 fix: isolate Codex ACP auth 2026-04-22 23:18:56 +01:00
Peter Steinberger
819ff0463a fix(config): protect model config merges 2026-04-22 23:18:05 +01:00
Peter Steinberger
f88da75ed9 refactor(channels): centralize runtime binding routes 2026-04-22 23:16:57 +01:00
Peter Steinberger
85d2a9ec1f test(cron): add docker mcp cleanup e2e 2026-04-22 23:12:18 +01:00
Peter Steinberger
816d7a7232 chore(extensions): update runtime dependency manifests 2026-04-22 23:11:43 +01:00
Devin Robison
b76edc09e6 fix(gateway): reauthorize session history SSE updates (#70237)
* fix(gateway): reauthorize session history SSE updates

* docs(changelog): note session history sse reauth

* fix(gateway): use live proxy config for sse reauth

* fix(gateway): skip unrelated session sse reauth

* fix(gateway): filter sse transcript updates early, log work failures, forward-declare cleanup bindings
2026-04-22 16:11:32 -06:00
Peter Steinberger
698f154c28 fix(qa): recheck Matrix sync events after poll 2026-04-22 23:11:27 +01:00
Peter Steinberger
a32a6c2f89 fix: stop generating qa npm sidecars 2026-04-22 23:11:01 +01:00
Peter Steinberger
f66098f8f6 test(github-copilot): add live Responses ID rewrite probe 2026-04-22 23:09:31 +01:00
Peter Steinberger
03c1fff8f6 test(qa): add OpenAI native web search live scenario 2026-04-22 23:06:55 +01:00
Peter Steinberger
1a90893e90 test: keep extension directory filters covered 2026-04-22 23:06:26 +01:00
Val Alexander
eb689f3535 fix(ui): shorten Control UI clear action label (#70355) 2026-04-22 16:52:53 -05:00
Peter Steinberger
e56a6f87ec fix: exclude qa extensions from npm package 2026-04-22 22:48:28 +01:00
Peter Steinberger
ebe32e5cee feat(openai): enable native web search 2026-04-22 22:47:26 +01:00
Peter Steinberger
276d222283 build(deps): bump fast-xml-parser override 2026-04-22 22:45:57 +01:00
wirjo
c7e5289fd2 fix: propagate AWS SDK auth sentinel for IMDS/instance role Bedrock auth (#68964)
* fix: propagate AWS SDK auth sentinel for IMDS/instance role Bedrock auth

When Bedrock auth resolves via AWS SDK default credential chain (IMDS,
ECS task role) with no explicit API key, the auth controller returned
early without calling setRuntimeApiKey(). This left pi's authStorage
unaware that the provider is authenticated, causing 'No API key found
for amazon-bedrock' errors.

Now, when mode is 'aws-sdk' and no explicit API key is available:
1. Try prepareProviderRuntimeAuth to resolve runtime credentials
2. If that returns a real apiKey, use it with auth refresh scheduling
3. Otherwise inject a '__aws_sdk_auth__' sentinel so pi's
   hasConfiguredAuth() passes and the AWS SDK handles request signing

This is a focused fix in auth-controller.ts only, avoiding the risky
model-auth-runtime-shared.ts changes that could re-introduce the
fake-apiKey injection pattern on ECS (see prior regressions #49891,
#50699, #54274).

Fixes #62995

* fix(pi-auth): clean up aws-sdk sentinel fallback

* docs(changelog): note aws-sdk Bedrock auth fix

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-22 14:45:26 -07:00
Peter Steinberger
207d7303b7 test: avoid argv warmup race 2026-04-22 22:42:45 +01:00
Peter Steinberger
2e13f224d6 fix(openai-responses): normalize Copilot response item IDs (#69362) (thanks @Menci) 2026-04-22 22:40:43 +01:00
Vincent Koc
4f9169c6dd fix(hooks): avoid stale skill workshop startup fallback 2026-04-22 14:39:58 -07:00
Peter Steinberger
7f637eafe2 test: run single-channel extension batches 2026-04-22 22:39:17 +01:00
Peter Steinberger
d1e06407bf chore: add extension runtime dependency manifests 2026-04-22 22:36:40 +01:00
Peter Steinberger
6ab3751287 fix: preserve Azure OpenAI completions api version 2026-04-22 22:36:40 +01:00
Peter Steinberger
cb16d22780 fix(cron): retire bundled mcp runtimes 2026-04-22 22:30:47 +01:00
Peter Steinberger
1dc5aad316 test: align matrix acp room binding expectations 2026-04-22 22:30:22 +01:00
Peter Steinberger
8a3e130db8 fix(slack): honor focused thread bindings 2026-04-22 22:29:48 +01:00
Peter Steinberger
cc1e843c90 docs(changelog): note config prefix recovery 2026-04-22 22:29:01 +01:00
Peter Steinberger
5d50b0c48f fix(config): recover prefixed config JSON 2026-04-22 22:29:01 +01:00
Peter Steinberger
77dbc1cda6 ci: rebalance test workers 2026-04-22 22:26:02 +01:00
Vincent Koc
65ae1e54de fix(hooks): avoid stale thread ownership startup fallback 2026-04-22 14:19:13 -07:00
Peter Steinberger
50c95d1d21 refactor(channels): centralize conversation resolution 2026-04-22 22:16:08 +01:00
Vincent Koc
f1372681a8 fix(diffs): refresh live viewer access policy 2026-04-22 14:14:30 -07:00
Peter Steinberger
0588dfe15d fix(config): parse quoted bracket paths 2026-04-22 22:11:45 +01:00
Peter Steinberger
a971884104 test(mcp): strengthen stdio lifecycle coverage 2026-04-22 22:11:30 +01:00
Peter Steinberger
56828545b4 ci: parallelize agents test files 2026-04-22 22:09:25 +01:00
Peter Steinberger
a1319aaadd fix(update): skip package no-op installs 2026-04-22 22:05:29 +01:00
Peter Steinberger
64fb6f71b4 fix(gateway): recover invalid config before startup 2026-04-22 22:05:29 +01:00
Peter Steinberger
f70a46b703 fix(config): preserve authored config writes 2026-04-22 22:05:29 +01:00
Peter Steinberger
5f7b44045d fix(mcp): tear down stdio process trees 2026-04-22 22:04:22 +01:00
Peter Steinberger
2c45879120 fix(config): render warning newlines 2026-04-22 22:04:09 +01:00
Peter Steinberger
b6fbf46eca fix(cron): repair malformed cron job ids via doctor 2026-04-22 22:03:58 +01:00
Peter Steinberger
2e38e09b04 test: harden parallels smoke harness 2026-04-22 22:01:04 +01:00
Peter Steinberger
054fda206e ci: rotate stuck build-smoke queue 2026-04-22 21:59:48 +01:00
Vincent Koc
0f0d399c71 fix(hooks): stop memory-core runtime config fallback 2026-04-22 13:57:10 -07:00
Peter Steinberger
4cb4aad7b1 build: harden tsdown wrapper 2026-04-22 21:54:56 +01:00
Vincent Koc
d25ff59c8b docs(changelog): note pi session tool activation fix 2026-04-22 13:54:04 -07:00
Vincent Koc
fc07b23437 fix(agents): restore pi session tool activation 2026-04-22 13:54:04 -07:00
Vincent Koc
42400813a7 test(plugins): pin live config hook guards 2026-04-22 13:50:51 -07:00
Peter Steinberger
aad1be102d fix(types): narrow live thread ownership config 2026-04-22 21:48:59 +01:00
Peter Steinberger
b648830632 fix: clarify browser playwright-core install guidance 2026-04-22 21:47:58 +01:00
Vincent Koc
99c1bc2cce docs(changelog): note websocket endpoint classifier fix 2026-04-22 13:44:51 -07:00
Vincent Koc
e250ea3668 fix(agents): centralize native websocket endpoint checks 2026-04-22 13:44:51 -07:00
Vincent Koc
4c675216f1 fix(qa): deflake parity approval preflight 2026-04-22 13:43:29 -07:00
Vincent Koc
db5895fd2a refactor(hooks): centralize live plugin config lookup 2026-04-22 13:38:38 -07:00
Peter Steinberger
ee63b9ee49 fix(memory-lancedb): retry failed runtime initialization 2026-04-22 21:20:28 +01:00
Vincent Koc
eae0039aa4 fix(hooks): use live memory-core config during dreaming runs 2026-04-22 13:10:19 -07:00
Peter Steinberger
c4aeeb2762 test(slack): provide send config in identity fallback tests 2026-04-22 21:09:42 +01:00
Zetarcos
38001cdeaa fix(discord): normalize ACP thread binding targets
Normalize Discord ACP thread-binding channel targets at the REST/thread-create boundary while preserving current-conversation binding keys.\n\nThanks @Zetarcos.
2026-04-22 21:09:26 +01:00
martingarramon
238b31a00c test(slack): cover send.ts customize-scope fallback retry path (#69009)
Adds 5 vitest cases for postSlackMessageBestEffort's silent retry
behavior when Slack rejects a chat:write.customize-identity post:

- Retry on err.data.needed matching chat:write.customize
- Retry on chat:write.customize in response_metadata.acceptedScopes
- Retry on chat:write.customize in response_metadata.scopes
- Rethrow on different missing_scope (e.g. channels:history)
- Rethrow when identity is empty (hasCustomIdentity returns false)
2026-04-22 16:06:44 -04:00
Vincent Koc
bc4a097464 fix(hooks): respect live lancedb memory config 2026-04-22 13:06:02 -07:00
Peter Steinberger
3704e3f580 ci: keep extension test fanout under two minutes 2026-04-22 21:06:00 +01:00
Peter Steinberger
6639b21ade test(media): harden media store URI validation 2026-04-22 21:05:41 +01:00
Devin Matthews
5528793adf fix: honor explicit strict-agentic retry contract
Honor explicit strict-agentic execution contracts for incomplete-turn retry guards across providers, including local/compatible models that opt in without relying on OpenAI model inference.

Validation:
- pnpm test src/agents/pi-embedded-runner/run.incomplete-turn.test.ts
- pnpm check:changed
- GitHub CI + parity gate green

Thanks @ziomancer.
2026-04-22 21:03:03 +01:00
Peter Steinberger
c0cafb6bbe perf(plugins): cache normalized jiti aliases 2026-04-22 21:02:29 +01:00
Vincent Koc
834e50f83c fix(hooks): use live thread ownership config 2026-04-22 13:01:32 -07:00
Vincent Koc
fbf554397f fix(hooks): respect live skill workshop config 2026-04-22 12:59:27 -07:00
Val Alexander
9ea5484fa1 fix: normalize opus 4.7 context window
Normalize Anthropic-owned Opus 4.7 context reporting to 1M while keeping inferred and bare discovery paths conservative.

- normalize Anthropic and claude-cli Opus 4.7 runtime/status context metadata to 1M
- keep inferred-provider and bare discovery ids on discovered conservative limits
- add regression coverage for provider, lookup, status, and discovery-cache paths
- keep the Telegram abort-signal wrapper typing narrow so changed-scope validation stays green
2026-04-22 14:58:16 -05:00
Peter Steinberger
c542d42f6f ci: balance extension tests across fewer workers 2026-04-22 20:55:38 +01:00
Vincent Koc
dd47b56243 fix(hooks): refresh active memory config at runtime 2026-04-22 12:55:12 -07:00
Peter Steinberger
f9cbaae19e ci: rotate cancelled docs queue 2026-04-22 20:51:48 +01:00
Josh Lehman
ccc99d85bf fix: restore Pi embedded tool allowlist
Restore the Pi embedded session tool allowlist for OpenAI/OpenAI Codex GPT-5 runs and compaction sessions after Pi 0.68.1 began treating session tools as a global allowlist.

Local validation: pnpm check:changed.
GitHub validation: check/check-additional/node shards green; parity gate red on unrelated config.patch stale/rate-limit QA harness scenario after plugins.allow restart.
2026-04-22 20:51:42 +01:00
Tak Hoffman
78d491d909 feat(commands): gate /models add with modelsWrite (#70321) 2026-04-22 14:49:07 -05:00
Vincent Koc
1ebd8e0bb6 fix(hooks): use live config for memory dreaming runtime 2026-04-22 12:47:57 -07:00
Peter Steinberger
6261f42ac0 ci: merge short auto-reply node shards 2026-04-22 20:47:49 +01:00
Vincent Koc
1e33d63f64 test(memory): pin disabled lifecycle hook wiring 2026-04-22 12:43:23 -07:00
Peter Steinberger
f97c6f8a04 fix(discord): harden partial thread channels 2026-04-22 20:41:50 +01:00
Vincent Koc
e71da6705b fix(hooks): skip skill workshop capture when review is off 2026-04-22 12:41:04 -07:00
Peter Steinberger
8fcca8a5e1 ci: rotate main concurrency queue 2026-04-22 20:39:49 +01:00
Vincent Koc
1704dceca2 test(skill-workshop): pin disabled hook wiring 2026-04-22 12:38:38 -07:00
Vincent Koc
4ed2ea5035 fix(hooks): tighten thread ownership mention matching 2026-04-22 12:36:37 -07:00
Peter Steinberger
2aaac45c07 ci: move node aggregate checks off blacksmith 2026-04-22 20:36:27 +01:00
Vincent Koc
dbba830417 fix(hooks): track thread ownership mentions case-insensitively 2026-04-22 12:34:27 -07:00
Vincent Koc
f9f836eba4 fix(hooks): normalize thread ownership slack id casing 2026-04-22 12:32:33 -07:00
Peter Steinberger
5567f4cb01 docs: note media delivery fixes 2026-04-22 20:32:05 +01:00
Peter Steinberger
976398715f fix(image): resolve custom provider model IDs 2026-04-22 20:32:05 +01:00
Peter Steinberger
81f247b1ae fix(agents): dedupe emitted TTS media 2026-04-22 20:32:05 +01:00
Peter Steinberger
e5b67b7ebd fix(media): load inbound media store URIs 2026-04-22 20:32:05 +01:00
Peter Steinberger
0e761cdba8 fix(gateway): redact audio payloads from chat history 2026-04-22 20:32:05 +01:00
Peter Steinberger
64a98dea8d fix(discord): restore DM reactions and guild activation 2026-04-22 20:29:50 +01:00
Peter Steinberger
f7a52573b0 fix: clear phantom Claude CLI resumes (#70317)
Verify Claude CLI session transcripts before reuse and clear phantom bindings with transcript-missing instead of passing stale --resume ids.\n\nFixes #70177.
2026-04-22 20:29:17 +01:00
Vincent Koc
ec75545a82 fix(hooks): normalize thread ownership channel allowlists 2026-04-22 12:29:08 -07:00
Peter Steinberger
9c733956c0 fix(plugins): repair bundled deps on activation 2026-04-22 20:27:42 +01:00
Vincent Koc
4663e7394b fix(hooks): canonicalize slack thread ownership ids 2026-04-22 12:26:31 -07:00
Vincent Koc
7d088f198f fix(hooks): fail open without thread ownership routing 2026-04-22 12:24:15 -07:00
Peter Steinberger
e6a9e9a700 test: cover Telegram webhook timeout reply continuation 2026-04-22 20:23:53 +01:00
Vincent Koc
9a14307306 test(plugins): pin bundled hook names 2026-04-22 12:22:44 -07:00
Peter Steinberger
d8935ca838 perf: keep gateway live probes off helper imports 2026-04-22 20:22:14 +01:00
Vincent Koc
d0bf9cc19e test(plugins): pin bundled hook registration surfaces 2026-04-22 12:20:21 -07:00
anirudhmarc
24266af1ce fix(amazon-bedrock): inject cache points for application inference profile ARNs (#69953)
* fix(amazon-bedrock): inject cache points for application inference profile ARNs

pi-ai's internal supportsPromptCaching checks model.id for specific Claude
model name patterns (e.g. "-4-", "claude-3-7-sonnet"), which fails for
application inference profile ARNs that don't contain the model name.
This causes prompt caching to silently break for Bedrock users with
application inference profiles.

Work around this by detecting when pi-ai would miss cache point injection
(via piAiWouldInjectCachePoints mirror) and patching the Converse API
payload via onPayload to add cachePoint blocks to the system prompt and
last user message — matching the same format pi-ai uses natively.

The fix is safe:
- Checks for existing cache points to avoid double-injection
- Respects cacheRetention: "none"
- Defaults to "short" retention (matching pi-ai default)
- Becomes a no-op once upstream pi-mono#2925 is fixed

Fixes #19279
Upstream: https://github.com/badlogic/pi-mono/issues/2925

* fix(amazon-bedrock): tighten app-profile cache injection

---------

Co-authored-by: Your Name <you@example.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-22 12:19:29 -07:00
Peter Steinberger
4c5394d0ba fix: lower Telegram webhook callback timeout (#70146) (thanks @friday-james) 2026-04-22 20:19:12 +01:00
friday-james
43fc38e46c fix(telegram): lower webhook callback timeout to 5s
#16763 added `onTimeout: "return"` with `timeoutMilliseconds: 10_000`
(grammY default). In practice, Telegram's webhook servers abort the
read well before 10s when handler latency is LLM-bound: `getWebhookInfo`
reports `last_error_message: "Read timeout expired"` and pending updates
pile up, cascading into multi-minute reply lag.

Reproducible A/B on identical infra (same region, same bot token):
- Minimal Python echo bot: 5 back-to-back webhook RTTs 341-642ms, clean.
- OpenClaw current main: intermittent Read timeout expired, 1-5 min lag.

The handler still runs to completion; only the Telegram-facing ack is
sooner. grammY's deployment guide suggests 5s for long-running handlers.

No new config surface; minimal one-line change to the existing constant
and its test assertion. If a configurable timeout is wanted, that can be
a follow-up (see stale #7754).
2026-04-22 20:19:12 +01:00
Vincent Koc
e1341941d5 test(plugins): guard legacy bundled hook regressions 2026-04-22 12:18:27 -07:00
Peter Steinberger
67e5cca7a4 test: tighten Telegram polling conflict coverage (#69873) (thanks @hclsys) 2026-04-22 20:16:14 +01:00
HCL
3a11435c7d test(telegram): update monitor test for #69787 transport rebuild on 409
Sibling test in monitor.test.ts asserted the pre-fix behavior (single
transport reused across cycles on 409). My #69787 change rebuilds the
transport on 409 so Telegram sees a fresh TCP socket — update the
assertion to match.

Two transports are now expected: the initial one plus the rebuild
after the conflict.
2026-04-22 20:16:14 +01:00
HCL
83a906c95c fix(telegram): mark polling transport dirty on 409 conflict (#69787)
When getUpdates returns 409 Conflict (e.g.
'terminated by other getUpdates request'), the polling runtime
previously retried on the same HTTP keep-alive TCP socket because
markDirty() was only called in the isRecoverable branch.

Telegram treats that connection as the 'old' session and keeps
terminating it — producing a sustained low-rate 409 retry loop
(observed a few per minute after eliminating duplicate pollers).

Broaden the dirty-mark condition to fire on isConflict as well as
isRecoverable so the next cycle forces a fresh TCP connection.

Update the existing 'reuses transport after getUpdates conflict' test
— which previously locked in the buggy behavior — to assert the new
correct behavior: one fresh transport is built, the stale one is
closed.
2026-04-22 20:16:14 +01:00
Peter Steinberger
8f38691e79 fix: preserve Slack download cfg token fallback (#70160) (thanks @martingarramon) 2026-04-22 20:14:00 +01:00
Martin Garramon
44b1bad333 fix(slack): pass cfg into resolveToken from downloadSlackFile call site
Commit 95331e5cc5 ("fix(channels): thread runtime config through sends")
migrated resolveToken to a 3-arg signature (explicit, accountId, cfg) and
updated the getClient call site at actions.ts:83. The sibling call inside
downloadSlackFile at actions.ts:445 was not migrated and still dropped
opts.cfg, so the cfg-only resolution branch was unreachable from that path.

Current production callers (action-runtime.ts:386-389) always inject a
resolved readToken into opts.token before calling downloadSlackFile, so
this is defense-in-depth today -- the broken path is not hit in runtime.
Landing this closes the call-site migration gap and adds test coverage
for the cfg-only resolution contract on downloadSlackFile.

Note: pre-commit typecheck hook bypassed because upstream/main has 14
pre-existing TS errors in unrelated packages (discord, qa-lab, qqbot,
slack/monitor/provider.ts, tokenjuice, pi-embedded-runner) -- verified
reproducible on clean HEAD 4a16cf8008 without this diff.
2026-04-22 20:14:00 +01:00
Peter Steinberger
7ff8f8cef8 ci: narrow windows check scope 2026-04-22 20:13:37 +01:00
Peter Steinberger
bfc72b5256 fix: route Slack HTTP webhook dispatch (#70275) (thanks @FroeMic) 2026-04-22 20:12:09 +01:00
froemic
7ecff96425 Fix Slack HTTP route registry dispatch 2026-04-22 20:12:09 +01:00
Vincent Koc
988fe85f2c test(memory): exercise registered auto-recall hook 2026-04-22 12:11:46 -07:00
zqchris
b24ae8b18b fix(auto-reply): preserve streaming reply directives (#70243)
Preserve streamed MEDIA/reply/audio directives across chunk boundaries and phase-aware final_answer delivery.\n\nThanks @zqchris.
2026-04-22 20:11:00 +01:00
Peter Steinberger
b1b1979841 ci: skip windows for test-only changes 2026-04-22 20:10:27 +01:00
Vincent Koc
0d68128aed test(memory): exercise registered auto-capture hook 2026-04-22 12:07:03 -07:00
Peter Steinberger
8b89d37a2b ci: rotate stale concurrency group 2026-04-22 20:05:10 +01:00
Neerav Makwana
5462d4d5c5 fix: drop silent parent replies while subagents are pending (#69942)
Drop bare parent NO_REPLY payloads while spawned subagents are pending, preserving quiet parent turns until child completion delivers the real reply.\n\nThanks @neeravmakwana.
2026-04-22 20:04:38 +01:00
Vincent Koc
aee9f476c8 test(skill-workshop): exercise registered prompt hook 2026-04-22 12:03:31 -07:00
Peter Steinberger
3c89f5d537 ci: add scoped docker gateway e2e 2026-04-22 20:02:23 +01:00
HFConsultant
647f4ee8ce fix: persist CLI session clearing atomically (#70298)
Persist stale CLI session clearing through the session-store merge path and add regression coverage for Claude binding removal.\n\nThanks @HFConsultant.
2026-04-22 20:01:35 +01:00
Vincent Koc
e3fc1a237b test(acpx): exercise registered reply_dispatch hook 2026-04-22 11:59:54 -07:00
Vincent Koc
f4bbbcbfb3 fix(hooks): canonicalize thread ownership conversation ids 2026-04-22 11:57:29 -07:00
Felix Miao
449cad510d fix: honor ACP spawn model overrides (#70210)
Honor explicit ACP sessions_spawn model overrides and preserve ACP runtime cwd options.\n\nThanks @felix-miao.
2026-04-22 19:55:23 +01:00
Vincent Koc
c09591b086 test(memory): drop stale dreaming hook doubles 2026-04-22 11:53:00 -07:00
Peter Steinberger
170496c105 ci: fold build smoke into artifact job 2026-04-22 19:52:13 +01:00
Vincent Koc
5fbafa7e47 fix(hooks): prefer shared outbound conversation context 2026-04-22 11:49:25 -07:00
Vincent Koc
62a4abbc9f refactor(hooks): centralize matrix subagent hook wiring 2026-04-22 11:45:33 -07:00
Peter Steinberger
2251516281 fix(discord): break monitor threading import cycle 2026-04-22 19:44:22 +01:00
Peter Steinberger
6294182cbb ci: parallelize extension batch groups 2026-04-22 19:39:08 +01:00
Peter Steinberger
b0d4e64170 refactor(discord): share partial channel test fixtures 2026-04-22 19:38:45 +01:00
Peter Steinberger
ec5d403f5b refactor(discord): share channel action param parsing 2026-04-22 19:38:45 +01:00
Peter Steinberger
8bd387976d refactor(discord): centralize thread channel context 2026-04-22 19:38:45 +01:00
Vincent Koc
bbcd185215 refactor(hooks): centralize bundled subagent hook wiring 2026-04-22 11:37:18 -07:00
Peter Steinberger
d30f252c1b ci: use dist cache instead of artifact upload 2026-04-22 19:31:25 +01:00
Peter Steinberger
4b2b261367 fix(plugins): preserve source activation config 2026-04-22 19:26:12 +01:00
Vincent Koc
6d003cbcee fix(hooks): expose typed gateway startup context 2026-04-22 11:22:51 -07:00
Peter Steinberger
3e24898690 fix: stabilize Claude CLI session prompt hashing 2026-04-22 19:21:51 +01:00
Peter Steinberger
ea29e654d7 fix(cli-session): forward static prompt hash input 2026-04-22 19:21:51 +01:00
Zijun Lin
e1ffe97984 fix: address review feedback — handle empty static prompt and remove stray blank lines
- Always pass extraSystemPromptStatic as string (even when empty) so the
  fallback in prepare.ts never accidentally hashes dynamic content
- Use explicit undefined check (params.extraSystemPromptStatic !== undefined)
  instead of ?? nullish coalescing to avoid edge case where empty static
  string falls through to hashing the full dynamic prompt
- Remove extra blank line
2026-04-22 19:21:51 +01:00
Zijun Lin
d1c414305b fix(cli-session): only hash static extraSystemPrompt for session reuse
The extraSystemPrompt includes per-message dynamic content from
buildInboundMetaSystemPrompt() (timestamps, message IDs, sender metadata)
that changes on every inbound message. This causes the extraSystemPromptHash
to differ every turn, triggering a session reset with reason='system-prompt'
and discarding all CLI session context.

Fix: split extraSystemPrompt into dynamic (inbound meta) and static
(group context, group intro, group system prompt, exec override hints)
portions. Only hash the static portion for session reuse validation.

The full extraSystemPrompt (dynamic + static) is still sent to the CLI
as before — only the session stability hash uses the static subset.

Fixes #70100
2026-04-22 19:21:51 +01:00
Peter Steinberger
d48763caf9 test: keep config fallback test on generic plugin channel 2026-04-22 19:20:15 +01:00
Peter Steinberger
03846d63ec refactor: use memory slot defaults in core paths 2026-04-22 19:18:18 +01:00
Peter Steinberger
80a16339e1 refactor: declare channel add flags in manifests 2026-04-22 19:13:51 +01:00
Peter Steinberger
6488e0dd0c test: keep hook and slack tests on public boundaries 2026-04-22 19:09:18 +01:00
Peter Steinberger
86667d670e refactor: move doctor capabilities to channel manifests 2026-04-22 19:05:53 +01:00
Peter Steinberger
510a8f9ebc fix: share reply media context (#68111) (thanks @ayeshakhalid192007-dev) 2026-04-22 19:02:44 +01:00
ayeshakhalid192007-dev
8d4e6a39b5 test(agent-runner): regression — createReplyMediaPathNormalizer.runtime not called when normalizer injected 2026-04-22 19:02:44 +01:00
ayeshakhalid192007-dev
552d5dcbce fix(agent-runner): share media-path normalizer with runAgentTurnWithFallback to prevent duplicate outbound media 2026-04-22 19:02:44 +01:00
ayeshakhalid192007-dev
88760f88c2 fix(agent-runner): accept injected normalizeMediaPaths in runAgentTurnWithFallback 2026-04-22 19:02:44 +01:00
Peter Steinberger
5ad06d0b20 refactor: build channel setup input generically 2026-04-22 18:57:45 +01:00
Vincent Koc
1e61279b35 refactor(memory): migrate lancedb recall to prompt-build hook 2026-04-22 10:56:14 -07:00
Peter Steinberger
921a5416e4 refactor: move channel doctor migrations to plugins 2026-04-22 18:55:18 +01:00
Peter Steinberger
9d5d2f9cdd fix: make Discord thread parent inheritance opt-in (#69986) (thanks @Blahdude) 2026-04-22 18:54:22 +01:00
Oliver Camp
956cf9b6b2 fix(discord): make thread parent inheritance opt-in 2026-04-22 18:54:22 +01:00
Peter Steinberger
40e19cc9a1 ci: downsize install smoke runner 2026-04-22 18:54:06 +01:00
Vincent Koc
b5b03fbaee test(slack): drop obsolete adapter hook test 2026-04-22 10:53:44 -07:00
Vincent Koc
e593122465 fix(hooks): standardize outbound routing metadata 2026-04-22 10:53:44 -07:00
Peter Steinberger
b0f6c54645 ci: run install smoke for runtime dep staging 2026-04-22 18:51:38 +01:00
Peter Steinberger
a12fcd3f18 fix: harden Discord voice commands in threads 2026-04-22 18:49:58 +01:00
Peter Steinberger
9d66a900e5 fix(plugins): harden bundled runtime dep staging 2026-04-22 18:49:13 +01:00
Hana Chang
0e9c632444 fix(discord): use resolveDiscordChannelNameSafe for voice channel override name
Applies the same safe-accessor pattern to the adjacent name field.
If @buape/carbon implements name as a getter that also reads _rawData
(like parentId), the previous `"name" in channel` pattern would throw
for the same reason. Aligns with the fix for parentId in the same call
site.
2026-04-22 18:47:41 +01:00
Hana Chang
5c5fa5f38b fix(discord): use resolveDiscordChannelParentIdSafe in voice command path
#69908 switched native slash commands, listeners, and the model picker to
the safe accessor for partial thread channels, but the voice /join command
still reads channel.parentId through the unsafe "parentId" in channel
pattern. Route it through the same helper so the voice command path does
not crash with "Cannot access rawData on partial Channel" when invoked
from inside a thread on @buape/carbon >=0.16.
2026-04-22 18:47:41 +01:00
Claw Kowalski
43366cd541 fix(discord): thread runtime config through guild actions 2026-04-22 18:47:30 +01:00
Peter Steinberger
e9d16cbd8c test: keep loader fixture inside plugin boundary 2026-04-22 18:46:57 +01:00
Peter Steinberger
860cc1b3fe fix(config): preserve source config during recovery 2026-04-22 18:42:53 +01:00
Peter Steinberger
557f4fc689 docs: update claude cli stdio notes 2026-04-22 18:40:51 +01:00
Peter Steinberger
d8c9185f3f ci: add fast docker install smoke 2026-04-22 18:39:03 +01:00
Peter Steinberger
dad4b3e7fb fix: default claude cli to stdio sessions 2026-04-22 18:38:32 +01:00
Peter Steinberger
9337e1bd8a fix(agents): accept silent no-reply turns 2026-04-22 18:36:15 +01:00
Peter Steinberger
9d27d09d47 fix: add plugin load debug shape 2026-04-22 18:31:37 +01:00
Peter Steinberger
63776bc999 test: stabilize audio directive tag test 2026-04-22 18:26:07 +01:00
Peter Steinberger
a2512f0243 fix: load staged dist-runtime plugins in docker 2026-04-22 18:22:39 +01:00
Peter Steinberger
72c765e736 ci: parallelize additional boundary guards 2026-04-22 18:21:05 +01:00
Peter Steinberger
a9be41d8c7 ci: keep workflow edits off windows lane 2026-04-22 18:16:11 +01:00
Peter Steinberger
2afad03931 ci: trim gateway watch build profile 2026-04-22 18:11:48 +01:00
Peter Steinberger
024592fb1d Revert "ci: reuse build artifacts for gateway topology"
This reverts commit be317769e6.
2026-04-22 18:10:02 +01:00
Devin Robison
5b32c3138c telegram: align model picker callback auth (#70235)
* telegram: align model picker callback auth

* docs(changelog): note telegram model callback auth fix

* fix(telegram): use runtime config for model callback auth
2026-04-22 11:06:01 -06:00
Peter Steinberger
be317769e6 ci: reuse build artifacts for gateway topology 2026-04-22 18:05:27 +01:00
Tak Hoffman
f328c21046 feat: Add /models add hot-reload model registration (#70211)
* feat(models): add chat model registration with hot reload

* docs(changelog): add models entry for pr 70211

* fix(models): harden add flow follow-ups

* fix models add review follow-ups

* harden models add config writes

* tighten plugin boundary invariant

* move models add adapters behind sdk facades

* avoid ollama-specific core facade
2026-04-22 12:00:30 -05:00
Devin Robison
0623079e98 fix(dotenv): block connector endpoint workspace overrides (#70240)
* fix(dotenv): block connector endpoint workspace overrides

* docs(changelog): note dotenv endpoint blocklist

* fix(dotenv): block Matrix per-account scoped homeserver overrides
2026-04-22 10:58:32 -06:00
Peter Steinberger
8b8df813d0 ci: keep native lanes native scoped 2026-04-22 17:53:38 +01:00
Peter Steinberger
03cf97a33e ci: consolidate short test workers 2026-04-22 17:49:06 +01:00
Peter Steinberger
6370013bb7 ci: rebalance runtime config tests 2026-04-22 17:37:54 +01:00
Peter Steinberger
e8240a2628 ci: keep build smoke on blacksmith 2026-04-22 17:33:40 +01:00
Peter Steinberger
d8913d3901 ci: offload short linux checks 2026-04-22 17:30:54 +01:00
Peter Steinberger
8febc20e80 ci: reduce blacksmith test pressure 2026-04-22 17:26:00 +01:00
Ayaan Zaidi
486d0ec235 fix(gateway): preserve restart continuation chat type 2026-04-22 21:49:49 +05:30
Peter Steinberger
4ef1c06f9e ci: rebalance agentic node tests 2026-04-22 17:18:32 +01:00
Peter Steinberger
fd93b7f2ab perf(test): avoid bundled setup in auto-enable tests 2026-04-22 17:13:42 +01:00
Devin Robison
dd46783c34 fix(pairing): clear stale requests on device removal (#70239)
* fix(pairing): clear stale requests on device removal

* docs(changelog): note pairing stale request cleanup
2026-04-22 10:05:05 -06:00
Ayaan Zaidi
81e0022b4d refactor(gateway): unify startup task execution 2026-04-22 21:31:19 +05:30
Jason Perlow
53ad1a6066 fix(gateway): allow silent metadata-upgrade pairing for loopback CLI clients (#70224)
Loopback CLI clients (cli_container_local, shared_secret_loopback_local)
with valid shared-secret auth previously got disconnected with 1008
pairing required whenever the paired device record's platform or
deviceFamily string differed from what the CLI claimed at connect time.

PR #69431 added the shared_secret_loopback_local locality but deferred
the metadata-upgrade reason from the auto-approval allowlist. That
deferral created an unrecoverable handshake loop in practice: every CLI
connect triggers a fresh metadata-upgrade request, the Control UI has
no approval surface for this reason, and non-interactive shells cannot
complete pairing. This broke every non-interactive openclaw agent use
case when paired device keys are replicated across hosts or installs
are migrated across platforms.

Extend shouldAllowSilentLocalPairing to auto-approve metadata-upgrade
for cli_container_local and shared_secret_loopback_local localities
only. Browser / Control-UI / remote paths retain existing approval-
required behavior. Gateway still logs every metadata refresh via the
existing security audit line for operator review.

Add 4 unit tests covering the decision table for metadata-upgrade
across all four localities.

Related: #69397, #69431
2026-04-22 09:58:53 -06:00
Ayaan Zaidi
25e01c182c docs(changelog): note restart sentinel atomic writes 2026-04-22 20:44:10 +05:30
Ayaan Zaidi
d497de7697 fix(gateway): write restart sentinels atomically 2026-04-22 20:44:10 +05:30
Peter Steinberger
fb70d3ac67 ci: refresh ci concurrency group 2026-04-22 15:53:37 +01:00
Peter Steinberger
ed97cc7210 ci: skip aggregate fan-in after cancellation 2026-04-22 15:52:25 +01:00
Ayaan Zaidi
6f25befc4f docs(changelog): thank cron contributors 2026-04-22 20:18:15 +05:30
Ayaan Zaidi
7085687a16 docs(changelog): correct cron contributors 2026-04-22 20:16:53 +05:30
Ayaan Zaidi
34b0aac3b5 docs(changelog): fix cron attribution 2026-04-22 20:15:04 +05:30
Peter Steinberger
c73f7d6596 ci: move lightweight automation off blacksmith 2026-04-22 15:44:34 +01:00
VACInc
962b25b4a6 fix: preserve restart continuations after reboot (#63406) (thanks @VACInc)
* gateway: add restart continuation sentinel

* gateway: address restart continuation review

* gateway: handle restart continuation edge cases

* gateway: keep restart continuations on threaded delivery path

* fix(gateway): harden restart continuation routing

* test(gateway): cover restart continuation edge cases

* docs(agent): clarify restart continuation usage

* fix: preserve restart continuations after reboot (#63406) (thanks @VACInc)

---------

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-04-22 20:09:07 +05:30
Garming
a43be09dca fix(doctor): skip token generation for trusted-proxy and none auth modes (#59055)
runGatewayAuthHealth() only excluded 'password' and 'token' (with existing
token) from its needsToken check. When gateway.auth.mode was set to
'trusted-proxy' or 'none', doctor --fix would incorrectly:

1. Flag the config as 'missing a token'
2. Prompt to generate a gateway token
3. Overwrite auth.mode to 'token' in openclaw.json

This silently broke trusted-proxy deployments (common in SaaS/reverse-proxy
setups) by replacing the delegated auth mode with token auth.

The fix aligns runGatewayAuthHealth() with the existing
hasExplicitGatewayInstallAuthMode() in auth-install-policy.ts, which
already correctly returns false for 'password', 'none', and 'trusted-proxy'.

Co-authored-by: wujiaming88 <wujiaming88@example.com>
2026-04-22 22:38:27 +08:00
Peter Steinberger
38135ff6b4 ci: keep cpu-sensitive lanes larger 2026-04-22 15:08:47 +01:00
Peter Steinberger
ba9589256c build: refresh a2ui bundle hash 2026-04-22 15:07:23 +01:00
Peter Steinberger
cdf5f66298 ci: keep long matrix aggregates on blacksmith 2026-04-22 15:00:17 +01:00
Peter Steinberger
0f4ec84a2c fix: fail closed on plugin integrity drift 2026-04-22 14:59:52 +01:00
Peter Steinberger
dc2c3a4920 fix(gateway): harden WS pairing locality 2026-04-22 14:55:58 +01:00
Peter Steinberger
95e430f670 ci: run aggregate checks off blacksmith 2026-04-22 14:53:41 +01:00
Peter Steinberger
fd01a66e30 ci: downsize blacksmith runners 2026-04-22 14:39:20 +01:00
Peter Steinberger
d7ea136384 fix(agent): align pi session tool options 2026-04-22 14:39:20 +01:00
Peter Steinberger
fef830f4cf chore: update dependencies 2026-04-22 14:35:00 +01:00
Peter Steinberger
0d12422418 ci: consolidate test shard fanout 2026-04-22 14:23:43 +01:00
pashpashpash
cd41bd1359 fix(codex): apply GPT-5 prompt overlay (#70175) 2026-04-22 22:00:23 +09:00
cxy
608cfd36f5 fix(qqbot): add interaction intents (#70143)
* feat(qqbot): add intents interaction

* fix(qqbot): add interaction intents (#70143) (thanks @cxyhhhhh)

---------

Co-authored-by: sliverp <870080352@qq.com>
2026-04-22 20:03:33 +08:00
Ayaan Zaidi
4a16cf8008 fix: require cli auth epoch version (#70132) 2026-04-22 17:03:33 +05:30
Ayaan Zaidi
7fd8eeecf2 fix: update cli session changelog (#70132) 2026-04-22 17:03:33 +05:30
Ayaan Zaidi
9ad58ddc7e test(cli): cover oauth auth epoch continuity 2026-04-22 17:03:33 +05:30
Ayaan Zaidi
1ff461fe7b fix(cli): stabilize oauth session auth epochs 2026-04-22 17:03:33 +05:30
Nimrod Gutman
8778521167 fix(plugins): avoid doctor crash on legacy interactive state (#70135)
* fix(plugins): hydrate legacy interactive state

* fix(plugins): avoid doctor crash on legacy interactive state (#70135) (thanks @ngutman)
2026-04-22 14:17:09 +03:00
Nimrod Gutman
cfda375bb6 chore(pi): remove local pr prompts
Remove repo-local /landpr and /reviewpr prompt templates so maintainers use the externally maintained workflow instead.
These flows remain available from the external maintainers repo via globally installed Pi skills and prompts.
2026-04-22 13:38:47 +03:00
Ted Li
13fae1685f fix(config): accept truncateAfterCompaction (#68395)
Merged via squash.

Prepared head SHA: bf45148a75
Co-authored-by: MonkeyLeeT <6754057+MonkeyLeeT@users.noreply.github.com>
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Reviewed-by: @hxy91819
2026-04-22 18:31:03 +08:00
Ayaan Zaidi
16f016f07e fix: update cli session changelog (#70106) 2026-04-22 15:35:21 +05:30
Ayaan Zaidi
1e3e077370 fix(gateway): preserve cli session binding metadata 2026-04-22 15:35:21 +05:30
Ayaan Zaidi
7a2203be50 fix(cli): upgrade legacy mcp session reuse 2026-04-22 15:35:21 +05:30
Ayaan Zaidi
18869acf46 fix(cli): keep provider-owned sessions through implicit expiry 2026-04-22 15:35:21 +05:30
Sliverp
e36e0e8ad2 fix: lower the log level from info to debug (#70108) 2026-04-22 17:58:49 +08:00
Jacky
fbdf502e08 place permission under each branch of bot permissions for discord docs (#69218)
Merged via squash.

Prepared head SHA: dd6ae52d90
Co-authored-by: epicseven-cup <59263116+epicseven-cup@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
2026-04-22 11:49:15 +02:00
pashpashpash
abf940db61 fix(codex): unchain app-server defaults (#70082) 2026-04-22 17:53:49 +09:00
Val Alexander
43a941b51c fix(pair): render /pair qr as media (#70047)
* fix(pair): render pair qr as media

* fix(gateway): preserve media reply threading

* fix(gateway): harden webchat media replies

* fix(plugin-sdk): keep trustedLocalMedia internal

* docs(changelog): note pair qr media fix

* Update CHANGELOG with recent fixes and enhancements

Updated changelog to include recent fixes and enhancements.
2026-04-22 03:31:09 -05:00
Ayaan Zaidi
81ca7bc40b fix: keep claude cli sessions warm (#69679)
* feat(cli): keep claude cli sessions warm

* test(cli): cover claude live session reuse

* fix(cli): harden claude live session reuse

* fix(cli): redact mcp session key logs

* fix(cli): bound claude live session turns

* fix(cli): reuse claude live sessions on resume

* refactor(cli): canonicalize claude live argv

* fix(cli): preserve claude live resume state

* fix(cli): close dead claude live sessions

* fix(cli): serialize claude live session creates

* fix(cli): count pending claude live sessions

* fix(cli): tighten claude live resume abort

* fix(cli): reject closed claude live sessions

* fix(cli): refresh claude live fingerprints

* fix(cli): stabilize MCP resume hash

* fix: preserve claude live inline resume (#69679)

---------

Co-authored-by: Frank Yang <frank.ekn@gmail.com>
2026-04-22 13:44:18 +05:30
Val Alexander
dab46a7e98 qa: harden parity gate execution (#70045) 2026-04-22 03:08:25 -05:00
Peter Steinberger
bee2e0f38f fix: keep custom pi tools executable 2026-04-22 08:52:55 +01:00
Peter Steinberger
4431d6c5d0 fix: harden tokenjuice host typing 2026-04-22 08:52:55 +01:00
Peter Steinberger
d8892ee227 test: harden qa private runtime staging 2026-04-22 08:52:55 +01:00
Peter Steinberger
eb67964239 ci: build private qa parity runtime 2026-04-22 08:52:55 +01:00
Peter Steinberger
dd9adc57c2 test: harden qa parity runtime staging 2026-04-22 08:52:55 +01:00
Peter Steinberger
137f64d0c0 fix: drop stale socket mode opt-in 2026-04-22 08:52:55 +01:00
Peter Steinberger
8bfb4024f6 test: harden qa parity config cleanup 2026-04-22 08:52:55 +01:00
Peter Steinberger
cd088d8a16 ci: build runtime before parity gate 2026-04-22 08:52:55 +01:00
Peter Steinberger
764bb310f7 ci: pin qa parity tool profile 2026-04-22 08:52:55 +01:00
Peter Steinberger
0cd785d8a5 ci: stabilize parity gate runner 2026-04-22 08:52:55 +01:00
Peter Steinberger
895b2690c4 ci: serialize parity gate scenarios 2026-04-22 08:52:55 +01:00
Peter Steinberger
5bb8f5ae8d docs: update changelog for channel health (#69833) (thanks @bek91) 2026-04-22 08:52:55 +01:00
Peter Steinberger
d8d0380297 fix: use transport activity for stale health 2026-04-22 08:52:55 +01:00
Bek
270003aefd fix: clean up slack socket waiters on start hooks 2026-04-22 08:52:55 +01:00
Bek
cd1977bf16 fix: make slack socket health event-driven 2026-04-22 08:52:55 +01:00
Vincent Koc
da86ce7887 feat(openai): add codex device-code auth and fix login options in menu (#69557)
Merged via squash.

Prepared head SHA: 4918ed69f1
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Reviewed-by: @BunsDev
2026-04-22 02:47:20 -05:00
Dewaldt Huysamen
d4eb236523 fix(release-check): assert bundled plugin runtime deps after packed postinstall (#70035)
* fix(release-check): assert bundled plugin runtime deps after packed postinstall

Release-check already validates source dist/extensions runtime deps are staged, but runPackedBundledChannelEntrySmoke never re-validates after the packed postinstall runs against the installed tarball. That gap is how 2026.4.21 shipped without @whiskeysockets/baileys in dist/extensions/whatsapp/node_modules, because the source staging passed while the installed layout was left broken.

Re-use collectBuiltBundledPluginStagedRuntimeDependencyErrors against the installed packageRoot right after runPackedBundledPluginPostinstall and fail release-check if any declared runtime dependency is missing from the plugin-local node_modules.

* fix(release-check): check postinstalled dep sentinels at packageRoot/node_modules

Codex review on #70035 caught that collectInstalledBundledPluginRuntimeDepErrors was pointing at dist/extensions/<id>/node_modules, but packed postinstall installs and probes sentinels at packageRoot/node_modules (see dependencySentinelPath in scripts/postinstall-bundled-plugins.mjs). The previous implementation would have falsely failed release-check on healthy packed installs while still missing the original WhatsApp regression.

Reuse discoverBundledPluginRuntimeDeps from postinstall-bundled-plugins.mjs so the release guard uses the exact same dep discovery and sentinel paths the packed postinstall uses. Update the test fixtures accordingly so they model the real install layout.
2026-04-22 00:31:40 -07:00
Vincent Koc
55b297ef15 fix(agents): keep mocked OpenAI Responses on HTTP (#69815)
* fix(agents): keep mocked OpenAI responses on HTTP

* docs(changelog): add entry for mocked responses fix
2026-04-22 00:25:02 -07:00
pashpashpash
1dd3fb1611 Fix Codex auth handoff for the app-server harness (#69990)
* Codex: fix auth bridge token shape

* Codex: preserve selected auth tokens

* Codex: prefer selected profile id token

* Codex: honor inherited Codex home

---------

Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
2026-04-22 16:22:29 +09:00
Vincent Koc
a4cafde0da docs(changelog): mention tokenjuice embedded support (#70039) 2026-04-22 00:15:16 -07:00
Vincent Koc
22717878cc docs(tokenjuice): add bundled plugin guide (#70038)
* docs(tokenjuice): add bundled plugin guide

* docs(tokenjuice): sort nav entry
2026-04-22 00:14:32 -07:00
Onur Solmaz
142e26d6cd docs(skill): tighten duplicate triage mirror rules 2026-04-22 09:09:51 +02:00
Vincent Koc
91ac485246 feat(tokenjuice): bundle the native adapter (#69946)
* feat(plugins): register embedded extension factories

* feat(tokenjuice): bundle the native adapter

* fix(tokenjuice): gate the bundled embedded extension seam

* fix(tokenjuice): refresh runtime sidecar baseline

* fix(plugins): harden bundled embedded extensions

* fix(plugins): install source bundled runtime deps

* fix(tokenjuice): sync lockfile importer

* fix(plugins): validate reused runtime dep versions

* fix(plugins): restore tokenjuice CI contract

* fix(plugins): remove tokenjuice dts bridge

* fix(tokenjuice): repair openclaw type shim

* fix(plugins): harden bundled runtime deps

* fix(plugins): keep source checkout runtime deps local

* fix(plugins): isolate bundled runtime dep installs

* fix(cli): keep plugin startup registration non-activating

* fix(cli): keep loader overrides out of plugin cli options
2026-04-21 23:58:37 -07:00
Alex Knight
201385548c perf(slack): narrow runtime-setter + lazy-load 4 modules + narrow 2 SDK surfaces (#69317)
Lazy load modules showing a ~50% gateway startup performance improvement
2026-04-22 16:42:43 +10:00
Peter Steinberger
fd2c883673 perf(test): skip setup promotion metadata fallback 2026-04-22 07:13:24 +01:00
Peter Steinberger
053147451b docs: generalize plugin runtime comments 2026-04-22 07:13:15 +01:00
fuller-stack-dev
561f2e52c7 docs(tui): document local config repair flow (#69995) (thanks @fuller-stack-dev)
* docs(tui): document local config repair flow

* docs(tui): clarify local TUI examples

* docs(config): gate local TUI repair flow

* docs(tui): fix local repair docs

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-04-22 11:42:59 +05:30
Peter Steinberger
4fbd314e1f refactor: gate setup promotion by manifest feature 2026-04-22 07:07:08 +01:00
Peter Steinberger
866a120d69 docs: generalize core routing comments 2026-04-22 07:01:34 +01:00
Peter Steinberger
b24d153f23 perf(test): avoid bundled channel fallback in model override tests 2026-04-22 07:01:19 +01:00
Peter Steinberger
4d6756d45d refactor: generalize voice audio compatibility 2026-04-22 06:58:45 +01:00
Peter Steinberger
8b2ef40775 docs: remove bundled channel examples from core types 2026-04-22 06:55:47 +01:00
Peter Steinberger
b619d39e54 fix(channels): preserve setup promotion fallbacks 2026-04-22 06:47:51 +01:00
Peter Steinberger
182d0fcee2 fix(telegram): isolate sent-message cache stores 2026-04-22 06:47:51 +01:00
Peter Steinberger
95331e5cc5 fix(channels): thread runtime config through sends 2026-04-22 06:47:51 +01:00
Peter Steinberger
e1897419de fix(config): enforce resolved runtime channel config 2026-04-22 06:47:50 +01:00
Peter Steinberger
b70531bf24 docs: generalize core channel examples 2026-04-22 06:47:41 +01:00
Jonathan
9f5dc4045c docs: fix stale community links in README and CONTRIBUTING (#69945)
Co-authored-by: Jonathan Amponsah <amponsahjonathan442@gmail.com>
2026-04-21 22:47:16 -07:00
JuniperSling
bcd1dec3dc feat(tencent): remove Token Plan provider and auth (#69996)
Co-authored-by: albertxyu <albertxyu@tencent.com>
2026-04-21 22:46:07 -07:00
Peter Steinberger
21e04350ab test: generalize media fetch token fixtures 2026-04-22 06:45:09 +01:00
Ayaan Zaidi
54311a7a34 fix(agents): guard replay convert hook 2026-04-22 11:15:03 +05:30
Peter Steinberger
344a88f931 refactor: remove plugin tool display overrides from core 2026-04-22 06:43:48 +01:00
Peter Steinberger
62864fb22c test: reuse plugin auto-enable fixture environment 2026-04-22 06:43:12 +01:00
Vincent Koc
6d6845ea9d fix(googlechat): harden google auth transport (#69812)
* fix(googlechat): localize google auth gaxios compat

* fix(googlechat): declare undici for staged runtime deps

* fix(googlechat): harden google auth transport

* fix(googlechat): narrow credential file reads

* fix(googlechat): preserve auth proxy transport

* fix(googlechat): allow symlinked auth files

* fix(googlechat): atomically load auth files

* fix(googlechat): eagerly buffer auth responses

* fix(googlechat): cap auth response buffering

* fix(googlechat): pin staged auth runtime deps

* fix(googlechat): buffer auth responses as array buffers

* Update CHANGELOG.md

* fix(googlechat): reject unstreamed auth responses

* fix(googlechat): use ambient fetch for auth transport

* fix(googlechat): keep guarded auth fetch on runtime path

* fix(googlechat): align staged zod range

* chore(lockfile): sync googlechat zod spec
2026-04-21 22:40:57 -07:00
Peter Steinberger
d94a981a33 refactor: keep plugin login policy out of core 2026-04-22 06:39:48 +01:00
Peter Steinberger
ec8ea02bb7 test: generalize legacy state migration coverage 2026-04-22 06:39:48 +01:00
Peter Steinberger
4a9dd3fe49 refactor: drop provider reconnect shim 2026-04-22 06:39:48 +01:00
Peter Steinberger
cb426b3b20 refactor: generalize route target parsing 2026-04-22 06:39:48 +01:00
Vincent Koc
7c13a48e49 chore(agents): prefer local validation over testbox 2026-04-21 22:37:03 -07:00
Peter Steinberger
1a7dfbbaba docs: record testbox full-suite profile 2026-04-22 06:36:11 +01:00
Peter Steinberger
0a670a058d perf(ci): unblock node compat and trim runtime compat test 2026-04-22 06:34:08 +01:00
pashpashpash
dc4e97472d Make harness failures fail honestly (#69981)
* Agents: fail honestly on harness errors

* Docs: clarify Codex harness fallback
2026-04-22 14:33:21 +09:00
Ayaan Zaidi
a0ccf69259 fix: normalize assistant replay content (#69850) (thanks @fuller-stack-dev) 2026-04-22 11:01:14 +05:30
Ayaan Zaidi
e4da220478 fix(agents): distill replay content normalization 2026-04-22 11:01:14 +05:30
FullerStackDev
f58dd0f9b6 fix(agents): harden replay normalization guards 2026-04-22 11:01:14 +05:30
FullerStackDev
3105597f8f fix(agents): normalize malformed assistant replay content 2026-04-22 11:01:14 +05:30
Peter Steinberger
925c3f3fa8 test: align plugin test contracts 2026-04-22 06:29:13 +01:00
Peter Steinberger
0285afe86f fix: stabilize testbox test suite 2026-04-22 06:29:13 +01:00
Shakker
08d5ad3828 fix: skip clean run-node runtime restaging 2026-04-22 06:28:50 +01:00
Peter Steinberger
cc91e8ecf9 fix(channels): repair bundled setup runtime deps 2026-04-22 06:26:13 +01:00
dependabot[bot]
38f8bc5592 chore(deps): bump the android-deps group across 1 directory with 17 updates (#67592)
* build(deps): bump the android-deps group across 1 directory with 17 updates

Bumps the android-deps group with 12 updates in the /apps/android directory:

| Package | From | To |
| --- | --- | --- |
| org.jlleitschuh.gradle.ktlint | `14.0.1` | `14.2.0` |
| [org.jetbrains.kotlin.plugin.compose](https://github.com/JetBrains/kotlin) | `2.2.21` | `2.3.20` |
| [org.jetbrains.kotlin.plugin.serialization](https://github.com/JetBrains/kotlin) | `2.2.21` | `2.3.20` |
| androidx.compose:compose-bom | `2026.02.00` | `2026.03.01` |
| androidx.activity:activity-compose | `1.12.2` | `1.13.0` |
| [org.jetbrains.kotlinx:kotlinx-serialization-json](https://github.com/Kotlin/kotlinx.serialization) | `1.10.0` | `1.11.0` |
| [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java) | `1.83` | `1.84` |
| [org.commonmark:commonmark](https://github.com/commonmark/commonmark-java) | `0.27.1` | `0.28.0` |
| [io.kotest:kotest-runner-junit5-jvm](https://github.com/kotest/kotest) | `6.1.3` | `6.1.11` |
| [org.junit.vintage:junit-vintage-engine](https://github.com/junit-team/junit-framework) | `6.0.2` | `6.0.3` |
| androidx.test.ext:junit | `1.2.1` | `1.3.0` |
| [gradle-wrapper](https://github.com/gradle/gradle) | `9.3.1` | `9.4.1` |

Updates `org.jlleitschuh.gradle.ktlint` from 14.0.1 to 14.2.0

Updates `org.jetbrains.kotlin.plugin.compose` from 2.2.21 to 2.3.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.2.21...v2.3.20)

Updates `org.jetbrains.kotlin.plugin.serialization` from 2.2.21 to 2.3.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.2.21...v2.3.20)

Updates `androidx.compose:compose-bom` from 2026.02.00 to 2026.03.01

Updates `androidx.activity:activity-compose` from 1.12.2 to 1.13.0

Updates `org.jetbrains.kotlinx:kotlinx-serialization-json` from 1.10.0 to 1.11.0
- [Release notes](https://github.com/Kotlin/kotlinx.serialization/releases)
- [Changelog](https://github.com/Kotlin/kotlinx.serialization/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Kotlin/kotlinx.serialization/compare/v1.10.0...v1.11.0)

Updates `org.bouncycastle:bcprov-jdk18on` from 1.83 to 1.84
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

Updates `org.commonmark:commonmark` from 0.27.1 to 0.28.0
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0)

Updates `org.commonmark:commonmark-ext-autolink` from 0.27.1 to 0.28.0
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0)

Updates `org.commonmark:commonmark-ext-gfm-strikethrough` from 0.27.1 to 0.28.0
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0)

Updates `org.commonmark:commonmark-ext-gfm-tables` from 0.27.1 to 0.28.0
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0)

Updates `org.commonmark:commonmark-ext-task-list-items` from 0.27.1 to 0.28.0
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.27.1...commonmark-parent-0.28.0)

Updates `io.kotest:kotest-runner-junit5-jvm` from 6.1.3 to 6.1.11
- [Release notes](https://github.com/kotest/kotest/releases)
- [Commits](https://github.com/kotest/kotest/compare/6.1.3...6.1.11)

Updates `io.kotest:kotest-assertions-core-jvm` from 6.1.3 to 6.1.11
- [Release notes](https://github.com/kotest/kotest/releases)
- [Commits](https://github.com/kotest/kotest/compare/6.1.3...6.1.11)

Updates `org.junit.vintage:junit-vintage-engine` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.0.2...r6.0.3)

Updates `androidx.test.ext:junit` from 1.2.1 to 1.3.0

Updates `gradle-wrapper` from 9.3.1 to 9.4.1
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.3.1...v9.4.1)

---
updated-dependencies:
- dependency-name: org.jlleitschuh.gradle.ktlint
  dependency-version: 14.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.jetbrains.kotlin.plugin.compose
  dependency-version: 2.3.20
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.jetbrains.kotlin.plugin.serialization
  dependency-version: 2.3.20
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: androidx.compose:compose-bom
  dependency-version: 2026.03.01
  dependency-type: direct:production
  dependency-group: android-deps
- dependency-name: androidx.activity:activity-compose
  dependency-version: 1.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.jetbrains.kotlinx:kotlinx-serialization-json
  dependency-version: 1.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.bouncycastle:bcprov-jdk18on
  dependency-version: '1.84'
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.commonmark:commonmark
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.commonmark:commonmark-ext-autolink
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.commonmark:commonmark-ext-gfm-strikethrough
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.commonmark:commonmark-ext-gfm-tables
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: org.commonmark:commonmark-ext-task-list-items
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: io.kotest:kotest-runner-junit5-jvm
  dependency-version: 6.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: android-deps
- dependency-name: io.kotest:kotest-assertions-core-jvm
  dependency-version: 6.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: android-deps
- dependency-name: org.junit.vintage:junit-vintage-engine
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: android-deps
- dependency-name: androidx.test.ext:junit
  dependency-version: 1.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
- dependency-name: gradle-wrapper
  dependency-version: 9.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: android-deps
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(android): remove Kotlin 2.3 warning failures

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-21 22:25:27 -07:00
Peter Steinberger
80f4c931e8 test: align telegram cron test doubles 2026-04-22 06:19:20 +01:00
Peter Steinberger
e39784decd ci: move preflight off blacksmith runners 2026-04-22 06:12:33 +01:00
Peter Steinberger
40719bcb74 refactor: move cron output policy to channel plugins 2026-04-22 06:11:49 +01:00
Peter Steinberger
3fd2a94404 refactor: generalize command sender identity checks 2026-04-22 06:11:49 +01:00
Peter Steinberger
6639bbbc2e refactor: generalize conversation id labels 2026-04-22 06:11:49 +01:00
Peter Steinberger
e20a5eeddb refactor: keep legacy web search config in doctor 2026-04-22 06:11:49 +01:00
Peter Steinberger
a6dce7cf19 refactor: resolve web search secrets by target path 2026-04-22 06:11:49 +01:00
Peter Steinberger
89741c7a23 refactor: use generic web search runtime credential hooks 2026-04-22 06:11:49 +01:00
Peter Steinberger
94f670b893 refactor: use generic web search credential hooks 2026-04-22 06:11:49 +01:00
Peter Steinberger
0c863124bb refactor: derive setup promotion rules from plugins 2026-04-22 06:11:49 +01:00
Peter Steinberger
db055a5c0d refactor: move WhatsApp group inference out of core 2026-04-22 06:11:49 +01:00
Peter Steinberger
bdcbb6b49d refactor: move Feishu model override parsing to plugin 2026-04-22 06:11:49 +01:00
Peter Steinberger
7189b49f81 refactor: move media defaults into plugin manifests 2026-04-22 06:11:49 +01:00
Peter Steinberger
2e775fb03e refactor: move stale socket modes to channel status 2026-04-22 06:11:49 +01:00
Peter Steinberger
0a027ff591 refactor: derive CLI web credential targets 2026-04-22 06:11:49 +01:00
Peter Steinberger
bc9c2cc162 refactor: derive web credential secret targets from manifests 2026-04-22 06:11:49 +01:00
Peter Steinberger
de616055f7 perf(ci): trim preflight setup and stream test waits 2026-04-22 06:11:02 +01:00
Vincent Koc
812d012b98 docs(changelog): note Tencent provider plugin 2026-04-21 22:06:35 -07:00
Ayaan Zaidi
77de199b1c fix: improve configure startup (#69984) 2026-04-22 10:33:14 +05:30
Ayaan Zaidi
f4966351a1 fix(configure): bound gateway hint probes 2026-04-22 10:33:14 +05:30
Ayaan Zaidi
c1b21a2a3a fix(cli): skip configure preaction bootstrap 2026-04-22 10:33:14 +05:30
Peter Steinberger
262899f495 ci: shallow checkout live reusable jobs 2026-04-22 06:00:01 +01:00
JuniperSling
d8b9be468a feat(tencent): add bundled Tencent Cloud provider plugin (Tokenhub + Token Plan) (#68460)
* feat(tencent): add bundled Tencent Cloud provider plugin (Tokenhub + Token Plan)

* fix(tencent): use provider-specific default model aliases

Both Tencent providers previously defaulted to the same alias "HY3 Preview",
which collides in buildModelAliasIndex (single alias map, keyed by normalized
alias). When both providers are onboarded, alias-based selection routed to
whichever provider was configured last.

Disambiguate the fallback aliases so resolution is deterministic regardless
of onboarding order:
  - tencent-tokenhub   -> "HY3 Preview (TokenHub)"
  - tencent-token-plan -> "HY3 Preview (Token Plan)"

* docs(tencent): rename model to "Hy3 preview" and drop "HY3" family name

Align with the external-facing product name:
  - model display name: "HY3 Preview" -> "Hy3 preview"
  - family/umbrella references in docs and auth hints: "HY3" -> "Hy3 preview"
  - internal cost constant: HY3_COST -> HY_COST

Model call id (hy3-preview) is unchanged.

* docs(tencent): use "Hy" as the family name in generic references

Keep specific model references as "Hy3 preview" (model catalog names,
onboarding aliases, Available-models docs entries), but switch
family/umbrella references to the plain "Hy" family name so future Hy
versions fit without doc churn:

  - auth hints: "Hy via Tencent TokenHub Gateway" / "Hy via Token Plan"
  - docs intro + Use-case table: "Tencent Hy models" / "call Hy via ..."
  - models.ts pricing comment: "Hy pricing"

* feat(tencent): add tiered pricing for Hy3 preview model

---------

Co-authored-by: albertxyu <albertxyu@tencent.com>
2026-04-21 21:59:22 -07:00
Peter Steinberger
c07b388f77 ci: keep pnpm alignment scoped to CI 2026-04-22 05:58:50 +01:00
Peter Steinberger
fbddef34bd perf(ci): trim provider catalog test setup 2026-04-22 05:57:22 +01:00
Peter Steinberger
dbf3eca590 test: stabilize workspace fallback assertion 2026-04-22 05:50:57 +01:00
pashpashpash
89932593bb Show typing while heartbeats are running (#69963)
* Heartbeat: show typing during runs

* Heartbeat: fix typing review issues

* Discord: preserve heartbeat typing config
2026-04-22 13:50:27 +09:00
Zihao WAN
d4f91a354e fix(ollama): forward think:false for qwen3 chat requests (#69967)
Forward top-level Ollama think flags on native /api/chat requests so --thinking off sends think:false.\n\nThanks @WZH8898.
2026-04-22 05:49:16 +01:00
fuller-stack-dev
276c00015c fix: add local embedded TUI mode (#66767) (thanks @fuller-stack-dev)
* feat(tui): add local embedded TUI mode with terminal/chat aliases

Adds a gateway-free local TUI path so users can run openclaw in their
terminal without needing a running gateway process.

- TuiBackend interface abstraction (tui-backend.ts) with EmbeddedTuiBackend
  implementation that drives the agent loop in-process
- openclaw tui --local flag for local embedded mode
- openclaw terminal / openclaw chat aliases that imply --local
- /auth slash command with codex CLI delegation to avoid prolite plan issue
- Default model display fallback on startup
- Local-aware status text and log suppression
- Concise auth error hints, raw HTML 403 suppression
- Onboarding hatch flow launches local TUI (no gateway required)
- Commander alias bug fix in run-main.ts (.aliases() check)
- All new and updated tests passing (145/145)

* TUI: fix alias detection, cross-platform codex lookup, and history byte-budget safeguards

* TUI: remove RuntimeEnv type annotation to fix CI oxlint error

* TUI: filter gateway-dependent tools and auto-approve plugin hooks in embedded mode

* TUI: suppress console noise and add embedded mode system prompt note

* TUI: reduce embedded-mode tool filtering from 15 to 7, add local session tools

* TUI: fix remaining PR review comments

* TUI: address latest review feedback and CI drift

* Core: align prompt helper with latest base

* Core: match prompt helper formatting with base

* Core: restore prompt helper from latest base

* fix(tui): preserve local auth fallback in source checkouts

* fix(tts): guard telephony provider invocation

* fix(tui): support Windows codex auth shim

* fix(tui): harden local auth flow

* fix: preserve embedded tool-first run events

* fix(tui): keep embedded plugin approvals gated

* fix(tui): restore embedded attempt import

* fix(tui): resolve sessions in embedded stub

* fix: add embedded TUI changelog entry (#66767) (thanks @fuller-stack-dev)

* fix: pass setup TUI local mode through relaunch (#66767) (thanks @fuller-stack-dev)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-04-22 10:08:57 +05:30
Peter Steinberger
d733d547c0 ci: use fastest Blacksmith testbox runner 2026-04-22 05:33:05 +01:00
dependabot[bot]
68a55cc434 build(deps): bump docker/build-push-action from 6 to 7 (#48053)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 21:30:47 -07:00
dependabot[bot]
ba8fb6b2b8 build(deps): bump the swift-deps group across 1 directory with 3 updates (#60168)
* build(deps): bump the swift-deps group across 1 directory with 3 updates

Bumps the swift-deps group with 3 updates in the /apps/macos directory: [github.com/orchetect/menubarextraaccess](https://github.com/orchetect/MenuBarExtraAccess), [github.com/apple/swift-log](https://github.com/apple/swift-log) and [github.com/sparkle-project/sparkle](https://github.com/sparkle-project/Sparkle).


Updates `github.com/orchetect/menubarextraaccess` from 1.2.2 to 1.3.0
- [Release notes](https://github.com/orchetect/MenuBarExtraAccess/releases)
- [Commits](https://github.com/orchetect/MenuBarExtraAccess/compare/1.2.2...1.3.0)

Updates `github.com/apple/swift-log` from 1.10.1 to 1.11.0
- [Release notes](https://github.com/apple/swift-log/releases)
- [Commits](https://github.com/apple/swift-log/compare/1.10.1...1.11.0)

Updates `github.com/sparkle-project/sparkle` from 2.9.0 to 2.9.1
- [Release notes](https://github.com/sparkle-project/Sparkle/releases)
- [Commits](https://github.com/sparkle-project/Sparkle/compare/2.9.0...2.9.1)

---
updated-dependencies:
- dependency-name: github.com/orchetect/menubarextraaccess
  dependency-version: 1.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swift-deps
- dependency-name: github.com/apple/swift-log
  dependency-version: 1.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: swift-deps
- dependency-name: github.com/sparkle-project/sparkle
  dependency-version: 2.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swift-deps
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(macos): restore MenuBarExtraAccess ordering

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-21 21:30:14 -07:00
dependabot[bot]
00da59124d build(deps): bump actions/setup-node from 4 to 6 (#61769)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 21:29:52 -07:00
dependabot[bot]
bcadf60b4d chore(deps): bump actions/create-github-app-token from 2 to 3.0.0 (#65099)
Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 2 to 3.0.0.
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](https://github.com/actions/create-github-app-token/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 21:19:58 -07:00
dependabot[bot]
16c4a04a69 build(deps): bump androidx.test.uiautomator:uiautomator in /apps/android (#48059)
Bumps androidx.test.uiautomator:uiautomator from 2.4.0-alpha06 to 2.4.0-beta02.

---
updated-dependencies:
- dependency-name: androidx.test.uiautomator:uiautomator
  dependency-version: 2.4.0-beta02
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 21:19:51 -07:00
dependabot[bot]
7550d426dd chore(deps): bump actions/github-script from 8 to 9 (#65098)
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 21:19:26 -07:00
Peter Steinberger
261b07b1d6 ci: prepare Blacksmith testbox shell 2026-04-22 05:17:30 +01:00
Peter Steinberger
092f292ceb ci: use smaller Blacksmith testbox runner 2026-04-22 05:14:55 +01:00
Peter Steinberger
4e22fc9498 ci: cancel stale push sanity runs 2026-04-22 05:11:57 +01:00
Peter Steinberger
a99490fba4 fix(plugins): support root-owned bundled runtime deps 2026-04-22 05:02:35 +01:00
Peter Steinberger
ba0250e4f3 ci: add Blacksmith testbox workflow 2026-04-22 04:59:38 +01:00
Peter Steinberger
094ca706ed perf(build): use tsgo for a2ui renderer compile 2026-04-22 04:57:55 +01:00
Shakker
3528a17b67 fix: scope static catalog discovery fallback 2026-04-22 04:51:38 +01:00
Shakker
bbe3dc6c2f fix: sanitize models list terminal output 2026-04-22 04:51:38 +01:00
Shakker
754125947a fix: include partial provider discovery fallbacks 2026-04-22 04:51:38 +01:00
Shakker
ae35795c04 docs: add models catalog changelog 2026-04-22 04:51:38 +01:00
Shakker
10959aa980 fix: restrict static model catalogs to bundled providers 2026-04-22 04:51:38 +01:00
Shakker
f3da6e96b7 fix: bound static provider catalog listing 2026-04-22 04:51:38 +01:00
Shakker
d6c7b468ea fix: honor provider hook aliases in catalog filters 2026-04-22 04:51:38 +01:00
Shakker
f9bac5038c fix: harden static provider catalog path 2026-04-22 04:51:38 +01:00
Shakker
d73c31110b fix: harden static provider catalog listing 2026-04-22 04:51:38 +01:00
Shakker
04ecf284fc fix: use static provider catalogs for model listing 2026-04-22 04:51:38 +01:00
Shakker
651d5e0022 docs: document provider catalog model discovery 2026-04-22 04:51:38 +01:00
Shakker
8ba52acc41 fix: add Kimi K2.6 provider catalog rows 2026-04-22 04:51:38 +01:00
Shakker
cc78dd2044 fix: show provider catalog models in all list 2026-04-22 04:51:38 +01:00
Peter Steinberger
11f38afbfc docs: note memory-core cron startup warning fix (#69941) (thanks @Sanjays2402) 2026-04-22 04:50:25 +01:00
Sanjay Santhanam
a37321ad5f fix(memory-core): suppress startup "cron service unavailable" warning (closes #69939)
memory-core registers a gateway:startup hook that runs reconcileManagedDreamingCron() before deps.cron is attached to the startup event (the startup hook is deferred via a 250ms setTimeout in server.impl).

Downgrade the first startup-time "cron service unavailable" warning to a debug log, and rely on the existing runtime reconciliation path to warn if the cron service truly stays unavailable after boot. The managed dreaming cron job itself runs correctly — this was a log-noise regression, not a functional failure.

Signed-off-by: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com>
2026-04-22 04:50:25 +01:00
Peter Steinberger
f027d8faa7 refactor(discord): share native interaction channel context 2026-04-22 04:47:57 +01:00
Peter Steinberger
00cdab99bf test: harden ci timing cleanup 2026-04-22 04:45:19 +01:00
Peter Steinberger
4d223950a0 perf(ci): use tsgo for sdk declaration emit 2026-04-22 04:45:19 +01:00
Gustavo Madeira Santana
a197b544fe fix(doctor): route setup doctor discovery (#69919)
Merged via squash.

Prepared head SHA: 90c7067941
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 23:40:22 -04:00
Peter Steinberger
a8a023779d fix(discord): preserve fetched thread parent for plugin commands (#69908) (thanks @neeravmakwana) 2026-04-22 04:31:31 +01:00
Neerav Makwana
349d86c152 fix(discord): move in-check inside try/catch in channel-access helper
Wrap the `key in channel` probe inside the existing `try/catch` in `readDiscordChannelPropertySafe` so a throwing Proxy `has` trap (or any other reflective error on the presence check) degrades to `undefined` instead of propagating, matching the existing behavior for throwing getters on the value read.

Add a regression test that exposes the interaction channel through a Proxy whose `has` trap throws on `parentId` and asserts the slash-command path still defers and dispatches.

No behavior change for Carbon prototype getters or plain-object channels: the safe accessor still traverses the prototype chain (required for Carbon's `GuildThreadChannel.parentId`), still returns `undefined` for missing or throwing reads, and still preserves null-to-undefined coercion downstream.
2026-04-22 04:31:31 +01:00
Neerav Makwana
dbf8fd0db7 fix(discord): read channel.parentId through safe accessor on partial thread channels
The Carbon `GuildThreadChannel.parentId` getter throws "Cannot access rawData on partial Channel" whenever Discord delivers a partial thread (for example when an interaction channel is unhydrated). The existing `"parentId" in channel` guard did not help because the `in` operator returns true for prototype getters without invoking them, so the read still crashed `/new` and similar slash commands, guild reactions, and the native model picker when invoked from inside a thread.

Expose a `resolveDiscordChannelParentIdSafe` helper alongside the other channel accessors and use it everywhere we currently read `channel.parentId` from the inbound Discord channel. When the getter throws, the helper returns `undefined`, and the downstream code already falls back to re-fetching the thread id via `resolveDiscordChannelInfo`, keeping authorization/config lookups on the same inputs as before.

Add a regression test that installs a throwing `parentId` getter on a partial guild thread channel and asserts the slash-command path still defers and dispatches instead of surfacing an unauthorized reply.

Fixes #69861
2026-04-22 04:31:31 +01:00
Peter Steinberger
b0734664f8 style: format touched code 2026-04-22 04:24:45 +01:00
Peter Steinberger
2b09c3c7c7 perf(ci): use tsgo for extension boundary compiles 2026-04-22 04:22:38 +01:00
Peter Steinberger
23a017be7c fix: suppress Mattermost quoted reasoning replies (#69927) (thanks @lawrence3699) 2026-04-22 04:10:28 +01:00
lawrence3699
bb43c7b89f fix(mattermost): suppress reasoning previews 2026-04-22 04:10:28 +01:00
lawrence3699
367faac596 fix(mattermost): suppress reasoning-only replies 2026-04-22 04:10:28 +01:00
Peter Steinberger
115accfc82 ci: narrow extension boundary cache inputs 2026-04-22 04:08:42 +01:00
Peter Steinberger
24c409035c perf(gateway): lazy load deferred plugin bootstrap 2026-04-22 04:06:22 +01:00
Peter Steinberger
cb4ec1265f fix(agents): align embedded built-in tool types 2026-04-22 03:42:49 +01:00
Ayaan Zaidi
407107276f fix: report cron message trace channel (#69940) (thanks @davehappyminion) 2026-04-22 08:12:20 +05:30
Ayaan Zaidi
2da1406b29 test(cron): trim message trace comments 2026-04-22 08:12:20 +05:30
davehappyminion
9db67e79a5 fix(cron): narrow accountId spoof guard to explicit mismatch only
Addresses codex P1 review on PR #69940: the previous guard rejected
targets that simply omitted accountId, but message-tool fills accountId
from the agent's bound account at exec time (message-tool.ts:730-733),
so account-bound cron jobs legitimately start with target.accountId
undefined. Rejecting that case lost skipMessagingToolDelivery, causing
dispatchCronDelivery to double-send.

Now we only reject when the tool explicitly names a *different*
accountId — which is the real CWE-284 spoof vector. Omission matches.

Tests updated accordingly:
- matcher unit test: flipped "omit accountId" case from false to true;
  "accountIds differ" case preserved as the real spoof guard
- integration tests: one legitimate-default case (rewrite happens),
  one explicit-mismatch case (rewrite suppressed)

658 cron tests pass.
2026-04-22 08:12:20 +05:30
davehappyminion
851bef9c25 fix(cron): rewrite generic message provider in trace + guard accountId spoof
When a cron job sends via the generic `message` tool, the delivery trace
previously recorded `messageToolSentTo[i].channel = "message"` even
though the send was resolved to a specific channel (e.g. telegram). This
made `jq` diffing intended-vs-actual awkward for the happy path.

Fix:
- `normalizeMessagingToolTarget` now rewrites `channel: "message"`
  to the resolved channel when `matchesMessagingToolDeliveryTarget`
  confirms the tool send matches the resolved cron delivery target.
  Genuinely unmatched generic sends keep the literal "message" so
  audits can still flag them.

- `matchesMessagingToolDeliveryTarget` now requires strict accountId
  equality whenever the resolved delivery carries an `accountId`. An
  omitted `target.accountId` previously short-circuited the guard and
  was treated as a wildcard, letting a generic send spoof attribution to
  any bot identity in the cron delivery trace (CWE-284). This was
  flagged by Aisle on #69771.

Tests:
- Unit: `matchesMessagingToolDeliveryTarget` rejects omitted-accountId
  against account-tied delivery; still matches same-accountId.
- Integration: cron run trace rewrites generic "message" to the
  resolved channel, preserves accountId on both sides, and leaves the
  literal "message" provider in place when the tool send omits
  accountId against an account-tied delivery.
2026-04-22 08:12:20 +05:30
Peter Steinberger
e8f18f95d5 test(docker): cover slack bundled runtime deps 2026-04-22 03:40:37 +01:00
Peter Steinberger
25e2e64ce4 test(docker): cover bundled channel update deps 2026-04-22 03:40:37 +01:00
Peter Steinberger
f9b20c7d17 fix(plugins): repair bundled runtime deps during doctor 2026-04-22 03:40:37 +01:00
Peter Steinberger
6608a50b39 perf(ci): trim gateway watch regression build 2026-04-22 03:35:19 +01:00
Peter Steinberger
0f4dc42767 build: update pi packages to 0.68.1 2026-04-22 03:35:15 +01:00
Peter Steinberger
de6f548a7c fix: suppress disabled channel read-only presence 2026-04-22 03:21:07 +01:00
Aaron U'Ren
8d021ee7bf perf(plugins): prefer native jiti for bundled plugin dist modules (#69925)
Merged via squash.

Prepared head SHA: 1b2da10865
Co-authored-by: aauren <1392295+aauren@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 22:18:35 -04:00
Val Alexander
fe663de8c7 fix(ui): add clear pending config updates action (#68178)
Merged via squash.

Prepared head SHA: 1a3cb66fcb
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
Reviewed-by: @BunsDev
2026-04-21 21:11:51 -05:00
Peter Steinberger
e92079be6b fix: finish browser click timeout recovery (#63524) 2026-04-22 03:08:36 +01:00
Dongseok Paeng
cd82b94333 fix(browser): propagate click aborts through agent act routes
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-22 03:08:36 +01:00
Dongseok Paeng
adc05f090a fix(browser): time out stuck chrome mcp clicks
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-22 03:08:36 +01:00
Peter Steinberger
c97c5a5aff fix: canonicalize opencode-go base URL 2026-04-22 03:05:48 +01:00
Peter Steinberger
1801b90460 chore: bump version to 2026.4.22 2026-04-22 03:04:53 +01:00
Peter Steinberger
d41c6403d5 docs: credit Chrome MCP timeout fix (#69733) 2026-04-22 03:02:49 +01:00
ayeshakhalid192007-dev
55c415a1da fix(browser): address review comments on Chrome MCP timeout handling 2026-04-22 03:02:49 +01:00
ayeshakhalid192007-dev
88268aa2cd fix(browser): make chrome.internal tests pass on Linux (existsSync mock) 2026-04-22 03:02:49 +01:00
ayeshakhalid192007-dev
dc9c46a8df fix(browser): reset Chrome MCP session after navigate_page timeout
navigateChromeMcpPage() now always passes a timeout to the Chrome MCP
navigate_page tool (defaulting to CHROME_MCP_NAVIGATE_TIMEOUT_MS when
the caller omits timeoutMs), and callTool() grows an optional safety-net
that tears down a stuck session via Promise.race so the next caller gets
a fresh subprocess. The catch block gains a transport-identity guard to
avoid clobbering a concurrently-created replacement session.
2026-04-22 03:02:49 +01:00
Peter Steinberger
ef66798433 fix: preserve outbound thread sessions 2026-04-22 02:56:23 +01:00
Peter Steinberger
d87f8cc142 test: move cron validation off gateway server 2026-04-22 02:55:40 +01:00
Patrick Erichsen
529577e045 fix(memory/dreaming): surface blocked status when heartbeat is disabled for main (#69875)
* fix(memory/dreaming): surface blocked status in memory status when heartbeat disabled for main

Replace the hand-rolled heartbeat-rules logic in resolveDreamingBlockedReason
with the shared resolveHeartbeatSummaryForAgent helper, promoted from core to
the plugin-sdk via infra-runtime. Collapses the two disabled-reason branches
into a single message that points at a new Troubleshooting section in the
dreaming docs, so the silent-failure mode described in openclaw/openclaw#69843
becomes legible without the extension re-encoding heartbeat-enablement rules.

Refs openclaw/openclaw#69843, openclaw/openclaw#46046.

* refactor(memory/dreaming): share resolveDreamingBlockedReason across cli and /dreaming surfaces

- Move resolveDreamingBlockedReason from cli.runtime.ts into dreaming.ts as an exported helper and pin its heartbeat check to DEFAULT_AGENT_ID (now exported from plugin-sdk/routing) so the status-line check agrees with the cron's hardcoded sessionTarget even when the configured default agent is not main.
- Render the blocked reason from formatStatus in dreaming-command.ts directly under the enabled line, so /dreaming status, /dreaming on, /dreaming off, and bare /dreaming all flag that the cron is blocked instead of implying dreaming is healthy.
- Tighten the blocked-reason text to lead with user impact ('dreaming is enabled but will not run because heartbeat is disabled for main'), so operators immediately understand the config is toggled on but nothing is actually running.
- Tighten the dreaming Troubleshooting copy to name main explicitly and mention both surfaces.
- Add tests locking the new behavior across cli.test.ts (default-agent=ops still reports blocked for main) and dreaming-command.test.ts (/dreaming status ordering, /dreaming on surfacing, healthy-heartbeat omission).

Refs openclaw/openclaw#69843, openclaw/openclaw#46046.

* fix(memory/dreaming): check heartbeat for the resolved default agent, not the literal 'main'

sessionTarget: 'main' is a cron session-type enum variant meaning 'the default agent's main session', not an agent id (see src/cron/service/jobs.ts). buildManagedDreamingCronJob does not set agentId, and cron runtime resolves the missing agentId through resolveDefaultAgentId(cfg) before enqueuing or waking. The previous pin to DEFAULT_AGENT_ID could produce a false 'blocked' reading when a configured default agent is not 'main' and its heartbeat is fine, and could miss a real block when the default agent is not 'main' and that agent's heartbeat is actually off.

Switch resolveDreamingBlockedReason to resolveDefaultAgentId(cfg) and interpolate the resolved agent id into the message so the blocked line names the agent whose heartbeat is the blocker. Introduce a narrow local CRON_SESSION_TARGET_MAIN constant for the cron session-type enum variant (used by the sessionTarget type and value) so the remaining 'main' literal is semantically distinct from any agent id. Revert the DEFAULT_AGENT_ID export addition on plugin-sdk/routing; memory-core no longer needs it. Update the Troubleshooting doc wording and the cli test that was locking the wrong behaviour.

Refs openclaw/openclaw#69843, openclaw/openclaw#46046.

* fix(memory/dreaming): align blocked check with server-cron wake's defaults-only heartbeat

resolveDreamingBlockedReason was using resolveHeartbeatSummaryForAgent, which merges agents.defaults.heartbeat with agents.list[].heartbeat. The managed dreaming cron leaves job.agentId and job.sessionKey unset, so server-cron's wake wrapper cannot look up a per-agent entry and calls runHeartbeatOnce with agents.defaults.heartbeat only. Using the summary helper would disagree with the actual wake when the default agent overrides heartbeat.every differently from the defaults (either direction — false blocked when the override would run, or false healthy when defaults block).

Mirror the wake path explicitly: rule-1 enablement via isHeartbeatEnabledForAgent against the default agent, rule-3 interval via resolveHeartbeatIntervalMs with defaults-only heartbeat config. Comment points at server-cron so a future cleanup of that latent override-propagation gap sees the coupling.

Refs openclaw/openclaw#69843.
2026-04-21 18:51:43 -07:00
Peter Steinberger
b3b62ed004 perf: replace madge cycle scan 2026-04-22 02:46:16 +01:00
Patrick Erichsen
efb7d426cf fix(browser): reject ax<N> refs in act path instead of timing out (#69924) 2026-04-21 18:43:27 -07:00
claycurry
6a68f1dd57 Docs: link feature cards to relevant pages
Link docs feature cards to their intended destination pages in the English docs surfaces.

- add hrefs to the feature cards in docs/concepts/features.md
- add hrefs to the key capability cards in docs/index.md
- preserve current main branch copy while landing the navigation fix
2026-04-21 20:36:55 -05:00
Peter Steinberger
fb9a21ae8f fix: centralize draft preview finalization 2026-04-22 02:32:55 +01:00
Peter Steinberger
ffef84dea7 ci: start runtime tests without dist 2026-04-22 02:27:03 +01:00
Peter Steinberger
e5909f3e5d ci: scope mlx helper as macos native 2026-04-22 02:19:58 +01:00
Peter Steinberger
e836b5b6d7 ci: isolate mlx from macos swift checks 2026-04-22 02:12:07 +01:00
Peter Steinberger
710e4e9e51 ci: widen package boundary cache inputs 2026-04-22 01:53:22 +01:00
Gustavo Madeira Santana
f4478a142a Fix channel presence gating for disabled plugins (#69862)
Merged via squash.

Prepared head SHA: f76f6212b2
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 20:51:09 -04:00
Peter Steinberger
eb6006730d fix(line): guard outbound media targets 2026-04-22 01:48:14 +01:00
Peter Steinberger
66576f3355 test(extensions): fix lint-clean test assertions 2026-04-22 01:43:18 +01:00
Peter Steinberger
d57fe63ee0 ci: cache package boundary artifacts 2026-04-22 01:42:44 +01:00
Peter Steinberger
5c74e9da01 fix(qqbot): avoid eager storage directory creation 2026-04-22 01:42:10 +01:00
Peter Steinberger
540171ddbd docs: clarify ACP delivery model 2026-04-22 01:32:20 +01:00
Peter Steinberger
73d9746e6a ci: reuse swift build cache for unchanged inputs 2026-04-22 01:30:40 +01:00
Peter Steinberger
ce05418930 ci: preserve exact swift build cache 2026-04-22 01:26:05 +01:00
Gustavo Madeira Santana
819d15481d fix: validate plugin source entries before runtime inference (#69868)
Merged via squash.

Prepared head SHA: b67644cdda
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 20:12:19 -04:00
Gustavo Madeira Santana
19354c9a6a fix(discord): keep slash follow-ups ephemeral (#69869)
Merged via squash.

Prepared head SHA: 0f5ab77156
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 20:02:59 -04:00
Ron Cohen
08bc16853e WhatsApp: add group and direct system prompt support (#59553)
Merged via squash.

Prepared head SHA: 63e2b50e01
Co-authored-by: Bluetegu <1525690+Bluetegu@users.noreply.github.com>
Co-authored-by: omarshahine <10343873+omarshahine@users.noreply.github.com>
Reviewed-by: @omarshahine
2026-04-21 16:40:32 -07:00
Gustavo Madeira Santana
06a6dd5a6b chore(docs): update stale docs ref 2026-04-21 19:33:36 -04:00
Peter Steinberger
37463af5e1 ci: increase package boundary compile concurrency 2026-04-22 00:26:03 +01:00
Onur Solmaz
99787dbf45 docs(skills): add duplicate triage maintainer skill (#69780)
* docs(skills): add duplicate triage maintainer skill

* docs(skills): align duplicate triage with prtags sync

* docs(skills): remove local paths from duplicate triage skill

* docs(skills): use pr-search-cli naming consistently

* docs(skills): fix pr-search-cli command usage

* docs(skills): use tested release install commands

* docs(skills): treat prtags comment sync as automatic

* docs(skills): adjust duplicate triage skill title

* docs(skills): add duplicate triage UI metadata
2026-04-22 01:18:07 +02:00
Peter Steinberger
85c63942a5 ci: skip swift package patch in macos node lane 2026-04-22 00:16:45 +01:00
Peter Steinberger
a426ef5b6a ci: preserve swift build cache hits 2026-04-22 00:12:03 +01:00
Bek
e116b343b2 feat(slack): Annotate inbound Slack mention tokens in Slack RawBody and BodyForAgent content so the agent sees both the actionable Slack mention token and a human-readable name. (#65731)
* Annotate inbound Slack mentions in raw bodies

* Avoid shared regex state in Slack mention rendering

* Bound Slack mention lookups with concurrency

* slack: keep mention concurrency helper plugin-local

* test: stabilize node core CI assertions

* slack: cap mention lookups per inbound message

* test: reset suite gateway runtime state

* fix(slack): reuse plugin sdk concurrency helper
2026-04-21 19:03:50 -04:00
Peter Steinberger
6bf56d8637 ci: cap android checkout and use build cache 2026-04-22 00:02:40 +01:00
Peter Steinberger
cc8ecde364 ci: avoid external gradle action in android checks 2026-04-21 23:56:52 +01:00
Peter Steinberger
6966f018f7 ci: quiet mlx swift manifest warnings 2026-04-21 23:52:04 +01:00
Peter Steinberger
e822e71410 ci: cap stuck checkout retries 2026-04-21 23:47:17 +01:00
Peter Steinberger
df3fcbd716 test: lazy-load openai provider catalog contract 2026-04-21 23:35:37 +01:00
Bek
70683179a0 fix(slack): narrow first turn context seeding to remove redundant thread-starter content (#68402)
Fix Slack thread bootstrap replaying the bot's own prior turns into new sessions and duplicating the thread-starter prompt block.

Narrows first-turn context seeding to exclude only the current Slack bot's own starter/history entries, so self-authored turns no longer pollute new session prompts while preserving human and third-party bot context

Removes the redundant plain-text starter prelude in runPreparedReply() that doubled thread-starter content when no ThreadHistoryBody was present
2026-04-21 18:28:34 -04:00
Peter Steinberger
acf67c1a42 docs: tighten optimizetests skill 2026-04-21 23:24:51 +01:00
Bek
dfe0e49c8a fix(qmd): Dedup in-flight manager creation so only one full QMD manager arms per agent/config at a time, eliminating the concurrent exportSessions() collisions that triggered path changed during write errors (#65226)
Fixes concurrent manager creation races that caused SafeOpenErrors during session export.

Deduplicates in-flight manager creation so only one full QMD manager arms per agent/config at a time, eliminating the concurrent exportSessions() collisions that triggered path changed during write errors
Resolves and snapshots runtime inputs before cache reuse, replacing stale managers atomically when workspace/config changes, and aborting queued export work promptly on close()
2026-04-21 18:22:21 -04:00
Bek
1acb094579 fix: wrap oversized session lines before JSONL write (#64494)
updates the real session-export path so pathological transcript messages no longer become a single toxic export line for downstream indexing.
2026-04-21 18:18:22 -04:00
Gustavo Madeira Santana
66add9fcd9 perf(cli): lazy-load doctor plugin paths (#69840)
Merged via squash.

Prepared head SHA: ebf93ad913
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 18:17:19 -04:00
Bek
0e1d324dd8 fix(agents): Wake active requester sessions for subagent completions while keeping dormant sessions externally deliverable (#62963)
Route subagent completion announces through embedded-run wake for active requesters, preserve external delivery for dormant ones
2026-04-21 18:13:53 -04:00
Bek
14dcbd4044 fix(prompt): align system prompt messaging and subagent routing guidance (#64059)
replace legacy `to` with `target` in prompt
2026-04-21 18:10:53 -04:00
Peter Steinberger
824c4785e4 test: speed channel contract suites 2026-04-21 23:09:22 +01:00
Devin Robison
ee316dbc4b fix(tlon): guard memex upload target (#69794)
* fix(tlon): guard memex upload target

* fix(tlon): harden guarded memex upload

* fix(tlon): validate hosted memex upload targets

* fix(tlon): tighten hosted domain matching

* fix(tlon): reject non-standard memex upload ports

* fix(tlon): disable memex upload redirects

* test(tlon): drop redundant mock resets in memex upload test

* chore(lint): update tlon raw-fetch allowlist for guarded memex upload

* fix(tlon): reject unparseable ship URLs in hosted-ship classifier

* fix(lint): point tlon raw-fetch allowlist at fetch callee lines

* fix(tlon): guard custom-S3 upload through fetchWithSsrFGuard

* fix(tlon): preserve scheme-less hosted ship routing and allow explicit :443

* docs(changelog): note tlon upload guard

* fix(tlon): guard memex lookup and private s3 opt-in

* fix(tlon): validate upload result URLs
2026-04-21 15:57:49 -06:00
Peter Steinberger
74668ea8a1 fix(image-generation): log provider fallback failures 2026-04-21 22:50:09 +01:00
Vincent Koc
b5c4aaf2a7 fix(install): mirror node-domexception override for npm (#69819)
* fix(install): mirror node-domexception override for npm

* docs(changelog): credit npm install override fix

* fix(install): pin domexception override exactly

* docs(changelog): drop leftover npm fix merge markers

* Update CHANGELOG.md
2026-04-21 14:45:05 -07:00
Peter Steinberger
d1e3789e15 test: optimize slow test hotspots 2026-04-21 22:42:08 +01:00
Bek
49b233caa1 fix(slack): preserve thread aliases in runtime outbound sends (#62947)
Slack-threaded direct sends that go through the generic runtime wrapper now stay in the intended thread when the caller supplies threadTs.
2026-04-21 17:40:47 -04:00
Vincent Koc
475e6ff1d1 docs(gateway): replace user-facing 'extension' references with 'plugin' per terminology rules 2026-04-21 14:39:10 -07:00
Peter Steinberger
d2f68af615 docs: document Ollama image understanding 2026-04-21 22:33:56 +01:00
Vincent Koc
f1f6214fd5 docs(help): add frontmatter to gpt54-codex parity docs 2026-04-21 14:29:58 -07:00
Peter Steinberger
e71e543350 fix: route explicit image describe models 2026-04-21 22:25:45 +01:00
Peter Steinberger
a7ff7dd945 docs: note Ollama image routing (#69816) (thanks @soloclz) 2026-04-21 22:25:45 +01:00
soloclz
9a22cd212b fix(ollama): register media-understanding provider so image tool can route ollama/* models
Ollama chat models already support image inputs (extensions/ollama/src/stream.ts
extracts image parts and forwards them via the Ollama API), but the ollama
plugin did not register a MediaUnderstandingProvider. The image tool's provider
registry therefore had no 'ollama' entry, so requests like
`imageModel: 'ollama/qwen2.5vl:7b'` failed to resolve and fell back to
unrelated providers.

Register ollamaMediaUnderstandingProvider with:
- capabilities: ['image']
- describeImage/describeImages wired to the shared core helpers (reuses the
  same pi-ai complete path Ollama chat already goes through)
- no defaultModels or autoPriority: Ollama vision support depends on which
  model the user has pulled, so we don't pick a canonical default and don't
  auto-steal image duty from configured providers.

Fixes #69071 (and supersedes #60280).
2026-04-21 22:25:45 +01:00
Vincent Koc
b2f96f7f05 docs(providers): alphabetize Cloudflare/ComfyUI and vLLM/Vydra entries 2026-04-21 14:25:31 -07:00
Devin Robison
7be82d4fd1 fix(openshell): pin host writes to sandbox root (#69797)
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
2026-04-21 15:18:28 -06:00
Peter Steinberger
ae4c5cd460 fix: land ACP child sessions_send guard (#69817) (thanks @scotthuang) 2026-04-21 22:17:28 +01:00
scotthuang
8a7c21407a fix(agents): gate sessions_send A2A skip on requester ownership
Greptile/Codex review follow-ups on #69817:

- Narrow skipA2AFlow from target-only detection to a combined check that
  the caller is the parent of the target (new
  isRequesterParentOfBackgroundAcpSession helper). Under
  tools.sessions.visibility=all a non-parent sender can see the same
  oneshot ACP session; the previous guard would have suppressed their
  only follow-up delivery path. With requester ownership required, those
  senders continue through the normal A2A flow.
- When the A2A flow is skipped, return delivery.status="skipped" instead
  of "pending" so the parent LLM does not wait for a second result that
  will never arrive.
- Add unit tests for resolveAcpSessionInteractionMode and
  isRequesterParentOfBackgroundAcpSession covering both the new
  ownership gate and the existing target-type branches.
2026-04-21 22:17:28 +01:00
scotthuang
1c3fbbd72a fix(agents): skip sessions_send A2A flow for parent-owned ACP children
The A2A ping-pong + announce flow in runSessionsSendA2AFlow treats the
send target as a peer agent and echoes replies back and forth between
requester and target. When the target is an ACP child spawned by the
requester, this creates an infinite loop: the parent is woken with the
child's reply, generates a user-facing response, and has that response
forwarded back to the child as a new user message — effectively granting
the child an implicit sessions_send capability back to the parent.

ACP children already report their results through the
[Internal task completion event] announcement path, so no A2A flow is
needed when the send target is a parent-owned background ACP session.

Detect this case via isParentOwnedBackgroundAcpSession and short-circuit
startA2AFlow before runSessionsSendA2AFlow is invoked.
2026-04-21 22:17:28 +01:00
Vincent Koc
ff67a890af docs(channels): clean troubleshooting link labels, generic imessage path placeholder, drop msteams stamped date 2026-04-21 13:59:12 -07:00
Peter Steinberger
8d1b3d4578 ci: speed up release metadata pre-commit checks 2026-04-21 21:56:06 +01:00
Peter Steinberger
aa94501f5f feat(openai): default images to gpt-image-2 2026-04-21 21:49:16 +01:00
Peter Steinberger
0b1a35363e chore: start 2026.4.21 development 2026-04-21 21:42:15 +01:00
Vincent Koc
8f1a87ea47 docs: note Kimi K2.6 thinking-disabled on Fireworks and Ollama cloud onboard live-tag fetch 2026-04-21 13:41:10 -07:00
Vincent Koc
9702f0bf21 docs: tool-progress preview streaming, Control UI avatar auth, exec heredoc and external-content token sanitization 2026-04-21 13:39:55 -07:00
Devin Robison
3cb1a56bfc fix(gateway): derive loopback owner context from token (#69796)
* fix(gateway): derive loopback owner context from token

* docs(changelog): note loopback owner token hardening

* refactor(gateway): clarify loopback runtime cleanup

* fix(gateway): compare both loopback bearer classes
2026-04-21 14:39:48 -06:00
Peter Steinberger
674feda214 docs(plugins): document message presentation cards 2026-04-21 21:29:44 +01:00
Peter Steinberger
c742a706bf feat(plugins): add experimental skill workshop 2026-04-21 21:29:44 +01:00
Peter Steinberger
fd0970c077 refactor(channels): decouple presentation rendering 2026-04-21 21:29:44 +01:00
Peter Steinberger
d7a173e60e feat(plugin-sdk): add presentation and skills runtime contracts 2026-04-21 21:29:44 +01:00
Vincent Koc
78030d0d52 docs: plugin manifest precedence, QQBot engine/bot-approve/QR onboarding, web-search plugin-scoped SecretRefs 2026-04-21 13:26:25 -07:00
Vincent Koc
b4a59be9b6 docs: document stdio env filter, enforceOwnerForCommands, OPENCLAW_* .env blocking 2026-04-21 13:21:34 -07:00
Vincent Koc
32ccf27e60 docs: document WS broadcast scope gating and Control UI img-src CSP 2026-04-21 13:14:15 -07:00
Vincent Koc
7d7c0b1dfe docs: cover BB tapback fallback, iMessage/SMS routing, Mattermost streaming, Matrix mention-prefixed slash 2026-04-21 13:09:09 -07:00
Peter Steinberger
e5af4e3b5c ci(deps): gate extension-owned root dependencies 2026-04-21 21:08:08 +01:00
Devin Robison
b2e8b7d4bb fix(exec): block heredoc parameter expansion (#69795)
* fix(exec): block heredoc parameter expansion

* chore(changelog): note heredoc parameter expansion fix

* fix(exec): tighten heredoc expansion guardrails

* fix(exec): reject continued heredoc expansions

* fix(exec): buffer heredoc continuation chunks

* fix(exec): harden heredoc continuation parsing

* fix(exec): cap heredoc continuation chunks

* fix(exec): reject continued heredoc param expansion across delimiter

Bash splices `$VAR\\<newline>REST` into `$VARREST` inside an
unquoted heredoc body even when the continued physical line matches the
heredoc delimiter; the heredoc only terminates at EOF with a warning.
The analyzer previously shifted the pending heredoc the moment a line
equaled the delimiter, so a payload like `cat <<KEY\n$OPENAI_API_\\\nKEY`
passed allowlist review while the runtime would expand and print
$OPENAI_API_KEY.

Mirror bash's splicing: only treat a delimiter-matching line as the
terminator when no continuation chunks are pending, otherwise append it
to the logical line and evaluate it through the expansion check. The
tail handler does the same splice + expansion check before falling back
to "unterminated heredoc".
2026-04-21 14:01:35 -06:00
Peter Steinberger
ccfef0f13f chore: update appcast for 2026.4.20 2026-04-21 21:01:19 +01:00
Peter Steinberger
8d289306de ci: support release branch mac validation 2026-04-21 21:01:05 +01:00
Devin Robison
2ce16e558e fix(gateway): require auth for control UI avatar route (#69775)
* fix(gateway): require auth for control UI avatar route

* chore: add changelog for control UI avatar auth

* fix(control-ui): honor device auth for avatar urls

* fix(control-ui): avoid query tokens for avatar auth

* fix(control-ui): render authenticated avatar blob URLs in chat views

* fix(control-ui): restore normalizeOptionalString import in render helpers
2026-04-21 13:51:03 -06:00
Gustavo Madeira Santana
6b185e2849 perf: speed up discord channel registration (#69791)
Merged via squash.

Prepared head SHA: 231d8763b4
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 15:48:08 -04:00
Peter Steinberger
895ac965da test: cover Telegram session recreation 2026-04-21 20:36:32 +01:00
Peter Steinberger
0a6ce260ed fix(deps): keep qqbot connector plugin-local 2026-04-21 20:33:16 +01:00
Peter Steinberger
6f004ed4d4 feat(fireworks): add Kimi K2.6 model 2026-04-21 20:31:33 +01:00
Peter Steinberger
2514746b32 fix: sanitize LLM special tokens in external content 2026-04-21 20:29:02 +01:00
Shakker
fb7bfb411c docs: add Copilot Opus changelog (#69818) (thanks @shakkernerd) 2026-04-21 20:00:06 +01:00
Shakker
2161ed8259 fix: update Copilot Opus default to 4.7 2026-04-21 20:00:06 +01:00
Peter Steinberger
11efbf5a2e fix: prevent stale subagent failure announces 2026-04-21 19:59:12 +01:00
Tak Hoffman
dcf131e54c docs: restore general multi-gateway guidance (#69810) 2026-04-21 13:34:18 -05:00
Peter Steinberger
47cfdd2df1 test: cover active provider thinking registry 2026-04-21 19:24:26 +01:00
Peter Steinberger
61564147f3 fix: break provider thinking import cycle 2026-04-21 19:19:03 +01:00
Peter Steinberger
b2b43085bc ci: use larger Blacksmith macOS runners 2026-04-21 19:03:50 +01:00
Tak Hoffman
5218c1a01f docs: front-load rescue bot quickstart (#69803)
* docs: front-load rescue bot quickstart

* docs: recommend rescue port 19789

* docs: show rescue port in quickstart command
2026-04-21 13:01:23 -05:00
Agustin Rivera
38356c658a fix(synology): validate webhook file urls (#69784)
* fix(synology): validate webhook file urls

* fix(synology): restore file send throttle

* docs(changelog): note synology webhook file_url SSRF guard (#69784)

---------

Co-authored-by: Devin Robison <drobison@nvidia.com>
2026-04-21 12:00:28 -06:00
Peter Steinberger
bcfa781a1b fix: remap thinking levels on model switch 2026-04-21 18:53:49 +01:00
Gustavo Madeira Santana
24db09a19b fix(cli): keep channel status checks off plugin runtimes (#69479)
Merged via squash.

Prepared head SHA: 63f6e416a9
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 13:53:08 -04:00
Tak Hoffman
09c5669299 docs: clarify rescue bot gateway setup (#69788)
* docs: clarify rescue bot gateway setup

* docs: make rescue bot guide more prescriptive
2026-04-21 12:29:40 -05:00
Gustavo Madeira Santana
ddc1d9aa54 perf: speed up telegram channel registration (#69786)
Merged via squash.

Prepared head SHA: ac03f96e0d
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 13:24:28 -04:00
cxy
5e72e39c18 feat(qqbot): extract self-contained engine/ architecture with QR-code onboarding, approval handling (#67960)
* feat(qqbot): add core architecture modules

* feat(qqbot): extract engine modules with DI adapters

* refactor(qqbot): remove plugin-level TTS, delegate to framework

Remove qqbot's internal TTS implementation and unify voice synthesis
through the framework's global TTS provider registry.

- Delete engine/gateway/tts-config.ts (plugin-specific TTS config)
- Simplify TTSProvider interface to textToSpeech + audioFileToSilkBase64
- Remove dual-strategy TTS in handleAudioPayload (plugin + global fallback)
- Strip QQBotTtsSchema from config-schema, plugin.json, and tests
- Remove TTS diagnostics logging and hasTTS system prompt from gateway
- Delete ~260 lines of TTS code from utils/audio-convert.ts

Made-with: Cursor

* feat(qqbot): extract shared engine modules for config, tools, and audio

Add engine-layer modules that are self-contained and portable across
both the built-in and standalone qqbot packages:

- engine/config: account resolution helpers, field readers
- engine/tools: channel API proxy, remind scheduling logic
- engine/utils: audio format conversion, duration/error formatting,
  debug logging

Consolidate duplicate utility functions across the codebase:

- Merge debug-log.ts into log.ts
- Merge error-format.ts into format.ts with full .cause chain support
- Unify normalizeLowercase/readNumber/readBoolean/readStringMap into
  string-normalize.ts, removing private copies in resolve.ts,
  remind-logic.ts, and audio-convert.ts
- Remove dead formatDuration export from audio-convert.ts
- Delete unused config/schema.ts and config/helpers.ts

Made-with: Cursor

* refactor(qqbot): streamline account configuration and credential management

Refactor the QQBot account configuration logic by consolidating credential management into dedicated engine modules. Key changes include:

- Migrate credential clearing and validation logic to engine/config/credentials.ts.
- Simplify setup input validation and application in engine/config/setup-logic.ts.
- Enhance account resolution and configuration application in engine/config/resolve.ts.
- Update channel and messaging logic to utilize the new credential management functions.

This refactor improves code maintainability and clarity by separating concerns and reducing duplication across the codebase.

* feat(qqbot): simplify api architecture

* feat: 支持扫码绑定QQ机器人

* feat(qqbot): refactor gateway into inbound pipeline + outbound dispatch

- Extract handleMessage (620 lines) into three modules:
  - inbound-context.ts: InboundContext type definition
  - inbound-pipeline.ts: buildInboundContext()
  - outbound-dispatch.ts: dispatchOutbound()
- gateway.ts handleMessage reduced to ~35 line shell
- Unify parseRefIndices: support both ext prefix formats + MSG_TYPE_QUOTE
- Add ref/format-message-ref.ts for cache-miss quote formatting
- Remove [QQBot] to= from agentBody, use GroupSystemPrompt instead
- QueuedMessage: add msgType/msgElements for quote messages

* fix(qqbot): fix markdownSupport loss + dynamic User-Agent

Root cause: setOpenClawVersion() called _ensureInitialized(true) which
cleared _appRegistry, destroying the MessageApi instance created by
initApiConfig() with markdownSupport=true. Subsequent block deliver
calls created a default markdownSupport=false instance, causing:
1. Markdown messages sent as plain text (msg_type=0 instead of 2)
2. message_reference incorrectly added (only suppressed in MD mode)

Fix: ApiClient and TokenManager now accept userAgent as string | (() => string).
sender.ts passes the buildUserAgent function reference, so UA changes
propagate automatically on next request without rebuilding any objects.

- ApiClient: userAgent -> resolveUserAgent getter, called per-request
- TokenManager: same pattern
- types.ts: ApiClientConfig.userAgent supports string | (() => string)
- sender.ts: remove force re-init + _rebuildAppRegistry hack
  - initSender/setOpenClawVersion only update version variables
  - _ensureInitialized creates singletons once, never destroys them
  - _appRegistry is never cleared -> markdownSupport always preserved
- runtime.ts: inject framework version via setOpenClawVersion(runtime.version)
- gateway.ts: pass openclawVersion to initSender + registerPluginVersion
- slash-commands-impl.ts: remove fragile require("../package.json")

* feat(qqbot): implement native approval handling and configuration

Add a new approval handling system for QQBot that integrates with the existing framework. Key features include:

- Introduce `approval-handler.runtime.ts` for managing approval requests via QQ messages with inline keyboard support.
- Create `approval-native.ts` as the entry point for QQBot's approval capability, allowing for simplified approval processes without explicit approver lists.
- Implement configuration schema for exec approvals, enabling fine-grained control over who can approve requests.
- Enhance messaging and interaction handling to support approval decisions through button interactions.

This implementation streamlines the approval process, making it more user-friendly and efficient for QQBot users.

* refactor(qqbot): enhance error handling across API and messaging modules

This update introduces a centralized error formatting utility, `formatErrorMessage`, to improve consistency in error logging throughout the QQBot codebase. Key changes include:

- Integration of `formatErrorMessage` in various API client, messaging, and gateway modules to standardize error messages.
- Replacement of direct error message handling with the new utility to enhance readability and maintainability.

These improvements streamline error reporting and provide clearer insights into issues encountered during operation.

* refactor(qqbot): enhance API and messaging structure with type improvements

This update refines the API and messaging modules by introducing type enhancements and restructuring function signatures for better clarity and maintainability. Key changes include:

- Updated import statements to streamline type usage in  and .
- Refactored message sending functions to accept options objects, improving readability and flexibility.
- Introduced a new  method in  to facilitate external message-sent notifications.
- Enhanced error handling in the retry mechanism to ensure more robust behavior.

These modifications aim to improve the overall code quality and developer experience within the QQBot framework.

* feat: 优化文案

* refactor(qqbot): unify Logger interfaces + eliminate P0 code smells

Logger unification (17 files):
- Introduce single EngineLogger interface in engine/types.ts
  { info, error, warn?, debug? }
- Delete 5 fragmented Logger interfaces:
  GatewayLogger, ReconnectLogger, MessageRefLogger, PathLogger, SenderLogger
- Replace all references across engine/ to use EngineLogger directly

P0 code smell fixes (sender.ts + messages.ts + outbound-dispatch.ts):
- messages.ts: add public notifyMessageSent() method on MessageApi,
  replacing 8x 'as unknown as { messageSentHook }' private field hack
- sender.ts: extract notifyMediaHook() helper, deduplicate 4 media
  send functions (sendImage/sendVoice/sendVideo/sendFile)
- sender.ts: replace magic numbers 1/2/3/4 with MediaFileType enum
- sender.ts: remove 4 redundant 'as MessageResponse' type assertions
- outbound-dispatch.ts: remove 5 unnecessary 'as never' casts

* feat(qqbot): add /bot-clear-storage command + consolidate utils/types into engine/

/bot-clear-storage (slash-commands-impl.ts):
- Migrate from standalone version, aligned with its two-step flow:
  1. No args: scan ~/.openclaw/media/qqbot/downloads/{appId}/ and
     display file list with confirmation button
  2. --force: delete files + removeEmptyDirs cleanup
- C2C only (group chat returns hint)
- bot-help: exclude bot-upgrade and bot-clear-storage in group listings

Consolidate into engine/:
- Delete src/utils/audio-convert.ts (pure re-export shell, zero consumers)
- Move 5 test files from src/utils/ to src/engine/utils/ (fix import paths)
- Move src/types/silk-wasm.d.ts to src/engine/types/
- Remove empty src/utils/ and src/types/ directories

* refactor(qqbot): restructure API and bridge components for improved modularity

This update enhances the QQBot framework by reorganizing the API and bridge components, promoting better modularity and maintainability. Key changes include:

- Refactored import paths to streamline access to bridge tools and configurations.
- Introduced new bridge files for channel entry, runtime, and approval capabilities, centralizing related functionalities.
- Updated existing functions to utilize the new bridge structure, ensuring consistency across the codebase.
- Removed deprecated functions and types, simplifying the overall architecture.

These modifications aim to improve code clarity and facilitate future development within the QQBot ecosystem.

* refactor(qqbot): standardize engine log levels and unify log tag prefix

- Rename client.ts to api-client.ts to match ApiClient class name
- Downgrade ~60 non-critical info logs to debug level across 12 files
  (token request/response, HTTP request/response, session restore,
  media tag detection, image classification, quote detection,
  attachment download/transcode, retry attempts, etc.)
- Unify log tag prefix to [qqbot:xxx] format across all engine modules
  ([core-api] -> [qqbot:api], [token:x] -> [qqbot:token:x],
  [retry] -> [qqbot:retry], [messages] -> [qqbot:messages],
  [sender:x] -> [qqbot:x])
- Remove unnecessary reqTs timestamp from api-client.ts log output
- Add dispatch event debug log in gateway-connection.ts
- Merge sendProactiveMessage into sendText, remove dead code
  (sendProactiveText import, getRefIdx, QQMessageResult type)
- Narrow allow-from.ts type from unknown[] to Array<string | number>

* refactor(qqbot): move interaction handler from bridge to engine

- Move onInteraction approval handler into engine/gateway.ts as
  createApprovalInteractionHandler(), eliminating the callback
  indirection through CoreGatewayContext
- Remove onInteraction from CoreGatewayContext interface and its
  unused InteractionEvent import from gateway/types.ts
- Remove getPlatformAdapter, parseApprovalButtonData and
  InteractionEvent imports from bridge/gateway.ts

* refactor(qqbot): route bridge and sender logs through framework logger

- Add bridge/logger.ts as a shared logger holder for bridge-layer
  modules, injected with ctx.log during gateway startup
- Replace all console.log/console.error in bridge/ with
  getBridgeLogger() calls (approval, bootstrap, tools)
- Restore framework logger support in sender.ts via initSender()
  so API-layer logs flow through OpenClaw log system
- Remove all direct debugLog/debugError imports from bridge/

* feat(qqbot): per-account isolated resource stack + multi-account logger

- sender.ts: global singletons (ApiClient/TokenManager/MediaApi) -> per-account AccountContext
  - Add _accountRegistry: Map<appId, AccountContext>
  - Each account owns independent client/tokenMgr/mediaApi/messageApi/logger
  - registerAccount() atomically sets up all resources
  - resolveAccount() routes to correct resource stack by appId
  - Remove _sharedLogger/_loggerRegistry/_appRegistry and old structures

- bridge/gateway.ts: createAccountLogger() with auto [accountId] prefix
  - registerAccount() merges logger + markdownSupport + full API resources

- engine-wide: remove ~60 manual [qqbot:${accountId}] log prefixes
  - Prefixes now auto-injected by per-account logger
  - Remove prefix/logPrefix parameter chains (outbound/outbound-deliver/typing-keepalive etc)

* feat(qqbot): completes fallback path for approval with multi-account isolation

When the execApprovals are not configured, multiple QQBot accounts' handlers will attempt to deliver the same approval message. The openid is account-level, and cross-account delivery will trigger a QQ Bot API 500 error.

- Add account ownership verification in the fallback shouldHandle: Only match the account's handler when the request includes turnSourceAccountId; if unbound, delivery is only permitted when the number of enabled+secret accounts is ≤1.

- Consolidate account ownership determination into the unified export `matchesQQBotApprovalAccount` in `exec-approvals.ts`, with both capability and native runtime paths sharing the same logic to eliminate redundancy.

* feat(qqbot): optimize permission validation strategy

* feat(qqbot): show plugin version in /bot-version and /bot-help

Align /bot-version output with the standalone openclaw-qqbot build so users see both the QQBot plugin version and the OpenClaw framework version. Append the plugin version as a footer in /bot-help as well, matching the standalone UX.

Also fix the plugin version lookup that previously rendered as 'vunknown': the old code used a hardcoded '../../package.json' relative path which resolved to 'src/package.json' (non-existent) when executed from raw sources, so the require threw and the default 'unknown' value was retained. The same broken value also leaked into the QQ Bot API User-Agent header.

Replace the hardcoded path with a dedicated helper (bridge/plugin-version.ts) that walks up the directory tree from import.meta.url and validates the manifest's name field (@openclaw/qqbot) to avoid misreading the monorepo root package.json. Covered by 6 unit tests.

* feat(qqbot): trust shared ~/.openclaw/media root for payload files

Add getOpenClawMediaDir() and include it alongside getQQBotMediaDir() in the allowed roots of resolveQQBotPayloadLocalFilePath, so framework-produced attachments under sibling directories (e.g. media/outbound/ written by saveMediaBuffer) are trusted by auto-routed sends without triggering the path-outside-storage guard.

Covered by a new test case that verifies files under ~/.openclaw/media/outbound/ resolve successfully.

* fix(qqbot): ensure PlatformAdapter is registered before approval delivery

After the framework centralized approval handler bootstrap (#62135), the native approval handler is spawned by the framework layer outside the qqbot gateway startAccount context. This means channel.ts's side-effect `import "./bridge/bootstrap.js"` may not have run, leaving PlatformAdapter unregistered when deliverPending calls resolveQQBotAccount -> getPlatformAdapter().

Extract ensurePlatformAdapter() from bootstrap.ts as an idempotent, re-entrant helper and call it in both capability.ts (load callback) and handler-runtime.ts (deliverPending entry) to guarantee the adapter is available regardless of initialization order.

* fix(qqbot): add lazy factory for PlatformAdapter to eliminate import-order dependency

The bundler splits qqbot code into multiple chunks where the adapter singleton and its consumers may live in different modules. When a consumer chunk evaluates before the bootstrap side-effect chunk, getPlatformAdapter() throws because the singleton is still null.

Introduce registerPlatformAdapterFactory() in adapter/index.ts so getPlatformAdapter() can auto-initialize the adapter on first access. bootstrap.ts registers the factory at module evaluation time alongside the existing eager registration path. Also add error logging in downloadFile's catch block to surface fetch failures.

* feat(qqbot): add /bot-approve slash command for exec approval config management

Add /bot-approve command to the built-in QQBot plugin, ported from the
standalone openclaw-qqbot implementation. This command allows users to
manage tools.exec.security and tools.exec.ask settings directly from QQ.

Supported sub-commands:
  /bot-approve on      - allowlist + on-miss (recommended)
  /bot-approve off     - full + off (no approval)
  /bot-approve always  - allowlist + always (strict mode)
  /bot-approve reset   - remove overrides, restore framework defaults
  /bot-approve status  - show current security/ask values

The runtime config API is injected via registerApproveRuntimeGetter()
following the existing dependency injection pattern used by
registerVersionResolver() and registerPluginVersion().

* fix(qqbot): ACK INTERACTION_CREATE events before processing approval buttons

Send PUT /interactions/{id} immediately upon receiving any
INTERACTION_CREATE event to prevent QQ from showing a timeout
error to the user. The ACK is fire-and-forget and does not block
subsequent approval button resolution.

Also resolve merge conflict in pnpm-lock.yaml (keep
@tencent-connect/qqbot-connector@1.1.0 and newer
@thi.ng/bitstream@2.4.46).

* feat(qqbot): enhance reminder functionality with delivery context and credential backup

This update improves the QQBot reminder system by introducing a delivery context for reminders, allowing for more flexible target resolution. Key changes include:

- Updated reminder logic to utilize a delivery envelope, ensuring that reminders are sent with the correct context.
- Implemented credential backup and recovery mechanisms to prevent loss of appId and clientSecret during hot upgrades.
- Added tests for credential backup functionality and admin resolver to ensure reliability.
- Enhanced the remind tool to automatically resolve the target from the current conversation context when not explicitly provided.

These enhancements aim to improve the user experience and reliability of the reminder feature within the QQBot framework.

* fix(qqbot): ensure PlatformAdapter is registered before gateway message processing

Call ensurePlatformAdapter() at the start of bridge/gateway.ts's
startGateway() to guarantee the adapter is available when engine
code (e.g. downloadFile in file-utils.ts) calls getPlatformAdapter().

When the bundler splits code into separate chunks, bootstrap.ts's
module-level side-effect registration may not have executed yet by
the time the gateway processes its first inbound attachment download.

Also fix the TS2339 error in registerApproveRuntimeGetter by using
getQQBotRuntime() (full PluginRuntime with config) instead of
getQQBotRuntimeForEngine() (GatewayPluginRuntime subset without config).

* fix(qqbot): make isAudioFile safe when OutboundAudioAdapter is not registered

sendMedia() calls isAudioFile() as part of its media-type dispatch logic
before any actual audio processing. When the audio adapter is not yet
registered (e.g. framework tool calls sendMedia before gateway startup),
isAudioFile() would throw 'OutboundAudioAdapter not registered' even
for non-audio files like images.

Wrap the getAudio() call in isAudioFile() with try/catch to return false
when the adapter is unavailable, allowing non-audio media sends to
proceed normally.

* refactor(qqbot): remove plugin startup/upgrade greeting pipeline

Drop the startup / upgrade greeting feature that was folded into the
previous reminder + credential-backup commit. The pipeline has proven
unnecessary for the fused build and its supporting admin-resolver
scaffolding has no other consumers, so both are removed wholesale.

- Delete engine/session/startup-greeting.ts and its tests: the
  first-launch "soul online" / "updated to vX.Y.Z" messages, the
  per-(accountId, appId) startup marker, the failure cooldown, and the
  legacy startup-marker.json migration path are all gone.
- Delete engine/session/admin-resolver.ts and its tests: admin openid
  persistence/resolution, upgrade-greeting-target load/clear and the
  sendStartupGreetings dispatcher only ever served the greeting flow
  and were not referenced elsewhere.
- channel.ts: drop the sendStartupGreetings import and the READY /
  RESUMED hooks that triggered greetings; credential-backup snapshots
  stay untouched.
- engine/utils/data-paths.ts: remove getAdminMarkerFile /
  getLegacyAdminMarkerFile / getUpgradeGreetingTargetFile /
  getStartupMarkerFile / getLegacyStartupMarkerFile along with the
  now-stale module docblock sections. Credential-backup helpers and
  safeName are preserved.

Net -655 LOC across 6 files. tsc --noEmit passes on
extensions/qqbot/tsconfig.json and no references to the removed
symbols remain in the workspace.

* fix(qqbot): resolve test failures in extension batch, contracts and bundled runtime deps

- bootstrap: replace sync require() with static imports for secret-input
  and temp-path so vitest resolve.alias works correctly (require bypasses
  vitest aliases causing Cannot find module errors)
- format: handle null/undefined in formatErrorMessage before JSON.stringify
  since JSON.stringify(undefined) returns JS undefined, not a string
- gateway/types: reword comment to avoid triggering the channel-import
  guardrail regex that forbids quoted openclaw/plugin-sdk references
- package.json: mirror @tencent-connect/qqbot-connector ^1.1.0 in root
  dependencies as required by bundled plugin runtime dependency checks

* chore: revert non-qqbot changes to align with upstream main

Revert modifications to src/agents/system-prompt, src/auto-reply/reply/dispatch-from-config, and src/canvas-host/a2ui build artifacts that were inadvertently included in the qqbot feature branch. Also fix .gitignore Core/ pattern to match subdirectories.

* fix(qqbot): remove unused logUnsupportedStructuredMediaTarget after API simplification

* fix(qqbot): restore channel-plugin-api.ts for bundled plugin surface convention

* fix(qqbot): update CI lint allowlists for restructured engine paths

- Update raw fetch() allowlist in check-no-raw-channel-fetch.mjs to
  reflect engine/ directory restructure (src/api.ts → src/engine/api/api-client.ts, etc.)
- Remove stale qqbot allowlist entry for deleted src/utils/audio-convert.ts

* fix(qqbot): eliminate os.tmpdir() in engine layer via adapter injection

- Make hasPlatformAdapter() also check for registered factory, so adapter
  is always discoverable once bootstrap has run
- Remove os.tmpdir() fallbacks in platform.ts getHomeDir()/getTempDir(),
  delegate entirely to PlatformAdapter.getTempDir() which calls
  resolvePreferredOpenClawTmpDir() under the hood
- Keeps engine/ layer free of openclaw/plugin-sdk imports

* chore(qqbot): update CHANGELOG for engine architecture refactor (#67960) (thanks @cxyhhhhh)

---------

Co-authored-by: Bobby <zkd8907@live.com>
Co-authored-by: neilhwang <neilhwang@tencent.com>
Co-authored-by: sliverp <870080352@qq.com>
2026-04-22 01:05:12 +08:00
Shadow
38aaa23e63 feat(channels): stream tool progress into preview edits (#69611) (thanks @thewilloftheshadow) 2026-04-21 11:51:16 -05:00
Gustavo Madeira Santana
13636c4521 perf(matrix): narrow register-time runtime surface (#69782)
Merged via squash.

Prepared head SHA: ec32828b52
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-21 12:50:53 -04:00
Patrick Erichsen
acb27bac3a fix(dev): release run-node build lock on SIGINT/SIGTERM/exit (#69785) 2026-04-21 09:33:19 -07:00
Devin Robison
e6e83e6ccf fix(control-ui): block remote image loads (#69773)
* fix(control-ui): block remote image loads

* fix(control-ui): reject protocol-relative avatar URLs

* docs(changelog): note control-ui image CSP tightening (#69773)
2026-04-21 10:30:32 -06:00
Devin Robison
2aa93d44a1 fix: require owner identity for owner-enforced commands (#69774)
* fix: require owner identity for owner-enforced commands

Stop wildcard channel allowlists from authorizing non-owner senders when a plugin requires owner-only commands.

Add a regression test for the owner-enforced wildcard allowFrom path.

* docs(changelog): note owner identity requirement for owner-enforced commands (#69774)
2026-04-21 10:16:33 -06:00
Patrick Erichsen
4fdd005b88 onboard: plain-prose security disclaimer, searchable pickers for search/plugins/model-provider (#69760) 2026-04-21 08:54:00 -07:00
Bruce MacDonald
1be94b7a37 onboard (ollama): populate cloud-only model list from ollama.com/api/tags (#68463)
Merged via squash.

Prepared head SHA: fb12af3d63
Co-authored-by: BruceMacD <5853428+BruceMacD@users.noreply.github.com>
Co-authored-by: BruceMacD <5853428+BruceMacD@users.noreply.github.com>
Reviewed-by: @BruceMacD
2026-04-21 08:51:54 -07:00
Peter Steinberger
06b4e3885e test: stabilize stale-pid ancestor override
(cherry picked from commit 4e25479cb2)
2026-04-21 16:45:22 +01:00
Peter Steinberger
34a52ea777 fix: lazy-load discord carbon runtime for npm install
Forward-port release branch fix without beta version file changes.

(cherry picked from commit 3243c14547)
2026-04-21 16:40:18 +01:00
Peter Steinberger
99c3ec15df test: accept codex not-approved fallback
(cherry picked from commit 542086ccea)
2026-04-21 16:40:07 +01:00
Peter Steinberger
68e97c9969 test: generalize codex rejected-permission fallback
(cherry picked from commit 1e9627f92d)
2026-04-21 16:40:07 +01:00
Peter Steinberger
f992542132 test: accept codex elevated execution fallback
(cherry picked from commit 26b359bebd)
2026-04-21 16:40:07 +01:00
Peter Steinberger
9a7a637117 test: accept codex sandbox approval fallback
(cherry picked from commit 8eac996344)
2026-04-21 16:40:07 +01:00
Peter Steinberger
de31f91417 test: accept codex active-model fallback
(cherry picked from commit 87b81fa66f)
2026-04-21 16:40:07 +01:00
Peter Steinberger
e01c76eaf9 fix: guard empty docker host args in install smoke
(cherry picked from commit ddd05f4e89)
2026-04-21 16:40:07 +01:00
Peter Steinberger
9d3c155bf8 fix: avoid empty bash arrays in linux smoke
(cherry picked from commit 2db45c7892)
2026-04-21 16:40:07 +01:00
Peter Steinberger
66a5864c2a fix: support older shells in parallels smoke
(cherry picked from commit 8ce7c4f08b)
2026-04-21 16:40:07 +01:00
Peter Steinberger
d2185bd45b fix: run packed bundled postinstall in release check
(cherry picked from commit e57e54e591)
2026-04-21 16:40:07 +01:00
Tak Hoffman
714598774f feat: add soft reset command (#68635)
* feat: add soft reset command

* fix: harden soft reset follow-up behavior

* fix: accept whitespace-delimited soft reset tails

* test: cover newline soft reset normalization

* fix: preserve stale sessions for soft reset

* fix: gate soft reset stale bypass

* fix: align soft reset auth gating

* fix: normalize soft reset session detection

* test: cover multiline soft reset session state

* test: cover multiline soft reset parsing
2026-04-21 10:17:52 -05:00
1929 changed files with 108269 additions and 27303 deletions

View File

@@ -0,0 +1,340 @@
---
name: blacksmith-testbox
description: >
Validate code changes against real CI when local execution is not
enough. Use for CI-parity checks, secrets/services, migrations, or
builds/tests that cannot run reliably on the local machine. Do not
replace repo-documented local test/build loops just because this
skill exists.
---
# Blacksmith Testbox
## Scope
Use Testbox when you need remote CI parity, injected secrets, hosted services,
or an OS/runtime image that your local machine cannot provide cheaply.
Do not default to Testbox for every local test/build loop. If the repo has
documented local commands for normal iteration, use those first so you keep
warm caches, local build state, and fast feedback.
Testbox is the expensive path. Reach for it deliberately.
## Install the CLI
If `blacksmith` is not installed, install it:
curl -fsSL https://get.blacksmith.sh | sh
For the canary channel (bleeding-edge):
BLACKSMITH_CHANNEL=canary sh -c 'curl -fsSL https://get.blacksmith.sh | sh'
Then authenticate:
blacksmith auth login
## Agent-triggered browser auth (non-interactive)
When an agent needs to ensure the user is authenticated before running testbox
commands (e.g. warmup, run), use browser-based auth with non-interactive mode.
This opens the browser for the user to sign in; the agent does not interact with
the browser. The org selector in the dashboard is skipped, so the user only sees
the sign-in flow.
**Required command** (`--organization` is required with `--non-interactive`):
blacksmith auth login --non-interactive --organization <org-slug>
The org slug can come from `BLACKSMITH_ORG` env var or the `--org` global flag.
If neither is set, the agent should use the project's known org (e.g. from repo
config or user context). Example:
blacksmith auth login --non-interactive --organization acme-corp
blacksmith --org acme-corp auth login --non-interactive --organization acme-corp
**Flow**: The CLI starts a local callback server, opens the browser to the
dashboard auth page, and blocks for up to 2 minutes. The user completes sign-in
and authorization in the browser. The dashboard redirects to localhost with the
token; the CLI saves credentials and exits. The agent then proceeds.
**Do not use** `--api-token` for this flow — that is for headless/token-based
auth. This skill focuses on browser-based auth when the user prefers signing in
via the web UI.
Optional flags:
- `--dashboard-url <url>` — Override dashboard URL (e.g. for staging)
## Decide first: local or Testbox
Before warming anything up, check the repo's own instructions.
Prefer local commands when:
- the repo documents a supported local test/build workflow
- you are iterating on unit tests, lint, typecheck, formatting, or other
local-only validation
- the value comes from warm local caches and fast repeat runs
- the command does not need remote secrets, hosted services, or CI-only images
Prefer Testbox when:
- the repo explicitly requires CI-parity or remote validation
- the command needs secrets, service containers, or provisioned infra
- 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:
- `pnpm check:changed`
- `pnpm test:changed`
- `pnpm test <path-or-filter>`
- `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.
## 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:
blacksmith testbox warmup ci-check-testbox.yml
# → tbx_01jkz5b3t9...
Save this ID. You need it for every `run` command.
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.
Options:
--ref <branch> 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)
## CRITICAL: Always run from the repo root
ALWAYS invoke `blacksmith testbox` commands from the **root of the git
repository**. The CLI syncs the current working directory to the testbox
using rsync with `--delete`. If you run from a subdirectory (e.g.
`cd backend && blacksmith testbox run ...`), rsync will mirror only that
subdirectory and **delete everything else** on the testbox — wiping other
directories like `dashboard/`, `cli/`, etc.
# CORRECT — run from repo root, use paths in the command
blacksmith testbox run --id <ID> "cd backend && php artisan test"
blacksmith testbox run --id <ID> "cd dashboard && npm test"
# WRONG — do NOT cd into a subdirectory before invoking the CLI
cd backend && blacksmith testbox run --id <ID> "php artisan test"
If your shell is in a subdirectory, `cd` back to the repo root first:
cd "$(git rev-parse --show-toplevel)"
blacksmith testbox run --id <ID> "cd backend && php artisan test"
## Running commands
blacksmith testbox run --id <ID> "<command>"
The `run` command automatically waits for the testbox to become ready if
it is still booting, so you can call `run` immediately after warmup without
needing to check status first.
## Downloading files from a testbox
Use the `download` command to retrieve files or directories from a running
testbox to your local machine. This is useful for fetching build artifacts,
test results, coverage reports, or any output generated on the testbox.
blacksmith testbox download --id <ID> <remote-path> [local-path]
The remote path is relative to the testbox working directory (same as `run`).
If no local path is specified, the file is saved to the current directory
using the same base name.
To download a directory, append a trailing `/` to the remote path — this
triggers recursive mode:
# Download a single file
blacksmith testbox download --id <ID> coverage/report.html
# Download a file to a specific local path
blacksmith testbox download --id <ID> build/output.tar.gz ./output.tar.gz
# Download an entire directory
blacksmith testbox download --id <ID> test-results/ ./results/
Options:
--ssh-private-key <path> Path to SSH private key (if warmup used --ssh-public-key)
## How file sync works
Understanding this model is critical for using Testbox correctly.
When you call `run`, the CLI performs a **delta sync** of your local changes
to the remote testbox before executing your command:
1. The testbox VM starts from a clean `actions/checkout` at the warmup ref.
The workflow's setup steps (e.g. `npm install`, `pip install`, `composer install`)
run during warmup and populate dependency directories on the remote VM.
2. On each `run`, the CLI uses **git** to detect which files changed locally
since the last sync. It syncs ONLY tracked files and untracked non-ignored
files (i.e. files that `git ls-files` reports).
3. **`.gitignore`'d directories are never synced.** This means directories
like `node_modules/`, `vendor/`, `.venv/`, `build/`, `dist/`, etc. are
NOT transferred from your local machine. The testbox uses its own copies
of those directories, populated during the warmup workflow steps.
4. If nothing has changed since the last sync (same git commit and working
tree state), the sync is skipped entirely for speed.
### Why this matters
- **Changing dependencies**: If you modify `package.json`, `requirements.txt`,
`composer.json`, `go.mod`, or similar dependency manifests, the lock/manifest
file will be synced but the actual dependency directory will NOT. You must
re-run the install command on the testbox:
blacksmith testbox run --id <ID> "npm install && npm test"
blacksmith testbox run --id <ID> "pip install -r requirements.txt && pytest"
blacksmith testbox run --id <ID> "composer install && phpunit"
- **Generated/build artifacts**: If your tests depend on a build step (e.g.
`npm run build`, `make`), and you changed source files that affect the build
output, re-run the build on the testbox before testing.
- **New untracked files**: New files you create locally ARE synced (as long as
they are not gitignored). You do not need to `git add` them first.
- **Deleted files**: Files you delete locally are also deleted on the remote
testbox. The sync model keeps the remote in lockstep with your local managed
file set.
## CRITICAL: Do not ban local tests
Do not assume local validation is forbidden. Many repos intentionally invest in
fast, warm local loops, and forcing every run through Testbox destroys that
advantage.
Use Testbox for the checks that actually need it: remote parity, secrets,
services, CI-only runners, or reproducibility against the workflow image.
If the repo says local tests/builds are the normal path, follow the repo.
## When to use
Use Testbox when:
- running database migrations or destructive environment checks
- running commands that depend on secrets or environment variables not present locally
- reproducing CI-only failures or validating against the workflow image
- validating behavior that needs provisioned services or remote runners
- doing a final parity check before commit/push when the repo or user wants that
Trim that list based on repo guidance. If the repo documents supported local
tests/builds, prefer local for routine iteration and keep Testbox for the
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
3. Write code while the testbox boots in the background.
4. Run the remote command when needed:
`blacksmith testbox run --id <ID> "npm test"`
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"`
7. If you need artifacts (coverage reports, build outputs, etc.), download them:
`blacksmith testbox download --id <ID> coverage/ ./coverage/`
8. Once green, commit and push.
## OpenClaw full test suite
For OpenClaw, use the repo package manager and the measured stable full-suite
profile below. It keeps six Vitest project shards active while limiting each
shard to one worker to avoid worker OOMs on Testbox:
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"
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.
## Examples
blacksmith testbox warmup ci-check-testbox.yml
# → tbx_01jkz5b3t9...
# Run tests
blacksmith testbox run --id <ID> "npm test -- --testPathPattern=handler.test"
blacksmith testbox run --id <ID> "go test ./pkg/api/... -run TestHandler -v"
blacksmith testbox run --id <ID> "python -m pytest tests/test_api.py -k test_auth"
# Re-install deps after changing package.json, then test
blacksmith testbox run --id <ID> "npm install && npm test"
# Build and test
blacksmith testbox run --id <ID> "npm run build && npm test"
# Download artifacts from the testbox
blacksmith testbox download --id <ID> coverage/lcov-report/ ./coverage/
blacksmith testbox download --id <ID> build/output.tar.gz
## Waiting for the testbox to be ready
The `run` command automatically waits for the testbox, so explicit waiting is
usually unnecessary. If you do need to check readiness separately (e.g. before
a series of runs), use the `--wait` flag. Do NOT use a sleep-and-recheck loop.
Correct: block until ready with a timeout:
blacksmith testbox status --id <ID> --wait [--wait-timeout 5m]
Wrong: never use sleep + status in a loop:
# BAD — do not do this
sleep 30 && blacksmith testbox status --id <ID>
while ! blacksmith testbox status --id <ID> | grep ready; do sleep 5; done
`--wait` polls the status and exits as soon as the testbox is ready (or when the
timeout is reached). Default timeout is 5m; use `--wait-timeout` for longer
(e.g. `10m`, `1h`).
## Managing testboxes
# Check status of a specific testbox
blacksmith testbox status --id <ID>
# List all active testboxes for the current repo
blacksmith testbox list
# Stop a testbox when you're done (frees resources)
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:
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 60
## With options
blacksmith testbox warmup ci-check-testbox.yml --ref main
blacksmith testbox warmup ci-check-testbox.yml --idle-timeout 60
blacksmith testbox run --id <ID> "go test ./..."

View File

@@ -22,16 +22,17 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- Windows: `90m`
- aggregate npm-update wrapper: `150m`
If a lane hits the cap, stop there, inspect the newest `/tmp/openclaw-parallels-*` run directory and phase log, then fix or rerun the smallest affected lane. Do not keep waiting on a capped lane.
- Actual OpenClaw npm install/update phases are a stricter budget than whole lanes: install phases should finish within 7 minutes, and update phases should finish within 5 minutes. If a phase named `install-main`, `install-latest`, `install-baseline`, or `install-baseline-package` exceeds 420s, or a phase named `update-dev` / same-guest `openclaw update` exceeds 300s, treat it as a failure/harness bug and start diagnosis from that phase log. Do not wait for a longer lane cap.
- Actual OpenClaw npm install/update phases are a stricter signal than whole-lane caps: install phases should normally finish within 7 minutes, and update phases should normally show meaningful progress within 5 minutes. If a phase named `install-main`, `install-latest`, `install-baseline`, or `install-baseline-package` exceeds 420s, or a phase named `update-dev` / same-guest `openclaw update` exceeds 300s without new markers, start diagnosis from that phase log and guest process state. Current Windows update phases can still pass after roughly 10-15 minutes because `doctor --fix` may install bundled plugin runtime deps; keep the script hard cap near 20 minutes unless the log is truly stale.
- For a full OS matrix, prefer running independent guest-family lanes in parallel when host capacity allows:
- `timeout --foreground 75m pnpm test:parallels:macos -- --json`
- `timeout --foreground 90m pnpm test:parallels:windows -- --json`
- `timeout --foreground 75m pnpm test:parallels:linux -- --json`
Keep each lane in its own shell/session and track the run directory for each one.
Keep each lane in its own shell/session and track the run directory for each one. Before starting the matrix, run any required host build/package gate to completion. When current-main tgz packaging is needed, the smoke scripts hold a shared package lock through `pnpm build`, inventory/staging, and `npm pack`; if that lock is missing or broken, serialize the matrix instead of accepting concurrent `dist` mutation.
- Do not run multiple smoke lanes against the same guest family at once. Tahoe lanes share the host HTTP port, and Windows/Linux lanes can collide on snapshot restore/start state if two jobs touch the same VM concurrently.
- Do not run the aggregate `pnpm test:parallels:npm-update` wrapper in parallel with individual macOS/Windows/Linux smoke lanes; it touches the same guest families and snapshots.
- Do not start Parallels lanes while any host command may rebuild, clean, or restage `dist` (`pnpm build`, `pnpm ui:build`, `pnpm release:check`, `pnpm test:install:smoke`, npm pack/install smoke, or Docker lanes that run package/build prep). Run the build/package gates first, let them finish, then start the VM matrix. Concurrent `dist` mutation can make host `npm pack` fail with missing files and wastes a full VM cycle.
- Do not start Parallels lanes while any unrelated host command may rebuild, clean, or restage `dist` (`pnpm build`, `pnpm ui:build`, `pnpm release:check`, `pnpm test:install:smoke`, npm pack/install smoke, or Docker lanes that run package/build prep). Run unrelated build/package gates first, let them finish, then start the VM matrix. Concurrent `dist` mutation can make host `npm pack` fail with missing files and wastes a full VM cycle.
- While running or optimizing the matrix, record wall-clock duration per lane and the slowest phase from `/tmp/openclaw-parallels-*` logs. Use that timing before changing smoke order, timeouts, or helper behavior.
- If a host build changes tracked generated files such as `src/canvas-host/a2ui/.bundle.hash`, stop before spending VM time. Commit the generated artifact separately or fix the generator drift, then rerun the smallest affected lane.
- If `main` is moving under active multi-agent work, prefer a detached worktree pinned to one commit for long Parallels suites. The smoke scripts now verify the packed tgz commit instead of live `git rev-parse HEAD`, but a pinned worktree still avoids noisy rebuild/version drift during reruns.
- For `openclaw update --channel dev` lanes, remember the guest clones GitHub `main`, not your local worktree. If a local fix exists but the rerun still fails inside the cloned dev checkout, do not treat that as disproof of the fix until the branch has been pushed.
- For `prlctl exec`, pass the VM name before `--current-user` (`prlctl exec "$VM" --current-user ...`), not the other way around.

View File

@@ -0,0 +1,41 @@
---
name: optimizetests
description: Optimize OpenClaw test runtime end to end. Use when the user asks for /optimizetests, slow-test review, import optimization, deduping tests, moving misplaced core coverage to extensions, or reducing CI/test wall time without adding shards or dropping coverage.
---
# Optimize Tests
Goal: real OpenClaw test/runtime speedups with coverage intact. Do not add shards,
skip assertions, weaken gates, or tune runner flags as the main fix.
## Runbook
1. Read `docs/help/testing.md`, `docs/ci.md`, and the scoped `AGENTS.md` files
for any subtree you will edit.
2. Establish evidence before edits:
- Full ranking: `pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/<name>.json`
- Targeted file: `timeout 240 /usr/bin/time -l pnpm test <file> --maxWorkers=1 --reporter=verbose`
- Import suspicion: add `OPENCLAW_VITEST_IMPORT_DURATIONS=1 OPENCLAW_VITEST_PRINT_IMPORT_BREAKDOWN=1`
3. Attack highest-return hotspots first:
- broad barrels or `importActual()` in hot tests
- per-test `vi.resetModules()` plus fresh imports
- expensive gateway/server/client setup where reset/reuse proves same behavior
- core tests asserting extension-owned behavior
- duplicated fixture construction or contract assertions
4. Prefer production-quality fixes:
- narrow runtime seams over broad mocks
- pure helpers for static parsing/metadata
- injected deps over module resets
- extension-owned tests for bundled plugin/provider/channel behavior
5. After each change, rerun the same benchmark and the proving test lane. Record
before/after wall time, Vitest duration, and max RSS when available.
6. Run `pnpm check:changed`; run broader gates (`pnpm check`, `pnpm test`,
`pnpm build`) when touched surfaces require them.
7. Commit scoped changes with `scripts/committer "<conventional message>" <paths...>`.
Push when requested. If CI is red, inspect with `gh run list/view`, fix, push,
repeat until current CI is green or a blocker is proven unrelated.
## Output
End with the pushed commit(s), before/after timings, gates run, current CI state,
and any remaining tail lanes that need separate optimization.

View File

@@ -0,0 +1,6 @@
interface:
display_name: "Optimize Tests"
short_description: "Benchmark and speed up OpenClaw tests"
default_prompt: "Use $optimizetests to benchmark slow OpenClaw tests, optimize imports and duplicated setup, move misplaced core coverage to extensions, verify gates, commit scoped changes, push, and keep CI green without adding shards or dropping coverage."
policy:
allow_implicit_invocation: false

View File

@@ -0,0 +1,485 @@
---
name: tag-duplicate-prs-issues
description: Maintainer workflow for deciding whether an OpenClaw pull request or issue is a duplicate, gathering evidence with ghreplica and pr-search-cli, grouping related work in prtags, and syncing the duplicate grouping back to GitHub through prtags. Use when Codex needs to search for duplicate PRs or issues, create or reuse a duplicate group, enforce one-group-per-target discipline, save duplicate judgments in prtags, or prepare group state for comment sync.
---
# Tag Duplicate PRs and Issues
Use this skill when a maintainer needs to decide whether a pull request or issue is a duplicate of existing work.
This skill is for maintainer triage and grouping.
It is not for reviewing the implementation quality of a PR.
## Required Setup
Do not start duplicate triage until this setup is complete.
### Install the companion skills
Install these skills first because they teach the agent how to use the two main CLIs correctly:
- `ghreplica` skill from the `ghreplica` repo at `skills/ghreplica/SKILL.md`
- `prtags` skill from the `prtags` repo at `skills/prtags/SKILL.md`
This skill assumes those two skills are available and can be used during the same run.
### Install the CLIs
Install `ghreplica` and `prtags` from their latest GitHub releases.
Do not rely on an old local build unless the maintainer explicitly wants to test unreleased behavior.
`ghreplica` CLI install path:
```bash
curl -fsSL https://raw.githubusercontent.com/dutifuldev/ghreplica/main/scripts/install-ghr.sh | bash -s -- --bin-dir "$HOME/.local/bin"
```
`prtags` CLI install path:
```bash
curl -fsSL https://raw.githubusercontent.com/dutifuldev/prtags/main/scripts/install-prtags.sh | bash -s -- --bin-dir "$HOME/.local/bin"
```
Use the `pr-search-cli` project with `uvx`.
The command itself is `pr-search`.
Do not require a permanent install unless the maintainer explicitly wants one.
```bash
uvx --from pr-search-cli pr-search status
uvx --from pr-search-cli pr-search code similar 67144
```
### Authenticate prtags
`prtags` should be logged in with the maintainer's own GitHub account through OAuth device flow.
Do not use a shared maintainer token for interactive triage.
```bash
prtags auth login
prtags auth status
```
The expected outcome is that `prtags` stores the logged-in maintainer identity locally and uses that account for authenticated writes.
## Missing-Setup Rule
Do not require an up-front preflight before starting the workflow.
Proceed with the normal steps until you actually need a tool or account state.
As soon as you discover that a required CLI is missing or `prtags` is not logged in, stop immediately.
Do not continue in a partial mode after that point.
If `ghr` is missing, ask the user to run the `ghreplica` install command.
If `prtags` is missing, ask the user to run both CLI install commands:
```bash
curl -fsSL https://raw.githubusercontent.com/dutifuldev/ghreplica/main/scripts/install-ghr.sh | bash -s -- --bin-dir "$HOME/.local/bin"
curl -fsSL https://raw.githubusercontent.com/dutifuldev/prtags/main/scripts/install-prtags.sh | bash -s -- --bin-dir "$HOME/.local/bin"
```
If `uvx --from pr-search-cli pr-search ...` fails because `uvx` or the `pr-search` launcher is not available, ask the user to make that command work before continuing.
If `prtags auth status` shows that the user is not logged in, ask the user to run:
```bash
prtags auth login
```
Resume only after the missing tool or login state has been fixed.
## Read-Path Default
For read-only GitHub operations in this workflow, use `ghr` as the default CLI.
Treat it as a drop-in replacement for the `gh` read operations you would normally use for PRs, issues, comments, reviews, and duplicate-search evidence.
Only fall back to `gh` when `ghr` is failing for a concrete reason, such as:
- the mirrored object is not present yet
- the mirror data is clearly stale or incomplete for the decision you need to make
- the `ghr` command errors, times out, or does not expose the specific read you need
When you fall back to `gh`, note that you did so and why.
If `ghr` is missing a fresh PR or issue but `gh` can read it, you may use `gh` for the read-side judgment.
If a later `prtags` target-level write fails because the same object is still missing from `ghreplica`, stop and report that the mirror has not caught up yet instead of forcing the write.
## Goal
For each target PR or issue:
1. gather duplicate evidence
2. decide whether it is a real duplicate
3. create or reuse one `prtags` group for that duplicate cluster
4. save the maintainer judgment in `prtags`
5. rely on normal `prtags` group writes to drive GitHub comment sync when that integration is configured
## Tool Roles
Use the tools with these boundaries:
- `ghreplica` is the raw evidence source
- use `ghr` first for normal GitHub read operations in this workflow
- use it for title/body/comment search, related PRs, overlapping files, overlapping ranges, and current PR or issue status
- resort to `gh` only when `ghr` cannot provide the needed read cleanly
- `pr-search-cli` is candidate generation and ranking
- use it to suggest likely duplicate PRs or issue-cluster context
- do not treat it as final truth
- do not create or expand a duplicate group only because `pr-search-cli` put multiple PRs in the same issue or duplicate cluster
- `prtags` is the maintainer curation layer
- use it to create or reuse one duplicate group
- use it to save the duplicate status, confidence, rationale, and group summary
- use it as the source of truth for the GitHub-facing group comment
## Working Rules
- Do not call something a duplicate only because the titles are similar.
- Do not call something a duplicate only because the same files changed.
- A duplicate cluster should be based on the same user-facing problem, the same intent, and substantially overlapping implementation or investigation context.
## One-Group Rule
Treat duplicate groups as exclusive.
A PR or issue should belong to at most one duplicate group at a time.
That means:
- before creating a new group, search for an existing group that already represents the same duplicate story
- if the target already appears to belong to a different duplicate group, stop and resolve that conflict first
- do not create a second group for the same target just because the wording is slightly different
- if two plausible existing groups overlap and you cannot safely merge the judgment, stop and ask the maintainer
This rule matters more than speed.
The skill should keep one coherent duplicate cluster per problem, not many near-duplicate clusters.
## What A Good Duplicate Group Represents
A duplicate group should describe the underlying problem and the intended fix direction.
Do not group items only because they share a keyword.
Good group shape:
- same user-facing bug or same maintainer-facing task
- same subsystem or code surface
- same intended change direction
- same likely duplicate-resolution path
Bad group shape:
- “all PRs that touch Slack”
- “all issues mentioning retry”
- “all auth-related items”
The group title should name the real problem.
The group description should summarize the intent and the code surface.
Examples:
- `gateway: startup regression from channel status bootstrap`
- `whatsapp: QR preflight timeout handling`
- `release: cross-OS validation handoff gaps`
## Evidence Checklist
Before declaring a duplicate, gather evidence from at least two categories.
Same-issue or same-cluster output from `pr-search-cli` counts only as candidate generation, not as one of the required proof categories by itself.
For PRs:
- same or nearly same problem statement
- same changed files or overlapping file ranges
- same fix direction
- same subsystem and failure mode
- same linked issue or same user-visible symptom
For issues:
- same user-visible problem
- same reproduction story or same failure mode
- same likely fix area
- same PRs already linked or discussed
- same maintainers already steering toward the same duplicate grouping
If you only have wording similarity, that is not enough.
## Step 1: Read The Target
Start by reading the target itself.
Use `ghr` first for this step even if you would normally reach for `gh`.
For a PR:
```bash
ghr pr view -R openclaw/openclaw <number> --comments
ghr pr reviews -R openclaw/openclaw <number>
ghr pr comments -R openclaw/openclaw <number>
```
For an issue:
```bash
ghr issue view -R openclaw/openclaw <number> --comments
ghr issue comments -R openclaw/openclaw <number>
```
Record:
- target type and number
- title
- problem statement
- proposed intent
- subsystem
- whether it is open, closed, or merged
- whether there is already a likely duplicate thread mentioned by humans
## Step 2: Search Broadly With ghreplica
Use `ghreplica` first because it is the most direct evidence source.
Do not switch to `gh` for ordinary reads unless `ghr` is missing data or failing.
### PR duplicate search
Run all of these when the target is a PR:
```bash
ghr search related-prs -R openclaw/openclaw <pr-number> --mode path_overlap --state all
ghr search related-prs -R openclaw/openclaw <pr-number> --mode range_overlap --state all
ghr search mentions -R openclaw/openclaw --query "<key phrase from title or body>" --mode fts --scope pull_requests --state all
ghr search mentions -R openclaw/openclaw --query "<subsystem or error phrase>" --mode fts --scope issues --state all
```
Use `prs-by-paths` or `prs-by-ranges` when the likely duplicate surface is already known:
```bash
ghr search prs-by-paths -R openclaw/openclaw --path src/example.ts --state all
ghr search prs-by-ranges -R openclaw/openclaw --path src/example.ts --start 20 --end 80 --state all
```
### Issue duplicate search
`ghreplica` does not have a special issue-to-issue “related issues” command.
For issues, search mirrored text and linked PR context instead.
Run targeted text searches:
```bash
ghr search mentions -R openclaw/openclaw --query "<issue title phrase>" --mode fts --scope issues --state all
ghr search mentions -R openclaw/openclaw --query "<error message or symptom>" --mode fts --scope issues --state all
ghr search mentions -R openclaw/openclaw --query "<subsystem phrase>" --mode fts --scope pull_requests --state all
```
Then inspect the candidate PRs or issues those searches uncover.
## Step 3: Use pr-search-cli As A Hint Layer
Use `pr-search-cli` after `ghreplica`.
It is good at surfacing candidates quickly, but it is not the final decision-maker.
Run it through the `pr-search` command.
For a PR:
```bash
uvx --from pr-search-cli pr-search -R openclaw/openclaw code similar <pr-number>
uvx --from pr-search-cli pr-search -R openclaw/openclaw code clusters for-pr <pr-number>
uvx --from pr-search-cli pr-search -R openclaw/openclaw issues for-pr <pr-number>
uvx --from pr-search-cli pr-search -R openclaw/openclaw issues duplicate-prs
```
Interpretation:
- `code similar` suggests PRs with similar change shape
- `code clusters for-pr` shows the PRs nearby code cluster
- `issues for-pr` shows which issue clusters the PR appears to belong to
- `issues duplicate-prs` is useful for spotting already-known duplicate PR patterns
Treat every `pr-search-cli` result as a hint to investigate, not as enough evidence to create or widen a duplicate group.
Multiple PRs can share the same issue or issue cluster while still taking meaningfully different fix paths.
For an issue:
- use `ghreplica` first to find candidate PRs or issue wording
- if the issue has linked PRs or a likely implementation PR, run `pr-search-cli` on those PRs
- treat issue-cluster output as supporting context, not as enough by itself to call the issue a duplicate
## Step 4: Decide The Outcome
Choose one of these outcomes:
- `not_duplicate`
- `duplicate_needs_judgment`
- `duplicate_confirmed`
Use `duplicate_confirmed` only when the evidence is strong enough that the maintainer could safely close or retag the duplicate item.
Use `duplicate_needs_judgment` when:
- the problem looks the same but the implementation goal differs
- the code overlap is weak
- the issue wording is ambiguous
- there may be two valid duplicate group interpretations
- the target appears to intersect two existing duplicate groups
## Step 5: Reuse Or Create One prtags Group
Before creating a group, search `prtags` for an existing one.
Start with text search over groups:
```bash
prtags search text -R openclaw/openclaw "<problem phrase>" --types group --limit 10
prtags search similar -R openclaw/openclaw "<problem summary>" --types group --limit 10
prtags group list -R openclaw/openclaw
```
Inspect likely groups:
```bash
prtags group get <group-id>
prtags group get <group-id> --include-metadata
```
Reuse an existing group when:
- it represents the same problem
- it already contains clearly related members
- adding the target would keep the group coherent
Do not widen an existing group just because `pr-search-cli` placed several PRs under the same issue or duplicate cluster.
Confirm that the actual implementation path and maintainer intent still match before adding the new member.
Create a new group only when no existing group clearly fits.
Create the group with a problem-based title and an intent-based description:
```bash
prtags group create -R openclaw/openclaw \
--kind mixed \
--title "<problem-centered title>" \
--description "<same intent, subsystem, and duplicate-resolution path>" \
--status open
```
Then attach the target and any known duplicate members:
```bash
prtags group add-pr <group-id> <pr-number>
prtags group add-issue <group-id> <issue-number>
```
If a target appears to already belong to another duplicate group and you cannot safely reuse that group, stop.
Do not create a second group.
## Step 6: Ensure The Annotation Fields Exist
Use `field ensure` so the skill is idempotent.
Recommended target-level fields:
```bash
prtags field ensure -R openclaw/openclaw --name duplicate_status --scope pull_request --type enum --enum-values not_duplicate,candidate,confirmed --filterable
prtags field ensure -R openclaw/openclaw --name duplicate_status --scope issue --type enum --enum-values not_duplicate,candidate,confirmed --filterable
prtags field ensure -R openclaw/openclaw --name duplicate_confidence --scope pull_request --type enum --enum-values low,medium,high --filterable
prtags field ensure -R openclaw/openclaw --name duplicate_confidence --scope issue --type enum --enum-values low,medium,high --filterable
prtags field ensure -R openclaw/openclaw --name duplicate_rationale --scope pull_request --type text --searchable
prtags field ensure -R openclaw/openclaw --name duplicate_rationale --scope issue --type text --searchable
```
Recommended group-level fields:
```bash
prtags field ensure -R openclaw/openclaw --name duplicate_confidence --scope group --type enum --enum-values low,medium,high --filterable
prtags field ensure -R openclaw/openclaw --name duplicate_rationale --scope group --type text --searchable
prtags field ensure -R openclaw/openclaw --name cluster_summary --scope group --type text --searchable
```
## Step 7: Save The Maintainer Judgment In prtags
For a PR:
```bash
prtags annotation pr set -R openclaw/openclaw <pr-number> \
duplicate_status=confirmed \
duplicate_confidence=high \
duplicate_rationale="<same problem, same fix direction, overlapping files and comments>"
```
For an issue:
```bash
prtags annotation issue set -R openclaw/openclaw <issue-number> \
duplicate_status=confirmed \
duplicate_confidence=high \
duplicate_rationale="<same user-visible problem and same intended fix path>"
```
For the group:
```bash
prtags annotation group set <group-id> \
duplicate_confidence=high \
cluster_summary="<one-sentence problem summary>" \
duplicate_rationale="<why these items belong in one duplicate cluster>"
```
When the evidence is incomplete, set `duplicate_status=candidate` and lower the confidence.
If a per-PR or per-issue annotation write fails because `prtags` cannot resolve the target through `ghreplica`, do not force a fallback write path.
Keep the group state you were able to write, report that the mirror is still missing the target object, and defer the target-level annotation until `ghreplica` catches up.
## Step 8: Let prtags Sync The Group Comment
Do not tell the agent to create a GitHub comment directly.
`prtags` owns the outbound GitHub comment as a derived projection of group state.
In the normal case, do not manually trigger comment sync.
When comment sync is configured, group writes already enqueue the derived comment projection automatically.
Use manual sync only as a repair or retry path:
```bash
prtags group sync-comments <group-id>
```
If the maintainer needs to see which groups still need attention, use:
```bash
prtags group list-comment-sync-targets -R openclaw/openclaw
```
The skill should treat the GitHub comment as a consequence of correct `prtags` group state.
It should not treat manual comment authoring as part of the normal duplicate workflow.
It should also not treat `sync-comments` as a required step for every duplicate decision.
## Output Format
Return a short maintainer report with these sections:
```text
Decision: duplicate_confirmed | duplicate_needs_judgment | not_duplicate
Target: PR #<n> | Issue #<n>
Confidence: high | medium | low
Evidence:
- ...
- ...
- ...
prtags actions:
- reused group <group-id> | created group <group-id>
- added members: ...
- annotations written: ...
- comment sync: automatic if configured | manual repair triggered for <group-id>
```
## Stop Conditions
Stop and escalate instead of forcing a duplicate decision when:
- the target appears to belong to two different duplicate groups
- the duplicate grouping is unclear
- the wording matches but the implementation goals differ
- two PRs touch the same files for different reasons
- two issues describe similar symptoms but likely different root causes
The maintainer should get one clean duplicate judgment or an explicit “needs judgment” result.
Do not blur the line.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Tag Duplicate PRs and Issues"
short_description: "Find duplicate PRs and issues, group them in prtags, and let prtags sync the GitHub comment"
default_prompt: "Use $tag-duplicate-prs-issues to decide whether an OpenClaw PR or issue is a duplicate, gather evidence with ghreplica and pr-search-cli, group related items in prtags, and save the duplicate judgment."

View File

@@ -54,6 +54,8 @@ OPENCLAW_GATEWAY_TOKEN=
# Optional additional providers
# ZAI_API_KEY=...
# AI_GATEWAY_API_KEY=...
# TOKENHUB_API_KEY=...
# LKEAP_API_KEY=...
# MINIMAX_API_KEY=...
# SYNTHETIC_API_KEY=...

View File

@@ -14,7 +14,7 @@ inputs:
pnpm-version:
description: pnpm version for corepack.
required: false
default: "10.32.1"
default: "10.33.0"
install-bun:
description: Whether to install Bun alongside Node.
required: false

View File

@@ -4,7 +4,7 @@ inputs:
pnpm-version:
description: pnpm version to activate via corepack.
required: false
default: "10.32.1"
default: "10.33.0"
cache-key-suffix:
description: Suffix appended to the cache key.
required: false

8
.github/labeler.yml vendored
View File

@@ -241,6 +241,10 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/open-prose/**"
"extensions: tokenjuice":
- changed-files:
- any-glob-to-any-file:
- "extensions/tokenjuice/**"
"extensions: webhooks":
- changed-files:
- any-glob-to-any-file:
@@ -269,6 +273,10 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/deepseek/**"
"extensions: tencent":
- changed-files:
- any-glob-to-any-file:
- "extensions/tencent/**"
"extensions: stepfun":
- changed-files:
- any-glob-to-any-file:

View File

@@ -22,22 +22,22 @@ jobs:
permissions:
issues: write
pull-requests: write
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token-fallback
if: steps.app-token.outcome == 'failure'
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Handle labeled items
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |

100
.github/workflows/ci-check-testbox.yml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: Blacksmith 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:
check:
permissions:
contents: read
name: "check"
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 30
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}/2 succeeded"
}
for attempt in 1 2; do
if checkout_attempt "$attempt"; then
exit 0
fi
echo "checkout attempt ${attempt}/2 failed"
sleep $((attempt * 5))
done
echo "checkout failed after 2 attempts" >&2
exit 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
- 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"

File diff suppressed because it is too large Load Diff

View File

@@ -62,7 +62,7 @@ jobs:
needs_autobuild: false
config_file: ""
- language: swift
runs_on: macos-latest
runs_on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-12vcpu-macos-latest' || 'macos-latest' }}
needs_node: false
needs_python: false
needs_java: false

View File

@@ -153,7 +153,7 @@ jobs:
- name: Build and push amd64 image
id: build
# WARNING: KEEP THE OFFICIAL DOCKER ACTION HERE; DO NOT SWITCH THIS BACK TO BLACKSMITH BLINDLY.
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64
@@ -167,7 +167,7 @@ jobs:
- name: Build and push amd64 slim image
id: build-slim
# WARNING: KEEP THE OFFICIAL DOCKER ACTION HERE; DO NOT SWITCH THIS BACK TO BLACKSMITH BLINDLY.
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64
@@ -270,7 +270,7 @@ jobs:
- name: Build and push arm64 image
id: build
# WARNING: KEEP THE OFFICIAL DOCKER ACTION HERE; DO NOT SWITCH THIS BACK TO BLACKSMITH BLINDLY.
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/arm64
@@ -284,7 +284,7 @@ jobs:
- name: Build and push arm64 slim image
id: build-slim
# WARNING: KEEP THE OFFICIAL DOCKER ACTION HERE; DO NOT SWITCH THIS BACK TO BLACKSMITH BLINDLY.
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/arm64

View File

@@ -23,7 +23,7 @@ jobs:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22.18.0"

View File

@@ -11,8 +11,8 @@ permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-{1}', github.workflow, github.event.pull_request.number) || format('{0}-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ${{ github.event_name == 'pull_request' && format('{0}-{1}', github.workflow, github.event.pull_request.number) || format('{0}-{1}', github.workflow, github.ref) }}
cancel-in-progress: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
@@ -20,7 +20,7 @@ env:
jobs:
preflight:
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
outputs:
docs_only: ${{ steps.manifest.outputs.docs_only }}
run_install_smoke: ${{ steps.manifest.outputs.run_install_smoke }}
@@ -58,13 +58,6 @@ jobs:
node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD
- name: Setup Node environment
if: steps.docs_scope.outputs.docs_only != 'true'
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
install-deps: "false"
- name: Build install-smoke CI manifest
id: manifest
env:
@@ -85,7 +78,7 @@ jobs:
install-smoke:
needs: [preflight]
if: needs.preflight.outputs.run_install_smoke == 'true'
runs-on: blacksmith-32vcpu-ubuntu-2404
runs-on: blacksmith-16vcpu-ubuntu-2404
env:
DOCKER_BUILD_SUMMARY: "false"
DOCKER_BUILD_RECORD_UPLOAD: "false"
@@ -98,6 +91,11 @@ jobs:
# Blacksmith's builder owns the Docker layer cache; keep smoke builds off
# explicit gha cache directives so local tags still load cleanly.
- name: Run QR package install smoke
env:
OPENCLAW_QR_SMOKE_FORCE_INSTALL: "1"
run: bash scripts/e2e/qr-import-docker.sh
- name: Build root Dockerfile smoke image
uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2
with:
@@ -114,6 +112,12 @@ jobs:
run: |
docker run --rm --entrypoint sh openclaw-dockerfile-smoke:local -lc 'which openclaw && openclaw --version'
- name: Run Docker gateway network e2e
env:
OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE: openclaw-dockerfile-smoke:local
OPENCLAW_GATEWAY_NETWORK_E2E_SKIP_BUILD: "1"
run: bash scripts/e2e/gateway-network-docker.sh
# This smoke validates that the build-arg path preinstalls the matrix
# runtime deps declared by the plugin and that matrix discovery stays
# healthy in the final runtime image.
@@ -215,3 +219,29 @@ jobs:
OPENCLAW_INSTALL_SMOKE_UPDATE_DIST_IMAGE: openclaw-dockerfile-smoke:local
OPENCLAW_INSTALL_SMOKE_UPDATE_SKIP_LOCAL_BUILD: "1"
run: bash scripts/test-install-sh-docker.sh
docker-e2e-fast:
needs: [preflight]
if: needs.preflight.outputs.run_install_smoke == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 8
env:
DOCKER_BUILD_SUMMARY: "false"
DOCKER_BUILD_RECORD_UPLOAD: "false"
steps:
- name: Checkout CLI
uses: actions/checkout@v6
- name: Set up Blacksmith Docker Builder
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
- name: Setup Node environment for package smoke
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
install-deps: "true"
- name: Run fast bundled plugin Docker E2E
env:
OPENCLAW_BUNDLED_CHANNEL_DEPS_E2E_IMAGE: openclaw-bundled-channel-fast:local
run: timeout 120s pnpm test:docker:bundled-channel-deps:fast

View File

@@ -30,15 +30,15 @@ jobs:
permissions:
contents: read
pull-requests: write
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token-fallback
if: steps.app-token.outcome == 'failure'
with:
@@ -50,7 +50,7 @@ jobs:
repo-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
sync-labels: true
- name: Apply PR size label
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
@@ -139,7 +139,7 @@ jobs:
labels: [targetSizeLabel],
});
- name: Apply maintainer or trusted-contributor label
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
@@ -210,7 +210,7 @@ jobs:
// });
// }
- name: Apply beta-blocker title label
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
@@ -263,7 +263,7 @@ jobs:
});
}
- name: Apply too-many-prs label
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
@@ -439,22 +439,22 @@ jobs:
permissions:
contents: read
pull-requests: write
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token-fallback
if: steps.app-token.outcome == 'failure'
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Backfill PR labels
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
@@ -737,22 +737,22 @@ jobs:
label-issues:
permissions:
issues: write
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token-fallback
if: steps.app-token.outcome == 'failure'
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Apply maintainer or trusted-contributor label
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
@@ -823,7 +823,7 @@ jobs:
// });
// }
- name: Apply beta-blocker title label
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |

View File

@@ -66,12 +66,13 @@ jobs:
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_MAIN_REF: origin/main
WORKFLOW_REF_NAME: ${{ github.ref_name }}
run: |
set -euo pipefail
RELEASE_SHA=$(git rev-parse HEAD)
RELEASE_MAIN_REF="refs/remotes/origin/${WORKFLOW_REF_NAME}"
export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
git fetch --no-tags origin "+refs/heads/${WORKFLOW_REF_NAME}:refs/remotes/origin/${WORKFLOW_REF_NAME}"
pnpm release:openclaw:npm:check
- name: Summarize next step

View File

@@ -222,7 +222,7 @@ jobs:
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 0
fetch-depth: 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
@@ -258,7 +258,7 @@ jobs:
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 0
fetch-depth: 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
@@ -303,7 +303,7 @@ jobs:
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 0
fetch-depth: 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
@@ -460,7 +460,7 @@ jobs:
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 0
fetch-depth: 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
@@ -602,7 +602,7 @@ jobs:
uses: actions/checkout@v6
with:
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
fetch-depth: 0
fetch-depth: 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env

View File

@@ -25,8 +25,8 @@ jobs:
parity-gate:
name: Run the GPT-5.4 / Opus 4.6 parity gate against the qa-lab mock
if: ${{ github.event.pull_request.draft != true }}
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 20
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 30
env:
# Fence the gate off from any real provider credentials. The qa-lab
# mock server + auth staging (PR N) should be enough to produce a
@@ -34,18 +34,24 @@ jobs:
# leak into the job env, fail hard instead of silently running
# against a live provider and burning real budget.
#
# The parity pack has 11 isolated scenario workers. Letting qa suite
# fan out to its default "all scenarios at once" mode on smaller CI
# VMs makes the short strict-agentic scenarios flaky, especially the
# approval-turn followthrough gate that expects a fast post-approval
# read within a 30s agent.wait timeout.
QA_PARITY_CONCURRENCY: "2"
# The parity pack has 11 isolated scenario workers. It exercises a real
# gateway child plus mock model turns and subagents, so keep it serial in
# CI even on the larger runner. Concurrent isolated gateway workers make
# the short strict-agentic scenarios flaky, especially the approval-turn
# followthrough gate that expects a fast post-approval read within a 30s
# agent.wait timeout.
QA_PARITY_CONCURRENCY: "1"
OPENCLAW_QA_TRANSPORT_READY_TIMEOUT_MS: "180000"
OPENAI_API_KEY: ""
ANTHROPIC_API_KEY: ""
OPENCLAW_LIVE_OPENAI_KEY: ""
OPENCLAW_LIVE_ANTHROPIC_KEY: ""
OPENCLAW_LIVE_GEMINI_KEY: ""
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ""
# The parity suite is a private QA command. Build that exact runtime up
# front so CI never tests a public dist plus a later no-clean QA overlay.
OPENCLAW_BUILD_PRIVATE_QA: "1"
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
steps:
- name: Checkout PR
uses: actions/checkout@v6
@@ -54,7 +60,7 @@ jobs:
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "22.18.0"
cache: "pnpm"
@@ -62,6 +68,12 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build private QA runtime
run: pnpm build
# The approval-turn sentinel still runs inside the full parity pack below.
# Keep the exact mock read-plan contract in deterministic unit tests instead
# of paying for a separate full-runtime preflight that has been flaky in CI.
- name: Run GPT-5.4 lane
run: |
pnpm openclaw qa suite \

View File

@@ -17,13 +17,13 @@ jobs:
pull-requests: write
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token-fallback
continue-on-error: true
with:
@@ -65,7 +65,7 @@ jobs:
- name: Check stale state cache
id: stale-state
if: always()
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token-fallback.outputs.token || steps.app-token.outputs.token }}
script: |
@@ -124,13 +124,13 @@ jobs:
issues: write
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v3
id: app-token
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Lock closed issues after 48h of no comments
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |

View File

@@ -11,7 +11,7 @@ permissions:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
cancel-in-progress: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
@@ -19,7 +19,7 @@ env:
jobs:
no-tabs:
if: github.event_name != 'workflow_dispatch'
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -51,7 +51,7 @@ jobs:
actionlint:
if: github.event_name != 'workflow_dispatch'
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -83,7 +83,7 @@ jobs:
generated-doc-baselines:
if: github.event_name == 'workflow_dispatch'
runs-on: blacksmith-16vcpu-ubuntu-2404
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6

2
.gitignore vendored
View File

@@ -36,6 +36,7 @@ apps/android/benchmark/results/
# Bun build artifacts
*.bun-build
apps/macos/.build/
apps/macos-mlx-tts/.build/
apps/shared/MoltbotKit/.build/
apps/shared/OpenClawKit/.build/
apps/shared/OpenClawKit/Package.resolved
@@ -57,6 +58,7 @@ vendor/
apps/ios/Clawdbot.xcodeproj/
apps/ios/Clawdbot.xcodeproj/**
apps/macos/.build/**
apps/macos-mlx-tts/.build/**
**/*.bun-build
apps/ios/*.xcfilelist

View File

@@ -1,73 +0,0 @@
---
description: Land a PR (merge with proper workflow)
---
Input
- PR: $1 <number|url>
- If missing: use the most recent PR mentioned in the conversation.
- If ambiguous: ask.
Do (end-to-end)
Goal: PR must end in GitHub state = MERGED (never CLOSED). Prefer `gh pr merge --squash`; use `--rebase` only when preserving commit history is required.
1. Assign PR to self:
- `gh pr edit <PR> --add-assignee @me`
2. Repo clean: `git status`.
3. Identify PR meta (author + head branch):
```sh
gh pr view <PR> --json number,title,author,headRefName,baseRefName,headRepository --jq '{number,title,author:.author.login,head:.headRefName,base:.baseRefName,headRepo:.headRepository.nameWithOwner}'
contrib=$(gh pr view <PR> --json author --jq .author.login)
head=$(gh pr view <PR> --json headRefName --jq .headRefName)
head_repo_url=$(gh pr view <PR> --json headRepository --jq .headRepository.url)
```
4. Fast-forward base:
- `git checkout main`
- `git pull --ff-only`
5. Create temp base branch from main:
- `git checkout -b temp/landpr-<ts-or-pr>`
6. Check out PR branch locally:
- `gh pr checkout <PR>`
7. Rebase PR branch onto temp base:
- `git rebase temp/landpr-<ts-or-pr>`
- Fix conflicts; keep history tidy.
8. Fix + tests + changelog:
- Implement fixes + add/adjust tests
- Update `CHANGELOG.md` and mention `#<PR>` + `@$contrib`
9. Decide merge strategy:
- Squash (preferred): use when we want a single clean commit
- Rebase: use only when we explicitly want to preserve commit history
- If unclear, ask
10. Full gate (BEFORE commit):
- `pnpm lint && pnpm build && pnpm test`
11. Commit via committer (final merge commit only includes PR # + thanks):
- For the final merge-ready commit: `committer "fix: <summary> (#<PR>) (thanks @$contrib)" CHANGELOG.md <changed files>`
- If you need intermediate fix commits before the final merge commit, keep those messages concise and **omit** PR number/thanks.
- `land_sha=$(git rev-parse HEAD)`
12. Push updated PR branch (rebase => usually needs force):
```sh
git remote add prhead "$head_repo_url.git" 2>/dev/null || git remote set-url prhead "$head_repo_url.git"
git push --force-with-lease prhead HEAD:$head
```
13. Merge PR (must show MERGED on GitHub):
- Squash (preferred): `gh pr merge <PR> --squash`
- Rebase (history-preserving fallback): `gh pr merge <PR> --rebase`
- Never `gh pr close` (closing is wrong)
14. Sync main:
- `git checkout main`
- `git pull --ff-only`
15. Comment on PR with what we did + SHAs + thanks:
```sh
merge_sha=$(gh pr view <PR> --json mergeCommit --jq '.mergeCommit.oid')
gh pr comment <PR> --body "Landed via temp rebase onto main.\n\n- Gate: pnpm lint && pnpm build && pnpm test\n- Land commit: $land_sha\n- Merge commit: $merge_sha\n\nThanks @$contrib!"
```
16. Verify PR state == MERGED:
- `gh pr view <PR> --json state --jq .state`
17. Delete temp branch:
- `git branch -D temp/landpr-<ts-or-pr>`

View File

@@ -1,134 +0,0 @@
---
description: Review a PR thoroughly without merging
---
Input
- PR: $1 <number|url>
- If missing: use the most recent PR mentioned in the conversation.
- If ambiguous: ask.
Do (review-only)
Goal: produce a thorough review and a clear recommendation (READY FOR /landpr vs NEEDS WORK vs INVALID CLAIM). Do NOT merge, do NOT push, do NOT make changes in the repo as part of this command.
0. Truthfulness + reality gate (required for bug-fix claims)
- Do not trust the issue text or PR summary by default; verify in code and evidence.
- If the PR claims to fix a bug linked to an issue, confirm the bug exists now (repro steps, logs, failing test, or clear code-path proof).
- Prove root cause with exact location (`path/file.ts:line` + explanation of why behavior is wrong).
- Verify fix targets the same code path as the root cause.
- Require a regression test when feasible (fails before fix, passes after fix). If not feasible, require explicit justification + manual verification evidence.
- Hallucination/BS red flags (treat as BLOCKER until disproven):
- claimed behavior not present in repo,
- issue/PR says "fixes #..." but changed files do not touch implicated path,
- only docs/comments changed for a runtime bug claim,
- vague AI-generated rationale without concrete evidence.
1. Identify PR meta + context
```sh
gh pr view <PR> --json number,title,state,isDraft,author,baseRefName,headRefName,headRepository,url,body,labels,assignees,reviewRequests,files,additions,deletions --jq '{number,title,url,state,isDraft,author:.author.login,base:.baseRefName,head:.headRefName,headRepo:.headRepository.nameWithOwner,additions,deletions,files:.files|length}'
```
2. Read the PR description carefully
- Summarize the stated goal, scope, and any "why now?" rationale.
- Call out any missing context: motivation, alternatives considered, rollout/compat notes, risk.
3. Read the diff thoroughly (prefer full diff)
```sh
gh pr diff <PR>
# If you need more surrounding context for files:
gh pr checkout <PR> # optional; still review-only
git show --stat
```
4. Validate the change is needed / valuable
- What user/customer/dev pain does this solve?
- Is this change the smallest reasonable fix?
- Are we introducing complexity for marginal benefit?
- Are we changing behavior/contract in a way that needs docs or a release note?
5. Evaluate implementation quality + optimality
- Correctness: edge cases, error handling, null/undefined, concurrency, ordering.
- Design: is the abstraction/architecture appropriate or over/under-engineered?
- Performance: hot paths, allocations, queries, network, N+1s, caching.
- Security/privacy: authz/authn, input validation, secrets, logging PII.
- Backwards compatibility: public APIs, config, migrations.
- Style consistency: formatting, naming, patterns used elsewhere.
6. Tests & verification
- Identify what's covered by tests (unit/integration/e2e).
- Are there regression tests for the bug fixed / scenario added?
- Missing tests? Call out exact cases that should be added.
- If tests are present, do they actually assert the important behavior (not just snapshots / happy path)?
7. Follow-up refactors / cleanup suggestions
- Any code that should be simplified before merge?
- Any TODOs that should be tickets vs addressed now?
- Any deprecations, docs, types, or lint rules we should adjust?
8. Key questions to answer explicitly
- Is the core claim substantiated by evidence, or is it likely invalid/hallucinated?
- Can we fix everything ourselves in a follow-up, or does the contributor need to update this PR?
- Any blocking concerns (must-fix before merge)?
- Is this PR ready to land, or does it need work?
9. Output (structured)
Produce a review with these sections:
A) TL;DR recommendation
- One of: READY FOR /landpr | NEEDS WORK | INVALID CLAIM (issue/bug not substantiated) | NEEDS DISCUSSION
- 13 sentence rationale.
B) Claim verification matrix (required)
- Fill this table:
| Field | Evidence |
| ----------------------------------------------- | -------- |
| Claimed problem | ... |
| Evidence observed (repro/log/test/code) | ... |
| Root cause location (`path:line`) | ... |
| Why this fix addresses that root cause | ... |
| Regression coverage (test name or manual proof) | ... |
- If any row is missing/weak, default to `NEEDS WORK` or `INVALID CLAIM`.
C) What changed
- Brief bullet summary of the diff/behavioral changes.
D) What's good
- Bullets: correctness, simplicity, tests, docs, ergonomics, etc.
E) Concerns / questions (actionable)
- Numbered list.
- Mark each item as:
- BLOCKER (must fix before merge)
- IMPORTANT (should fix before merge)
- NIT (optional)
- For each: point to the file/area and propose a concrete fix or alternative.
- If evidence for the core bug claim is missing, add a `BLOCKER` explicitly.
F) Tests
- What exists.
- What's missing (specific scenarios).
- State clearly whether there is a regression test for the claimed bug.
G) Follow-ups (optional)
- Non-blocking refactors/tickets to open later.
H) Suggested PR comment (optional)
- Offer: "Want me to draft a PR comment to the author?"
- If yes, provide a ready-to-paste comment summarizing the above, with clear asks.
Rules / Guardrails
- Review only: do not merge (`gh pr merge`), do not push branches, do not edit code.
- If you need clarification, ask questions rather than guessing.

View File

@@ -7,7 +7,7 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before touching a subt
- Repo: `https://github.com/openclaw/openclaw`
- Replies: repo-root file refs only, e.g. `extensions/telegram/src/index.ts:80`. No absolute paths, no `~/`.
- CODEOWNERS: maintenance/refactors/tests are ok. For larger behavior, product, security, or ownership-sensitive changes, get a listed owner request/review first.
- First pass: run docs list (`bin/docs-list` or `pnpm docs:list`; ignore if unavailable), then read only relevant docs/guides.
- First pass: run docs list (`pnpm docs:list`; ignore if unavailable), then read only relevant docs/guides.
- Missing deps: run `pnpm install`, rerun once, then report first actionable error.
- Use "plugin/plugins" in docs/UI/changelog. `extensions/` remains internal workspace layout.
- Add channel/plugin/app/doc surface: update `.github/labeler.yml` and matching GitHub labels.
@@ -65,6 +65,7 @@ Scoped guides:
- Normal full prod sweep: `pnpm check` (prod typecheck/lint/guards, no tests)
- Full tests: `pnpm test`
- Changed tests only: `pnpm test:changed`
- Local serial loop: `pnpm test:serial`
- Extension tests: `pnpm test:extensions` or `pnpm test extensions` = all extension shards; `pnpm test extensions/<id>` = one extension lane. Heavy channels/OpenAI have dedicated shards.
- Shard timing artifact: `.artifacts/vitest-shard-timings.json`; auto-used for balanced shard ordering. Disable with `OPENCLAW_TEST_PROJECTS_TIMINGS=0`.
- Targeted tests: `pnpm test <path-or-filter> [vitest args...]`; do not call raw `vitest`.
@@ -84,6 +85,7 @@ Scoped guides:
- `pnpm lint:apps`: Swift/app surface, separate from TS lint
- `pnpm lint:all`: legacy comparison lane
- Local heavy-check behavior: `OPENCLAW_LOCAL_CHECK=1` default; `OPENCLAW_LOCAL_CHECK_MODE=throttled|full`; `OPENCLAW_LOCAL_CHECK=0` for CI/shared runs.
- Local validation is local-first. Do not default to Blacksmith/Testbox for routine OpenClaw iteration; it burns warm caches and startup time. Use repo `pnpm` lanes first, then reach for remote CI/Testbox only for parity-only failures, secrets/services, or when explicitly requested.
## Gates

View File

@@ -2,6 +2,193 @@
Docs: https://docs.openclaw.ai
## Unreleased
### Changes
- Providers/Amazon Bedrock Mantle: add Claude Opus 4.7 through Mantle's Anthropic Messages route with provider-owned bearer-auth streaming, so the model is actually callable without treating AWS bearer tokens like Anthropic API keys. Thanks @wirjo.
- OpenAI/Responses: use OpenAI's native `web_search` tool automatically for direct OpenAI Responses models when web search is enabled and no managed search provider is pinned; explicit providers such as Brave keep the managed `web_search` tool.
- ACPX: add an explicit `openClawToolsMcpBridge` option that injects a core OpenClaw MCP server for selected built-in tools, starting with `cron`.
- Agents/sessions: add mailbox-style `sessions_list` filters for label, agent, and search plus visibility-scoped derived title and last-message previews. (#69839) Thanks @dangoZhang.
- Providers/GPT-5: move the GPT-5 prompt overlay into the shared provider runtime so compatible GPT-5 models receive the same behavior and heartbeat guidance through OpenAI, OpenRouter, OpenCode, Codex, and other GPT providers; add `agents.defaults.promptOverlays.gpt5.personality` as the global friendly-style toggle while keeping the OpenAI plugin setting as a fallback.
- Providers/xAI: add image generation, text-to-speech, and speech-to-text support, including `grok-imagine-image` / `grok-imagine-image-pro`, reference-image edits, six live xAI voices, MP3/WAV/PCM/G.711 TTS formats, `grok-stt` audio transcription, and xAI realtime transcription for Voice Call streaming. (#68694) Thanks @KateWilkins.
- Providers/STT: add Voice Call streaming transcription for Deepgram, ElevenLabs, and Mistral, and add ElevenLabs Scribe v2 batch audio transcription for inbound media.
- Models/commands: add `/models add <provider> <modelId>` so you can register a model from chat and use it without restarting the gateway; keep `/models` as a simple provider browser while adding clearer add guidance and copy-friendly command examples. (#70211) Thanks @Takhoffman.
- Pi/models: update the bundled pi packages to `0.68.1` and let the OpenCode Go catalog come from pi instead of plugin-maintained model aliases, adding the refreshed `opencode-go/kimi-k2.6`, Qwen, GLM, MiMo, and MiniMax entries.
- CLI/doctor plugins: lazy-load doctor plugin paths and prefer installed plugin `dist/*` runtime entries over source-adjacent JavaScript fallbacks, reducing the measured `doctor --non-interactive` runtime by about 74% while keeping cold doctor startup on built plugin artifacts. (#69840) Thanks @gumadeiras.
- WhatsApp/groups+direct: forward per-group and per-direct `systemPrompt` config into inbound context `GroupSystemPrompt` so configured per-chat behavioral instructions are injected on every turn. Supports `"*"` wildcard fallback and account-scoped overrides under `channels.whatsapp.accounts.<id>.{groups,direct}`; account maps fully replace root maps (no deep merge), matching the existing `requireMention` pattern. Closes #7011. (#59553) Thanks @Bluetegu.
- Plugins/startup: prefer native Jiti loading for built bundled plugin dist modules on supported runtimes, cutting measured bundled plugin load time by 82-90% while keeping source TypeScript on the transform path. (#69925) Thanks @aauren.
- Plugin SDK/Pi embedded runs: add a bundled-plugin embedded extension factory seam so native plugins can extend Pi embedded runs with async runtime hooks such as `tool_result` handling instead of falling back to the older synchronous persistence path. (#69946) Thanks @vincentkoc.
- Tokenjuice: add bundled native OpenClaw support for tokenjuice as an opt-in plugin that compacts noisy `exec` and `bash` tool results in Pi embedded runs. (#69946) Thanks @vincentkoc.
- Codex harness/hooks: route native Codex app-server turns through `before_prompt_build` and emit `before_compaction` / `after_compaction` for native compaction items so prompt and compaction hooks stop drifting from Pi. Thanks @vincentkoc.
- Codex harness/plugins: add a bundled-plugin Codex app-server extension seam for async `tool_result` middleware, fire `after_tool_call` for Codex tool runs, and route mirrored Codex transcript writes through `before_message_write` so tool integrations stop diverging from Pi. Thanks @vincentkoc.
- Codex harness/hooks: fire `llm_input`, `llm_output`, and `agent_end` for native Codex app-server turns so lifecycle hooks stop drifting from Pi. Thanks @vincentkoc.
- Providers/Tencent: add the bundled Tencent Cloud provider plugin with TokenHub and Token Plan onboarding, docs, `hy3-preview` model catalog entries, and tiered Hy3 pricing metadata. (#68460) Thanks @JuniperSling.
- TUI: add local embedded mode for running terminal chats without a Gateway while keeping plugin approval gates enforced. (#66767) Thanks @fuller-stack-dev.
- CLI/Claude: default `claude-cli` runs to warm stdio sessions, including custom configs that omit transport fields, and resume from the stored Claude session after Gateway restarts or idle exits. (#69679) Thanks @obviyus.
- Control UI/settings+chat: add a browser-local personal identity for the operator (name plus local-safe avatar), route user identity rendering through the shared chat/avatar path used by assistant and agent surfaces, and tighten Quick Settings, agent fallback chips, and narrow-screen chat layouts so personalization no longer wastes space or clips controls. (#70362) Thanks @BunsDev.
- Gateway/diagnostics: enable payload-free stability recording by default and add a support-ready diagnostics export with sanitized logs, status, health, config, and stability snapshots for bug reports. (#70324) Thanks @gumadeiras.
### Fixes
- Providers/OpenAI: harden Voice Call realtime transcription against OpenAI Realtime session-update drift, forward language and prompt hints, and add live coverage for realtime STT.
- Providers/Moonshot: stop strict-sanitizing Kimi's native tool_call IDs (shaped like `functions.<name>:<index>`) on the OpenAI-compatible transport, so multi-turn agentic flows through Kimi K2.6 no longer break after 2-3 tool-calling rounds when the serving layer fails to match mangled IDs against the original tool definitions. Adds a `sanitizeToolCallIds` opt-out to the shared `openai-compatible` replay family helper and wires Moonshot to it. Fixes #62319. (#70030) Thanks @LeoDu0314.
- Dependencies/security: override transitive `uuid` to `14.0.0`, clearing the runtime advisory across dependencies.
- Codex harness: ignore dynamic tool descriptions when deciding whether to reuse a native app-server thread while still fingerprinting tool schemas, so channel-specific copy changes no longer reset otherwise compatible Codex conversations. (#69976) Thanks @chen-zhang-cs-code.
- Codex harness: drop invalid legacy app-server `serviceTier` values such as `"priority"` before native thread and turn requests, while keeping supported Codex tiers limited to `"fast"` and `"flex"`. Fixes #64815.
- Codex harness: show bounded, sanitized permission target samples in app-server approval prompts, so native permission requests keep their specific hosts, roots, and paths visible without leaking home usernames or URL credentials. (#70340) Thanks @Lucenx9.
- Docs/Codex harness: narrow native compaction docs to the current start/completion signals, without promising a readable summary or kept-entry audit list yet. (#69612) Thanks @91wan.
- Providers/Amazon Bedrock: use known context-window metadata for discovered models while keeping the unknown-model fallback conservative, so compaction and overflow handling improve for newer Bedrock models without overstating unlisted model limits. Thanks @wirjo.
- Providers/Amazon Bedrock Mantle: refresh IAM-backed bearer tokens at runtime instead of baking discovery-time tokens into provider config, so long-lived Mantle sessions keep working after the initial token ages out. Thanks @wirjo.
- Config/includes: write through single-file top-level includes for isolated OpenClaw-owned mutations, so `plugins install` and `plugins update` update an included `plugins.json5` file instead of flattening modular `$include` configs. Fixes #41050 and #66048.
- Config/reload: plan gateway reloads from source-authored config instead of runtime-materialized snapshots, so plugin update writes no longer trigger false restarts from derived provider/plugin config paths. Fixes #68732.
- Plugins/update: skip npm plugin reinstall/config rewrites when the installed version and recorded artifact identity already match the registry target, let bare npm package names resolve back to tracked install records, and point already-installed `plugins install` attempts at `plugins update` / `--force` instead of a hook-pack fallback. Fixes #46955, #67957, and #68073.
- Agents/MCP: keep `mcp.servers` and bundle MCP tools available in Pi embedded
`coding` and `messaging` sessions while preserving `minimal` profile and
`tools.deny: ["bundle-mcp"]` opt-out behavior. Fixes #68875 and #68818.
- Plugins/startup: tolerate transient bundled-channel catalog/metadata drift while auto-enabling configured plugins, so CLI and gateway startup no longer crash when a channel id is known but its display metadata is unavailable.
- CLI/Claude: report CLI-backed reply runs as streaming while Claude/Codex CLI turns are still in flight, so WebChat keeps visible response state until the backend finishes. Fixes #70125.
- Slack/streaming: fall back to normal Slack replies for Slack Connect streams rejected before the SDK flushes its local buffer, so short replies no longer disappear or report success before Slack acknowledges delivery. Fixes #70295. (#70370) Thanks @mvanhorn.
- Codex harness: rotate the shared app-server websocket client when the configured bearer token changes, so auth-token refreshes reconnect with the new `Authorization` header instead of reusing a stale socket. (#70328) Thanks @Lucenx9.
- Channels/sandbox: derive runtime policy keys for external direct messages that share the main conversation, so sandbox/tool policy no longer treats channel-originated DMs as local main-session runs.
- Config/models: merge provider-scoped model allowlist updates and protect model/provider map writes from accidental full replacement, adding `config set --merge` for additive updates and `--replace` for intentional clobbers. Fixes #65920, #68392, and #68653.
- Agents/Pi auth: preserve AWS SDK-authenticated Bedrock runs for IMDS and task-role setups, clear stale refresh timers on sentinel fallback, and log unexpected runtime-auth prep failures instead of silently leaving the provider unauthenticated. Thanks @wirjo.
- Config/gateway: restore last-known-good config on critical clobber signatures such as missing metadata, missing `gateway.mode`, or sharp size drops, preventing gateway crash loops when a valid backup exists. Fixes #70336.
- Config/gateway: recover configs accidentally prefixed with non-JSON output during gateway startup or `openclaw doctor --fix`, preserving the clobbered file as a backup while leaving normal config reads read-only.
- Agents/GitHub Copilot: normalize connection-bound Responses item IDs in the Copilot provider wrapper so replayed histories no longer fail after the upstream connection changes. (#69362) Thanks @Menci.
- Pi embedded runs: pass real built-in tools into Pi session creation and then narrow active tool names after custom tool registration, so the runner and compaction paths compile cleanly and keep OpenClaw-managed custom tool allowlists without feeding string arrays into `createAgentSession`. Thanks @vincentkoc.
- Agents/OpenAI websocket: route native OpenAI websocket metadata and session-header decisions through the shared endpoint classifier so local mocks and custom `models.providers.openai.baseUrl` endpoints stay out of the native OpenAI path consistently across embedded-runner and websocket transport code. Thanks @vincentkoc.
- Cron/MCP: retire bundled MCP runtimes through one shared cleanup path for isolated cron run ends, persistent cron session rollover, and direct cron `deleteAfterRun` fallback cleanup. Fixes #69145, #68623, and #68827.
- MCP/gateway: tear down stdio MCP process trees on transport close and dispose bundled MCP runtimes during session delete/reset, preventing orphaned wrapper/server processes from accumulating. Fixes #68809 and #69465.
- Agents/MCP: retire bundled MCP runtimes after completed one-shot subagent cleanup and nested `sessions_send` steps, while keeping persistent subagent sessions warm.
- Config: render validation warnings with real line breaks instead of a literal `\n` sequence in CLI/audit output. Fixes #70140.
- Cron/doctor: repair malformed persisted cron job IDs through `openclaw doctor`, including legacy `jobId`, non-string `id`, and missing `id` rows, so `cron list` no longer needs display-layer coercion for corrupt store data. Fixes #70128.
- Discord: normalize prefixed channel targets only at the thread-binding API boundary, so `sessions_spawn({ runtime: "acp", thread: true })` can create child threads from Discord channels without breaking current-channel ACP bindings. (#68034) Thanks @Zetarcos.
- Discord: harden inbound thread metadata handling against partial Carbon channel getters, so non-command thread messages and queued jobs no longer crash when `name`, `parentId`, `parent`, or `ownerId` requires fetched raw data.
- Discord: let `message` tool reactions resolve `user:<id>` DM targets and preserve `channels.discord.guilds.<guild>.channels.<channel>.requireMention: false` during reply-stage activation fallback. Fixes #70165 and #69441.
- Plugins/startup: pre-normalize and cache Jiti alias maps before creating plugin loaders, so module-scoped loader filenames do not reintroduce per-plugin alias-normalization startup cost. Fixes #70186.
- ACP/Codex: run the bundled Codex ACP harness with an isolated `CODEX_HOME` and avoid writing incomplete ChatGPT auth bridge files, so Codex ACP sessions no longer clobber the user's real Codex CLI auth. Fixes #70234. Thanks @Lonobers88.
- Gateway/client: keep long-running RPCs such as ACP `agent.wait` calls in charge of their own timeout instead of closing the websocket on a missed app-level tick while work is still pending.
- Telegram/webhooks: lower the grammY webhook callback timeout to 5s so Telegram gets an early 200 response instead of retrying long-running updates as read timeouts. (#70146) Thanks @friday-james.
- Telegram/polling: rebuild the polling HTTP transport after `getUpdates` 409 conflicts, so retries use a fresh TCP connection instead of looping on a Telegram-terminated keep-alive socket. (#69873) Thanks @hclsys.
- Media delivery: strip persisted base64 audio payloads from webchat history, resolve stored `media://inbound/*` attachments before local-root checks, suppress duplicate Telegram voice/audio sends when TTS emits the same media twice, and support custom image-model IDs that already include their provider prefix.
- Slack/files: resolve `downloadFile` bot tokens from the runtime config when callers provide `cfg` without an explicit token or prebuilt client, preserving cfg-only file downloads outside the action runtime path. (#70160) Thanks @martingarramon.
- Slack/HTTP: dispatch registered Request URL webhooks through the same handler registry used by Slack monitor setup, so HTTP-mode Slack events no longer 404 after successful route registration. (#70275) Thanks @FroeMic.
- Slack/runtime bindings: route focused Slack thread replies through their bound ACP session instead of preparing replies against the default agent shell. Fixes #67739. Thanks @Frankla20.
- CLI/Claude: verify stored Claude CLI session ids have a readable project transcript before resuming, clearing phantom bindings with `reason=transcript-missing` instead of silently starting fresh under `--resume`. Fixes #70177.
- CLI sessions: persist CLI session clearing through the atomic session-store merge path, so expired Claude/Codex CLI bindings are actually removed before retrying without the stale session id. (#70298) Thanks @HFConsultant.
- ACP/sessions_spawn: honor explicit `model` overrides for ACP child sessions instead of silently falling back to the target agent default model. (#70210) Thanks @felix-miao.
- Diffs/viewer: re-read remote viewer access policy from live runtime config on each request, so toggling `plugins.entries.diffs.config.security.allowRemoteViewer` closes proxied viewer access immediately instead of waiting for a restart. Thanks @vincentkoc.
- Diffs/tooling: re-read `viewerBaseUrl`, presentation defaults, and viewer access policy from live runtime config, and fail closed when the live `diffs` plugin entry disappears instead of reviving startup viewer settings. Thanks @vincentkoc.
- Memory/LanceDB: stop resurrecting removed live `memory-lancedb` hook config from startup snapshots, so deleting or disabling the plugin entry shuts off auto-recall and auto-capture without a restart. Thanks @vincentkoc.
- Active Memory: stop reviving removed live `active-memory` config from startup snapshots, so removing the plugin entry turns the hook off immediately instead of waiting for a restart. Thanks @vincentkoc.
- Agents/subagents: drop bare `NO_REPLY` from the parent turn when the session still has pending spawned children, so direct-conversation surfaces such as Telegram DMs no longer rewrite the sentinel into visible fallback chatter while waiting for the child completion event. (#69942) Thanks @neeravmakwana.
- Plugins/install: keep bundled plugin dependencies off npm install while repairing them when plugins activate from a packaged install, including Feishu/Lark, Browser, and direct bundled channel setup-entry loads.
- CLI/channels: skip and cache bundled channel plugin, setup, and secrets load failures during read-only discovery, so one broken unused bundled channel cannot crash `openclaw status` or bootstrap secret scans.
- Memory/LanceDB: retry initialization after a failed LanceDB load and report unsupported Intel macOS native runtime clearly instead of caching the failure or repeatedly attempting an install that cannot work.
- CLI/Claude: hash only static extra system prompt parts when deciding whether to reuse a CLI session, so per-message inbound metadata no longer resets Claude CLI conversations on every turn. (#70122) Thanks @zijunl.
- Hooks/Slack: standardize shared message hook routing fields (`threadId` / `replyToId`) and stop Slack outbound delivery from re-running `message_sending` inside the channel adapter, so plugins like thread-ownership make one outbound routing decision per reply. Thanks @vincentkoc.
- Auto-reply/media: share one run-scoped reply media context between streamed block delivery and final payload filtering, so a local `MEDIA:` attachment is staged once and duplicate media sends are suppressed reliably. (#68111) Thanks @ayeshakhalid192007-dev.
- Plugins/gateway hooks: expose startup config, workspace dir, and a live cron getter on the typed `gateway_start` hook, and move memory-core managed dreaming off the internal `gateway:startup` bridge so cron reconciliation stays on the public plugin hook path. Thanks @vincentkoc.
- Plugins/config: read plugin trust decisions from the source config snapshot when a resolved runtime snapshot is active, so `plugins.allow` remains enforced and `doctor`/gateway startup no longer warn that the allowlist is empty when it is configured. Fixes #70161. Also fixes #70141.
- Gateway/restart: preserve group and channel chat context when resuming an agent turn after a Gateway restart, so continuation replies keep the same prompt, routing, and tool-status behavior as the original conversation.
- Gateway/pairing: shared-secret loopback CLI clients now silently auto-approve `metadata-upgrade` pairing (platform / device family refresh) instead of being disconnected with `1008 pairing required`. This matches the scope-upgrade and role-upgrade behavior added in #69431 and unblocks non-interactive CLI automation when a paired-device record has a stale platform string (e.g. device key replicated across hosts, install migrated between OSes, or platform-string format changed between OpenClaw versions). Browser / Control-UI clients keep the existing approval-required flow for metadata changes.
- Gateway/pairing: treat any forwarded-header evidence (`Forwarded`, `X-Forwarded-*`, or `X-Real-IP`) as proxied WebSocket traffic before pairing locality checks, so reverse-proxy topologies cannot use the loopback shared-secret helper auto-pairing path.
- Agents/OpenAI: treat exact `NO_REPLY` assistant output as a deliberate silent reply in embedded runs, so GPT-5.4 turns with signed reasoning plus a silent final no longer surface a false incomplete-turn error.
- Auto-reply/streaming: preserve streamed reply directives through chunk boundaries and phase-aware `final_answer` delivery, so split `MEDIA:<path>` lines, voice tags, and reply targets reach channel delivery instead of leaking as text or being dropped. (#70243) Thanks @zqchris.
- Anthropic/Claude Opus 4.7: normalize Opus 4.7 and `claude-cli` Opus 4.7 variants to a 1M context window in resolved runtime metadata and active-agent status/context reporting, so they no longer inherit the stale 200k fallback. Thanks @BunsDev.
- Gateway/pairing webchat: render `/pair qr` replies as structured media instead of raw markdown text, preserve inline reply threading and silent-control handling on media replies, avoid persisting sensitive QR images into transcript history, and keep local webchat media embedding behind internal-only trust markers. (#70047) Thanks @BunsDev.
- Codex harness: default app-server runs to unchained local execution, so OpenAI heartbeats can use network and shell tools without stalling behind native Codex approvals or the workspace-write sandbox.
- Codex harness: fail closed for unknown native app-server approval methods instead of routing unsupported future approval shapes through OpenClaw approval grants. (#70356) Thanks @Lucenx9.
- Codex harness: apply the GPT-5 behavior and heartbeat prompt overlay to native Codex app-server runs, so `codex/gpt-5.x` sessions get the same follow-through, tool-use, and proactive heartbeat guidance as OpenAI GPT-5 runs.
- Codex harness: add an explicit Guardian mode for Codex app-server approvals, plus a Docker live probe for approved and ask-back Guardian decisions, while keeping default app-server runs unchained for unattended local heartbeats. The legacy `OPENCLAW_CODEX_APP_SERVER_GUARDIAN` shortcut is removed; use plugin config `appServer.mode: "guardian"` or `OPENCLAW_CODEX_APP_SERVER_MODE=guardian`. Thanks @pashpashpash.
- OpenAI/Responses: keep embedded OpenAI Responses runs on HTTP when `models.providers.openai.baseUrl` points at a local mock or other non-public endpoint, so mocked/custom endpoints no longer drift onto the hardcoded public websocket transport. (#69815) Thanks @vincentkoc.
- Channels/config: require resolved runtime config on channel send/action/client helpers and block runtime helper `loadConfig()` calls, so SecretRefs are resolved at startup/boundaries instead of being re-read during sends.
- Discord: pass resolved runtime config through guild and moderation action helpers, so thread-originated Discord commands can run channel, member, role, and guild actions without falling back to runtime config reads. (#70215) Thanks @szponeczek.
- CLI/channels: preserve bundled setup promotion metadata when a loaded partial channel plugin omits it, so adding a non-default account still moves legacy single-account fields such as Telegram `streaming` into `accounts.default`.
- Telegram: keep the sent-message ownership cache isolated per configured session store, so own-message reaction filtering remains correct with custom `session.store` paths.
- Security/update: fail closed when exact pinned npm plugin or hook-pack updates detect integrity drift, and expose aborted plugin drift details in `openclaw update --json`.
- Ollama: forward OpenClaw thinking control to native `/api/chat` requests as top-level `think`, so `/think off` and `openclaw agent --thinking off` suppress thinking on models such as qwen3 instead of idling until the watchdog fires. Fixes #69902. (#69967) Thanks @WZH8898.
- Memory-core/dreaming: suppress the startup-only managed dreaming cron unavailable warning when the cron service is still attaching, while preserving the runtime warning if cron genuinely remains unavailable. Fixes #69939. (#69941) Thanks @Sanjays2402.
- Mattermost: suppress reasoning-only payloads even when they arrive as blockquoted `> Reasoning:` text, preventing `/reasoning on` from leaking thinking into channel posts. (#69927) Thanks @lawrence3699.
- Discord: read `channel.parentId` through a safe accessor in the slash-command, reaction, and model-picker paths so partial `GuildThreadChannel` prototype getters no longer throw `Cannot access rawData on partial Channel` when commands like `/new` run from inside a thread. Fixes #69861. (#69908) Thanks @neeravmakwana.
- Discord: use safe channel name and parent accessors across voice command authorization, so `/vc` commands from partial Discord thread channels no longer crash on Carbon rawData getters. (#70199) Thanks @hanamizuki.
- Discord: make auto-thread parent transcript inheritance opt-in via `channels.discord.thread.inheritParent`, keeping newly created Discord thread sessions isolated by default while preserving explicit inheritance for configured accounts. Fixes #69907. (#69986) Thanks @Blahdude.
- Browser/Chrome MCP: reset cached existing-session control sessions when a `navigate_page` call times out, so one stuck navigation no longer poisons the browser profile until a gateway restart. (#69733) Thanks @ayeshakhalid192007-dev.
- Browser/Chrome MCP: propagate click timeouts and abort signals to existing-session actions so a stuck click fails fast and reconnects instead of poisoning the browser tool until gateway restart. (#63524) Thanks @dongseok0.
- Amazon Bedrock/prompt caching: resolve opaque application inference profile targets before injecting Bedrock cache points, require every routed target to support explicit cache points, and retry transient profile lookups instead of caching a false negative for the rest of the process. (#69953) Thanks @anirudhmarc and @vincentkoc.
- Gateway/channel health: base stale-socket recovery on provider-proven transport activity instead of inbound app-event freshness, preventing quiet Slack, Discord, Telegram, Matrix, and local-style channels from being restarted solely because no user traffic arrived. (#69833) Thanks @bek91.
- OpenCode Go: canonicalize stale bundled `opencode-go` base URLs from `/go` or `/go/v1` to `/zen/go` or `/zen/go/v1`, so older generated model metadata stops hitting the 404 HTML endpoint. (#69898)
- CLI/channels: honor `channels.<id>.enabled=false` as a hard read-only presence opt-out, so env vars, manifest env vars, or stale persisted auth state no longer make disabled channel plugins appear in status, doctor, or setup-only discovery.
- Channels/preview streaming: centralize draft-preview finalization so Slack, Discord, Mattermost, and Matrix no longer flush temporary preview messages for media/error finals, and preserve first-reply threading for normal fallback delivery.
- Discord: keep slash command follow-up chunks ephemeral when the command is configured for ephemeral replies, so long `/status` output no longer leaks fallback model or runtime details into the public channel. (#69869) thanks @gumadeiras.
- Gateway/session history: re-check current auth and `chat.history` scope before later SSE keepalives and transcript updates, so active session-history streams close before delivering post-revocation events.
- Plugins/discovery: reject package plugin source entries that escape the package directory before explicit runtime entries or inferred built JavaScript peers can be used. (#69868) thanks @gumadeiras.
- CLI/channels: resolve channel presence through a shared policy that keeps ambient env vars and stale persisted auth from surfacing disabled bundled plugins in status, doctor, security audit, and cron delivery validation unless the channel or plugin is effectively enabled or explicitly configured. (#69862) Thanks @gumadeiras.
- Doctor/plugins: hydrate legacy partial interactive handler state before plugin reload clears dedupe caches, so `openclaw doctor` and post-update doctor runs no longer crash with `Cannot read properties of undefined (reading 'clear')`. (#70135) Thanks @ngutman.
- Control UI/config: preserve intentionally empty raw config snapshots when clearing pending updates so reset restores the original bytes instead of synthesizing JSON for blank config files. (#68178) Thanks @BunsDev.
- memory-core/dreaming: surface a `Dreaming status: blocked` line in `openclaw memory status` when dreaming is enabled but the heartbeat that drives the managed cron is not firing for the default agent, and add a Troubleshooting section to the dreaming docs covering the two common causes (per-agent `heartbeat` blocks excluding `main`, and `heartbeat.every` set to `0`/empty/invalid), so the silent failure described in #69843 becomes legible on the status surface.
- Cron/run-log: report generic `message` tool sends under the resolved delivery channel when they match the cron target, while preserving account-specific mismatch checks for delivery traces. (#69940) Thanks @davehappyminion.
- Doctor/channels: merge configured-channel doctor hooks across read-only, loaded, setup, and runtime plugin discovery so partial adapters no longer hide runtime-only compatibility repair or allowlist warnings, preserve disabled-channel opt-outs, and ignore malformed hook values before they can mask valid fallbacks. (#69919) Thanks @gumadeiras.
- Models/CLI: show bundled provider-owned static catalog rows in `models list --all` before auth is configured, including Kimi K2.6 rows for Moonshot, OpenRouter, and Vercel AI Gateway, while keeping local-only and workspace plugin catalog paths isolated. (#69909) Thanks @shakkernerd.
- Configure: skip generic CLI startup bootstrap for `openclaw configure` and bound hint-only gateway probes so the onboarding TUI reaches its first prompt faster when the Gateway is unavailable. (#69984) Thanks @obviyus.
- Agents/harness: surface selected plugin harness failures directly instead of replaying the same turn through embedded PI, preventing misleading secondary PI auth errors and avoiding duplicate side effects.
- OpenAI Codex: add a ChatGPT device-code auth option beside browser OAuth, so headless or callback-hostile setups can sign in without relying on the localhost browser callback. (#69557) Thanks @vincentkoc.
- CLI sessions: keep provider-owned CLI sessions through implicit daily expiry while preserving explicit reset behavior, and retain Claude CLI binding metadata across gateway agent requests. (#70106) Thanks @obviyus.
- fix(config): accept truncateAfterCompaction (#68395). Thanks @MonkeyLeeT
- CLI/Claude: keep Claude CLI session bindings stable across OAuth access-token refreshes, so gateway restarts continue the same Claude conversation instead of minting a fresh one. (#70132) Thanks @obviyus.
- QQBot: add `INTERACTION` intent (`1 << 26`) to the gateway constants and include it in the `FULL_INTENTS` mask so interaction events are received. (#70143) Thanks @cxyhhhhh.
- Gateway/restart: preserve one-shot continuation instructions across gateway restarts so agents can resume and reply back to the original chat after reboot. (#63406) Thanks @VACInc.
- Gateway/restart: write restart sentinel files atomically so interrupted writes cannot leave a truncated sentinel behind. (#70225) Thanks @obviyus.
- Pairing: remove stale pending requests for a device when that paired device is deleted, so an old repair approval cannot recreate the removed device from leftover state.
- Security/dotenv: block workspace `.env` overrides for Matrix, Mattermost, IRC, and Synology endpoint settings so cloned workspaces cannot redirect bundled connector traffic through local endpoint config. (#70240) Thanks @drobison00.
- Telegram: require the same `/models` authorization for group model-picker callbacks, so unauthorized participants can no longer browse or change the session model through inline buttons. (#70235) Thanks @drobison00.
- Agents/Pi: keep the filtered tool-name allowlist active for embedded OpenAI/OpenAI Codex GPT-5 runs and compaction sessions, so bundled and client tools still execute after the Pi `0.68.1` session-tool allowlist change instead of stopping at plan-only replies with no tool call. (#70281) Thanks @jalehman.
- Agents/Pi: honor explicit `strict-agentic` execution contracts for incomplete-turn retry guards across providers, so manually opted-in local or compatible models get the same retry behavior without relying on OpenAI model inference. (#66750) Thanks @ziomancer.
- OpenShell/sandbox: pin verified file reads to an already-opened descriptor, walk the ancestor chain for symlinked parents on platforms without fd-path readlink, and re-check file identity so parent symlink swaps cannot redirect in-sandbox reads to host files outside the allowed mount root. (#69798) Thanks @drobison00.
- Gateway/Control UI: require authenticated Control UI read access before serving `/__openclaw/control-ui-config.json` when `gateway.auth` is enabled, so unauthenticated callers can no longer read bootstrap metadata. (#70247) Thanks @drobison00.
## 2026.4.21
### Changes
- OpenAI/images: default the bundled image-generation provider and live media smoke tests to `gpt-image-2`, and advertise the newer 2K/4K OpenAI size hints in image-generation docs and tool metadata.
- Plugins/skills: add the Skill Workshop plugin, which captures reusable workflow corrections as pending or auto-applied workspace skills, runs threshold-based reviewer passes for stronger completion bias on reusable procedures, quarantines unsafe proposals, and refreshes skill availability after safe writes.
- Plugin SDK/channels: add presentation and skills runtime contracts, decouple channel presentation rendering, and document message presentation cards so plugins can own richer interactive surfaces without channel-specific glue.
- Fireworks/models: add Kimi K2.6 (`fireworks/accounts/fireworks/models/kimi-k2p6`) to the bundled catalog and live-model priority list, while keeping Kimi thinking disabled for Fireworks K2.6 requests.
- Onboard/wizard: simplify the security disclaimer copy, and switch remaining onboarding pickers with long dynamic option lists to searchable autocompletes for search providers, plugin configuration, and model provider filtering.
- Channels/preview streaming: stream tool-progress updates into live preview edits for Discord, Slack, and Telegram so in-flight replies show incremental tool state in the same preview message before finalization. (#69611) Thanks @thewilloftheshadow.
- Ollama/onboard: populate the cloud-only model list from `ollama.com/api/tags`, cap the discovered list at 500, and fall back to static suggestions when ollama.com is unavailable. (#68463) Thanks @BruceMacD.
- QQBot: extract a self-contained engine architecture with QR-code onboarding, native approval handling via `/bot-approve`, per-account resource stacks, credential backup/restore, shared media storage, and unified API/bridge/gateway modules. (#67960) Thanks @cxyhhhhh.
- Matrix/startup: narrow Matrix runtime registration and defer setup/doctor surfaces so cold plugin registration spends about 1.8s less in `setChannelRuntime`. (#69782) Thanks @gumadeiras.
- Telegram/plugin startup: load Telegram's bundled runtime setter through a narrow sidecar and native built-sidecar loading, cutting measured setup-runtime registration by about 14s while preserving runtime API compatibility. (#69786) Thanks @gumadeiras.
- Discord/plugin startup: lazy-load the Carbon UI runtime and load Discord's bundled runtime setter through a narrow sidecar, cutting measured registration time by about 98% while keeping packaged installs off Carbon until the Discord UI surface is needed. (#69791) Thanks @gumadeiras.
### Fixes
- Agents/ACP: skip the `sessions_send` A2A ping-pong flow when a parent sends to its own background oneshot ACP child, preventing parent/child echo loops while preserving normal A2A delivery for non-parent senders. (#69817) Thanks @scotthuang.
- Image generation: log failed provider/model candidates at warn level before automatic provider fallback, so OpenAI image failures are visible in the gateway log even when a later provider succeeds.
- Agents/subagents: stop terminal failed subagent runs from freezing or announcing captured reply text, so failover-exhausted runs report a clean failure instead of replaying stale assistant/tool output.
- Security/external content: strip common self-hosted LLM chat-template special-token literals, including Qwen/ChatML, Llama, Gemma, Mistral, Phi, and GPT-OSS markers, from wrapped external content and metadata, preventing tokenizer-layer role-boundary spoofing against OpenAI-compatible backends that preserve special tokens in user text.
- npm/install: mirror the `node-domexception` alias into root `package.json` `overrides`, so npm installs stop surfacing the deprecated `google-auth-library -> gaxios -> node-fetch -> fetch-blob -> node-domexception` chain pulled through Pi/Google runtime deps. Thanks @vincentkoc.
- Auth/commands: require owner identity (an owner-candidate match or internal `operator.admin`) for owner-enforced commands instead of treating wildcard channel `allowFrom` or empty owner-candidate lists as sufficient, so non-owner senders can no longer reach owner-only commands through a permissive fallback when `enforceOwnerForCommands=true` and `commands.ownerAllowFrom` is unset. (#69774) Thanks @drobison00.
- Control UI/CSP: tighten `img-src` to `'self' data:` only, and make Control UI avatar helpers drop remote `http(s)` and protocol-relative URLs so the UI falls back to the built-in logo/badge instead of issuing arbitrary remote image fetches. Same-origin avatar routes (relative paths) and `data:image/...` avatars still render. (#69773)
- CLI/channels: keep `status`, `health`, `channels list`, and `channels status` on read-only channel metadata when Telegram, Slack, Discord, or third-party channel plugins are configured, avoiding full bundled plugin runtime imports on those cold paths. Fixes #69042. (#69479) Thanks @gumadeiras.
- Synology Chat: validate outbound webhook `file_url` values against the shared SSRF policy before forwarding to the NAS, rejecting malformed URLs, non-`http(s)` schemes, and private/blocked network targets so the NAS cannot be used as a confused deputy to fetch internal addresses. (#69784) Thanks @eleqtrizit.
- LINE: validate outbound media URLs against the shared public-network guard before handing them to LINE, preserving arbitrary public HTTPS media while rejecting loopback, link-local, and private-network targets.
- Gateway/Control UI: require gateway auth on the Control UI avatar route (`GET /avatar/<agentId>` and `?meta=1` metadata) when auth is configured, matching the sibling assistant-media route, and propagate the existing gateway token through the UI avatar fetch (bearer header + authenticated blob URL) so authenticated dashboards still load local avatars. (#69775)
- Google Chat/auth: replace the Google auth `gaxios` shim with a scoped SSRF-guarded transport, validate service-account auth endpoints against trusted Google URLs, and let the plugin own its staged `gaxios` auth runtime instead of patching process-wide globals or the root CLI startup path. Thanks @vincentkoc.
- Exec/allowlist: reject POSIX parameter expansion forms such as `$VAR`, `$?`, `$$`, `$1`, and `$@` inside unquoted heredocs during shell approval analysis, so these heredocs no longer pass allowlist review as plain text. (#69795) Thanks @drobison00.
- Gateway/MCP loopback: derive owner-only tool visibility from distinct authenticated owner vs non-owner loopback bearers instead of the caller-controlled owner header, so non-owner MCP child processes cannot recover owner access by spoofing request metadata. (#69796)
- GitHub Copilot: update the default Opus model from `claude-opus-4.6` to `claude-opus-4.7` after GitHub removed Copilot support for 4.6. (#69818) Thanks @shakkernerd.
- OpenShell: pin host-side sandbox writes under the mounted root so symlink-parent rebinds cannot redirect `writeFile` outside the workspace during local mirror updates. (#69797) Thanks @drobison00.
- Ollama/media understanding: register Ollama as an image-capable media-understanding provider so `agents.defaults.imageModel.primary` values like `ollama/qwen2.5vl:7b` route through the Ollama plugin instead of failing as unknown models. (#69816) Thanks @soloclz.
- CLI/media understanding: make `openclaw infer image describe --model <provider/model>` execute the explicit image model instead of skipping description when that model supports native vision.
- Usage/providers: keep plugin-owned usage auth enabled when manifest-declared provider auth env vars such as `MINIMAX_CODE_PLAN_KEY` are present, so `/usage` can resolve MiniMax billing credentials through the provider plugin.
- Tlon/uploads: route both hosted Memex upload targets and custom-S3 presigned upload URLs through the shared SSRF guard so blocked private or loopback destinations fail before upload, while public upload URLs continue through the existing hosted upload flow. (#69794) Thanks @drobison00.
- Channels/thread routing: keep outbound replies in existing Slack, Mattermost, Matrix, Telegram, Discord, and QA-channel thread sessions by sharing the Plugin SDK thread-aware route builder across bundled plugins.
- Agents/replay: normalize restored assistant text content before provider replay and prompt submission, so legacy or repaired sessions no longer crash on `assistantMsg.content.flatMap`. (#69850) Thanks @fuller-stack-dev.
## 2026.4.20
### Changes
@@ -32,7 +219,6 @@ Docs: https://docs.openclaw.ai
- Webchat/images: treat inline image attachments as media for empty-turn gating while still ignoring metadata-only blank turns. (#69474) Thanks @Jaswir.
- Discord/think: only show `adaptive` in `/think` autocomplete for provider/model pairs that actually support provider-managed adaptive thinking, so GPT/OpenAI models no longer advertise an Anthropic-only option.
- Thinking: only expose `max` for models that explicitly support provider max reasoning, and remap stored `max` settings to the largest supported thinking mode when users switch to another model.
- Thinking/UI: drive `/think` options and chat/Sessions pickers from provider-owned thinking profiles, so custom model level sets such as binary `on/off`, Gemini 3 Pro `off/low/high`, Anthropic `adaptive/max`, and OpenAI `xhigh` stay in one runtime contract.
- Gateway/usage: bound the cost usage cache with FIFO eviction so date/range lookups cannot grow unbounded. (#68842) Thanks @Feelw00.
- OpenAI/Responses: resolve `/think` levels against each GPT model's supported reasoning efforts so `/think off` no longer becomes high reasoning or sends unsupported `reasoning.effort: "none"` payloads.
- Lobster/TaskFlow: allow managed approval resumes to use `approvalId` without a resume token, and persist that id in approval wait state. (#69559) Thanks @kirkluokun.
@@ -75,7 +261,6 @@ Docs: https://docs.openclaw.ai
- Codex/app-server: release the session lane when a downstream consumer throws while draining the `turn/completed` notification, so follow-up messages after a Codex plugin reply stop queueing behind a stale lane lock. Fixes #67996. (#69072) Thanks @ayeshakhalid192007-dev.
- Codex/app-server: default approval handling to `on-request` so Codex harness sessions do not start with overly permissive tool approvals. (#68721) Thanks @Lucenx9.
- Cron/delivery: keep isolated cron chat delivery tools available, resolve `channel: "last"` targets from the gateway, show delivery previews in `cron list/show`, and avoid duplicate fallback sends after direct message-tool delivery. (#69587) Thanks @obviyus.
- BlueBubbles: add opt-in `channels.bluebubbles.coalesceSameSenderDms` so a single composed message with text + pasted URL (which Apple splits into two webhooks ~0.8-2.0 s apart) arrives as one agent turn instead of two. When enabled, DM messages that are not linked via `associatedMessageGuid` hash to `dm:<chat>:<sender>` so the inbound debounce window merges them into a single merged turn — including URL-preview balloon events, DM control-command sends (which normally bypass debouncing), and rapid same-sender follow-ups. The default inbound debounce window widens from 500 ms to 2500 ms when the flag is set without an explicit `messages.inbound.byChannel.bluebubbles`, covering the observed Apple split-send cadence. Every source `messageId` folded into the merged view is committed to the inbound dedupe store after processing, so a later MessagePoller replay of any individual source event is recognized as a duplicate. Merged output is bounded (≤4000 chars text with an explicit `…[truncated]` marker, ≤20 attachments, first-plus-latest sampling beyond 10 source entries) so a rapid-fire flood inside the window cannot amplify the downstream prompt. Group chats and existing text+balloon follow-ups continue to key per-message. See [Coalescing split-send DMs](https://docs.openclaw.ai/channels/bluebubbles#coalescing-split-send-dms-command--url-in-one-composition) for scenarios, tuning, and troubleshooting. (#69258) Thanks @omarshahine.
- 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.
- Models/Kimi: default bundled Kimi thinking to off and normalize Anthropic-compatible `thinking` payloads so stale session `/think` state no longer silently re-enables reasoning on Kimi runs. (#68907) Thanks @frankekn.
- Control UI/cron: keep the runtime-only `last` delivery sentinel from being materialized into persisted cron delivery and failure-alert channel configs when jobs are created or edited. (#68829) Thanks @tianhaocui.
@@ -94,8 +279,8 @@ Docs: https://docs.openclaw.ai
- 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.
- 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.
- Doctor/gateway: surface pending device pairing requests, scope-upgrade approval drift, and stale device-token mismatch repair steps so `openclaw doctor --fix` no longer leaves pairing/auth setup failures unexplained. (#69210) Thanks @obviyus.
- Cron/isolated-agent: preserve explicit `delivery.mode: "none"` message targets for isolated runs without inheriting implicit `last` routing, so agent-initiated Telegram sends keep their authored destination while bare `mode:none` jobs stay targetless. (#69153) Thanks @obviyus.
- Cron/isolated-agent: keep `delivery.mode: "none"` 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 `to` target. (#69163) Thanks @obviyus.
- Cron/isolated-agent: preserve explicit `delivery.mode: "none"` message targets for isolated runs without inheriting implicit `last` routing, so agent-initiated Telegram sends keep their authored destination while bare `mode:none` jobs stay targetless. (#69153) Thanks @davehappyminion and @nikilster.
- Cron/isolated-agent: keep `delivery.mode: "none"` 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 `to` target. (#69163) Thanks @davehappyminion and @nikilster.
- Gateway/TUI: retry session history while the local gateway is still finishing startup, so `openclaw tui` reconnects no longer fail on transient `chat.history unavailable during gateway startup` errors. (#69164) Thanks @shakkernerd.
- BlueBubbles/reactions: fall back to `love` when an agent reacts with an emoji outside the iMessage tapback set (`love`/`like`/`dislike`/`laugh`/`emphasize`/`question`), so wider-vocabulary model reactions like `👀` still produce a visible tapback instead of failing the whole reaction request. Configured ack reactions still validate strictly via the new `normalizeBlueBubblesReactionInputStrict` path. (#64693) Thanks @zqchris.
- BlueBubbles: prefer iMessage over SMS when both chats exist for the same handle, honor explicit `sms:` targets, and never silently downgrade iMessage-available recipients. (#61781) Thanks @rmartin.
@@ -105,8 +290,6 @@ Docs: https://docs.openclaw.ai
- Slack: fix outbound replies failing with "unresolved SecretRef" for accounts configured via `file` or `exec` 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.
- Control UI/device pairing: explain scope and role approval upgrades during reconnects, and show requested versus approved access in the Control UI and `openclaw devices` so broader reconnects no longer look like lost pairings. (#69221) Thanks @obviyus.
- 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.
- Telegram/media: parse lowercase media directives in block replies and preserve outbound attachment filenames, so generated files send once with their original names. (#69641) Thanks @obviyus.
- Agents/Anthropic: honor explicit `cacheRetention: "long"` for custom `anthropic-messages` endpoints by applying the 1-hour ephemeral cache TTL independently of the Anthropic/Vertex hostname allowlist. Implicit and env-driven long retention still require an allowlisted host. (#67800) Thanks @MonkeyLeeT.
## 2026.4.19-beta.2

View File

@@ -6,7 +6,7 @@ Welcome to the lobster tank! 🦞
- **GitHub:** https://github.com/openclaw/openclaw
- **Vision:** [`VISION.md`](VISION.md)
- **Discord:** https://discord.gg/qkhbAGHRBT
- **Discord:** https://discord.gg/clawd
- **X/Twitter:** [@steipete](https://x.com/steipete) / [@openclaw](https://x.com/openclaw)
## Maintainers

View File

@@ -165,7 +165,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
- Chat commands: `/status`, `/new`, `/reset`, `/compact`, `/think <level>`, `/verbose on|off`, `/trace on|off`, `/usage off|tokens|full`, `/restart`, `/activation mention|always`
- Session tools: `sessions_list`, `sessions_history`, `sessions_send`
- Skills registry: [ClawHub](https://clawhub.com)
- Skills registry: [ClawHub](https://clawhub.ai)
- Architecture overview: [Architecture](https://docs.openclaw.ai/concepts/architecture)
## Docs by goal

View File

@@ -2,6 +2,118 @@
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>OpenClaw</title>
<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>
<item>
<title>2026.4.15</title>
<pubDate>Thu, 16 Apr 2026 23:33:29 +0000</pubDate>
@@ -204,192 +316,5 @@
]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.4.14/OpenClaw-2026.4.14.zip" length="47490719" type="application/octet-stream" sparkle:edSignature="KW4gq3qjhKPSQebRVL/mSgttTOhLVKtnWz7pNCZt29oEZ96yU14OnxxSsmtNHmDi4m7G7gfVOfndp80XKFQlCw=="/>
</item>
<item>
<title>2026.4.11</title>
<pubDate>Sun, 12 Apr 2026 00:37:09 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026041190</sparkle:version>
<sparkle:shortVersionString>2026.4.11</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.4.11</h2>
<h3>Changes</h3>
<ul>
<li>Dreaming/memory-wiki: add ChatGPT import ingestion plus new <code>Imported Insights</code> and <code>Memory Palace</code> diary subtabs so Dreaming can inspect imported source chats, compiled wiki pages, and full source pages directly from the UI. (#64505)</li>
<li>Control UI/webchat: render assistant media/reply/voice directives as structured chat bubbles, add the <code>[embed ...]</code> rich output tag, and gate external embed URLs behind config. (#64104)</li>
<li>Tools/video_generate: add URL-only generated asset delivery, typed <code>providerOptions</code>, reference audio inputs, per-asset role hints, <code>adaptive</code> aspect-ratio support, and a higher image-input cap so video providers can expose richer generation modes without forcing large files into memory. (#61987, #61988) Thanks @xieyongliang.</li>
<li>Feishu: improve document comment sessions with richer context parsing, comment reactions, and typing feedback so document-thread conversations behave more like chat conversations. (#63785)</li>
<li>Microsoft Teams: add reaction support, reaction listing, Graph pagination, and delegated OAuth setup for sending reactions while preserving application-auth read paths. (#51646)</li>
<li>Plugins: allow plugin manifests to declare activation and setup descriptors so plugin setup flows can describe required auth, pairing, and configuration steps without hardcoded core special cases. (#64780)</li>
<li>Ollama: cache <code>/api/show</code> context-window and capability metadata during model discovery so repeated picker refreshes stop refetching unchanged models, while still retrying after empty responses and invalidating on digest changes. (#64753) Thanks @ImLukeF.</li>
<li>Models/providers: surface how configured OpenAI-compatible endpoints are classified in embedded-agent debug logs, so local and proxy routing issues are easier to diagnose. (#64754) Thanks @ImLukeF.</li>
<li>QA/parity: add the GPT-5.4 vs Opus 4.6 agentic parity report gate with shared scenario coverage checks, stricter evidence heuristics, and skipped-scenario accounting for maintainer review. (#64441) Thanks @100yenadmin.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>OpenAI/Codex OAuth: stop rewriting the upstream authorize URL scopes so new Codex sign-ins do not fail with <code>invalid_scope</code> before returning an authorization code. (#64713) Thanks @fuller-stack-dev.</li>
<li>Audio transcription: disable pinned DNS only for OpenAI-compatible multipart requests, while still validating hostnames, so OpenAI, Groq, and Mistral transcription works again without weakening other request paths. (#64766) Thanks @GodsBoy.</li>
<li>macOS/Talk Mode: after granting microphone permission on first enable, continue starting Talk Mode instead of requiring a second toggle. (#62459) Thanks @ggarber.</li>
<li>Control UI/webchat: persist agent-run TTS audio replies into webchat history and preserve interleaved tool card pairing so generated audio and mixed tool output stay attached to the right messages. (#63514) Thanks @bittoby.</li>
<li>WhatsApp: honor the configured default account when the active listener helper is used without an explicit account id, so named default accounts do not get registered under <code>default</code>. (#53918) Thanks @yhyatt.</li>
<li>ACP/agents: suppress commentary-phase child assistant relay text in ACP parent stream updates, so spawned child runs stop leaking internal progress chatter into the parent session. Thanks @vincentkoc.</li>
<li>Agents/timeouts: honor explicit run timeouts in the LLM idle watchdog and align default timeout config so slow models can keep working until the configured limit instead of using the wrong idle window.</li>
<li>Config: include <code>asyncCompletion</code> in the generated zod schema so documented async completion config no longer fails with an unrecognized-key error. (#63618)</li>
<li>Google/Veo: stop sending the unsupported <code>numberOfVideos</code> request field so Gemini Developer API Veo runs do not fail before OpenClaw can complete the intended Google video generation path. (#64723) Thanks @velvet-shark.</li>
<li>QA/packaging: stop packaged CLI startup and completion cache generation from reading repo-only QA scenario markdown, ship the bundled QA scenario pack in npm releases, and keep <code>openclaw completion --write-state</code> working even if QA setup is broken. (#64648) Thanks @obviyus.</li>
<li>Codex/QA: keep Codex app-server coordination chatter out of visible replies, add a live QA leak scenario, and classify leaked harness meta text as a QA failure instead of a successful reply. Thanks @vincentkoc.</li>
<li>WhatsApp: route <code>message react</code> through the gateway-owned action path so reactions use the live WhatsApp listener in both DM and group chats, matching <code>message send</code> and <code>message poll</code>. Thanks @mcaxtr.</li>
<li>Auto-reply/WhatsApp: preserve inbound image attachment notes after media understanding so image edits keep the real saved media path instead of hallucinating a missing local path. (#64918) Thanks @ngutman.</li>
<li>Telegram/sessions: keep topic-scoped session initialization on the canonical topic transcript path when inbound turns omit <code>MessageThreadId</code>, so one topic session no longer alternates between bare and topic-qualified transcript files. (#64869) Thanks @jalehman.</li>
<li>Agents/failover: scope assistant-side fallback classification and surfaced provider errors to the current attempt instead of stale session history, so cross-provider fallback runs stop inheriting the previous provider's failure. (#62907) Thanks @stainlu.</li>
<li>MiniMax/OAuth: write <code>api: "anthropic-messages"</code> and <code>authHeader: true</code> into the <code>minimax-portal</code> config patch during <code>openclaw configure</code>, so re-authenticated portal setups keep Bearer auth routing working. (#64964) Thanks @ryanlee666.</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.11/OpenClaw-2026.4.11.zip" length="47317969" type="application/octet-stream" sparkle:edSignature="v9bUsh1mBBPtpMn7kKYAvO8MNJHAeMj7UkmkkuDSC8NvwPx2Fo3+NEeyAyA9s9Vax6L7i+eHSpwzAmtwpnHcCA=="/>
</item>
<item>
<title>2026.4.10</title>
<pubDate>Sat, 11 Apr 2026 03:17:02 +0000</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>2026041090</sparkle:version>
<sparkle:shortVersionString>2026.4.10</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>OpenClaw 2026.4.10</h2>
<h3>Changes</h3>
<ul>
<li>Models/Codex: add the bundled Codex provider and plugin-owned app-server harness so <code>codex/gpt-*</code> models use Codex-managed auth, native threads, model discovery, and compaction while <code>openai/gpt-*</code> stays on the normal OpenAI provider path. (#64298)</li>
<li>Memory/Active Memory: add a new optional Active Memory plugin that gives OpenClaw a dedicated memory sub-agent right before the main reply, so ongoing chats can automatically pull in relevant preferences, context, and past details without making users remember to manually say "remember this" or "search memory" first. Includes configurable message/recent/full context modes, live <code>/verbose</code> inspection, advanced prompt/thinking overrides for tuning, and opt-in transcript persistence for debugging. Docs: https://docs.openclaw.ai/concepts/active-memory. (#63286) Thanks @Takhoffman.</li>
<li>macOS/Talk: add an experimental local MLX speech provider for Talk Mode, with explicit provider selection, local utterance playback, interruption handling, and system-voice fallback. (#63539) Thanks @ImLukeF.</li>
<li>Tools/video generation: add Seedance 2.0 model refs to the bundled fal provider and submit the provider-specific duration, resolution, audio, and seed metadata fields needed for live Seedance 2.0 runs.</li>
<li>Microsoft Teams: add message actions for pin, unpin, read, react, and listing reactions. (#53432) Thanks @sudie-codes.</li>
<li>QA/Matrix: add a live <code>openclaw qa matrix</code> lane backed by a disposable Matrix homeserver, shared live-transport seams, and Matrix-specific transport coverage for threading, reactions, restart, and allowlist behavior. (#64489) Thanks @gumadeiras.</li>
<li>QA/Telegram: add a live <code>openclaw qa telegram</code> lane for private-group bot-to-bot checks, harden its artifact handling, and preserve native Telegram command reply threading for QA verification. (#64303) Thanks @obviyus.</li>
<li>QA/testing: add a <code>--runner multipass</code> lane for <code>openclaw qa suite</code> so repo-backed QA scenarios can run inside a disposable Linux VM and write back the usual report, summary, and VM logs. (#63426) Thanks @shakkernerd.</li>
<li>CLI/exec policy: add a local <code>openclaw exec-policy</code> command with <code>show</code>, <code>preset</code>, and <code>set</code> subcommands for synchronizing requested <code>tools.exec.*</code> config with the local exec approvals file, plus follow-up hardening for node-host rejection, rollback safety, and sync conflict detection. (#64050)</li>
<li>Gateway: add a <code>commands.list</code> RPC so remote gateway clients can discover runtime-native, text, skill, and plugin commands with surface-aware naming and serialized argument metadata. (#62656) Thanks @samzong.</li>
<li>Models/providers: add per-provider <code>models.providers.*.request.allowPrivateNetwork</code> for trusted self-hosted OpenAI-compatible endpoints, keep the opt-in scoped to model request surfaces, and refresh cached WebSocket managers when request transport overrides change. (#63671) Thanks @qas.</li>
<li>Feishu: standardize request user agents and register the bot as an AI agent so Feishu deployments identify OpenClaw consistently. (#63835) Thanks @evandance.</li>
<li>Matrix/partial streaming: add MSC4357 live markers to draft preview sends and edits so supporting Matrix clients can render a live/typewriter animation and stop it when the final edit lands. (#63513) Thanks @TigerInYourDream.</li>
<li>Control UI/dreaming: simplify the Scene and Diary surfaces, preserve unknown phase state for partial status payloads, and stabilize waiting-entry recency ordering so Dreaming status and review lists stay clear and deterministic. (#64035) Thanks @davemorin.</li>
<li>Agents: add an opt-in strict-agentic embedded Pi execution contract for GPT-5-family runs so plan-only or filler turns keep acting until they hit a real blocker. (#64241) Thanks @100yenadmin.</li>
<li>Agents/OpenAI: add provider-owned OpenAI/Codex tool schema compatibility and surface embedded-run replay/liveness state for long-running runs. (#64300) Thanks @100yenadmin.</li>
<li>Docs i18n: chunk raw doc translation, reject truncated tagged outputs, avoid ambiguous body-only wrapper unwrapping, and recover from terminated Pi translation sessions without changing the default <code>openai/gpt-5.4</code> path. (#62969, #63808) Thanks @hxy91819.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Browser/security: tighten browser and sandbox navigation defenses across strict SSRF defaults, hostname allowlists, interaction-driven redirects, subframes, CDP discovery, existing sessions, tab actions, noVNC, marker-span sanitization, and Docker CDP source-range enforcement. (#61404, #63332, #63882, #63885, #63889, #64367, #64370, #64371)</li>
<li>Security/tools: harden exec preflight reads, host env denylisting, node output boundaries, outbound host-media reads, profile-mutation authorization, plugin install dependency scanning, ACPX tool hooks, Gmail watcher token redaction, and oversized realtime WebSocket frame handling. (#62333, #62661, #62662, #63277, #63551, #63553, #63886, #63890, #63891, #64459)</li>
<li>OpenAI/Codex: add required Codex OAuth scopes, classify provider/runtime failures more clearly, stop suggesting <code>/elevated full</code> when auto-approved host exec is unavailable, add OpenAI/Codex tool-schema compatibility, and preserve embedded-run replay/liveness truth across compaction retries and mutating side effects. (#64300, #64439) Thanks @100yenadmin.</li>
<li>CLI/WhatsApp media sends: route gateway-mode outbound sends with <code>--media</code> through the channel <code>sendMedia</code> path and preserve media access context, so WhatsApp document and attachment sends stop silently dropping the file while still delivering the caption. (#64478, #64492) Thanks @ShionEria.</li>
<li>Microsoft Teams: restore media downloads for personal DMs, Bot Framework <code>a:</code> conversations, OneDrive/SharePoint shared files, and Graph-backed chat IDs; accept Bot Framework audience tokens; prevent feedback-learning filename collisions; keep long tool chains alive with typing indicators; add SSO sign-in callbacks; inject parent context for thread replies; and deliver cron announcements to Teams conversation IDs. (#54932, #55383, #55386, #58001, #58249, #58774, #59731, #60956, #62219, #62674, #63063, #63942, #63945, #63949, #63951, #63953, #64087, #64088, #64089)</li>
<li>Gateway/tailscale: start Tailscale exposure and the gateway update check before awaiting channel and plugin sidecar startup so remote operators are not locked out when startup sidecars stall.</li>
<li>Gateway/startup: keep WebSocket RPC available while channels and plugin sidecars start, hold <code>chat.history</code> unavailable until startup sidecars finish so synchronous history reads cannot stall startup (reported in #63450), refresh advertised gateway methods after deferred plugin reloads, and enforce the pre-auth WebSocket upgrade budget before the no-handler 503 path so upgrade floods cannot bypass connection limits during that window. (#63480) Thanks @neeravmakwana.</li>
<li>WhatsApp: keep inbound replies, media, composing indicators, and queued outbound deliveries attached to the current socket across reconnect gaps, including fresh retry-eligible sends after the listener comes back. (#30806, #46299, #62892, #63916) Thanks @mcaxtr.</li>
<li>Gateway/thread routing: preserve Slack, Telegram, Mattermost, Matrix, ACP, restart-sentinel, and agent announce delivery targets so subagent, cron, stream-relay, session fallback, and restart messages land back in the originating thread, topic, or room casing. (#54840, #57056, #63143, #63228, #63506, #64343, #64391)</li>
<li>Models/fallback: preserve <code>/models</code> selection across transient primary-model failures and config reloads, allow timeout cooldown probes, classify OpenRouter no-endpoints responses, detect llama.cpp context overflows, and keep provider/runtime context metadata stable through reloads. (#61472, #64196, #64471)</li>
<li>Agents/BTW: keep <code>/btw</code> side questions working after tool-use turns by stripping replayed tool blocks, hidden reasoning, and malformed image payloads, omitting empty tool arrays, allowing Bedrock <code>auth: "aws-sdk"</code>, and routing Feishu <code>/btw</code> plus <code>/stop</code> through bounded out-of-band lanes. (#64218, #64219, #64225, #64324) Thanks @ngutman.</li>
<li>Control UI/BTW: render <code>/btw</code> side results as dismissible ephemeral cards in the browser, send <code>/btw</code> immediately during active runs, and clear stale BTW cards on reset flows so webchat matches the intended detached side-question behavior. (#64290) Thanks @ngutman.</li>
<li>Commands/targeting: use the selected agent or session for command output, send policy, usage/cost, context reports, model lists, bash sandbox hints, BTW/compact working directories, plugin commands, and session exports so multi-agent commands describe and mutate the intended target instead of the requester.</li>
<li>Conversation bindings: normalize focused/current conversation ids, preserve binding metadata on account and Discord rebinds, avoid stale Discord lifecycle windows, and keep generic activity touches persisted so reply routing survives rebinds and restarts.</li>
<li>iMessage/self-chat: distinguish normal DM outbound rows from true self-chat using <code>destination_caller_id</code> plus chat participants, preserve multi-handle self-chat aliases, drop ambiguous reflected echoes, and strip wrapped imsg RPC text fields. (#61619, #63868, #63980, #63989, #64000) Thanks @neeravmakwana.</li>
<li>Matrix: keep multi-account room scoping consistent, keep packaged crypto migrations warning-only when appropriate, preserve ordered block streaming, add explicit Matrix block-streaming opt-in, and resolve verification/bootstrap from the packaged runtime entry. (#58449, #59249, #59266, #64373) Thanks @gumadeiras.</li>
<li>Telegram/security: tighten Telegram <code>allowFrom</code> sender validation and keep <code>/whoami</code> allowlist reporting in sync with command auth checks.</li>
<li>Agents/timeouts: extend the default LLM idle window to 120s and keep silent no-token idle timeouts on recovery paths, so slow models can retry or fall back before users see an error.</li>
<li>Gateway/agents: preserve configured model selection and richer <code>IDENTITY.md</code> content across agent create/update flows and workspace moves, and fail safely instead of silently overwriting unreadable identity files. (#61577) Thanks @samzong.</li>
<li>Skills/TaskFlow: restore valid frontmatter fences for the bundled <code>taskflow</code> and <code>taskflow-inbox-triage</code> skills and copy bundled <code>SKILL.md</code> files as hard dist-runtime copies so skills stay discoverable and loadable after updates. (#64166, #64469) Thanks @extrasmall0.</li>
<li>Skills: respect overridden home directories when loading personal skills so service, test, and custom launch environments read the intended user skill directory instead of the process home.</li>
<li>Windows/exec: settle supervisor waits from child exit state after stdout and stderr drain even when <code>close</code> never arrives, so CLI commands stop hanging or dying with forced <code>SIGKILL</code> on Windows. (#64072) Thanks @obviyus.</li>
<li>Browser/sandbox: prevent sandbox browser CDP startup hangs by recreating containers when the browser security hash changes and by waiting on the correct sandbox browser lifecycle. (#62873) Thanks @Syysean.</li>
<li>QQBot/streaming: make block streaming configurable per QQ bot account via <code>streaming.mode</code> (<code>"partial"</code> | <code>"off"</code>, default <code>"partial"</code>) instead of hardcoding it off, so responses can be delivered incrementally. (#63746)</li>
<li>QQBot/config: allow extra fields in <code>channels.qqbot</code> and <code>channels.qqbot.accounts.*</code> so extended qqbot builds can add new config options without gateway startup failing on schema validation. (#64075) Thanks @WideLee.</li>
<li>Dreaming/gateway: require <code>operator.admin</code> for persistent <code>/dreaming on|off</code> changes and treat missing gateway client scopes as unprivileged instead of silently allowing config writes. (#63872) Thanks @mbelinky.</li>
<li>Gateway/pairing: prefer explicit QR bootstrap auth over earlier Tailscale auth classification so iOS <code>/pair qr</code> silent bootstrap pairing does not fall through to <code>pairing required</code>. (#59232) Thanks @ngutman.</li>
<li>Browser/control: auto-generate browser-control auth tokens for <code>none</code> and <code>trusted-proxy</code> modes, and route browser auth/profile/doctor helpers through the public browser plugin facades. (#63280, #63957) Thanks @pgondhi987.</li>
<li>Browser/act: centralize <code>/act</code> request normalization and execution dispatch while adding stable machine-readable route-level error codes for invalid requests, selector misuse, evaluate-disabled gating, target mismatch, and existing-session unsupported actions. (#63977) Thanks @joshavant.</li>
<li>Security/QQBot: enforce media storage boundaries for all outbound local file paths and route image-size probes through SSRF-guarded media fetching instead of raw <code>fetch()</code>. (#63271, #63495) Thanks @pgondhi987.</li>
<li>Channel setup: ignore workspace plugin shadows when resolving trusted channel setup catalog entries so onboarding and setup flows keep using the bundled, trusted setup contract.</li>
<li>Gateway/memory startup: load the explicitly selected memory-slot plugin during gateway startup, while keeping restrictive allowlists and implicit default memory slots from auto-starting unrelated memory plugins. (#64423) Thanks @EronFan.</li>
<li>Config/plugins: let config writes keep disabled plugin entries without forcing required plugin config schemas or crashing raw plugin validation, and avoid re-activating plugin registry state during schema checks. (#54971, #63296) Thanks @fuller-stack-dev.</li>
<li>Config validation: surface the actual offending field for strict-schema union failures in bindings, including top-level unexpected keys on the matching ACP branch. (#40841) Thanks @Hollychou924.</li>
<li>Wizard/plugin config: coerce integer-typed plugin config fields from interactive text input so integer schema values persist as numbers instead of failing validation. (#63346) Thanks @jalehman.</li>
<li>Daemon/gateway install: preserve safe custom service env vars on forced reinstall, merge prior custom PATH segments behind the managed service PATH, and stop removed managed env keys from persisting as custom carryover. (#63136) Thanks @WarrenJones.</li>
<li>Cron/scheduling: treat <code>nextRunAtMs <= 0</code> as invalid across cron update, maintenance, timer, and stale-delivery paths so corrupted zero timestamps self-heal instead of causing immediate runs or skipped deliveries. (#63507) Thanks @WarrenJones.</li>
<li>Cron/auth: resolve auth profiles consistently for isolated cron jobs so scheduled runs use the same configured provider credentials as interactive sessions. (#62797) Thanks @neeravmakwana.</li>
<li>Tasks: let <code>openclaw tasks cancel</code> cancel stuck background tasks that never reached a normal terminal state. (#62506) Thanks @neeravmakwana.</li>
<li>Sessions/model selection: preserve catalog-backed session model labels, provider-qualified context limits, and already-qualified session model refs when catalog metadata is unavailable, so model selection and memory/context budgets survive reloads without bogus provider prefixes. (#61382, #62493) Thanks @Mule-ME.</li>
<li>Status: show configured fallback models in <code>/status</code> and shared session status cards so per-agent fallback configuration is visible before a live failover happens. (#33111) Thanks @AnCoSONG.</li>
<li><code>/context detail</code> now compares the tracked prompt estimate with cached context usage and surfaces untracked provider/runtime overhead when present. (#28391) Thanks @ImLukeF.</li>
<li>Gateway/sessions: scope bare <code>sessions.create</code> aliases like <code>main</code> to the requested agent while preserving the canonical <code>global</code> and <code>unknown</code> sentinel keys. (#58207) Thanks @jalehman.</li>
<li>Gateway/session reset: emit the typed <code>before_reset</code> hook for gateway <code>/new</code> and <code>/reset</code>, preserving reset-hook behavior even when the previous transcript has already been archived. (#53872) Thanks @VACInc.</li>
<li>Plugins/commands: pass the active host <code>sessionKey</code> into plugin command contexts, and include <code>sessionId</code> when it is already available from the active session entry, so bundled and third-party commands can resolve the current conversation reliably. (#59044) Thanks @jalehman.</li>
<li>Agents/auth: honor <code>models.providers.*.authHeader</code> for pi embedded runner model requests by injecting <code>Authorization: Bearer <apiKey></code> when requested. (#54390) Thanks @lndyzwdxhs.</li>
<li>Claude CLI: clear inherited Anthropic auth/header environment aliases before spawning Claude Code and add sanitized CLI backend auth-env diagnostics for debugging gateway-run provider selection.</li>
<li>Agents/failover: classify AbortError and stream-abort messages as timeout so Ollama NDJSON stream aborts stop showing <code>reason=unknown</code> in model fallback logs. (#58324) Thanks @yelog.</li>
<li>Fireworks/FirePass: disable Kimi K2.5 Turbo reasoning output by forcing thinking off on the FirePass path and hardening the provider wrapper so hidden reasoning no longer leaks into visible replies. (#63607) Thanks @frankekn.</li>
<li>Discord: update Carbon to v0.15.0. Thanks @thewilloftheshadow.</li>
<li>Config/Discord: coerce safe integer numeric Discord IDs to strings during config validation, keep unsafe or precision-losing numeric snowflakes rejected, and align <code>openclaw doctor</code> repair guidance with the same fail-closed behavior. (#45125) Thanks @moliendocode.</li>
<li>BlueBubbles/config: accept <code>enrichGroupParticipantsFromContacts</code> in the core strict config schema so gateways no longer fail validation or startup when the BlueBubbles plugin writes that field. (#56889) Thanks @zqchris.</li>
<li>Feishu/webhooks: read webhook bodies through the pre-auth guard so unauthenticated webhook traffic stays under the same body budget as other protected channel ingress paths.</li>
<li>Tools/web_fetch: add an opt-in <code>tools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRange</code> config so fake-IP proxy environments that resolve public sites into <code>198.18.0.0/15</code> can use <code>web_fetch</code> without weakening the default SSRF block. (#61830) Thanks @xing-xing-coder.</li>
<li>Dreaming/cron: reconcile managed dreaming cron from startup config and runtime lifecycle changes, but only recover managed dreaming cron state during heartbeat-triggered dreaming checks so ordinary chat traffic does not recreate removed jobs. (#63873, #63929, #63938) Thanks @mbelinky.</li>
<li>Memory/lancedb: accept <code>dreaming</code> config when <code>memory-lancedb</code> owns the memory slot so Dreaming surfaces can read slot-owner settings without schema rejection. (#63874) Thanks @mbelinky.</li>
<li>Control UI/dreaming: keep the Dreaming trace area contained and scrollable so overlays no longer cover tabs or blow out the page layout. (#63875) Thanks @mbelinky.</li>
<li>Dreaming/narrative: harden request-scoped diary fallback so scheduled dreaming only falls back on the dedicated subagent-runtime error, stop trusting spoofable raw error-code objects, and avoid leaking workspace paths when local fallback writes fail. (#64156) Thanks @mbelinky.</li>
<li>Dreaming/diary: add idempotent narrative subagent runs, preserve restrictive <code>DREAMS.md</code> permissions during atomic writes, and surface temp cleanup failures so repeated sweeps do not double-run the same narrative request or silently weaken diary safety. (#63876) Thanks @mbelinky.</li>
<li>Heartbeats/sessions: remove stale accumulated isolated heartbeat session keys when the next tick converges them back to the canonical sibling, so repaired sessions stop showing orphaned <code>:heartbeat:heartbeat</code> variants in session listings. (#59606) Thanks @rogerdigital.</li>
<li>Gateway/run cleanup: fix stale run-context TTL cleanup so the new maintenance sweep resets orphaned run sequence state and prevents unbounded run-context growth. (#52731) Thanks @artwalker.</li>
<li>UI/compaction: keep the compaction indicator in a retry-pending state until the run actually finishes, so the UI does not show <code>Context compacted</code> before compaction actually finishes. (#55132) Thanks @mpz4life.</li>
<li>Cron/tool schemas: keep cron tool schemas strict-model-friendly while still preserving <code>failureAlert=false</code>, nullable <code>agentId</code>/<code>sessionKey</code>, and flattened add/update recovery for the newly exposed cron job fields. (#55043) Thanks @brunolorente.</li>
<li>Git metadata: read commit ids from packed refs as well as loose refs so version and status metadata stay accurate after repository maintenance. (#63943)</li>
<li>Gateway: keep <code>commands.list</code> skill entries categorized under tools and include provider-aware plugin <code>nativeName</code> metadata even when <code>scope=text</code>, so remote clients can group skills correctly and map text-surface plugin commands back to native aliases. (#64147)</li>
<li>TUI: reset footer activity to idle when switching sessions so a stale streaming indicator cannot persist after the selection changes. (#63988) Thanks @neeravmakwana.</li>
<li>Claude CLI: stop marking spawned Claude Code runs as host-managed so they keep using normal CLI subscription behavior. (#64023) Thanks @Alex-Alaniz.</li>
<li>Codex auth: brand Codex OAuth flows as OpenClaw in user-visible auth prompts and diagnostics.</li>
<li>Gateway/pairing: fail closed for paired device records that have no device tokens, and reject pairing approvals whose requested scopes do not match the requested device roles.</li>
<li>ACP/gateway chat: classify lifecycle errors before forwarding them to ACP clients so refusals use ACP's refusal stop reason while transient backend errors continue to finish as normal turns.</li>
<li>Claude CLI/skills: pass eligible OpenClaw skills into CLI runs, including native Claude Code skill resolution via a temporary plugin plus per-run skill env/API key injection. (#62686, #62723) Thanks @zomars.</li>
<li>Discord: keep generated auto-thread names working with reasoning models by giving title generation enough output budget for thinking plus visible title text. (#64172) Thanks @hanamizuki.</li>
<li>Heartbeat: ignore doc-only Markdown fence markers in the default <code>HEARTBEAT.md</code> template so comment-only heartbeat scaffolds skip API calls again. (#61690, #63434) Thanks @ravyg.</li>
<li>Reply/skills: keep resolved skill and memory secret config stable through embedded reply runs so raw SecretRefs in secondary skill settings no longer crash replies when the gateway already has the live env. (#64249) Thanks @mbelinky.</li>
<li>Dreaming/startup: keep plugin-registered startup hooks alive across workspace hook reloads and include dreaming startup owners in the gateway startup plugin scope, so managed Dreaming cron registration comes back reliably after gateway boot. (#62327, #64258) Thanks @mbelinky.</li>
<li>Plugins: treat duplicate <code>registerService</code> calls from the same plugin id as idempotent so snapshot and activation loads no longer emit spurious <code>service already registered</code> diagnostics. (#62033, #64128) Thanks @ly85206559.</li>
<li>Discord/TTS: route auto voice replies through the native voice-note path so Discord receives Opus voice messages instead of regular audio attachments. (#64096) Thanks @LiuHuaize.</li>
<li>Config/plugins: use plugin-owned command alias metadata when <code>plugins.allow</code> contains runtime command names like <code>dreaming</code>, and point users at the owning plugin instead of stale plugin-not-found guidance. (#64191, #64242) Thanks @feiskyer.</li>
<li>Agents/Gemini: strip orphaned <code>required</code> entries from Gemini tool schemas so provider validation no longer rejects tools after schema cleanup or union flattening. (#64284) Thanks @xxxxxmax.</li>
<li>Assistant text: strip Qwen-style XML tool call payloads from visible replies so web and channel messages no longer show raw <code><tool_call><function=...></code> output. (#63999, #64214) Thanks @MoerAI.</li>
<li>Daemon/gateway: prevent systemd restart storms on configuration errors by exiting with <code>EX_CONFIG</code> and adding generated unit restart-prevention guards. (#63913) Thanks @neo1027144-creator.</li>
<li>Agents/exec: prevent gateway crash ("Agent listener invoked outside active run") when a subagent exec tool produces stdout/stderr after the agent run has ended or been aborted. (#62821) Thanks @openperf.</li>
<li>Gateway/OpenAI compat: return real <code>usage</code> for non-stream <code>/v1/chat/completions</code> responses, emit the final usage chunk when <code>stream_options.include_usage=true</code>, and bound usage-gated stream finalization after lifecycle end. (#62986) Thanks @Lellansin.</li>
<li>Matrix/migration: keep packaged warning-only crypto migrations from being misclassified as actionable when only helper chunks are present, so startup and doctor stay on the warning-only path instead of creating unnecessary migration snapshots. (#64373) Thanks @gumadeiras.</li>
<li>Matrix/ACP thread bindings: preserve canonical room casing and parent conversation routing during ACP session spawn so mixed-case room ids bind correctly from top-level rooms and existing Matrix threads. (#64343) Thanks @gumadeiras.</li>
<li>Agents/subagents: deduplicate delivered completion announces so retry or re-entry cleanup does not inject duplicate internal-context completion turns into the parent session. (#61525) Thanks @100yenadmin.</li>
<li>Agents/exec: keep sandboxed <code>tools.exec.host=auto</code> sessions from honoring per-call <code>host=node</code> or <code>host=gateway</code> overrides while a sandbox runtime is active, and stop advertising node routing in that state so exec stays on the sandbox host. (#63880)</li>
<li>Agents/subagents: preserve archived delete-mode runs until <code>sessions.delete</code> succeeds and prevent overlapping archive sweeps from duplicating in-flight cleanup attempts. (#61801) Thanks @100yenadmin.</li>
<li>Cron/isolated agent: run scheduled agent turns as non-owner senders so owner-only tools stay unavailable during cron execution. (#63878)</li>
<li>Discord/sandbox: include <code>image</code> in sandbox media param normalization so Discord event cover images cannot bypass sandbox path rewriting. (#64377) Thanks @mmaps.</li>
<li>Agents/exec: extend exec completion detection to cover local background exec formats so the owner-downgrade fires correctly for all exec paths. (#64376) Thanks @mmaps.</li>
<li>Security/dependencies: pin axios to 1.15.0 and add a plugin install dependency denylist that blocks known malicious packages before install. (#63891) Thanks @mmaps.</li>
<li>Browser/security: apply three-phase interaction navigation guard to pressKey and type(submit) so delayed JS redirects from keypress cannot bypass SSRF policy. (#63889) Thanks @mmaps.</li>
</ul>
<ul>
<li>Browser/security: guard existing-session Chrome MCP interaction routes with SSRF post-checks so delayed navigation from click, type, press, and evaluate cannot bypass the configured policy. (#64370) Thanks @eleqtrizit.</li>
<li>Browser/security: default browser SSRF policy to strict mode so unconfigured installs block private-network navigation, and align external-content marker span mapping so ZWS-injected boundary spoofs are fully sanitized. (#63885) Thanks @eleqtrizit.</li>
<li>Browser/security: apply SSRF navigation policy to subframe document navigations so iframe-targeted private-network hops are blocked without quarantining the parent page. (#64371) Thanks @eleqtrizit.</li>
<li>Hooks/security: mark agent hook system events as untrusted and sanitize hook display names before cron metadata reuse. (#64372) Thanks @eleqtrizit.</li>
<li>Daemon/launchd: keep <code>openclaw gateway stop</code> persistent without uninstalling the macOS LaunchAgent, re-enable it on explicit restart or repair, and harden launchd label handling. (#64447) Thanks @ngutman.</li>
<li>Plugins/context engines: preserve <code>plugins.slots.contextEngine</code> through normalization and keep explicitly selected workspace context-engine plugins enabled, so loader diagnostics and plugin activation stop dropping that slot selection. (#64192) Thanks @hclsys.</li>
<li>Heartbeat: stop top-level <code>interval:</code> and <code>prompt:</code> fields outside the <code>tasks:</code> block from bleeding into the last parsed heartbeat task. (#64488) Thanks @Rahulkumar070.</li>
<li>Agents/OpenAI replay: preserve malformed function-call arguments in stored assistant history, avoid double-encoding preserved raw strings on replay, and coerce replayed string args back to objects at Anthropic and Google provider boundaries. (#61956) Thanks @100yenadmin.</li>
<li>Heartbeat/config: accept and honor <code>agents.defaults.heartbeat.timeoutSeconds</code> and per-agent heartbeat timeout overrides for heartbeat agent turns. (#64491) Thanks @cedillarack.</li>
<li>CLI/devices: make implicit <code>openclaw devices approve</code> selection preview-only and require approving the exact request ID, preventing latest-request races during device pairing. (#64160) Thanks @coygeek.</li>
<li>Media/security: honor sender-scoped <code>toolsBySender</code> policy for outbound host-media reads so denied senders cannot trigger host file disclosure via attachment hydration. (#64459) Thanks @eleqtrizit.</li>
<li>Browser/security: reject strict-policy hostname navigation unless the hostname is an explicit allowlist exception or IP literal, and route CDP HTTP discovery through the pinned SSRF fetch path. (#64367) Thanks @eleqtrizit.</li>
<li>Models/vLLM: ignore empty <code>tool_calls</code> arrays from reasoning-model OpenAI-compatible replies, reset false <code>toolUse</code> stop reasons when no actual tool calls were parsed, and stop sending <code>tool_choice</code> unless tools are present so vLLM reasoning responses no longer hang indefinitely. (#61197, #61534) Thanks @balajisiva.</li>
<li>Heartbeat/scheduling: spread interval heartbeats across stable per-agent phases derived from gateway identity, so provider traffic is distributed more uniformly across the configured interval instead of clustering around startup-relative times. (#64560) Thanks @odysseus0.</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.10/OpenClaw-2026.4.10.zip" length="47259509" type="application/octet-stream" sparkle:edSignature="XY9FHxx09r2O9rlFs3t5UV9Zk2rGXSpWw5InazJhb661kgp6OKiOrrNTV631b2StWze5tnSEPXakkOCXq7O6DQ=="/>
</item>
</channel>
</rss>

View File

@@ -65,8 +65,8 @@ android {
applicationId = "ai.openclaw.app"
minSdk = 31
targetSdk = 36
versionCode = 2026042000
versionName = "2026.4.20"
versionCode = 2026042200
versionName = "2026.4.22"
ndk {
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
@@ -182,13 +182,13 @@ ktlint {
}
dependencies {
val composeBom = platform("androidx.compose:compose-bom:2026.02.00")
val composeBom = platform("androidx.compose:compose-bom:2026.03.01")
implementation(composeBom)
androidTestImplementation(composeBom)
implementation("androidx.core:core-ktx:1.17.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0")
implementation("androidx.activity:activity-compose:1.12.2")
implementation("androidx.activity:activity-compose:1.13.0")
implementation("androidx.webkit:webkit:1.15.0")
implementation("androidx.compose.ui:ui")
@@ -204,17 +204,17 @@ dependencies {
implementation("com.google.android.material:material:1.13.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0")
implementation("androidx.security:security-crypto:1.1.0")
implementation("androidx.exifinterface:exifinterface:1.4.2")
implementation("com.squareup.okhttp3:okhttp:5.3.2")
implementation("org.bouncycastle:bcprov-jdk18on:1.83")
implementation("org.commonmark:commonmark:0.27.1")
implementation("org.commonmark:commonmark-ext-autolink:0.27.1")
implementation("org.commonmark:commonmark-ext-gfm-strikethrough:0.27.1")
implementation("org.commonmark:commonmark-ext-gfm-tables:0.27.1")
implementation("org.commonmark:commonmark-ext-task-list-items:0.27.1")
implementation("org.bouncycastle:bcprov-jdk18on:1.84")
implementation("org.commonmark:commonmark:0.28.0")
implementation("org.commonmark:commonmark-ext-autolink:0.28.0")
implementation("org.commonmark:commonmark-ext-gfm-strikethrough:0.28.0")
implementation("org.commonmark:commonmark-ext-gfm-tables:0.28.0")
implementation("org.commonmark:commonmark-ext-task-list-items:0.28.0")
// CameraX (for node.invoke camera.* parity)
implementation("androidx.camera:camera-core:1.5.2")
@@ -228,11 +228,11 @@ dependencies {
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")
testImplementation("io.kotest:kotest-runner-junit5-jvm:6.1.3")
testImplementation("io.kotest:kotest-assertions-core-jvm:6.1.3")
testImplementation("io.kotest:kotest-runner-junit5-jvm:6.1.11")
testImplementation("io.kotest:kotest-assertions-core-jvm:6.1.11")
testImplementation("com.squareup.okhttp3:mockwebserver:5.3.2")
testImplementation("org.robolectric:robolectric:4.16.1")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:6.0.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:6.0.3")
}
tasks.withType<Test>().configureEach {

View File

@@ -403,7 +403,7 @@ class ChatController(
val ts = payload["ts"].asLongOrNull() ?: System.currentTimeMillis()
if (phase == "start") {
val args = data?.get("args").asObjectOrNull()
val args = data.get("args").asObjectOrNull()
pendingToolCallsById[toolCallId] =
ChatPendingToolCall(
toolCallId = toolCallId,

View File

@@ -468,7 +468,7 @@ class GatewayDiscovery(
for (r in records) {
val strings: List<String> =
try {
r.strings.mapNotNull { it as? String }
r.strings
} catch (_: Throwable) {
emptyList()
}

View File

@@ -1240,7 +1240,7 @@ private fun queryInstalledApps(
.mapNotNull { packageName ->
runCatching {
val appInfo = packageManager.getApplicationInfo(packageName, 0)
val label = packageManager.getApplicationLabel(appInfo)?.toString()?.trim().orEmpty()
val label = packageManager.getApplicationLabel(appInfo).toString().trim()
InstalledApp(
label = if (label.isEmpty()) packageName else label,
packageName = packageName,

View File

@@ -555,7 +555,7 @@ private fun InlineBase64Image(base64: String, mimeType: String?) {
if (image != null) {
Image(
bitmap = image!!,
bitmap = image,
contentDescription = mimeType ?: "image",
contentScale = ContentScale.Fit,
modifier = Modifier.fillMaxWidth(),

View File

@@ -246,7 +246,7 @@ private fun ChatBase64Image(base64: String, mimeType: String?) {
modifier = Modifier.fillMaxWidth(),
) {
Image(
bitmap = image!!,
bitmap = image,
contentDescription = mimeType ?: "attachment",
contentScale = ContentScale.Fit,
modifier = Modifier.fillMaxWidth(),

View File

@@ -40,6 +40,6 @@ ktlint {
dependencies {
implementation("androidx.benchmark:benchmark-macro-junit4:1.4.1")
implementation("androidx.test.ext:junit:1.2.1")
implementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha06")
implementation("androidx.test.ext:junit:1.3.0")
implementation("androidx.test.uiautomator:uiautomator:2.4.0-beta02")
}

View File

@@ -1,7 +1,7 @@
plugins {
id("com.android.application") version "9.1.0" apply false
id("com.android.test") version "9.1.0" apply false
id("org.jlleitschuh.gradle.ktlint") version "14.0.1" apply false
id("org.jetbrains.kotlin.plugin.compose") version "2.2.21" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "2.2.21" apply false
id("org.jlleitschuh.gradle.ktlint") version "14.2.0" apply false
id("org.jetbrains.kotlin.plugin.compose") version "2.3.20" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "2.3.20" apply false
}

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

17
apps/android/gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -112,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -170,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -200,18 +200,17 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m" "--enable-native-access=ALL-UNNAMED"'
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.

View File

@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -34,7 +36,7 @@ set APP_HOME=%DIRNAME%
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" "--enable-native-access=ALL-UNNAMED"
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -68,11 +70,10 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -1,5 +1,13 @@
# OpenClaw iOS Changelog
## 2026.4.22 - 2026-04-22
Maintenance update for the current OpenClaw development release.
## 2026.4.21 - 2026-04-21
Maintenance update for the current OpenClaw development release.
## 2026.4.20 - 2026-04-20
Maintenance update for the current OpenClaw release.

View File

@@ -2,8 +2,8 @@
// Source of truth: apps/ios/version.json
// Generated by scripts/ios-sync-versioning.ts.
OPENCLAW_IOS_VERSION = 2026.4.20
OPENCLAW_MARKETING_VERSION = 2026.4.20
OPENCLAW_IOS_VERSION = 2026.4.22
OPENCLAW_MARKETING_VERSION = 2026.4.22
OPENCLAW_BUILD_VERSION = 1
#include? "../build/Version.xcconfig"

View File

@@ -1 +1 @@
Maintenance update for the current OpenClaw release.
Maintenance update for the current OpenClaw development release.

View File

@@ -1,3 +1,3 @@
{
"version": "2026.4.20"
"version": "2026.4.22"
}

View File

@@ -0,0 +1,141 @@
{
"originHash" : "6b8aa02e612c43e309033a83de5f83b88d9c4267f124d1e062f66385dbbaa7ec",
"pins" : [
{
"identity" : "eventsource",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mattt/EventSource.git",
"state" : {
"revision" : "a3a85a85214caf642abaa96ae664e4c772a59f6e",
"version" : "1.4.1"
}
},
{
"identity" : "mlx-audio-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Blaizzy/mlx-audio-swift",
"state" : {
"revision" : "fcbd04daa1bfebe881932f630af2ba6ce9af3274",
"version" : "0.1.2"
}
},
{
"identity" : "mlx-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ml-explore/mlx-swift.git",
"state" : {
"revision" : "61b9e011e09a62b489f6bd647958f1555bdf2896",
"version" : "0.31.3"
}
},
{
"identity" : "mlx-swift-lm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ml-explore/mlx-swift-lm.git",
"state" : {
"revision" : "25b00d4e22e61ec9c41efda47990cd2084ec87ff",
"version" : "2.31.3"
}
},
{
"identity" : "swift-asn1",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-asn1.git",
"state" : {
"revision" : "eb50cbd14606a9161cbc5d452f18797c90ef0bab",
"version" : "1.7.0"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7",
"version" : "1.3.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "6675bc0ff86e61436e615df6fc5174e043e57924",
"version" : "1.4.1"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "476538ccb827f2dd18efc5de754cc87d77127a47",
"version" : "4.4.0"
}
},
{
"identity" : "swift-huggingface",
"kind" : "remoteSourceControl",
"location" : "https://github.com/huggingface/swift-huggingface.git",
"state" : {
"revision" : "b721959445b617d0bf03910b2b4aced345fd93bf",
"version" : "0.9.0"
}
},
{
"identity" : "swift-jinja",
"kind" : "remoteSourceControl",
"location" : "https://github.com/huggingface/swift-jinja.git",
"state" : {
"revision" : "0aeefadec459ce8e11a333769950fb86183aca43",
"version" : "2.3.5"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "cd6710454f25733900e133c6caf5188952763c36",
"version" : "2.98.0"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics",
"state" : {
"revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2",
"version" : "1.1.1"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "7c6ad0fc39d0763e0b699210e4124afd5041c5df",
"version" : "1.6.4"
}
},
{
"identity" : "swift-transformers",
"kind" : "remoteSourceControl",
"location" : "https://github.com/huggingface/swift-transformers.git",
"state" : {
"revision" : "58c4bc11963a140358d791f678a60a2745a23146",
"version" : "1.2.1"
}
},
{
"identity" : "yyjson",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ibireme/yyjson.git",
"state" : {
"revision" : "8b4a38dc994a110abaec8a400615567bd996105f",
"version" : "0.12.0"
}
}
],
"version" : 3
}

View File

@@ -0,0 +1,27 @@
// swift-tools-version: 6.2
// Isolated MLX TTS helper package. Keep this out of apps/macos/Package.swift so
// normal macOS app tests do not compile the full MLX audio stack.
import PackageDescription
let package = Package(
name: "OpenClawMLXTTS",
platforms: [
.macOS(.v15),
],
products: [
.executable(name: "openclaw-mlx-tts", targets: ["OpenClawMLXTTSHelper"]),
],
dependencies: [
.package(url: "https://github.com/Blaizzy/mlx-audio-swift", exact: "0.1.2"),
],
targets: [
.executableTarget(
name: "OpenClawMLXTTSHelper",
dependencies: [
.product(name: "MLXAudioTTS", package: "mlx-audio-swift"),
],
swiftSettings: [
.enableUpcomingFeature("StrictConcurrency"),
]),
])

View File

@@ -0,0 +1,182 @@
import Foundation
import MLXAudioTTS
// swiftformat:disable wrap wrapMultilineStatementBraces trailingCommas redundantSelf extensionAccessControl
@main
enum OpenClawMLXTTSHelper {
static func main() async {
do {
let options = try Options.parse(CommandLine.arguments.dropFirst())
let data = try await synthesize(options)
try data.write(to: options.outputURL, options: [.atomic])
} catch {
FileHandle.standardError.write(Data("openclaw-mlx-tts: \(error)\n".utf8))
exit(1)
}
}
private static func synthesize(_ options: Options) async throws -> Data {
let model = try await TTS.loadModel(modelRepo: options.modelRepo)
let audio = try await UncheckedSpeechModel(raw: model).generateAudio(
text: options.text,
voice: options.voice,
language: options.language)
return makeWavData(samples: audio, sampleRate: Double(model.sampleRate))
}
private struct Options {
let text: String
let modelRepo: String
let outputURL: URL
let language: String?
let voice: String?
static func parse(_ rawArguments: ArraySlice<String>) throws -> Options {
var text: String?
var modelRepo = "mlx-community/Soprano-80M-bf16"
var outputPath: String?
var language: String?
var voice: String?
var iterator = rawArguments.makeIterator()
while let argument = iterator.next() {
switch argument {
case "--text", "-t":
text = try nextValue(&iterator, argument)
case "--model":
modelRepo = try nextValue(&iterator, argument)
case "--output", "-o":
outputPath = try nextValue(&iterator, argument)
case "--language":
language = try nextValue(&iterator, argument)
case "--voice", "-v":
voice = try nextValue(&iterator, argument)
case "--help", "-h":
throw Usage.requested
default:
if text == nil, !argument.hasPrefix("-") {
text = argument
} else {
throw Usage.invalid("unknown option \(argument)")
}
}
}
guard let text = text?.trimmingCharacters(in: .whitespacesAndNewlines), !text.isEmpty else {
throw Usage.invalid("missing --text")
}
guard let outputPath, !outputPath.isEmpty else {
throw Usage.invalid("missing --output")
}
return Options(
text: text,
modelRepo: modelRepo,
outputURL: URL(fileURLWithPath: outputPath),
language: language?.nilIfBlank,
voice: voice?.nilIfBlank)
}
private static func nextValue(
_ iterator: inout ArraySlice<String>.Iterator,
_ option: String) throws -> String
{
guard let value = iterator.next(), !value.isEmpty else {
throw Usage.invalid("missing value for \(option)")
}
return value
}
}
private enum Usage: Error, CustomStringConvertible {
case requested
case invalid(String)
var description: String {
switch self {
case .requested:
"usage: openclaw-mlx-tts --text <text> --output <wav> [--model <hf-repo>] [--language <id>] [--voice <name>]"
case let .invalid(message):
"\(message)\nusage: openclaw-mlx-tts --text <text> --output <wav> [--model <hf-repo>] [--language <id>] [--voice <name>]"
}
}
}
private static func makeWavData(samples: [Float], sampleRate: Double) -> Data {
let channels: UInt16 = 1
let bitsPerSample: UInt16 = 16
let blockAlign = channels * (bitsPerSample / 8)
let sampleRateInt = UInt32(sampleRate.rounded())
let byteRate = sampleRateInt * UInt32(blockAlign)
let dataSize = UInt32(samples.count) * UInt32(blockAlign)
var data = Data(capacity: Int(44 + dataSize))
data.append(contentsOf: [0x52, 0x49, 0x46, 0x46]) // RIFF
data.appendLEUInt32(36 + dataSize)
data.append(contentsOf: [0x57, 0x41, 0x56, 0x45]) // WAVE
data.append(contentsOf: [0x66, 0x6D, 0x74, 0x20]) // fmt
data.appendLEUInt32(16)
data.appendLEUInt16(1)
data.appendLEUInt16(channels)
data.appendLEUInt32(sampleRateInt)
data.appendLEUInt32(byteRate)
data.appendLEUInt16(blockAlign)
data.appendLEUInt16(bitsPerSample)
data.append(contentsOf: [0x64, 0x61, 0x74, 0x61]) // data
data.appendLEUInt32(dataSize)
for sample in samples {
let clamped = max(-1.0, min(1.0, sample))
let scaled = Int16((clamped * Float(Int16.max)).rounded())
data.appendLEInt16(scaled)
}
return data
}
}
private struct UncheckedSpeechModel {
let raw: any SpeechGenerationModel
func generateAudio(
text: String,
voice: String?,
language: String?) async throws -> [Float] {
let generatedAudio = try await raw.generate(
text: text,
voice: voice,
refAudio: nil,
refText: nil,
language: language)
return generatedAudio.asArray(Float.self)
}
}
extension UncheckedSpeechModel: @unchecked Sendable {}
private extension String {
var nilIfBlank: String? {
let trimmed = self.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
}
}
private extension Data {
mutating func appendLEUInt16(_ value: UInt16) {
var littleEndian = value.littleEndian
Swift.withUnsafeBytes(of: &littleEndian) { append(contentsOf: $0) }
}
mutating func appendLEUInt32(_ value: UInt32) {
var littleEndian = value.littleEndian
Swift.withUnsafeBytes(of: &littleEndian) { append(contentsOf: $0) }
}
mutating func appendLEInt16(_ value: Int16) {
var littleEndian = value.littleEndian
Swift.withUnsafeBytes(of: &littleEndian) { append(contentsOf: $0) }
}
}
// swiftformat:enable wrap wrapMultilineStatementBraces trailingCommas redundantSelf extensionAccessControl

View File

@@ -1,5 +1,5 @@
{
"originHash" : "31972864afdac74537794e1a3b7bd22484c09ec1be8e3624fb9ea582e9222ad9",
"originHash" : "7a8088405ec5e396c14d737c110ff5651ff25dabcd437a0fee92e57018c5360a",
"pins" : [
{
"identity" : "axorcist",
@@ -28,49 +28,13 @@
"version" : "0.1.0"
}
},
{
"identity" : "eventsource",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mattt/EventSource.git",
"state" : {
"revision" : "a3a85a85214caf642abaa96ae664e4c772a59f6e",
"version" : "1.4.1"
}
},
{
"identity" : "menubarextraaccess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/orchetect/MenuBarExtraAccess",
"state" : {
"revision" : "707dff6f55217b3ef5b6be84ced3e83511d4df5c",
"version" : "1.2.2"
}
},
{
"identity" : "mlx-audio-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Blaizzy/mlx-audio-swift",
"state" : {
"revision" : "fcbd04daa1bfebe881932f630af2ba6ce9af3274",
"version" : "0.1.2"
}
},
{
"identity" : "mlx-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ml-explore/mlx-swift.git",
"state" : {
"revision" : "61b9e011e09a62b489f6bd647958f1555bdf2896",
"version" : "0.31.3"
}
},
{
"identity" : "mlx-swift-lm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ml-explore/mlx-swift-lm.git",
"state" : {
"revision" : "25b00d4e22e61ec9c41efda47990cd2084ec87ff",
"version" : "2.31.3"
"revision" : "33bb0e4b1e407feac791e047dcaaf9c69b25fd26",
"version" : "1.3.0"
}
},
{
@@ -87,8 +51,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/sparkle-project/Sparkle",
"state" : {
"revision" : "21d8df80440b1ca3b65fa82e40782f1e5a9e6ba2",
"version" : "2.9.0"
"revision" : "066e75a8b3e99962685d6a90cdd5293ebffd9261",
"version" : "2.9.1"
}
},
{
@@ -100,33 +64,6 @@
"version" : "1.2.1"
}
},
{
"identity" : "swift-asn1",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-asn1.git",
"state" : {
"revision" : "9f542610331815e29cc3821d3b6f488db8715517",
"version" : "1.6.0"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7",
"version" : "1.3.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "6675bc0ff86e61436e615df6fc5174e043e57924",
"version" : "1.4.1"
}
},
{
"identity" : "swift-concurrency-extras",
"kind" : "remoteSourceControl",
@@ -136,49 +73,13 @@
"version" : "1.3.2"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "bb4ba815dab96d4edc1e0b86d7b9acf9ff973a84",
"version" : "4.3.1"
}
},
{
"identity" : "swift-huggingface",
"kind" : "remoteSourceControl",
"location" : "https://github.com/huggingface/swift-huggingface.git",
"state" : {
"revision" : "b721959445b617d0bf03910b2b4aced345fd93bf",
"version" : "0.9.0"
}
},
{
"identity" : "swift-jinja",
"kind" : "remoteSourceControl",
"location" : "https://github.com/huggingface/swift-jinja.git",
"state" : {
"revision" : "0aeefadec459ce8e11a333769950fb86183aca43",
"version" : "2.3.5"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "bbd81b6725ae874c69e9b8c8804d462356b55523",
"version" : "1.10.1"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "558f24a4647193b5a0e2104031b71c55d31ff83a",
"version" : "2.97.1"
"revision" : "5073617dac96330a486245e4c0179cb0a6fd2256",
"version" : "1.12.0"
}
},
{
@@ -208,15 +109,6 @@
"version" : "1.6.4"
}
},
{
"identity" : "swift-transformers",
"kind" : "remoteSourceControl",
"location" : "https://github.com/huggingface/swift-transformers.git",
"state" : {
"revision" : "58c4bc11963a140358d791f678a60a2745a23146",
"version" : "1.2.1"
}
},
{
"identity" : "swiftui-math",
"kind" : "remoteSourceControl",
@@ -234,15 +126,6 @@
"revision" : "5b06b811c0f5313b6b84bbef98c635a630638c38",
"version" : "0.3.1"
}
},
{
"identity" : "yyjson",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ibireme/yyjson.git",
"state" : {
"revision" : "8b4a38dc994a110abaec8a400615567bd996105f",
"version" : "0.12.0"
}
}
],
"version" : 3

View File

@@ -15,12 +15,11 @@ let package = Package(
.executable(name: "openclaw-mac", targets: ["OpenClawMacCLI"]),
],
dependencies: [
.package(url: "https://github.com/orchetect/MenuBarExtraAccess", exact: "1.2.2"),
.package(url: "https://github.com/orchetect/MenuBarExtraAccess", exact: "1.3.0"),
.package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.4.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.10.1"),
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.9.0"),
.package(url: "https://github.com/steipete/Peekaboo.git", branch: "main"),
.package(url: "https://github.com/Blaizzy/mlx-audio-swift", exact: "0.1.2"),
.package(path: "../shared/OpenClawKit"),
.package(path: "../../Swabble"),
],
@@ -55,7 +54,6 @@ let package = Package(
.product(name: "Sparkle", package: "Sparkle"),
.product(name: "PeekabooBridge", package: "Peekaboo"),
.product(name: "PeekabooAutomationKit", package: "Peekaboo"),
.product(name: "MLXAudioTTS", package: "mlx-audio-swift"),
],
exclude: [
"Resources/Info.plist",

View File

@@ -51,7 +51,6 @@ struct OpenClawApp: App {
animationsEnabled: self.state.iconAnimationsEnabled && !self.isGatewaySleeping,
iconState: self.effectiveIconState)
}
.menuBarExtraStyle(.menu)
.menuBarExtraAccess(isPresented: self.$isMenuPresented) { item in
self.statusItem = item
MenuSessionsInjector.shared.install(into: item)
@@ -59,6 +58,7 @@ struct OpenClawApp: App {
self.installStatusItemMouseHandler(for: item)
self.updateHoverHUDSuppression()
}
.menuBarExtraStyle(.menu)
.onChange(of: self.state.isPaused) { _, paused in
self.applyStatusItemAppearance(paused: paused, sleeping: self.isGatewaySleeping)
if self.state.connectionMode == .local {

View File

@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.4.20</string>
<string>2026.4.22</string>
<key>CFBundleVersion</key>
<string>2026042000</string>
<string>2026042200</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@@ -1,5 +1,4 @@
import Foundation
import MLXAudioTTS
import OSLog
// swiftformat:disable wrap wrapMultilineStatementBraces trailingCommas redundantSelf extensionAccessControl
@@ -18,13 +17,14 @@ final class TalkMLXSpeechSynthesizer {
private let logger = Logger(subsystem: "ai.openclaw", category: "talk.mlx")
private var currentToken = UUID()
private var modelRepo: String?
private var model: (any SpeechGenerationModel)?
private var currentProcess: Process?
private init() {}
func stop() {
self.currentToken = UUID()
self.currentProcess?.terminate()
self.currentProcess = nil
}
func synthesize(
@@ -39,59 +39,93 @@ final class TalkMLXSpeechSynthesizer {
let token = UUID()
self.currentToken = token
let tempDir = FileManager.default.temporaryDirectory
.appendingPathComponent("openclaw-mlx-tts-\(token.uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: tempDir) }
let outputURL = tempDir.appendingPathComponent("speech.wav")
let invocation = Self.helperInvocation()
let resolvedRepo = Self.resolvedModelRepo(modelRepo)
let rawModel = try await self.loadModel(
modelRepo: resolvedRepo,
token: token)
let model = UncheckedSpeechModel(raw: rawModel)
var arguments = invocation.argumentPrefix
arguments += [
"--text", trimmed,
"--model", resolvedRepo,
"--output", outputURL.path,
]
if let language = language?.trimmingCharacters(in: .whitespacesAndNewlines), !language.isEmpty {
arguments += ["--language", language]
}
if let voicePreset = voicePreset?.trimmingCharacters(in: .whitespacesAndNewlines), !voicePreset.isEmpty {
arguments += ["--voice", voicePreset]
}
self.logger.info("talk mlx helper start modelRepo=\(resolvedRepo, privacy: .public)")
let process = Process()
process.executableURL = invocation.executableURL
process.arguments = arguments
let stderr = Pipe()
process.standardError = stderr
process.standardOutput = Pipe()
self.currentProcess = process
let status: Int32
do {
status = try await Self.run(process)
} catch {
self.currentProcess = nil
self.logger.error("talk mlx helper launch failed: \(error.localizedDescription, privacy: .public)")
throw SynthesizeError.modelLoadFailed(invocation.displayName)
}
self.currentProcess = nil
guard self.currentToken == token else {
throw SynthesizeError.canceled
}
let audioData: Data
do {
let audio = try await model.generateAudio(
text: trimmed,
voice: voicePreset,
language: language)
audioData = Self.makeWavData(
samples: audio,
sampleRate: Double(model.sampleRateValue()))
} catch {
guard status == 0 else {
let errorText = Self.readPipe(stderr)
self.logger.error(
"talk mlx generation failed: \(error.localizedDescription, privacy: .public)")
"talk mlx helper failed status=\(status, privacy: .public): \(errorText, privacy: .public)")
throw SynthesizeError.audioGenerationFailed
}
guard self.currentToken == token else {
throw SynthesizeError.canceled
do {
return try Data(contentsOf: outputURL)
} catch {
self.logger.error("talk mlx helper output missing: \(error.localizedDescription, privacy: .public)")
throw SynthesizeError.audioGenerationFailed
}
return audioData
}
private func loadModel(
modelRepo: String,
token: UUID) async throws -> any SpeechGenerationModel {
if let model = self.model, self.modelRepo == modelRepo {
return model
private struct HelperInvocation {
let executableURL: URL
let argumentPrefix: [String]
let displayName: String
}
private static func helperInvocation() -> HelperInvocation {
let fileManager = FileManager.default
if let override = ProcessInfo.processInfo.environment["OPENCLAW_MLX_TTS_BIN"], !override.isEmpty {
return HelperInvocation(
executableURL: URL(fileURLWithPath: override),
argumentPrefix: [],
displayName: override)
}
self.logger.info("talk mlx loading modelRepo=\(modelRepo, privacy: .public)")
do {
let model = try await TTS.loadModel(modelRepo: modelRepo)
guard self.currentToken == token else {
throw SynthesizeError.canceled
if let executableDir = Bundle.main.executableURL?.deletingLastPathComponent() {
let bundled = executableDir.appendingPathComponent("openclaw-mlx-tts")
if fileManager.isExecutableFile(atPath: bundled.path) {
return HelperInvocation(
executableURL: bundled,
argumentPrefix: [],
displayName: bundled.path)
}
self.model = model
self.modelRepo = modelRepo
return model
} catch is CancellationError {
throw SynthesizeError.canceled
} catch {
self.logger.error(
"talk mlx load failed: \(error.localizedDescription, privacy: .public)")
throw SynthesizeError.modelLoadFailed(modelRepo)
}
return HelperInvocation(
executableURL: URL(fileURLWithPath: "/usr/bin/env"),
argumentPrefix: ["openclaw-mlx-tts"],
displayName: "openclaw-mlx-tts")
}
private static func resolvedModelRepo(_ modelRepo: String?) -> String {
@@ -99,80 +133,26 @@ final class TalkMLXSpeechSynthesizer {
return trimmed.isEmpty ? Self.defaultModelRepo : trimmed
}
private static func makeWavData(samples: [Float], sampleRate: Double) -> Data {
let channels: UInt16 = 1
let bitsPerSample: UInt16 = 16
let blockAlign = channels * (bitsPerSample / 8)
let sampleRateInt = UInt32(sampleRate.rounded())
let byteRate = sampleRateInt * UInt32(blockAlign)
let dataSize = UInt32(samples.count) * UInt32(blockAlign)
var data = Data(capacity: Int(44 + dataSize))
data.append(contentsOf: [0x52, 0x49, 0x46, 0x46]) // RIFF
data.appendLEUInt32(36 + dataSize)
data.append(contentsOf: [0x57, 0x41, 0x56, 0x45]) // WAVE
data.append(contentsOf: [0x66, 0x6D, 0x74, 0x20]) // fmt
data.appendLEUInt32(16)
data.appendLEUInt16(1)
data.appendLEUInt16(channels)
data.appendLEUInt32(sampleRateInt)
data.appendLEUInt32(byteRate)
data.appendLEUInt16(blockAlign)
data.appendLEUInt16(bitsPerSample)
data.append(contentsOf: [0x64, 0x61, 0x74, 0x61]) // data
data.appendLEUInt32(dataSize)
for sample in samples {
let clamped = max(-1.0, min(1.0, sample))
let scaled = Int16((clamped * Float(Int16.max)).rounded())
data.appendLEInt16(scaled)
private static func run(_ process: Process) async throws -> Int32 {
try await withCheckedThrowingContinuation { continuation in
process.terminationHandler = { process in
continuation.resume(returning: process.terminationStatus)
}
do {
try process.run()
} catch {
continuation.resume(throwing: error)
}
}
return data
}
private static func readPipe(_ pipe: Pipe) -> String {
let data = (try? pipe.fileHandleForReading.readToEnd()) ?? Data()
let text = String(data: data, encoding: .utf8) ?? ""
return text.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
extension TalkMLXSpeechSynthesizer: @unchecked Sendable {}
private struct UncheckedSpeechModel {
let raw: any SpeechGenerationModel
func sampleRateValue() -> Int {
raw.sampleRate
}
func generateAudio(
text: String,
voice: String?,
language: String?) async throws -> [Float] {
let generatedAudio = try await raw.generate(
text: text,
voice: voice,
refAudio: nil,
refText: nil,
language: language)
return generatedAudio.asArray(Float.self)
}
}
extension UncheckedSpeechModel: @unchecked Sendable {}
extension Data {
fileprivate mutating func appendLEUInt16(_ value: UInt16) {
var littleEndian = value.littleEndian
Swift.withUnsafeBytes(of: &littleEndian) { append(contentsOf: $0) }
}
fileprivate mutating func appendLEUInt32(_ value: UInt32) {
var littleEndian = value.littleEndian
Swift.withUnsafeBytes(of: &littleEndian) { append(contentsOf: $0) }
}
fileprivate mutating func appendLEInt16(_ value: Int16) {
var littleEndian = value.littleEndian
Swift.withUnsafeBytes(of: &littleEndian) { append(contentsOf: $0) }
}
}
// swiftformat:enable wrap wrapMultilineStatementBraces trailingCommas redundantSelf extensionAccessControl

View File

@@ -590,6 +590,7 @@ public struct AgentParams: Codable, Sendable {
public let timeout: Int?
public let besteffortdeliver: Bool?
public let lane: String?
public let cleanupbundlemcponrunend: Bool?
public let extrasystemprompt: String?
public let bootstrapcontextmode: AnyCodable?
public let bootstrapcontextrunkind: AnyCodable?
@@ -621,6 +622,7 @@ public struct AgentParams: Codable, Sendable {
timeout: Int?,
besteffortdeliver: Bool?,
lane: String?,
cleanupbundlemcponrunend: Bool?,
extrasystemprompt: String?,
bootstrapcontextmode: AnyCodable?,
bootstrapcontextrunkind: AnyCodable?,
@@ -651,6 +653,7 @@ public struct AgentParams: Codable, Sendable {
self.timeout = timeout
self.besteffortdeliver = besteffortdeliver
self.lane = lane
self.cleanupbundlemcponrunend = cleanupbundlemcponrunend
self.extrasystemprompt = extrasystemprompt
self.bootstrapcontextmode = bootstrapcontextmode
self.bootstrapcontextrunkind = bootstrapcontextrunkind
@@ -683,6 +686,7 @@ public struct AgentParams: Codable, Sendable {
case timeout
case besteffortdeliver = "bestEffortDeliver"
case lane
case cleanupbundlemcponrunend = "cleanupBundleMcpOnRunEnd"
case extrasystemprompt = "extraSystemPrompt"
case bootstrapcontextmode = "bootstrapContextMode"
case bootstrapcontextrunkind = "bootstrapContextRunKind"

View File

@@ -382,223 +382,6 @@
}
}
},
"whatsapp_login": {
"emoji": "🟢",
"title": "WhatsApp Login",
"actions": {
"start": {
"label": "start"
},
"wait": {
"label": "wait"
}
}
},
"discord": {
"emoji": "💬",
"title": "Discord",
"actions": {
"react": {
"label": "react",
"detailKeys": [
"channelId",
"messageId",
"emoji"
]
},
"reactions": {
"label": "reactions",
"detailKeys": [
"channelId",
"messageId"
]
},
"sticker": {
"label": "sticker",
"detailKeys": [
"to",
"stickerIds"
]
},
"poll": {
"label": "poll",
"detailKeys": [
"question",
"to"
]
},
"permissions": {
"label": "permissions",
"detailKeys": [
"channelId"
]
},
"readMessages": {
"label": "read messages",
"detailKeys": [
"channelId",
"limit"
]
},
"sendMessage": {
"label": "send",
"detailKeys": [
"to",
"content"
]
},
"editMessage": {
"label": "edit",
"detailKeys": [
"channelId",
"messageId"
]
},
"deleteMessage": {
"label": "delete",
"detailKeys": [
"channelId",
"messageId"
]
},
"threadCreate": {
"label": "thread create",
"detailKeys": [
"channelId",
"name"
]
},
"threadList": {
"label": "thread list",
"detailKeys": [
"guildId",
"channelId"
]
},
"threadReply": {
"label": "thread reply",
"detailKeys": [
"channelId",
"content"
]
},
"pinMessage": {
"label": "pin",
"detailKeys": [
"channelId",
"messageId"
]
},
"unpinMessage": {
"label": "unpin",
"detailKeys": [
"channelId",
"messageId"
]
},
"listPins": {
"label": "list pins",
"detailKeys": [
"channelId"
]
},
"searchMessages": {
"label": "search",
"detailKeys": [
"guildId",
"content"
]
},
"memberInfo": {
"label": "member",
"detailKeys": [
"guildId",
"userId"
]
},
"roleInfo": {
"label": "roles",
"detailKeys": [
"guildId"
]
},
"emojiList": {
"label": "emoji list",
"detailKeys": [
"guildId"
]
},
"roleAdd": {
"label": "role add",
"detailKeys": [
"guildId",
"userId",
"roleId"
]
},
"roleRemove": {
"label": "role remove",
"detailKeys": [
"guildId",
"userId",
"roleId"
]
},
"channelInfo": {
"label": "channel",
"detailKeys": [
"channelId"
]
},
"channelList": {
"label": "channels",
"detailKeys": [
"guildId"
]
},
"voiceStatus": {
"label": "voice",
"detailKeys": [
"guildId",
"userId"
]
},
"eventList": {
"label": "events",
"detailKeys": [
"guildId"
]
},
"eventCreate": {
"label": "event create",
"detailKeys": [
"guildId",
"name"
]
},
"timeout": {
"label": "timeout",
"detailKeys": [
"guildId",
"userId"
]
},
"kick": {
"label": "kick",
"detailKeys": [
"guildId",
"userId"
]
},
"ban": {
"label": "ban",
"detailKeys": [
"guildId",
"userId"
]
}
}
},
"exec": {
"emoji": "🛠️",
"title": "Exec",
@@ -629,8 +412,13 @@
"title": "Sessions",
"detailKeys": [
"kinds",
"label",
"agentId",
"search",
"limit",
"activeMinutes",
"includeDerivedTitles",
"includeLastMessage",
"messageLimit"
]
},

View File

@@ -590,6 +590,7 @@ public struct AgentParams: Codable, Sendable {
public let timeout: Int?
public let besteffortdeliver: Bool?
public let lane: String?
public let cleanupbundlemcponrunend: Bool?
public let extrasystemprompt: String?
public let bootstrapcontextmode: AnyCodable?
public let bootstrapcontextrunkind: AnyCodable?
@@ -621,6 +622,7 @@ public struct AgentParams: Codable, Sendable {
timeout: Int?,
besteffortdeliver: Bool?,
lane: String?,
cleanupbundlemcponrunend: Bool?,
extrasystemprompt: String?,
bootstrapcontextmode: AnyCodable?,
bootstrapcontextrunkind: AnyCodable?,
@@ -651,6 +653,7 @@ public struct AgentParams: Codable, Sendable {
self.timeout = timeout
self.besteffortdeliver = besteffortdeliver
self.lane = lane
self.cleanupbundlemcponrunend = cleanupbundlemcponrunend
self.extrasystemprompt = extrasystemprompt
self.bootstrapcontextmode = bootstrapcontextmode
self.bootstrapcontextrunkind = bootstrapcontextrunkind
@@ -683,6 +686,7 @@ public struct AgentParams: Codable, Sendable {
case timeout
case besteffortdeliver = "bestEffortDeliver"
case lane
case cleanupbundlemcponrunend = "cleanupBundleMcpOnRunEnd"
case extrasystemprompt = "extraSystemPrompt"
case bootstrapcontextmode = "bootstrapContextMode"
case bootstrapcontextrunkind = "bootstrapContextRunKind"

View File

@@ -1,4 +1,4 @@
cc473bcd00e63c3d3f351e4de1ceb390aae88dddce8616929e98a9d94412b1b9 config-baseline.json
7956c319e82d288d496a51cb2ff4485ab72ef4900cb089f99e1df8b9ef3bfb73 config-baseline.core.json
cd467228990cdbdebde2fa87d8b1384b94c149e791f2e67250bf17b13162d4a1 config-baseline.channel.json
17a73724e5082b3aa846c220d38115916fb6003887439e6794510a99fc73f7de config-baseline.plugin.json
b05357fa162ba1f1d4ed192671b758d3905602678ff61148568840c6544d6222 config-baseline.json
a4e167f169db58d71c385a31fa2b980772f9fee963e70dd9553f63536cae5aed config-baseline.core.json
35d132fe176bd2bf9f0e46b29de91baba63ec4db3317cc5b294a982b46d16ba9 config-baseline.channel.json
3703c5345288adb9eee8cda3b592147cf4fed25a7782bed21ca83c88c3ca1cc0 config-baseline.plugin.json

View File

@@ -1,2 +1,2 @@
f135ddc1802b7f8b2d29bf495fd0ac1f497a89bab8164ca8c7c8f18efc010e6e plugin-sdk-api-baseline.json
a47d06095ec5c3701a94888a11e89700d8a8511db46fa3122fb9407e160707b6 plugin-sdk-api-baseline.jsonl
2b7093a57992029cc70126d33544e02eed6c3076a3a6b4ffa6aef7664da0f33d plugin-sdk-api-baseline.json
ea6a2f2326565517b6c42a4d334f615163fb434dbad5e0b8d134c92767714256 plugin-sdk-api-baseline.jsonl

View File

@@ -195,6 +195,10 @@
"source": "Doctor",
"target": "Doctor"
},
{
"source": "Config",
"target": "配置"
},
{
"source": "Memory Wiki",
"target": "Memory Wiki"

View File

@@ -227,7 +227,7 @@ Completion cleanup is also runtime-aware:
- Isolated cron completion best-effort closes tracked browser tabs/processes for the cron session before the run fully tears down.
- Isolated cron delivery waits out descendant subagent follow-up when needed and
suppresses stale parent acknowledgement text instead of announcing it.
- Subagent completion delivery prefers the latest visible assistant text; if that is empty it falls back to sanitized latest tool/toolResult text, and timeout-only tool-call runs can collapse to a short partial-progress summary.
- Subagent completion delivery prefers the latest visible assistant text; if that is empty it falls back to sanitized latest tool/toolResult text, and timeout-only tool-call runs can collapse to a short partial-progress summary. Terminal failed runs announce failure status without replaying captured reply text.
- Cleanup failures do not mask the real task outcome.
### `tasks flow list|show|cancel`

View File

@@ -363,7 +363,7 @@ BlueBubbles supports advanced message actions when enabled in config:
Available actions:
- **react**: Add/remove tapback reactions (`messageId`, `emoji`, `remove`)
- **react**: Add/remove tapback reactions (`messageId`, `emoji`, `remove`). iMessage's native tapback set is `love`, `like`, `dislike`, `laugh`, `emphasize`, and `question`. When an agent picks an emoji outside that set (for example `👀`), the reaction tool falls back to `love` so the tapback still renders instead of failing the whole request. Configured ack reactions still validate strictly and error on unknown values.
- **edit**: Edit a sent message (`messageId`, `text`)
- **unsend**: Unsend a message (`messageId`)
- **reply**: Reply to a specific message (`messageId`, `text`, `to`)
@@ -554,6 +554,10 @@ Prefer `chat_guid` for stable routing:
- Direct handles: `+15555550123`, `user@example.com`
- If a direct handle does not have an existing DM chat, OpenClaw will create one via `POST /api/v1/chat/new`. This requires the BlueBubbles Private API to be enabled.
### iMessage vs SMS routing
When the same handle has both an iMessage and an SMS chat on the Mac (for example a phone number that is iMessage-registered but has also received green-bubble fallbacks), OpenClaw prefers the iMessage chat and never silently downgrades to SMS. To force the SMS chat, use an explicit `sms:` target prefix (for example `sms:+15555550123`). Handles without a matching iMessage chat still send through whatever chat BlueBubbles reports.
## Security
- Webhook requests are authenticated by comparing `guid`/`password` query params or headers against `channels.bluebubbles.password`.

View File

@@ -23,10 +23,14 @@ host configuration.
## Session key shapes (examples)
Direct messages collapse to the agents **main** session:
Direct messages collapse to the agents **main** session by default:
- `agent:<agentId>:<mainKey>` (default: `agent:main:main`)
Even when direct-message conversation history is shared with main, sandbox and
tool policy use a derived per-account direct-chat runtime key for external DMs
so channel-originated messages are not treated like local main-session runs.
Groups and channels remain isolated per channel:
- Groups: `agent:<agentId>:<channel>:group:<id>`

View File

@@ -61,15 +61,18 @@ You will need to create a new application with a bot, add the bot to your server
- `bot`
- `applications.commands`
A **Bot Permissions** section will appear below. Enable:
A **Bot Permissions** section will appear below. Enable at least:
- View Channels
- Send Messages
- Read Message History
- Embed Links
- Attach Files
- Add Reactions (optional)
**General Permissions**
- View Channels
**Text Permissions**
- Send Messages
- Read Message History
- Embed Links
- Attach Files
- Add Reactions (optional)
This is the baseline set for normal text channels. If you plan to post in Discord threads, including forum or media channel workflows that create or continue a thread, also enable **Send Messages in Threads**.
Copy the generated URL at the bottom, paste it into your browser, select your server, and click **Continue** to connect. You should now see your bot in the Discord server.
</Step>
@@ -304,7 +307,7 @@ By default, components are single use. Set `components.reusable=true` to allow b
To restrict who can click a button, set `allowedUsers` on that button (Discord user IDs, tags, or `*`). When configured, unmatched users receive an ephemeral denial.
The `/model` and `/models` slash commands open an interactive model picker with provider and model dropdowns plus a Submit step. The picker reply is ephemeral and only the invoking user can use it.
The `/model` and `/models` slash commands open an interactive model picker with provider and model dropdowns plus a Submit step. Unless `commands.modelsWrite=false`, `/models add` also supports adding a new provider/model entry from chat, and newly added models show up without restarting the gateway. The picker reply is ephemeral and only the invoking user can use it.
File attachments:
@@ -520,13 +523,16 @@ Use `bindings[].match.roles` to route Discord guild members to different agents
Typical baseline permissions:
- View Channels
- Send Messages
- Read Message History
- Embed Links
- Attach Files
- Add Reactions (optional)
**General Permissions**
- View Channels
**Text Permissions**
- Send Messages
- Read Message History
- Embed Links
- Attach Files
- Add Reactions (optional)
This is the baseline set for normal text channels. If you plan to post in Discord threads, including forum or media channel workflows that create or continue a thread, also enable **Send Messages in Threads**.
Avoid `Administrator` unless explicitly needed.
</Accordion>
@@ -593,6 +599,8 @@ Default slash command settings:
- `channels.discord.streamMode` is a legacy alias and is auto-migrated.
- `partial` edits a single preview message as tokens arrive.
- `block` emits draft-sized chunks (use `draftChunk` to tune size and breakpoints).
- Media, error, and explicit-reply finals cancel pending preview edits without flushing a temporary draft before normal delivery.
- `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same draft preview message (default: `true`). Set `false` to keep separate tool/progress messages.
Example:
@@ -1237,7 +1245,7 @@ High-signal Discord fields:
- inbound worker: `inboundWorker.runTimeoutMs`
- reply/history: `replyToMode`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit`
- delivery: `textChunkLimit`, `chunkMode`, `maxLinesPerMessage`
- streaming: `streaming` (legacy alias: `streamMode`), `draftChunk`, `blockStreaming`, `blockStreamingCoalesce`
- streaming: `streaming` (legacy alias: `streamMode`), `streaming.preview.toolProgress`, `draftChunk`, `blockStreaming`, `blockStreamingCoalesce`
- media/retry: `mediaMaxMb`, `retry`
- `mediaMaxMb` caps outbound Discord uploads (default: `100MB`)
- actions: `actions.*`

View File

@@ -408,6 +408,10 @@ The agent system prompt includes a group intro on the first turn of a new group
- List chats: `imsg chats --limit 20`.
- Group replies always go back to the same `chat_id`.
## WhatsApp system prompts
See [WhatsApp](/channels/whatsapp#system-prompts) for the canonical WhatsApp system prompt rules, including group and direct prompt resolution, wildcard behavior, and account override semantics.
## WhatsApp specifics
See [Group messages](/channels/group-messages) for WhatsApp-only behavior (history injection, mention handling details).

View File

@@ -50,7 +50,7 @@ imsg rpc --help
imessage: {
enabled: true,
cliPath: "/usr/local/bin/imsg",
dbPath: "/Users/<you>/Library/Messages/chat.db",
dbPath: "/Users/user/Library/Messages/chat.db",
},
},
}

View File

@@ -205,6 +205,8 @@ The LINE plugin supports sending images, videos, and audio files through the age
- **Videos**: sent with explicit preview and content-type handling.
- **Audio**: sent as LINE audio messages.
Outbound media URLs must be public HTTPS URLs. OpenClaw validates the target hostname before handing the URL to LINE and rejects loopback, link-local, and private-network targets.
Generic media sends fall back to the existing image-only route when a LINE-specific path is not available.
## Troubleshooting

View File

@@ -884,6 +884,12 @@ Per-account override:
Related docs: [Exec approvals](/tools/exec-approvals)
## Slash commands
Matrix slash commands (for example `/new`, `/reset`, `/model`) work directly in DMs. In rooms, OpenClaw also recognizes slash commands that are prefixed with the bot's own Matrix mention, so `@bot:server /new` triggers the command path without needing a custom mention regex. This keeps the bot responsive to room-style `@mention /command` posts that Element and similar clients emit when a user tab-completes the bot before typing the command.
Authorization rules still apply: command senders must satisfy DM or room allowlist/owner policies just like plain messages.
## Multi-account
```json5

View File

@@ -244,6 +244,31 @@ Notes:
- Retries apply to transient failures such as rate limits, 5xx responses, and network or timeout errors.
- 4xx client errors other than `429` are treated as permanent and are not retried.
## Preview streaming
Mattermost streams thinking, tool activity, and partial reply text into a single **draft preview post** that finalizes in place when the final answer is safe to send. The preview updates on the same post id instead of spamming the channel with per-chunk messages. Media/error finals cancel pending preview edits and use normal delivery instead of flushing a throwaway preview post.
Enable via `channels.mattermost.streaming`:
```json5
{
channels: {
mattermost: {
streaming: "partial", // off | partial | block | progress
},
},
}
```
Notes:
- `partial` is the usual choice: one preview post that is edited as the reply grows, then finalized with the complete answer.
- `block` uses append-style draft chunks inside the preview post.
- `progress` shows a status preview while generating and only posts the final answer at completion.
- `off` disables preview streaming.
- If the stream cannot be finalized in place (for example the post was deleted mid-stream), OpenClaw falls back to sending a fresh final post so the reply is never lost.
- See [Streaming](/concepts/streaming#preview-streaming-modes) for the channel-mapping matrix.
## Reactions (message tool)
- Use `message action=react` with `channel=mattermost`.

View File

@@ -9,8 +9,6 @@ title: "Microsoft Teams"
> "Abandon all hope, ye who enter here."
Updated: 2026-03-25
Status: text + DM attachments are supported; channel/group file sending requires `sharePointSiteId` + Graph permissions (see [Sending files in group chats](#sending-files-in-group-chats)). Polls are sent via Adaptive Cards. Message actions expose explicit `upload-file` for file-first sends.
## Bundled plugin
@@ -611,7 +609,7 @@ Teams markdown is more limited than Slack or Discord:
- Basic formatting works: **bold**, _italic_, `code`, links
- Complex markdown (tables, nested lists) may not render correctly
- Adaptive Cards are supported for polls and arbitrary card sends (see below)
- Adaptive Cards are supported for polls and semantic presentation sends (see below)
## Configuration
@@ -783,11 +781,11 @@ 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).
## Adaptive Cards (arbitrary)
## Presentation Cards
Send any Adaptive Card JSON to Teams users or conversations using the `message` tool or CLI.
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.
The `card` parameter accepts an Adaptive Card JSON object. When `card` is provided, the message text is optional.
The `presentation` parameter accepts semantic blocks. When `presentation` is provided, the message text is optional.
**Agent tool:**
@@ -796,10 +794,9 @@ The `card` parameter accepts an Adaptive Card JSON object. When `card` is provid
action: "send",
channel: "msteams",
target: "user:<id>",
card: {
type: "AdaptiveCard",
version: "1.5",
body: [{ type: "TextBlock", text: "Hello!" }],
presentation: {
title: "Hello",
blocks: [{ type: "text", text: "Hello!" }],
},
}
```
@@ -809,10 +806,10 @@ The `card` parameter accepts an Adaptive Card JSON object. When `card` is provid
```bash
openclaw message send --channel msteams \
--target "conversation:19:abc...@thread.tacv2" \
--card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}'
--presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'
```
See [Adaptive Cards documentation](https://adaptivecards.io/) for card schema and examples. For target format details, see [Target formats](#target-formats) below.
For target format details, see [Target formats](#target-formats) below.
## Target formats
@@ -837,9 +834,9 @@ openclaw message send --channel msteams --target "user:John Smith" --message "He
# Send to a group chat or channel
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"
# Send an Adaptive Card to a conversation
# Send a presentation card to a conversation
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \
--card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}'
--presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello"}]}'
```
**Agent tool examples:**
@@ -858,10 +855,9 @@ openclaw message send --channel msteams --target "conversation:19:abc...@thread.
action: "send",
channel: "msteams",
target: "conversation:19:abc...@thread.tacv2",
card: {
type: "AdaptiveCard",
version: "1.5",
body: [{ type: "TextBlock", text: "Hello" }],
presentation: {
title: "Hello",
blocks: [{ type: "text", text: "Hello" }],
},
}
```

View File

@@ -171,16 +171,36 @@ Outbound audio upload/transcode behavior can also be tuned with
Built-in commands intercepted before the AI queue:
| Command | Description |
| -------------- | ------------------------------------ |
| `/bot-ping` | Latency test |
| `/bot-version` | Show the OpenClaw framework version |
| `/bot-help` | List all commands |
| `/bot-upgrade` | Show the QQBot upgrade guide link |
| `/bot-logs` | Export recent gateway logs as a file |
| Command | Description |
| -------------- | -------------------------------------------------------------------------------------------------------- |
| `/bot-ping` | Latency test |
| `/bot-version` | Show the OpenClaw framework version |
| `/bot-help` | List all commands |
| `/bot-upgrade` | Show the QQBot upgrade guide link |
| `/bot-logs` | Export recent gateway logs as a file |
| `/bot-approve` | Approve a pending QQ Bot action (for example, confirming a C2C or group upload) through the native flow. |
Append `?` to any command for usage help (for example `/bot-upgrade ?`).
## Engine architecture
QQ Bot ships as a self-contained engine inside the plugin:
- Each account owns an isolated resource stack (WebSocket connection, API client, token cache, media storage root) keyed by `appId`. Accounts never share inbound/outbound state.
- The multi-account logger tags log lines with the owning account so diagnostics stay separable when you run several bots under one gateway.
- Inbound, outbound, and gateway bridge paths share a single media payload root under `~/.openclaw/media`, so uploads, downloads, and transcode caches land under one guarded directory instead of a per-subsystem tree.
- Credentials can be backed up and restored as part of standard OpenClaw credential snapshots; the engine re-attaches each account's resource stack on restore without requiring a fresh QR-code pair.
## QR-code onboarding
As an alternative to pasting `AppID:AppSecret` manually, the engine supports a QR-code onboarding flow for linking a QQ Bot to OpenClaw:
1. Run the QQ Bot setup path (for example `openclaw channels add --channel qqbot`) and pick the QR-code flow when prompted.
2. Scan the generated QR code with the phone app tied to the target QQ Bot.
3. Approve the pairing on the phone. OpenClaw persists the returned credentials into `credentials/` under the right account scope.
Approval prompts generated by the bot itself (for example, "allow this action?" flows exposed by the QQ Bot API) surface as native OpenClaw prompts that you can accept with `/bot-approve` rather than replying through the raw QQ client.
## Troubleshooting
- **Bot replies "gone to Mars":** credentials not configured or Gateway not started.

View File

@@ -361,8 +361,8 @@ Surface different features that extend the above defaults.
},
{
"command": "/models",
"description": "List providers or models for a provider",
"usage_hint": "[provider] [page] [limit=<n>|size=<n>|all]"
"description": "List providers/models or add a model",
"usage_hint": "[provider] [page] [limit=<n>|size=<n>|all] | add <provider> <modelId>"
},
{
"command": "/help",
@@ -734,6 +734,7 @@ Notes:
- `partial` (default): replace preview text with the latest partial output.
- `block`: append chunked preview updates.
- `progress`: show progress status text while generating, then send final text.
- `streaming.preview.toolProgress`: when draft preview is active, route tool/progress updates into the same edited preview message (default: `true`). Set `false` to keep separate tool/progress messages.
`channels.slack.streaming.nativeTransport` controls Slack native text streaming when `channels.slack.streaming.mode` is `partial` (default: `true`).
@@ -741,6 +742,7 @@ Notes:
- Channel and group-chat roots can still use the normal draft preview when native streaming is unavailable.
- Top-level Slack DMs stay off-thread by default, so they do not show the thread-style preview; use thread replies or `typingReaction` if you want visible progress there.
- Media and non-text payloads fall back to normal delivery.
- Media/error finals cancel pending preview edits without flushing a temporary draft; eligible text/block finals flush only when they can edit the preview in place.
- If streaming fails mid-reply, OpenClaw falls back to normal delivery for remaining payloads.
Use draft preview instead of Slack native text streaming:
@@ -971,7 +973,7 @@ Primary reference:
- compatibility toggle: `dangerouslyAllowNameMatching` (break-glass; keep off unless needed)
- channel access: `groupPolicy`, `channels.*`, `channels.*.users`, `channels.*.requireMention`
- threading/history: `replyToMode`, `replyToModeByChatType`, `thread.*`, `historyLimit`, `dmHistoryLimit`, `dms.*.historyLimit`
- delivery: `textChunkLimit`, `chunkMode`, `mediaMaxMb`, `streaming`, `streaming.nativeTransport`
- delivery: `textChunkLimit`, `chunkMode`, `mediaMaxMb`, `streaming`, `streaming.nativeTransport`, `streaming.preview.toolProgress`
- ops/features: `configWrites`, `commands.native`, `slashCommand.*`, `actions.*`, `userToken`, `userTokenReadOnly`
## Troubleshooting

View File

@@ -113,6 +113,7 @@ openclaw message send --channel synology-chat --target synology-chat:123456 --te
```
Media sends are supported by URL-based file delivery.
Outbound file URLs must use `http` or `https`, and private or otherwise blocked network targets are rejected before OpenClaw forwards the URL to the NAS webhook.
## Multi-account

View File

@@ -275,6 +275,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
- `channels.telegram.streaming` is `off | partial | block | progress` (default: `partial`)
- `progress` maps to `partial` on Telegram (compat with cross-channel naming)
- `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `true`). Set `false` to keep separate tool/progress messages.
- legacy `channels.telegram.streamMode` and boolean `streaming` values are auto-mapped
For text-only replies:
@@ -802,7 +803,8 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \
Telegram send also supports:
- `--buttons` for inline keyboards when `channels.telegram.capabilities.inlineButtons` allows it
- `--presentation` with `buttons` blocks for inline keyboards when `channels.telegram.capabilities.inlineButtons` allows it
- `--pin` or `--delivery '{"pin":true}'` to request pinned delivery when the bot can pin in that chat
- `--force-document` to send outbound images and GIFs as documents instead of compressed photo or animated-media uploads
Action gating:
@@ -1028,6 +1030,7 @@ Primary reference:
- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
- `channels.telegram.linkPreview`: toggle link previews for outbound messages (default: true).
- `channels.telegram.streaming`: `off | partial | block | progress` (live stream preview; default: `partial`; `progress` maps to `partial`; `block` is legacy preview mode compatibility). Telegram preview streaming uses a single preview message that is edited in place.
- `channels.telegram.streaming.preview.toolProgress`: reuse the live preview message for tool/progress updates when preview streaming is active (default: `true`). Set `false` to keep separate tool/progress messages.
- `channels.telegram.mediaMaxMb`: inbound/outbound Telegram media cap (MB, default: 100).
- `channels.telegram.retry`: retry policy for Telegram send helpers (CLI/tools/actions) on recoverable outbound API errors (attempts, minDelayMs, maxDelayMs, jitter).
- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to enabled on Node 22+, with WSL2 defaulting to disabled.
@@ -1057,7 +1060,7 @@ Telegram-specific high-signal fields:
- exec approvals: `execApprovals`, `accounts.*.execApprovals`
- command/menu: `commands.native`, `commands.nativeSkills`, `customCommands`
- threading/replies: `replyToMode`
- streaming: `streaming` (preview), `blockStreaming`
- streaming: `streaming` (preview), `streaming.preview.toolProgress`, `blockStreaming`
- formatting/delivery: `textChunkLimit`, `chunkMode`, `linkPreview`, `responsePrefix`
- media/network: `mediaMaxMb`, `timeoutSeconds`, `pollingStallThresholdMs`, `retry`, `network.autoSelectFamily`, `network.dangerouslyAllowPrivateNetwork`, `proxy`
- webhook: `webhookUrl`, `webhookSecret`, `webhookPath`, `webhookHost`

View File

@@ -39,7 +39,7 @@ Healthy baseline:
| 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. |
Full troubleshooting: [/channels/whatsapp#troubleshooting](/channels/whatsapp#troubleshooting)
Full troubleshooting: [WhatsApp troubleshooting](/channels/whatsapp#troubleshooting)
## Telegram
@@ -54,7 +54,7 @@ Full troubleshooting: [/channels/whatsapp#troubleshooting](/channels/whatsapp#tr
| `setMyCommands` rejected at startup | Inspect logs for `BOT_COMMANDS_TOO_MUCH` | Reduce plugin/skill/custom Telegram commands or disable native menus. |
| Upgraded and allowlist blocks you | `openclaw security audit` and config allowlists | Run `openclaw doctor --fix` or replace `@username` with numeric sender IDs. |
Full troubleshooting: [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting)
Full troubleshooting: [Telegram troubleshooting](/channels/telegram#troubleshooting)
## Discord
@@ -66,7 +66,7 @@ Full troubleshooting: [/channels/telegram#troubleshooting](/channels/telegram#tr
| Group messages ignored | Check logs for mention gating drops | Mention bot or set guild/channel `requireMention: false`. |
| DM replies missing | `openclaw pairing list discord` | Approve DM pairing or adjust DM policy. |
Full troubleshooting: [/channels/discord#troubleshooting](/channels/discord#troubleshooting)
Full troubleshooting: [Discord troubleshooting](/channels/discord#troubleshooting)
## Slack
@@ -78,7 +78,7 @@ Full troubleshooting: [/channels/discord#troubleshooting](/channels/discord#trou
| DMs blocked | `openclaw pairing list slack` | Approve pairing or relax DM policy. |
| Channel message ignored | Check `groupPolicy` and channel allowlist | Allow the channel or switch policy to `open`. |
Full troubleshooting: [/channels/slack#troubleshooting](/channels/slack#troubleshooting)
Full troubleshooting: [Slack troubleshooting](/channels/slack#troubleshooting)
## iMessage and BlueBubbles
@@ -92,8 +92,8 @@ Full troubleshooting: [/channels/slack#troubleshooting](/channels/slack#troubles
Full troubleshooting:
- [/channels/imessage#troubleshooting](/channels/imessage#troubleshooting)
- [/channels/bluebubbles#troubleshooting](/channels/bluebubbles#troubleshooting)
- [iMessage troubleshooting](/channels/imessage#troubleshooting)
- [BlueBubbles troubleshooting](/channels/bluebubbles#troubleshooting)
## Signal
@@ -105,7 +105,7 @@ Full troubleshooting:
| DM blocked | `openclaw pairing list signal` | Approve sender or adjust DM policy. |
| Group replies do not trigger | Check group allowlist and mention patterns | Add sender/group or loosen gating. |
Full troubleshooting: [/channels/signal#troubleshooting](/channels/signal#troubleshooting)
Full troubleshooting: [Signal troubleshooting](/channels/signal#troubleshooting)
## QQ Bot
@@ -118,7 +118,7 @@ Full troubleshooting: [/channels/signal#troubleshooting](/channels/signal#troubl
| Voice not transcribed | Check STT provider config | Configure `channels.qqbot.stt` or `tools.media.audio`. |
| Proactive messages not arriving | Check QQ platform interaction requirements | QQ may block bot-initiated messages without recent interaction. |
Full troubleshooting: [/channels/qqbot#troubleshooting](/channels/qqbot#troubleshooting)
Full troubleshooting: [QQ Bot troubleshooting](/channels/qqbot#troubleshooting)
## Matrix

View File

@@ -465,6 +465,75 @@ Behavior notes:
</Accordion>
</AccordionGroup>
## System prompts
WhatsApp supports Telegram-style system prompts for groups and direct chats via the `groups` and `direct` maps.
Resolution hierarchy for group messages:
The effective `groups` map is determined first: if the account defines its own `groups`, it fully replaces the root `groups` map (no deep merge). Prompt lookup then runs on the resulting single map:
1. **Group-specific system prompt** (`groups["<groupId>"].systemPrompt`): used if the specific group entry defines a `systemPrompt`.
2. **Group wildcard system prompt** (`groups["*"].systemPrompt`): used when the specific group entry is absent or defines no `systemPrompt`.
Resolution hierarchy for direct messages:
The effective `direct` map is determined first: if the account defines its own `direct`, it fully replaces the root `direct` map (no deep merge). Prompt lookup then runs on the resulting single map:
1. **Direct-specific system prompt** (`direct["<peerId>"].systemPrompt`): used if the specific peer entry defines a `systemPrompt`.
2. **Direct wildcard system prompt** (`direct["*"].systemPrompt`): used when the specific peer entry is absent or defines no `systemPrompt`.
Note: `dms` remains the lightweight per-DM history override bucket (`dms.<id>.historyLimit`); prompt overrides live under `direct`.
**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.
Important behavior:
- `channels.whatsapp.groups` is both a per-group config map and the chat-level group allowlist. At either the root or account scope, `groups["*"]` means "all groups are admitted" for that scope.
- Only add a wildcard group `systemPrompt` when you already want that scope to admit all groups. If you still want only a fixed set of group IDs to be eligible, do not use `groups["*"]` for the prompt default. Instead, repeat the prompt on each explicitly allowlisted group entry.
- Group admission and sender authorization are separate checks. `groups["*"]` widens the set of groups that can reach group handling, but it does not by itself authorize every sender in those groups. Sender access is still controlled separately by `channels.whatsapp.groupPolicy` and `channels.whatsapp.groupAllowFrom`.
- `channels.whatsapp.direct` does not have the same side effect for DMs. `direct["*"]` only provides a default direct-chat config after a DM is already admitted by `dmPolicy` plus `allowFrom` or pairing-store rules.
Example:
```json5
{
channels: {
whatsapp: {
groups: {
// Use only if all groups should be admitted at the root scope.
// Applies to all accounts that do not define their own groups map.
"*": { systemPrompt: "Default prompt for all groups." },
},
direct: {
// Applies to all accounts that do not define their own direct map.
"*": { systemPrompt: "Default prompt for all direct chats." },
},
accounts: {
work: {
groups: {
// This account defines its own groups, so root groups are fully
// replaced. To keep a wildcard, define "*" explicitly here too.
"120363406415684625@g.us": {
requireMention: false,
systemPrompt: "Focus on project management.",
},
// Use only if all groups should be admitted in this account.
"*": { systemPrompt: "Default prompt for work groups." },
},
direct: {
// This account defines its own direct map, so root direct entries are
// fully replaced. To keep a wildcard, define "*" explicitly here too.
"+15551234567": { systemPrompt: "Prompt for a specific work direct chat." },
"*": { systemPrompt: "Default prompt for work direct chats." },
},
},
},
},
},
}
```
## Configuration reference pointers
Primary reference:
@@ -478,6 +547,7 @@ High-signal WhatsApp fields:
- multi-account: `accounts.<id>.enabled`, `accounts.<id>.authDir`, account-level overrides
- operations: `configWrites`, `debounceMs`, `web.enabled`, `web.heartbeatSeconds`, `web.reconnect.*`
- session behavior: `session.dmScope`, `historyLimit`, `dmHistoryLimit`, `dms.<id>.historyLimit`
- prompts: `groups.<id>.systemPrompt`, `groups["*"].systemPrompt`, `direct.<id>.systemPrompt`, `direct["*"].systemPrompt`
## Related

View File

@@ -12,28 +12,28 @@ The CI runs on every push to `main` and every pull request. It uses smart scopin
## Job Overview
| Job | Purpose | When it runs |
| -------------------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------- |
| `preflight` | Detect docs-only changes, changed scopes, changed extensions, and build the CI manifest | Always on non-draft pushes and PRs |
| `security-scm-fast` | Private key detection and workflow audit via `zizmor` | Always on non-draft pushes and PRs |
| `security-dependency-audit` | Dependency-free production lockfile audit against npm advisories | Always on non-draft pushes and PRs |
| `security-fast` | Required aggregate for the fast security jobs | Always on non-draft pushes and PRs |
| `build-artifacts` | Build `dist/` and the Control UI once, upload reusable artifacts for downstream jobs | Node-relevant changes |
| `checks-fast-core` | Fast Linux correctness lanes such as bundled/plugin-contract/protocol checks | Node-relevant changes |
| `checks-fast-contracts-channels` | Sharded channel contract checks with a stable aggregate check result | Node-relevant changes |
| `checks-node-extensions` | Full bundled-plugin test shards across the extension suite | Node-relevant changes |
| `checks-node-core-test` | Core Node test shards, excluding channel, bundled, contract, and extension lanes | Node-relevant changes |
| `extension-fast` | Focused tests for only the changed bundled plugins | When extension changes are detected |
| `check` | Sharded main local gate equivalent: prod types, lint, guards, test types, and strict smoke | Node-relevant changes |
| `check-additional` | Architecture, boundary, extension-surface guards, package-boundary, and gateway-watch shards | Node-relevant changes |
| `build-smoke` | Built-CLI smoke tests and startup-memory smoke | Node-relevant changes |
| `checks` | Remaining Linux Node lanes: channel tests and push-only Node 22 compatibility | Node-relevant changes |
| `check-docs` | Docs formatting, lint, and broken-link checks | Docs changed |
| `skills-python` | Ruff + pytest for Python-backed skills | Python-skill-relevant changes |
| `checks-windows` | Windows-specific test lanes | Windows-relevant changes |
| `macos-node` | macOS TypeScript test lane using the shared built artifacts | macOS-relevant changes |
| `macos-swift` | Swift lint, build, and tests for the macOS app | macOS-relevant changes |
| `android` | Android build and test matrix | Android-relevant changes |
| Job | Purpose | When it runs |
| -------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------ |
| `preflight` | Detect docs-only changes, changed scopes, changed extensions, and build the CI manifest | Always on non-draft pushes and PRs |
| `security-scm-fast` | Private key detection and workflow audit via `zizmor` | Always on non-draft pushes and PRs |
| `security-dependency-audit` | Dependency-free production lockfile audit against npm advisories | Always on non-draft pushes and PRs |
| `security-fast` | Required aggregate for the fast security jobs | Always on non-draft pushes and PRs |
| `build-artifacts` | Build `dist/` and the Control UI once, upload reusable artifacts for downstream jobs | Node-relevant changes |
| `checks-fast-core` | Fast Linux correctness lanes such as bundled/plugin-contract/protocol checks | Node-relevant changes |
| `checks-fast-contracts-channels` | Sharded channel contract checks with a stable aggregate check result | Node-relevant changes |
| `checks-node-extensions` | Full bundled-plugin test shards across the extension suite | Node-relevant changes |
| `checks-node-core-test` | Core Node test shards, excluding channel, bundled, contract, and extension lanes | Node-relevant changes |
| `extension-fast` | Focused tests for only the changed bundled plugins | Pull requests with extension changes |
| `check` | Sharded main local gate equivalent: prod types, lint, guards, test types, and strict smoke | Node-relevant changes |
| `check-additional` | Architecture, boundary, extension-surface guards, package-boundary, and gateway-watch shards | Node-relevant changes |
| `build-smoke` | Built-CLI smoke tests and startup-memory smoke | Node-relevant changes |
| `checks` | Remaining Linux Node lanes: channel tests and push-only Node 22 compatibility | Node-relevant changes |
| `check-docs` | Docs formatting, lint, and broken-link checks | Docs changed |
| `skills-python` | Ruff + pytest for Python-backed skills | Python-skill-relevant changes |
| `checks-windows` | Windows-specific test lanes | Windows-relevant changes |
| `macos-node` | macOS TypeScript test lane using the shared built artifacts | macOS-relevant changes |
| `macos-swift` | Swift lint, build, and tests for the macOS app | macOS-relevant changes |
| `android` | Android unit tests for both flavors plus one debug APK build | Android-relevant changes |
## Fail-Fast Order
@@ -42,26 +42,34 @@ Jobs are ordered so cheap checks fail before expensive ones run:
1. `preflight` decides which lanes exist at all. The `docs-scope` and `changed-scope` logic are steps inside this job, not standalone jobs.
2. `security-scm-fast`, `security-dependency-audit`, `security-fast`, `check`, `check-additional`, `check-docs`, and `skills-python` fail quickly without waiting on the heavier artifact and platform matrix jobs.
3. `build-artifacts` overlaps with the fast Linux lanes so downstream consumers can start as soon as the shared build is ready.
4. Heavier platform and runtime lanes fan out after that: `checks-fast-core`, `checks-fast-contracts-channels`, `checks-node-extensions`, `checks-node-core-test`, `extension-fast`, `checks`, `checks-windows`, `macos-node`, `macos-swift`, and `android`.
4. Heavier platform and runtime lanes fan out after that: `checks-fast-core`, `checks-fast-contracts-channels`, `checks-node-extensions`, `checks-node-core-test`, PR-only `extension-fast`, `checks`, `checks-windows`, `macos-node`, `macos-swift`, and `android`.
Scope logic lives in `scripts/ci-changed-scope.mjs` and is covered by unit tests in `src/scripts/ci-changed-scope.test.ts`.
The separate `install-smoke` workflow reuses the same scope script through its own `preflight` job. It computes `run_install_smoke` from the narrower changed-smoke signal, so Docker/install smoke only runs for install, packaging, and container-relevant changes.
CI workflow edits validate the Node CI graph plus workflow linting, but do not force Windows, Android, or macOS native builds by themselves; those platform lanes stay scoped to platform source changes.
Windows Node checks are scoped to Windows-specific process/path wrappers, npm/pnpm/UI runner helpers, package manager config, and the CI workflow surfaces that execute that lane; unrelated source, plugin, install-smoke, and test-only changes stay on the Linux Node lanes so they do not reserve a 16-vCPU Windows worker for coverage that is already exercised by the normal test shards.
The separate `install-smoke` workflow reuses the same scope script through its own `preflight` job. It computes `run_install_smoke` from the narrower changed-smoke signal, so Docker/install smoke runs for install, packaging, container-relevant changes, bundled extension production changes, and the core plugin/channel/gateway/Plugin SDK surfaces that the Docker smoke jobs exercise. Test-only and docs-only edits do not reserve Docker workers. Its QR package smoke forces the Docker `pnpm install` layer to rerun while preserving the BuildKit pnpm store cache, so it still exercises installation without redownloading dependencies on every run. Its gateway-network e2e reuses the runtime image built earlier in the job, so it adds real container-to-container WebSocket coverage without adding another Docker build. A separate `docker-e2e-fast` job runs the bounded bundled-plugin Docker profile under a 120-second command timeout: setup-entry dependency repair plus synthetic bundled-loader failure isolation. The full bundled update/channel matrix remains manual/full-suite because it performs repeated real npm update and doctor repair passes.
Local changed-lane logic lives in `scripts/changed-lanes.mjs` and is executed by `scripts/check-changed.mjs`. That local gate is stricter about architecture boundaries than the broad CI platform scope: core production changes run core prod typecheck plus core tests, core test-only changes run only core test typecheck/tests, extension production changes run extension prod typecheck plus extension tests, and extension test-only changes run only extension test typecheck/tests. Public Plugin SDK or plugin-contract changes expand to extension validation because extensions depend on those core contracts. Unknown root/config changes fail safe to all lanes.
Local changed-lane logic lives in `scripts/changed-lanes.mjs` and is executed by `scripts/check-changed.mjs`. That local gate is stricter about architecture boundaries than the broad CI platform scope: core production changes run core prod typecheck plus core tests, core test-only changes run only core test typecheck/tests, extension production changes run extension prod typecheck plus extension tests, and extension test-only changes run only extension test typecheck/tests. Public Plugin SDK or plugin-contract changes expand to extension validation because extensions depend on those core contracts. Release metadata-only version bumps run targeted version/config/root-dependency checks. Unknown root/config changes fail safe to all lanes.
On pushes, the `checks` matrix adds the push-only `compat-node22` lane. On pull requests, that lane is skipped and the matrix stays focused on the normal test/channel lanes.
The slowest Node test families are split into include-file shards so each job stays small: channel contracts split registry and core coverage into eight weighted shards each, auto-reply reply command tests split into four include-pattern shards, and the other large auto-reply reply prefix groups split into two shards each. `check-additional` also separates package-boundary compile/canary work from runtime topology gateway/architecture work.
The slowest Node test families are split or balanced so each job stays small: channel contracts split registry and core coverage into six weighted shards total, bundled plugin tests balance across six extension workers, auto-reply runs as three balanced workers instead of six tiny workers, and agentic gateway/plugin configs are spread across the existing source-only agentic Node jobs instead of waiting on built artifacts. Broad browser, QA, media, and miscellaneous plugin tests use their dedicated Vitest configs instead of the shared plugin catch-all. The broad agents lane uses the shared Vitest file-parallel scheduler because it is import/scheduling dominated rather than owned by a single slow test file. `runtime-config` runs with the infra core-runtime shard to keep the shared runtime shard from owning the tail. `check-additional` keeps package-boundary compile/canary work together and separates runtime topology architecture from gateway watch coverage; the boundary guard shard runs its small independent guards concurrently inside one job, and the gateway watch regression reuses a same-run built `dist/` and `dist-runtime/` tar artifact from `build-artifacts` so it measures watch stability without rebuilding runtime artifacts in its own worker.
Android CI runs both `testPlayDebugUnitTest` and `testThirdPartyDebugUnitTest`, then builds the Play debug APK. The third-party flavor has no separate source set or manifest; its unit-test lane still compiles that flavor with the SMS/call-log BuildConfig flags, while avoiding a duplicate debug APK packaging job on every Android-relevant push.
`extension-fast` is PR-only because push runs already execute the full bundled plugin shards. That keeps changed-plugin feedback for reviews without reserving an extra Blacksmith worker on `main` for coverage already present in `checks-node-extensions`.
GitHub may mark superseded jobs as `cancelled` when a newer push lands on the same PR or `main` ref. Treat that as CI noise unless the newest run for the same ref is also failing. The aggregate shard checks call out this cancellation case explicitly so it is easier to distinguish from a test failure.
GitHub may mark superseded jobs as `cancelled` when a newer push lands on the same PR or `main` ref. Treat that as CI noise unless the newest run for the same ref is also failing. Aggregate shard checks use `!cancelled() && always()` so they still report normal shard failures but do not queue after the whole workflow has already been superseded.
The CI concurrency key is versioned (`CI-v7-*`) so a GitHub-side zombie in an old queue group cannot indefinitely block newer main runs.
## Runners
| Runner | Jobs |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `blacksmith-16vcpu-ubuntu-2404` | `preflight`, `security-scm-fast`, `security-dependency-audit`, `security-fast`, `build-artifacts`, Linux checks, docs checks, Python skills, `android` |
| `blacksmith-32vcpu-windows-2025` | `checks-windows` |
| `macos-latest` | `macos-node`, `macos-swift` |
| Runner | Jobs |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ubuntu-24.04` | `preflight`, fast security jobs and aggregates (`security-scm-fast`, `security-dependency-audit`, `security-fast`), fast protocol/contract/bundled checks, sharded channel contract checks, `check` shards except lint, `check-additional` shards and aggregates, Node test aggregate verifiers, docs checks, Python skills, workflow-sanity, labeler, auto-response; install-smoke preflight also uses GitHub-hosted Ubuntu so the Blacksmith matrix can queue earlier |
| `blacksmith-8vcpu-ubuntu-2404` | `build-artifacts`, build-smoke, Linux Node test shards, bundled plugin test shards, remaining built-artifact consumers, `android` |
| `blacksmith-16vcpu-ubuntu-2404` | `check-lint`, which remains CPU-sensitive enough that 8 vCPU cost more than it saved; install-smoke Docker builds, where 32-vCPU queue time cost more than it saved |
| `blacksmith-16vcpu-windows-2025` | `checks-windows` |
| `blacksmith-6vcpu-macos-latest` | `macos-node` on `openclaw/openclaw`; forks fall back to `macos-latest` |
| `blacksmith-12vcpu-macos-latest` | `macos-swift` on `openclaw/openclaw`; forks fall back to `macos-latest` |
## Local Equivalents
@@ -79,4 +87,5 @@ pnpm test:channels
pnpm test:contracts:channels
pnpm check:docs # docs format + lint + broken links
pnpm build # build dist when CI artifact/build-smoke lanes matter
node scripts/ci-run-timings.mjs <run-id> # summarize wall time, queue time, and slowest jobs
```

View File

@@ -166,9 +166,11 @@ Per-session `mcpServers` are not supported in bridge mode. If an ACP client
sends them during `newSession` or `loadSession`, the bridge returns a clear
error instead of silently ignoring them.
If you want ACPX-backed sessions to see OpenClaw plugin tools, enable the
gateway-side ACPX plugin bridge instead of trying to pass per-session
`mcpServers`. See [ACP Agents](/tools/acp-agents#plugin-tools-mcp-bridge).
If you want ACPX-backed sessions to see OpenClaw plugin tools or selected
built-in tools such as `cron`, enable the gateway-side ACPX MCP bridges instead
of trying to pass per-session `mcpServers`. See
[ACP Agents](/tools/acp-agents#plugin-tools-mcp-bridge) and
[OpenClaw tools MCP bridge](/tools/acp-agents#openclaw-tools-mcp-bridge).
## Use from `acpx` (Codex, Claude, other ACP clients)

View File

@@ -38,6 +38,7 @@ openclaw config get browser.executablePath
openclaw config set browser.executablePath "/usr/bin/google-chrome"
openclaw config set agents.defaults.heartbeat.every "2h"
openclaw config set agents.list[0].tools.exec.node "node-id-or-name"
openclaw config set agents.defaults.models '{"openai-codex/gpt-5.4":{}}' --strict-json --merge
openclaw config set channels.discord.token --ref-provider default --ref-source env --ref-id DISCORD_BOT_TOKEN
openclaw config set secrets.providers.vaultfile --provider-source file --provider-path /etc/openclaw/secrets.json --provider-mode json
openclaw config unset plugins.entries.brave.config.webSearch.apiKey
@@ -105,6 +106,22 @@ openclaw config set channels.whatsapp.groups '["*"]' --strict-json
`config get <path> --json` prints the raw value as JSON instead of terminal-formatted text.
Object assignment replaces the target path by default. Protected map/list paths
that commonly hold user-added entries, such as `agents.defaults.models`,
`models.providers`, `models.providers.<id>.models`, `plugins.entries`, and
`auth.profiles`, refuse replacements that would remove existing entries unless
you pass `--replace`.
Use `--merge` when adding entries to those maps:
```bash
openclaw config set agents.defaults.models '{"openai-codex/gpt-5.4":{}}' --strict-json --merge
openclaw config set models.providers.ollama.models '[{"id":"llama3.2","name":"Llama 3.2"}]' --strict-json --merge
```
Use `--replace` only when you intentionally want the provided value to become
the complete target value.
## `config set` modes
`openclaw config set` supports four assignment styles:
@@ -342,6 +359,9 @@ If dry-run fails:
post-change config before committing it to disk. If the new payload fails schema
validation or looks like a destructive clobber, the active config is left alone
and the rejected payload is saved beside it as `openclaw.json.rejected.*`.
The active config path must be a regular file. Symlinked `openclaw.json`
layouts are unsupported for writes; use `OPENCLAW_CONFIG_PATH` to point directly
at the real file instead.
Prefer CLI writes for small edits:
@@ -366,7 +386,7 @@ last-known-good backup during startup or hot reload. See
## Subcommands
- `config file`: Print the active config file path (resolved from `OPENCLAW_CONFIG_PATH` or default location).
- `config file`: Print the active config file path (resolved from `OPENCLAW_CONFIG_PATH` or default location). The path should name a regular file, not a symlink.
Restart the gateway after edits.
@@ -379,3 +399,31 @@ gateway.
openclaw config validate
openclaw config validate --json
```
After `openclaw config validate` is passing, you can use the local TUI to have
an embedded agent compare the active config against the docs while you validate
each change from the same terminal:
If validation is already failing, start with `openclaw configure` or
`openclaw doctor --fix`. `openclaw chat` does not bypass the invalid-config
guard.
```bash
openclaw chat
```
Then inside the TUI:
```text
!openclaw config file
!openclaw docs gateway auth token secretref
!openclaw config validate
!openclaw doctor
```
Typical repair loop:
- Ask the agent to compare your current config with the relevant docs page and suggest the smallest fix.
- Apply targeted edits with `openclaw config set` or `openclaw configure`.
- Rerun `openclaw config validate` after each change.
- If validation passes but the runtime is still unhealthy, run `openclaw doctor` or `openclaw doctor --fix` for migration and repair help.

View File

@@ -11,6 +11,8 @@ 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.
When configure starts from a provider auth choice, the default-model and
allowlist pickers prefer that provider automatically. For paired providers such

View File

@@ -42,6 +42,7 @@ 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 requiring write access to the installed OpenClaw package. 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 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.
- Doctor includes a memory-search readiness check and can recommend `openclaw configure --section model` when embedding credentials are missing.

View File

@@ -111,6 +111,59 @@ Options:
- `--days <days>`: number of days to include (default `30`).
### `gateway stability`
Fetch the recent diagnostic stability recorder from a running Gateway.
```bash
openclaw gateway stability
openclaw gateway stability --type payload.large
openclaw gateway stability --bundle latest
openclaw gateway stability --bundle latest --export
openclaw gateway stability --json
```
Options:
- `--limit <limit>`: maximum number of recent events to include (default `25`, max `1000`).
- `--type <type>`: filter by diagnostic event type, such as `payload.large` or `diagnostic.memory.pressure`.
- `--since-seq <seq>`: include only events after a diagnostic sequence number.
- `--bundle [path]`: read a persisted stability bundle instead of calling the running Gateway. Use `--bundle latest` (or just `--bundle`) for the newest bundle under the state directory, or pass a bundle JSON path directly.
- `--export`: write a shareable support diagnostics zip instead of printing stability details.
- `--output <path>`: output path for `--export`.
Notes:
- The recorder is active by default. Set `diagnostics.enabled: false` only when you need to disable Gateway diagnostic heartbeat collection.
- Records keep operational metadata: event names, counts, byte sizes, memory readings, queue/session state, channel/plugin names, and redacted session summaries. They do not keep chat text, webhook bodies, tool outputs, raw request or response bodies, tokens, cookies, secret values, hostnames, or raw session ids.
- On fatal Gateway exits, shutdown timeouts, and restart startup failures, OpenClaw writes the same diagnostic snapshot to `~/.openclaw/logs/stability/openclaw-stability-*.json` when the recorder has events. Inspect the newest bundle with `openclaw gateway stability --bundle latest`; `--limit`, `--type`, and `--since-seq` also apply to bundle output.
### `gateway diagnostics export`
Write a local diagnostics zip that is designed to attach to bug reports.
```bash
openclaw gateway diagnostics export
openclaw gateway diagnostics export --output openclaw-diagnostics.zip
openclaw gateway diagnostics export --json
```
Options:
- `--output <path>`: output zip path. Defaults to a support export under the state directory.
- `--log-lines <count>`: maximum sanitized log lines to include (default `5000`).
- `--log-bytes <bytes>`: maximum log bytes to inspect (default `1000000`).
- `--url <url>`: Gateway WebSocket URL for the health snapshot.
- `--token <token>`: Gateway token for the health snapshot.
- `--password <password>`: Gateway password for the health snapshot.
- `--timeout <ms>`: status/health snapshot timeout (default `3000`).
- `--no-stability-bundle`: skip persisted stability bundle lookup.
- `--json`: print the written path, size, and manifest as JSON.
The export contains a manifest, a Markdown summary, config shape, sanitized config details, sanitized log summaries, sanitized Gateway status/health snapshots, and the newest stability bundle when one exists.
It is meant to be shared. It keeps operational details that help debugging, such as safe OpenClaw log fields, subsystem names, status codes, durations, configured modes, ports, plugin ids, provider ids, non-secret feature settings, and redacted operational log messages. It omits or redacts chat text, webhook bodies, tool outputs, credentials, cookies, account/message identifiers, prompt/instruction text, hostnames, and secret values. When a LogTape-style message looks like user/chat/tool payload text, the export keeps only that a message was omitted plus its byte count.
### `gateway status`
`gateway status` shows the Gateway service (launchd/systemd/schtasks) plus an optional probe of connectivity/auth capability.

View File

@@ -1532,6 +1532,9 @@ Options:
- `--json`
- `--plain`
`--all` includes bundled provider-owned static catalog rows before auth is
configured. Rows remain unavailable until matching provider credentials exist.
### `models status`
Options:

View File

@@ -104,18 +104,18 @@ Benefits:
This table maps common inference tasks to the corresponding infer command.
| Task | Command | Notes |
| ----------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------- |
| Run a text/model prompt | `openclaw infer model run --prompt "..." --json` | Uses the normal local path by default |
| Generate an image | `openclaw infer image generate --prompt "..." --json` | Use `image edit` when starting from an existing file |
| Describe an image file | `openclaw infer image describe --file ./image.png --json` | `--model` must be `<provider/model>` |
| Transcribe audio | `openclaw infer audio transcribe --file ./memo.m4a --json` | `--model` must be `<provider/model>` |
| Synthesize speech | `openclaw infer tts convert --text "..." --output ./speech.mp3 --json` | `tts status` is gateway-oriented |
| Generate a video | `openclaw infer video generate --prompt "..." --json` | |
| Describe a video file | `openclaw infer video describe --file ./clip.mp4 --json` | `--model` must be `<provider/model>` |
| Search the web | `openclaw infer web search --query "..." --json` | |
| Fetch a web page | `openclaw infer web fetch --url https://example.com --json` | |
| Create embeddings | `openclaw infer embedding create --text "..." --json` | |
| Task | Command | Notes |
| ----------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------- |
| Run a text/model prompt | `openclaw infer model run --prompt "..." --json` | Uses the normal local path by default |
| Generate an image | `openclaw infer image generate --prompt "..." --json` | Use `image edit` when starting from an existing file |
| Describe an image file | `openclaw infer image describe --file ./image.png --json` | `--model` must be an image-capable `<provider/model>` |
| Transcribe audio | `openclaw infer audio transcribe --file ./memo.m4a --json` | `--model` must be `<provider/model>` |
| Synthesize speech | `openclaw infer tts convert --text "..." --output ./speech.mp3 --json` | `tts status` is gateway-oriented |
| Generate a video | `openclaw infer video generate --prompt "..." --json` | |
| Describe a video file | `openclaw infer video describe --file ./clip.mp4 --json` | `--model` must be `<provider/model>` |
| Search the web | `openclaw infer web search --query "..." --json` | |
| Fetch a web page | `openclaw infer web fetch --url https://example.com --json` | |
| Create embeddings | `openclaw infer embedding create --text "..." --json` | |
## Behavior
@@ -123,6 +123,7 @@ This table maps common inference tasks to the corresponding infer command.
- Use `--json` when the output will be consumed by another command or script.
- Use `--provider` or `--model provider/model` when a specific backend is required.
- For `image describe`, `audio transcribe`, and `video describe`, `--model` must use the form `<provider/model>`.
- For `image describe`, an explicit `--model` runs that provider/model directly. The model must be image-capable in the model catalog or provider config.
- Stateless execution commands default to local.
- Gateway-managed state commands default to gateway.
- The normal local path does not require the gateway to be running.
@@ -152,12 +153,14 @@ openclaw infer image generate --prompt "friendly lobster illustration" --json
openclaw infer image generate --prompt "cinematic product photo of headphones" --json
openclaw infer image describe --file ./photo.jpg --json
openclaw infer image describe --file ./ui-screenshot.png --model openai/gpt-4.1-mini --json
openclaw infer image describe --file ./photo.jpg --model ollama/qwen2.5vl:7b --json
```
Notes:
- Use `image edit` when starting from existing input files.
- For `image describe`, `--model` must be `<provider/model>`.
- For `image describe`, `--model` must be an image-capable `<provider/model>`.
- For local Ollama vision models, pull the model first and set `OLLAMA_API_KEY` to any placeholder value, for example `ollama-local`. See [Ollama](/providers/ollama#vision-and-image-description).
## Audio
@@ -240,7 +243,7 @@ Infer commands normalize JSON output under a shared envelope:
"capability": "image.generate",
"transport": "local",
"provider": "openai",
"model": "gpt-image-1",
"model": "gpt-image-2",
"attempts": [],
"outputs": []
}

View File

@@ -369,6 +369,9 @@ Important behavior:
reachable right now
- runtime adapters decide which transport shapes they actually support at
execution time
- embedded Pi exposes configured MCP tools in normal `coding` and `messaging`
tool profiles; `minimal` still hides them, and `tools.deny: ["bundle-mcp"]`
disables them explicitly
## Saved MCP server definitions
@@ -428,6 +431,12 @@ Launches a local child process and communicates over stdin/stdout.
| `env` | Extra environment variables |
| `cwd` / `workingDirectory` | Working directory for the process |
#### Stdio env safety filter
OpenClaw rejects interpreter-startup env keys that can alter how a stdio MCP server starts up before the first RPC, even if they appear in a server's `env` block. Blocked keys include `NODE_OPTIONS`, `PYTHONSTARTUP`, `PYTHONPATH`, `PERL5OPT`, `RUBYOPT`, `SHELLOPTS`, `PS4`, and similar runtime-control variables. Startup rejects these with a configuration error so they cannot inject an implicit prelude, swap the interpreter, or enable a debugger against the stdio process. Ordinary credential, proxy, and server-specific env vars (`GITHUB_TOKEN`, `HTTP_PROXY`, custom `*_API_KEY`, etc.) are unaffected.
If your MCP server genuinely needs one of the blocked variables, set it on the gateway host process instead of under the stdio server's `env`.
### SSE / HTTP transport
Connects to a remote MCP server over HTTP Server-Sent Events.

View File

@@ -67,15 +67,13 @@ Name lookup:
- `send`
- Channels: WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/Matrix/Microsoft Teams
- Required: `--target`, plus `--message` or `--media`
- Optional: `--media`, `--interactive`, `--buttons`, `--components`, `--card`, `--reply-to`, `--thread-id`, `--gif-playback`, `--force-document`, `--silent`
- Shared interactive payloads: `--interactive` sends a channel-native interactive JSON payload when supported
- Telegram only: `--buttons` (requires `channels.telegram.capabilities.inlineButtons` to allow it)
- Required: `--target`, plus `--message`, `--media`, or `--presentation`
- Optional: `--media`, `--presentation`, `--delivery`, `--pin`, `--reply-to`, `--thread-id`, `--gif-playback`, `--force-document`, `--silent`
- Shared presentation payloads: `--presentation` sends semantic blocks (`text`, `context`, `divider`, `buttons`, `select`) that core renders through the selected channel's declared capabilities. See [Message Presentation](/plugins/message-presentation).
- Generic delivery preferences: `--delivery` accepts delivery hints such as `{ "pin": true }`; `--pin` is shorthand for pinned delivery when the channel supports it.
- Telegram only: `--force-document` (send images and GIFs as documents to avoid Telegram compression)
- Telegram only: `--thread-id` (forum topic id)
- Slack only: `--thread-id` (thread timestamp; `--reply-to` uses the same field)
- Discord only: `--components` JSON payload
- Adaptive-card channels: `--card` JSON payload when supported
- Telegram + Discord: `--silent`
- WhatsApp only: `--gif-playback`
@@ -208,22 +206,22 @@ openclaw message send --channel discord \
--target channel:123 --message "hi" --reply-to 456
```
Send a Discord message with components:
Send a message with semantic buttons:
```
openclaw message send --channel discord \
--target channel:123 --message "Choose:" \
--components '{"text":"Choose a path","blocks":[{"type":"actions","buttons":[{"label":"Approve","style":"success"},{"label":"Decline","style":"danger"}]}]}'
--presentation '{"blocks":[{"type":"buttons","buttons":[{"label":"Approve","value":"approve","style":"success"},{"label":"Decline","value":"decline","style":"danger"}]}]}'
```
See [Discord components](/channels/discord#interactive-components) for the full schema.
Core renders the same `presentation` payload into Discord components, Slack blocks, Telegram inline buttons, Mattermost props, or Teams/Feishu cards depending on channel capability. See [Message Presentation](/plugins/message-presentation) for the full contract and fallback rules.
Send a shared interactive payload:
Send a richer presentation payload:
```bash
openclaw message send --channel googlechat --target spaces/AAA... \
--message "Choose:" \
--interactive '{"text":"Choose a path","blocks":[{"type":"actions","buttons":[{"label":"Approve"},{"label":"Decline"}]}]}'
--presentation '{"title":"Deploy approval","tone":"warning","blocks":[{"type":"text","text":"Choose a path"},{"type":"buttons","buttons":[{"label":"Approve","value":"approve"},{"label":"Decline","value":"decline"}]}]}'
```
Create a Discord poll:
@@ -277,19 +275,19 @@ openclaw message react --channel signal \
--emoji "✅" --target-author-uuid 123e4567-e89b-12d3-a456-426614174000
```
Send Telegram inline buttons:
Send Telegram inline buttons through generic presentation:
```
openclaw message send --channel telegram --target @mychat --message "Choose:" \
--buttons '[ [{"text":"Yes","callback_data":"cmd:yes"}], [{"text":"No","callback_data":"cmd:no"}] ]'
--presentation '{"blocks":[{"type":"buttons","buttons":[{"label":"Yes","value":"cmd:yes"},{"label":"No","value":"cmd:no"}]}]}'
```
Send a Teams Adaptive Card:
Send a Teams card through generic presentation:
```bash
openclaw message send --channel msteams \
--target conversation:19:abc@thread.tacv2 \
--card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Status update"}]}'
--presentation '{"title":"Status update","blocks":[{"type":"text","text":"Build completed"}]}'
```
Send a Telegram image as a document to avoid compression:

View File

@@ -43,6 +43,9 @@ Probe rows can come from auth profiles, env credentials, or `models.json`.
Notes:
- `models set <model-or-alias>` accepts `provider/model` or an alias.
- `models list --all` includes bundled provider-owned static catalog rows even
when you have not authenticated with that provider yet. Those rows still show
as unavailable until matching auth is configured.
- Model refs are parsed by splitting on the **first** `/`. If the model ID includes `/` (OpenRouter-style), include the provider prefix (example: `openrouter/moonshotai/kimi-k2`).
- If you omit the provider, OpenClaw resolves the input as an alias first, then
as a unique configured-provider match for that exact model id, and only then

View File

@@ -33,7 +33,7 @@ openclaw plugins enable <id>
openclaw plugins disable <id>
openclaw plugins uninstall <id>
openclaw plugins doctor
openclaw plugins update <id>
openclaw plugins update <id-or-npm-spec>
openclaw plugins update --all
openclaw plugins marketplace list <marketplace>
openclaw plugins marketplace list <marketplace> --json
@@ -76,6 +76,8 @@ bundled-plugin recovery path for plugins that explicitly opt into
`--force` reuses the existing install target and overwrites an already-installed
plugin or hook pack in place. Use it when you are intentionally reinstalling
the same id from a new local path, archive, ClawHub package, or npm artifact.
For routine upgrades of an already tracked npm plugin, prefer
`openclaw plugins update <id-or-npm-spec>`.
`--pin` applies to npm installs only. It is not supported with `--marketplace`,
because marketplace installs persist marketplace source metadata instead of an
@@ -243,9 +245,20 @@ or exact version. OpenClaw resolves that package name back to the tracked plugin
record, updates that installed plugin, and records the new npm spec for future
id-based updates.
Passing the npm package name without a version or tag also resolves back to the
tracked plugin record. Use this when a plugin was pinned to an exact version and
you want to move it back to the registry's default release line.
Before a live npm update, OpenClaw checks the installed package version against
the npm registry metadata. If the installed version and recorded artifact
identity already match the resolved target, the update is skipped without
downloading, reinstalling, or rewriting `openclaw.json`.
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.
OpenClaw treats that as npm artifact drift. The interactive
`openclaw plugins update` command prints the expected and actual hashes and asks
for confirmation before proceeding. Non-interactive update helpers fail closed
unless the caller supplies an explicit continuation policy.
`--dangerously-force-unsafe-install` is also available on `plugins update` as a
break-glass override for built-in dangerous-code scan false positives during
@@ -292,6 +305,10 @@ openclaw plugins doctor
compatibility notices. When everything is clean it prints `No plugin issues
detected.`
For module-shape failures such as missing `register`/`activate` exports, rerun
with `OPENCLAW_PLUGIN_LOAD_DEBUG=1` to include a compact export-shape summary in
the diagnostic output.
### Marketplace
```bash

View File

@@ -1,14 +1,17 @@
---
summary: "CLI reference for `openclaw tui` (terminal UI connected to the Gateway)"
summary: "CLI reference for `openclaw tui` (Gateway-backed or local embedded terminal UI)"
read_when:
- You want a terminal UI for the Gateway (remote-friendly)
- You want to pass url/token/session from scripts
- You want to run the TUI in local embedded mode without a Gateway
- You want to use openclaw chat or openclaw tui --local
title: "tui"
---
# `openclaw tui`
Open the terminal UI connected to the Gateway.
Open the terminal UI connected to the Gateway, or run it in local embedded
mode.
Related:
@@ -16,15 +19,48 @@ Related:
Notes:
- `chat` and `terminal` are aliases for `openclaw tui --local`.
- `--local` cannot be combined with `--url`, `--token`, or `--password`.
- `tui` resolves configured gateway auth SecretRefs for token/password auth when possible (`env`/`file`/`exec` providers).
- When launched from inside a configured agent workspace directory, TUI auto-selects that agent for the session key default (unless `--session` is explicitly `agent:<id>:...`).
- Local mode uses the embedded agent runtime directly. Most local tools work, but Gateway-only features are unavailable.
- Local mode adds `/auth [provider]` inside the TUI command surface.
## Examples
```bash
openclaw chat
openclaw tui --local
openclaw tui
openclaw tui --url ws://127.0.0.1:18789 --token <token>
openclaw tui --session main --deliver
openclaw chat --message "Compare my config to the docs and tell me what to fix"
# when run inside an agent workspace, infers that agent automatically
openclaw tui --session bugfix
```
## Config repair loop
Use local mode when the current config already validates and you want the
embedded agent to inspect it, compare it against the docs, and help repair it
from the same terminal:
If `openclaw config validate` is already failing, use `openclaw configure` or
`openclaw doctor --fix` first. `openclaw chat` does not bypass the invalid-
config guard.
```bash
openclaw chat
```
Then inside the TUI:
```text
!openclaw config file
!openclaw docs gateway auth token secretref
!openclaw config validate
!openclaw doctor
```
Apply targeted fixes with `openclaw config set` or `openclaw configure`, then
rerun `openclaw config validate`. See [TUI](/web/tui) and [Config](/cli/config).

View File

@@ -36,7 +36,9 @@ openclaw --update
- `--channel <stable|beta|dev>`: set the update channel (git + npm; persisted in config).
- `--tag <dist-tag|version|spec>`: override the package target for this update only. For package installs, `main` maps to `github:openclaw/openclaw#main`.
- `--dry-run`: preview planned update actions (channel/tag/target/restart flow) without writing config, installing, syncing plugins, or restarting.
- `--json`: print machine-readable `UpdateRunResult` JSON.
- `--json`: print machine-readable `UpdateRunResult` JSON, including
`postUpdate.plugins.integrityDrifts` when npm plugin artifact drift is
detected during post-update plugin sync.
- `--timeout <seconds>`: per-step timeout (default is 1200s).
- `--yes`: skip confirmation prompts (for example downgrade confirmation)
@@ -80,6 +82,12 @@ 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. If the installed version exactly
matches the target and no update-channel change needs to be persisted, the
command exits as skipped before package install, plugin sync, completion refresh,
or gateway restart work.
## Git checkout flow
Channels:
@@ -101,6 +109,11 @@ High-level:
8. Runs `openclaw doctor` as the final “safe update” check.
9. Syncs plugins to the active channel (dev uses bundled extensions; stable/beta uses npm) and updates npm-installed plugins.
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.
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.
## `--update` shorthand

View File

@@ -229,8 +229,20 @@ When enabled, the Gateway **Dreams** tab shows:
- a distinct grounded Scene lane for staged historical replay entries
- an expandable Dream Diary reader backed by `doctor.memory.dreamDiary`
## Troubleshooting
### Dreaming never runs (status shows blocked)
The managed dreaming cron rides the default agent's heartbeat. If heartbeat is not firing for that agent, the cron enqueues a system event that nobody consumes and dreaming silently does not run. Both `openclaw memory status` and `/dreaming status` will report `blocked` in that case and name the agent whose heartbeat is the blocker.
Two common causes:
- Another agent declares an explicit `heartbeat:` block. When any entry in `agents.list` has its own `heartbeat` block, only those agents heartbeat — the defaults stop applying to everyone else, so the default agent can go silent. Move the heartbeat settings to `agents.defaults.heartbeat`, or add an explicit `heartbeat` block on the default agent. See [Scope and precedence](/gateway/heartbeat#scope-and-precedence).
- `heartbeat.every` is `0`, empty, or unparseable. The cron has no interval to schedule against, so the heartbeat is effectively disabled. Set `every` to a positive duration such as `30m`. See [Defaults](/gateway/heartbeat#defaults).
## Related
- [Heartbeat](/gateway/heartbeat)
- [Memory](/concepts/memory)
- [Memory Search](/concepts/memory-search)
- [memory CLI](/cli/memory)

View File

@@ -10,22 +10,22 @@ title: "Features"
## Highlights
<Columns>
<Card title="Channels" icon="message-square">
<Card title="Channels" icon="message-square" href="/channels">
Discord, iMessage, Signal, Slack, Telegram, WhatsApp, WebChat, and more with a single Gateway.
</Card>
<Card title="Plugins" icon="plug">
<Card title="Plugins" icon="plug" href="/tools/plugin">
Bundled plugins add Matrix, Nextcloud Talk, Nostr, Twitch, Zalo, and more without separate installs in normal current releases.
</Card>
<Card title="Routing" icon="route">
<Card title="Routing" icon="route" href="/concepts/multi-agent">
Multi-agent routing with isolated sessions.
</Card>
<Card title="Media" icon="image">
<Card title="Media" icon="image" href="/nodes/images">
Images, audio, video, documents, and image/video generation.
</Card>
<Card title="Apps and UI" icon="monitor">
<Card title="Apps and UI" icon="monitor" href="/web/control-ui">
Web Control UI and macOS companion app.
</Card>
<Card title="Mobile nodes" icon="smartphone">
<Card title="Mobile nodes" icon="smartphone" href="/nodes">
iOS and Android nodes with pairing, voice/chat, and rich device commands.
</Card>
</Columns>

View File

@@ -167,6 +167,10 @@ Defaults live under `agents.defaults.silentReply` and
`agents.defaults.silentReplyRewrite`; `surfaces.<id>.silentReply` and
`surfaces.<id>.silentReplyRewrite` can override them per surface.
When the parent session has one or more pending spawned subagent runs, bare
silent replies are dropped on all surfaces instead of being rewritten, so the
parent stays quiet until the child completion event delivers the real reply.
## Related
- [Streaming](/concepts/streaming) — real-time message delivery

View File

@@ -173,7 +173,7 @@ Current bundled examples:
normalization (`input` / `output` and `prompt` / `completion` families), the
shared `openai-responses-defaults` stream family for native OpenAI/Codex
wrappers, provider-family metadata, bundled image-generation provider
registration for `gpt-image-1`, and bundled video-generation provider
registration for `gpt-image-2`, and bundled video-generation provider
registration for `sora-2`
- `google` and `google-gemini-cli`: Gemini 3.1 forward-compat fallback,
native Gemini replay validation, bootstrap replay sanitation, tagged
@@ -390,7 +390,8 @@ OpenClaw ships with the piai catalog. These providers require **no**
- Provider: `vercel-ai-gateway`
- Auth: `AI_GATEWAY_API_KEY`
- Example model: `vercel-ai-gateway/anthropic/claude-opus-4.6`
- Example models: `vercel-ai-gateway/anthropic/claude-opus-4.6`,
`vercel-ai-gateway/moonshotai/kimi-k2.6`
- CLI: `openclaw onboard --auth-choice ai-gateway-api-key`
### Kilo Gateway
@@ -411,7 +412,7 @@ See [/providers/kilocode](/providers/kilocode) for setup details.
### Other bundled provider plugins
- OpenRouter: `openrouter` (`OPENROUTER_API_KEY`)
- Example model: `openrouter/auto`
- Example models: `openrouter/auto`, `openrouter/moonshotai/kimi-k2.6`
- OpenClaw applies OpenRouter's documented app-attribution headers only when
the request actually targets `openrouter.ai`
- OpenRouter-specific Anthropic `cache_control` markers are likewise gated to

View File

@@ -67,6 +67,24 @@ to `zai/*`.
Provider configuration examples (including OpenCode) live in
[/providers/opencode](/providers/opencode).
### Safe allowlist edits
Use additive writes when updating `agents.defaults.models` by hand:
```bash
openclaw config set agents.defaults.models '{"openai-codex/gpt-5.4":{}}' --strict-json --merge
```
`openclaw config set` protects model/provider maps from accidental clobbers. A
plain object assignment to `agents.defaults.models`, `models.providers`, or
`models.providers.<id>.models` is rejected when it would remove existing
entries. Use `--merge` for additive changes; use `--replace` only when the
provided value should become the complete target value.
Interactive provider setup and `openclaw configure --section model` also merge
provider-scoped selections into the existing allowlist, so adding Codex,
Ollama, or another provider does not drop unrelated model entries.
## "Model is not allowed" (and why replies stop)
If `agents.defaults.models` is set, it becomes the **allowlist** for `/model` and for
@@ -114,6 +132,9 @@ Notes:
- `/model` (and `/model list`) is a compact, numbered picker (model family + available providers).
- On Discord, `/model` and `/models` open an interactive picker with provider and model dropdowns plus a Submit step.
- `/models add` is available by default and can be disabled with `commands.modelsWrite=false`.
- When enabled, `/models add <provider> <modelId>` is the fastest path; bare `/models add` starts a provider-first guided flow where supported.
- After `/models add`, the new model becomes available in `/models` and `/model` without restarting the gateway.
- `/model <#>` selects from that picker.
- `/model` persists the new session selection immediately.
- If the agent is idle, the next run uses the new model right away.
@@ -132,6 +153,14 @@ Notes:
Full command behavior/config: [Slash commands](/tools/slash-commands).
Examples:
```text
/models add
/models add ollama glm-5.1:cloud
/models add lmstudio qwen/qwen3.5-9b
```
## CLI commands
```bash
@@ -167,6 +196,10 @@ Shows configured models by default. Useful flags:
- `--plain`: one model per line
- `--json`: machinereadable output
`--all` includes bundled provider-owned static catalog rows before auth is
configured, so discovery-only views can show models that are unavailable until
you add matching provider credentials.
### `models status`
Shows the resolved primary model, fallbacks, image model, and an auth overview

View File

@@ -16,7 +16,7 @@ orchestrate sub-agents.
| Tool | What it does |
| ------------------ | --------------------------------------------------------------------------- |
| `sessions_list` | List sessions with optional filters (kind, recency) |
| `sessions_list` | List sessions with optional filters (kind, label, agent, recency, preview) |
| `sessions_history` | Read the transcript of a specific session |
| `sessions_send` | Send a message to another session and optionally wait |
| `sessions_spawn` | Spawn an isolated sub-agent session for background work |
@@ -26,9 +26,13 @@ orchestrate sub-agents.
## Listing and reading sessions
`sessions_list` returns sessions with their key, kind, channel, model, token
counts, and timestamps. Filter by kind (`main`, `group`, `cron`, `hook`,
`node`) or recency (`activeMinutes`).
`sessions_list` returns sessions with their key, agentId, kind, channel, model,
token counts, and timestamps. Filter by kind (`main`, `group`, `cron`, `hook`,
`node`), exact `label`, exact `agentId`, search text, or recency
(`activeMinutes`). When you need mailbox-style triage, it can also ask for
derived titles, last-message previews, or bounded recent messages. Preview
transcript reads are scoped to sessions visible under the configured session
tool visibility policy.
`sessions_history` fetches the conversation transcript for a specific session.
By default, tool results are excluded -- pass `includeTools: true` to see them.

View File

@@ -69,6 +69,10 @@ Sessions are reused until they expire:
When both daily and idle resets are configured, whichever expires first wins.
Sessions with an active provider-owned CLI session are not cut by the implicit
daily default. Use `/reset` or configure `session.reset` explicitly when those
sessions should expire on a timer.
## Where state lives
All session state is owned by the **gateway**. UI clients query the gateway for

View File

@@ -118,11 +118,12 @@ Modes:
### Channel mapping
| Channel | `off` | `partial` | `block` | `progress` |
| -------- | ----- | --------- | ------- | ----------------- |
| Telegram | ✅ | ✅ | ✅ | maps to `partial` |
| Discord | ✅ | ✅ | ✅ | maps to `partial` |
| Slack | ✅ | ✅ | ✅ | ✅ |
| Channel | `off` | `partial` | `block` | `progress` |
| ---------- | ----- | --------- | ------- | ----------------- |
| Telegram | ✅ | ✅ | ✅ | maps to `partial` |
| Discord | ✅ | ✅ | ✅ | maps to `partial` |
| Slack | ✅ | ✅ | ✅ | ✅ |
| Mattermost | ✅ | ✅ | ✅ | ✅ |
Slack-only:
@@ -148,12 +149,35 @@ Discord:
- Uses send + edit preview messages.
- `block` mode uses draft chunking (`draftChunk`).
- Preview streaming is skipped when Discord block streaming is explicitly enabled.
- Final media, error, and explicit-reply payloads cancel pending previews without flushing a new draft, then use normal delivery.
Slack:
- `partial` can use Slack native streaming (`chat.startStream`/`append`/`stop`) when available.
- `block` uses append-style draft previews.
- `progress` uses status preview text, then final answer.
- Final media/error payloads and progress finals do not create throwaway draft messages; only text/block finals that can edit the preview flush pending draft text.
Mattermost:
- Streams thinking, tool activity, and partial reply text into a single draft preview post that finalizes in place when the final answer is safe to send.
- Falls back to sending a fresh final post if the preview post was deleted or is otherwise unavailable at finalize time.
- Final media/error payloads cancel pending preview updates before normal delivery instead of flushing a temporary preview post.
Matrix:
- Draft previews finalize in place when the final text can reuse the preview event.
- Media-only, error, and reply-target-mismatch finals cancel pending preview updates before normal delivery; an already-visible stale preview is redacted.
### Tool-progress preview updates
Preview streaming can also include **tool-progress** updates — short status lines like "searching the web", "reading file", or "calling tool" — that appear in the same preview message while tools are running, ahead of the final reply. This keeps multi-step tool turns visually alive rather than silent between the first thinking preview and the final answer.
Supported surfaces:
- **Discord**, **Slack**, and **Telegram** stream tool-progress into the live preview edit.
- **Mattermost** already folds tool activity into its single draft preview post (see above).
- Tool-progress edits follow the active preview streaming mode; they are skipped when preview streaming is `off` or when block streaming has taken over the message.
## Related

View File

@@ -18,7 +18,8 @@ When `agents.defaults.typingMode` is **unset**, OpenClaw keeps the legacy behavi
- **Direct chats**: typing starts immediately once the model loop begins.
- **Group chats with a mention**: typing starts immediately.
- **Group chats without a mention**: typing starts only when message text begins streaming.
- **Heartbeat runs**: typing is disabled.
- **Heartbeat runs**: typing starts when the heartbeat run begins if the
resolved heartbeat target is a typing-capable chat and typing is not disabled.
## Modes
@@ -64,6 +65,11 @@ You can override mode or cadence per session:
matched case-insensitively).
- `thinking` only fires if the run streams reasoning (`reasoningLevel: "stream"`).
If the model doesnt emit reasoning deltas, typing wont start.
- Heartbeats never show typing, regardless of mode.
- Heartbeat typing is a liveness signal for the resolved delivery target. It
starts at heartbeat run start instead of following `message` or `thinking`
stream timing. Set `typingMode: "never"` to disable it.
- Heartbeats do not show typing when `target: "none"`, when the target cannot
be resolved, when chat delivery is disabled for the heartbeat, or when the
channel does not support typing.
- `typingIntervalSeconds` controls the **refresh cadence**, not the start time.
The default is 6 seconds.

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