Skip to content

fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps#42588

Merged
scoootscooob merged 2 commits intoopenclaw:mainfrom
MonkeyLeeT:fix/whatsapp-append-recent-messages
Mar 15, 2026
Merged

fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps#42588
scoootscooob merged 2 commits intoopenclaw:mainfrom
MonkeyLeeT:fix/whatsapp-append-recent-messages

Conversation

@MonkeyLeeT
Copy link
Copy Markdown
Contributor

@MonkeyLeeT MonkeyLeeT commented Mar 11, 2026

Summary

The channel-to-extensions refactor (extensions/whatsapp/) regressed the append upsert handling — it reverted to unconditionally skipping all "append" messages, losing the grace window fix from the original src/web/ code.

This PR restores the recency-based filter with an additional fix for protobuf Long timestamps:

  • Grace window: "append" messages within 60s of connection time are processed normally (not dropped as stale history sync)
  • Long-safe timestamps: Uses Number(msgTsRaw) instead of typeof === "number" check, which correctly handles both primitive numbers and protobuf Long objects via valueOf()

Changes

  • extensions/whatsapp/src/inbound/monitor.ts — replace unconditional continue with recency check using Number() conversion
  • extensions/whatsapp/src/monitor-inbox.append-upsert.test.ts — 5 tests covering: recent appends processed, old appends skipped, Long timestamps, null/undefined timestamps, non-append upserts unaffected

Test plan

  • 5 append upsert tests pass
  • Existing WhatsApp monitor tests unaffected

@openclaw-barnacle openclaw-barnacle bot added channel: whatsapp-web Channel integration: whatsapp-web size: S labels Mar 11, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR fixes silent dropping of WhatsApp append-type inbound messages after a Baileys gateway reconnect by introducing a 60-second recency window around connectedAtMs. The approach is sound, but there is a critical type-safety bug in the recency check that will cause the fix to fail in production for the exact scenario it targets.

Key findings:

  • Critical logic bug (monitor.ts line 421–423): Baileys types WAMessage.messageTimestamp as number | Long | null | undefined, where Long is a protobuf Long object. The guard typeof msgTsRaw === "number" returns false for Long values, causing msgTsMs to be set to 0. Since 0 < connectedAtMs - 60_000 is always true, every append message whose timestamp arrives as a Long will be skipped — i.e., silently dropped, exactly the bug this PR is meant to fix. The existing code directly above (line 205) already demonstrates the correct pattern: Number(msg.messageTimestamp) * 1000, which correctly calls Long.prototype.valueOf().
  • Test gap: All new test timestamps are plain JS numbers; none exercise the Long object path, so the type-guard bug is not caught by the new regression tests.
  • Existing test update (allows-messages-from-senders test) is correctly adjusted to use a stale timestamp — this change is clean and preserves the original test intent.

Confidence Score: 1/5

  • Not safe to merge — the type-guard bug will cause the fix to silently fail for Long-typed timestamps, which are the common case for group messages in production.
  • The core logic of the recency window is correct, but using typeof ... === "number" to guard against non-finite values also rejects protobuf Long objects, which Baileys commonly uses for message timestamps. This means the fix will not work for the primary affected scenario (group messages after reconnect) in real deployments. A one-line change (Number(msgTsRaw)) would fix it, but as written the PR does not solve the reported issue.
  • src/web/inbound/monitor.ts lines 421–423 — the Long type-guard bug

Last reviewed commit: a3e9280

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3e92800d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@MonkeyLeeT MonkeyLeeT changed the title fix(whatsapp): process recent append upserts instead of dropping all fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps Mar 15, 2026
@MonkeyLeeT MonkeyLeeT force-pushed the fix/whatsapp-append-recent-messages branch from 456022c to 64c3879 Compare March 15, 2026 01:19
@scoootscooob scoootscooob self-assigned this Mar 15, 2026
@scoootscooob scoootscooob force-pushed the fix/whatsapp-append-recent-messages branch from 4bc4957 to 444c081 Compare March 15, 2026 09:58
@scoootscooob scoootscooob requested review from a team and steipete as code owners March 15, 2026 09:58
@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation channel: bluebubbles Channel integration: bluebubbles channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser app: android App: android app: web-ui App: web-ui gateway Gateway runtime scripts Repository scripts commands Command implementations docker Docker and sandbox tooling agents Agent runtime and tooling channel: feishu Channel integration: feishu size: XL and removed size: S labels Mar 15, 2026
@scoootscooob scoootscooob force-pushed the fix/whatsapp-append-recent-messages branch from 444c081 to 09c0495 Compare March 15, 2026 10:02
@openclaw-barnacle openclaw-barnacle bot removed docs Improvements or additions to documentation channel: bluebubbles Channel integration: bluebubbles labels Mar 15, 2026
mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 15, 2026
* main: (26 commits)
  fix: preserve Telegram word boundaries when rechunking HTML (openclaw#47274)
  fix(gateway): skip Control UI pairing when auth.mode=none (closes openclaw#42931) (openclaw#47148)
  Deduplicate repeated tool call IDs for OpenAI-compatible APIs (openclaw#40996)
  fix(web): handle 515 Stream Error during WhatsApp QR pairing (openclaw#27910)
  fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps (openclaw#42588)
  fix(android): support android node  `calllog.search` (openclaw#44073)
  fix: forward forceDocument through sendPayload path (follow-up to openclaw#45111) (openclaw#47119)
  fix: Disable strict mode tools for non-native openai-completions compatible APIs (openclaw#45497)
  Docs: switch README logo to SVG assets (openclaw#47049)
  docs: replace outdated Clawdbot references with OpenClaw in skill docs (openclaw#41563)
  Docs: fix stale Clawdbot branding in agent workflow file (openclaw#46963)
  fix: harden compaction timeout follow-ups
  feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds (openclaw#46889)
  macOS: restrict canvas agent actions to trusted surfaces (openclaw#46790)
  Tlon: honor explicit empty allowlists and defer cite expansion (openclaw#46788)
  fix(context): skip eager warmup for non-model CLI commands
  fix(openrouter): silently dropped images for new OpenRouter models — runtime capability detection (openclaw#45824)
  External content: sanitize wrapped metadata (openclaw#46816)
  docs: reorder unreleased changelog
  fix(android): theme popup surfaces
  ...
dkdimou added a commit to dkdimou/openclaw that referenced this pull request Mar 15, 2026
* test: add parallels windows smoke harness

* fix: force-stop lingering gateway client sockets

* test: share gateway route auth helpers

* test: share browser route test helpers

* test: share gateway status auth fixtures

* test: share models list forward compat fixtures

* fix: tighten bonjour whitespace error coverage

* docs: reorder changelog highlights by user impact

* test: tighten proxy fetch helper coverage

* test: tighten path guard helper coverage

* test: tighten warning filter coverage

* test: tighten wsl detection coverage

* test: tighten system run command normalization coverage

* fix(feishu): preserve non-ASCII filenames in file uploads (#33912) (#34262)

* fix(feishu): preserve non-ASCII filenames in file uploads (#33912)

* style(feishu): format media test file

* fix(feishu): preserve UTF-8 filenames in file uploads (openclaw#34262) thanks @fabiaodemianyang

---------

Co-authored-by: Robin Waslander <[email protected]>

* test: tighten is-main helper coverage

* test: tighten json file helper coverage

* fix: resolve current ci regressions

* test: tighten backoff abort coverage

* docs(changelog): note upcoming security fixes

* test: tighten bonjour ciao coverage

* test: tighten channel activity account isolation

* test: tighten update channel display precedence

* test: tighten node list parse fallback coverage

* test: tighten package tag prefix matching

* test: tighten outbound identity normalization

* test: tighten outbound session context coverage

* macOS: respect exec-approvals.json settings in gateway prompter (#13707)

Fix macOS gateway exec approvals to respect exec-approvals.json.

This updates the macOS gateway prompter to resolve per-agent exec approval policy before deciding whether to show UI, use agentId for policy lookup, honor askFallback when prompts cannot be presented, and resolve no-prompt decisions from the configured security policy instead of hardcoded allow-once behavior. It also adds regression coverage for ask-policy and allowlist-fallback behavior, plus a changelog entry for the fix.

Co-authored-by: ImLukeF <[email protected]>

* fix: tighten target error hint coverage

* test: tighten prototype key matching

* test: tighten hostname normalization coverage

* fix(ui): keep oversized chat replies readable (#45559)

* fix(ui): keep oversized chat replies readable

* Update ui/src/ui/markdown.ts

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

* fix(ui): preserve oversized markdown whitespace

---------

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

* test: tighten openclaw exec env coverage

* fix: tighten pairing token blank handling

* test: tighten target error hint trimming

* test: tighten node shell platform normalization

* fix(gateway/ui): restore control-ui auth bypass and classify connect failures (#45512)

Merged via squash.

Prepared head SHA: 42b5595edec71897b479b3bbaa94bcb4ac6fab17
Co-authored-by: sallyom <[email protected]>
Co-authored-by: BunsDev <[email protected]>
Reviewed-by: @BunsDev

* fix(macos): prevent PortGuard from killing Docker Desktop in remote mode (#13798)

fix(macos): prevent PortGuardian from killing Docker Desktop in remote mode (#6755)

PortGuardian.sweep() was killing non-SSH processes holding the gateway
port in remote mode. When the gateway runs in a Docker container,
`com.docker.backend` owns the port-forward, so this could shut down
Docker Desktop entirely.

Changes:
- accept any process on the gateway port in remote mode
- add a defense-in-depth guard to skip kills in remote mode
- update remote-mode port diagnostics/reporting to match
- add regression coverage for Docker and local-mode behavior
- add a changelog entry for the fix

Co-Authored-By: ImLukeF <[email protected]>

* test: fix current ci regressions

* test: share outbound action runner helpers

* test: share telegram monitor startup helpers

* refactor: share self hosted provider plugin helpers

* test: share outbound delivery helpers

* refactor: share onboarding diagnostics type

* refactor: share delimited channel entry parsing

* refactor: share zalo send context validation

* refactor: share terminal note wrapping

* refactor: share tts request setup

* refactor: share gateway timeout parsing

* refactor: share session send context lines

* refactor: share memory tool builders

* refactor: share browser console result formatting

* refactor: share pinned sandbox entry finalization

* refactor: share tool result char estimation

* refactor: share agent tool fixture helpers

* test: share compaction retry timer helpers

* test: share embedded workspace attempt helpers

* refactor: share whatsapp outbound adapter base

* refactor: share zalo status issue helpers

* test: share whatsapp outbound poll fixtures

* refactor: share telegram reply chunk threading

* refactor: share daemon install cli setup

* fix: widen telegram reply progress typing

* refactor: share slack text truncation

* refactor: share allowlist wildcard matching

* refactor: declone model picker model ref parsing

* refactor: share dual text command gating

* test: share startup account lifecycle helpers

* test: share status issue assertion helpers

* fix: restore imessage control command flag

* test: share web fetch header helpers

* refactor: share session tool context setup

* test: share memory tool helpers

* refactor: share request url resolution

* Changelog: credit embedded runner queue deadlock fix

* fix(voicewake): avoid crash on foreign transcript ranges

* refactor(voicewake): mark transcript parameter unused

* docs(changelog): note voice wake crash fix

* fix: harden gateway status rpc smoke

* test: add parallels linux smoke harness

* fix(sessions): create transcript file on chat.inject when missing (#36645)

`chat.inject` called `appendAssistantTranscriptMessage` with
`createIfMissing: false`, causing a hard error when the transcript
file did not exist on disk despite having a valid `transcriptPath`
in session metadata. This commonly happens with ACP oneshot/run
sessions where the session entry is created but the transcript file
is not yet materialized.

The fix is a one-character change: `createIfMissing: true`. The
`ensureTranscriptFile` helper already handles directory creation
and file initialization safely.

Fixes #36170

Co-authored-by: Claude Opus 4.6 <[email protected]>

* fix: harden discord guild allowlist resolution

* chore: update dependencies

* Plugins: fail fast on channel and binding collisions (#45628)

* Plugins: reject duplicate channel ids

* Bindings: reject duplicate adapter registration

* Plugins: fail on export id mismatch

* feat: add node-connect skill

* test: share directory runtime helpers

* refactor: share open allowFrom config checks

* test: share send cfg threading helpers

* refactor: reduce extension channel setup duplication

* refactor: share extension channel status summaries

* test: share feishu startup mock modules

* test: share plugin api test harness

* refactor: share extension monitor runtime setup

* refactor: share extension deferred and runtime helpers

* test: share sandbox fs bridge seeded workspace

* test: share subagent gateway mock setup

* test: share models config merge helpers

* test: share workspace skills snapshot helpers

* test: share context lookup helpers

* test: share timeout failover assertions

* test: share model selection config helpers

* test: share provider discovery auth fixtures

* test: share subagent announce timeout helpers

* test: share workspace skill test helpers

* refactor: share exec host approval helpers

* test: share oauth profile fixtures

* test: share memory search config helpers

* fix(macos): align minimum Node.js version with runtime guard (22.16.0) (#45640)

* macOS: align minimum Node.js version with runtime guard

* macOS: add boundary and failure-message coverage for RuntimeLocator

* docs: add changelog note for the macOS runtime locator fix

* credit: original fix direction from @sumleo, cleaned up and rebased in #45640 by @ImLukeF

* fix(agents): preserve blank local custom-provider API keys after onboarding

Co-authored-by: Xinhua Gu <[email protected]>

* fix(browser): harden existing-session driver validation and session lifecycle (#45682)

* fix(browser): harden existing-session driver validation, session lifecycle, and code quality

Fix config validation rejecting existing-session profiles that lack
cdpPort/cdpUrl (they use Chrome MCP auto-connect instead). Fix callTool
tearing down the MCP session on tool-level errors (element not found,
script error), which caused expensive npx re-spawns. Skip unnecessary
CDP port allocation for existing-session profiles. Remove redundant
ensureChromeMcpAvailable call in isReachable.

Extract shared ARIA role sets (INTERACTIVE_ROLES, CONTENT_ROLES,
STRUCTURAL_ROLES) into snapshot-roles.ts so both the Playwright and
Chrome MCP snapshot paths stay in sync. Add usesChromeMcp capability
flag and replace ~20 scattered driver === "existing-session" string
checks with the centralized flag.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix(browser): harden existing-session driver validation and session lifecycle (#45682) (thanks @odysseus0)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>

* fix(ci): repair helper typing regressions

* fix: default Android TLS setup codes to port 443

* fix: unblock discord startup on deploy rate limits

* fix(feishu): add early event-level dedup to prevent duplicate replies (#43762)

* fix(feishu): add early event-level dedup to prevent duplicate replies

Add synchronous in-memory dedup at EventDispatcher handler level using
message_id as key with 5-minute TTL and 2000-entry cap.

This catches duplicate events immediately when they arrive from the Lark
SDK — before the inbound debouncer or processing queue — preventing the
race condition where two concurrent dispatches enter the pipeline before
either records the messageId in the downstream dedup layer.

Fixes the root cause reported in #42687.

* fix(feishu): correct inverted dedup condition

check() returns false on first call (new key) and true on subsequent
calls (duplicate). The previous `!check()` guard was inverted —
dropping every first delivery and passing all duplicates.

Remove the negation so the guard correctly drops duplicates.

* fix(feishu): simplify eventDedup key — drop redundant accountId prefix

eventDedup is already scoped per account (one instance per
registerEventHandlers call), so the accountId prefix in the cache key
is redundant. Use `evt:${messageId}` instead.

* fix(feishu): share inbound processing claim dedupe

---------

Co-authored-by: Tak Hoffman <[email protected]>

* fix(models): apply Gemini model-id normalization to google-vertex provider (#42435)

* fix(models): apply Gemini model-id normalization to google-vertex provider

The existing normalizeGoogleModelId() (which maps e.g. gemini-3.1-flash-lite
to gemini-3.1-flash-lite-preview) was only applied when the provider was
"google". Users configuring google-vertex/gemini-3.1-flash-lite would get
a "missing" model because the -preview suffix was never appended.

Extend the normalization to google-vertex in both model-selection
(parseModelRef path) and normalizeProviders (config normalization path).

Ref: https://github.com/openclaw/openclaw/issues/36838
Ref: https://github.com/openclaw/openclaw/pull/36918#issuecomment-4032732959


* fix(models): normalize google-vertex flash-lite

* fix(models): place unreleased changelog entry last

* fix(models): place unreleased changelog entry before releases

* fix(browser): add browser session selection

* build(android): add auto-bump signed aab release script

* build(android): strip unused dnsjava resolver service before R8

* test(discord): align rate limit error mock with carbon

* docs: fix changelog formatting

* fix: keep exec summaries inline

* build: shrink Android app release bundle

* Gateway: treat scope-limited probe RPC as degraded reachability (#45622)

* Gateway: treat scope-limited probe RPC as degraded

* Docs: clarify gateway probe degraded scope output

* test: fix CI type regressions in gateway and outbound suites

* Tests: fix Node24 diffs theme loading and Windows assertions

* Tests: fix extension typing after main rebase

* Tests: fix Windows CI regressions after rebase

* Tests: normalize executable path assertions on Windows

* Tests: remove duplicate gateway daemon result alias

* Tests: stabilize Windows approval path assertions

* Tests: fix Discord rate-limit startup fixture typing

* Tests: use Windows-friendly relative exec fixtures

---------

Co-authored-by: Mainframe <[email protected]>

* build: upload Android native debug symbols

* fix(browser): prefer user profile over chrome relay

* chore: bump pi to 0.58.0

* test: harden parallels all-os smoke harness

* fix: keep windows onboarding logs ascii-safe

* docs: reorder unreleased changelog by impact

* build: prepare 2026.3.13-beta.1

* ci: add npm token fallback for npm releases

* fix(gateway): bound unanswered client requests (#45689)

* fix(gateway): bound unanswered client requests

* fix(gateway): skip default timeout for expectFinal requests

* fix(gateway): preserve gateway call timeouts

* fix(gateway): localize request timeout policy

* fix(gateway): clamp explicit request timeouts

* fix(gateway): clamp default request timeout

* Revert "Browser: scope nested batch failures in switch"

This reverts commit aaeb348bb7cbbaebe14a471776909bff129499a3.

* build: prepare 2026.3.13 release

* fix: keep android canvas home visible after restart

* chore: update appcast for 2026.3.13 release

* fix(browser): restore batch playwright dispatch

* feat(cron): support custom session IDs and auto-bind to current session (#16511)

feat(cron): support persistent session targets for cron jobs (#9765)

Add support for `sessionTarget: "current"` and `session:<id>` so cron jobs can
bind to the creating session or a persistent named session instead of only
`main` or ephemeral `isolated` sessions.

Also:
- preserve custom session targets across reloads and restarts
- update gateway validation and normalization for the new target forms
- add cron coverage for current/custom session targets and fallback behavior
- fix merged CI regressions in Discord and diffs tests
- add a changelog entry for the new cron session behavior

Co-authored-by: kkhomej33-netizen <[email protected]>
Co-authored-by: ImLukeF <[email protected]>

* test: harden parallels beta smoke flows

* build: prepare 2026.3.14 cycle

* build: sync plugins for 2026.3.14

* build: refresh lockfile for plugin sync

* fix(agents): normalize abort-wrapped RESOURCE_EXHAUSTED into failover errors (#11972)

* fix: move cause-chain traversal before timeout heuristic (review feedback)

* fix: harden wrapped rate-limit failover (openclaw#39820) thanks @lupuletic

* style: format probe regression test (openclaw#39820) thanks @lupuletic

* fix: tighten runner failover test types (openclaw#39820) thanks @lupuletic

* fix: annotate shared failover mocks (openclaw#39820) thanks @lupuletic

* test(ci): isolate cron heartbeat delivery cases

* fix(mattermost): carry thread context to non-inbound reply paths (#44283)

Merged via squash.

Prepared head SHA: 2846a6cfa959019d3ed811ccafae6b757db3bdf3
Co-authored-by: teconomix <[email protected]>
Co-authored-by: mukhtharcm <[email protected]>
Reviewed-by: @mukhtharcm

* refactor: make OutboundSendDeps dynamic with channel-ID keys (#45517)

* refactor: make OutboundSendDeps dynamic with channel-ID keys

Replace hardcoded per-channel send fields (sendTelegram, sendDiscord,
etc.) with a dynamic index-signature type keyed by channel ID. This
unblocks moving channel implementations to extensions without breaking
the outbound dispatch contract.

- OutboundSendDeps and CliDeps are now { [channelId: string]: unknown }
- Each outbound adapter resolves its send fn via bracket access with cast
- Lazy-loading preserved via createLazySender with module cache
- Delete 6 deps-send-*.runtime.ts one-liner re-export files
- Harden guardrail scan against deleted-but-tracked files


* fix: preserve outbound send-deps compatibility

* style: fix formatting issues (import order, extra bracket, trailing whitespace)



* fix: resolve type errors from dynamic OutboundSendDeps in tests and extension

* fix: remove unused OutboundSendDeps import from deliver.test-helpers

* refactor(signal): move Signal channel code to extensions/signal/src/ (#45531)

Move all Signal channel implementation files from src/signal/ to
extensions/signal/src/ and replace originals with re-export shims.
This continues the channel plugin migration pattern used by other
extensions, keeping backward compatibility via shims while the real
code lives in the extension.

- Copy 32 .ts files (source + tests) to extensions/signal/src/
- Transform all relative import paths for the new location
- Create 2-line re-export shims in src/signal/ for each moved file
- Preserve existing extension files (channel.ts, runtime.ts, etc.)
- Change tsconfig.plugin-sdk.dts.json rootDir from "src" to "."
  to support cross-boundary re-exports from extensions/

* refactor: move iMessage channel to extensions/imessage (#45539)

* refactor: move WhatsApp channel implementation to extensions/ (#45725)

* refactor: move WhatsApp channel from src/web/ to extensions/whatsapp/

Move all WhatsApp implementation code (77 source/test files + 9 channel
plugin files) from src/web/ and src/channels/plugins/*/whatsapp* to
extensions/whatsapp/src/.

- Leave thin re-export shims at all original locations so cross-cutting
  imports continue to resolve
- Update plugin-sdk/whatsapp.ts to only re-export generic framework
  utilities; channel-specific functions imported locally by the extension
- Update vi.mock paths in 15 cross-cutting test files
- Rename outbound.ts -> send.ts to match extension naming conventions
  and avoid false positive in cfg-threading guard test
- Widen tsconfig.plugin-sdk.dts.json rootDir to support shim->extension
  cross-directory references

Part of the core-channels-to-extensions migration (PR 6/10).

* style: format WhatsApp extension files

* fix: correct stale import paths in WhatsApp extension tests

Fix vi.importActual, test mock, and hardcoded source paths that weren't
updated during the file move:
- media.test.ts: vi.importActual path
- onboarding.test.ts: vi.importActual path
- test-helpers.ts: test/mocks/baileys.js path
- monitor-inbox.test-harness.ts: incomplete media/store mock
- login.test.ts: hardcoded source file path
- message-action-runner.media.test.ts: vi.mock/importActual path

* refactor(slack): move Slack channel code to extensions/slack/src/ (#45621)

Move all Slack channel implementation files from src/slack/ to
extensions/slack/src/ and replace originals with shim re-exports.
This follows the extension migration pattern for channel plugins.

- Copy all .ts files to extensions/slack/src/ (preserving directory
  structure: monitor/, http/, monitor/events/, monitor/message-handler/)
- Transform import paths: external src/ imports use relative paths
  back to src/, internal slack imports stay relative within extension
- Replace all src/slack/ files with shim re-exports pointing to
  the extension copies
- Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." so
  the DTS build can follow shim chains into extensions/
- Update write-plugin-sdk-entry-dts.ts re-export path accordingly
- Preserve extensions/slack/index.ts, package.json, openclaw.plugin.json,
  src/channel.ts, src/runtime.ts, src/channel.test.ts (untouched)

* refactor: move Telegram channel implementation to extensions/ (#45635)

* refactor: move Telegram channel implementation to extensions/telegram/src/

Move all Telegram channel code (123 files + 10 bot/ files + 8 channel plugin
files) from src/telegram/ and src/channels/plugins/*/telegram.ts to
extensions/telegram/src/. Leave thin re-export shims at original locations so
cross-cutting src/ imports continue to resolve.

- Fix all relative import paths in moved files (../X/ -> ../../../src/X/)
- Fix vi.mock paths in 60 test files
- Fix inline typeof import() expressions
- Update tsconfig.plugin-sdk.dts.json rootDir to "." for cross-directory DTS
- Update write-plugin-sdk-entry-dts.ts for new rootDir structure
- Move channel plugin files with correct path remapping

* fix: support keyed telegram send deps

* fix: sync telegram extension copies with latest main

* fix: correct import paths and remove misplaced files in telegram extension

* fix: sync outbound-adapter with main (add sendTelegramPayloadMessages) and fix delivery.test import path

* refactor: move Discord channel implementation to extensions/ (#45660)

* refactor: move Discord channel implementation to extensions/discord/src/

Move all Discord source files from src/discord/ to extensions/discord/src/,
following the extension migration pattern. Source files in src/discord/ are
replaced with re-export shims. Channel-plugin files from
src/channels/plugins/*/discord* are similarly moved and shimmed.

- Copy all .ts source files preserving subdirectory structure (monitor/, voice/)
- Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues)
- Fix all relative imports to use correct paths from new location
- Create re-export shims at original locations for backward compatibility
- Delete test files from shim locations (tests live in extension now)
- Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate
  extension files outside src/
- Update write-plugin-sdk-entry-dts.ts to match new declaration output paths

* fix: add importOriginal to thread-bindings session-meta mock for extensions test

* style: fix formatting in thread-bindings lifecycle test

* refactor: remove channel shim directories, point all imports to extensions (#45967)

* refactor: remove channel shim directories, point all imports to extensions

Delete the 6 backward-compat shim directories (src/telegram, src/discord,
src/slack, src/signal, src/imessage, src/web) that were re-exporting from
extensions. Update all 112+ source files to import directly from
extensions/{channel}/src/ instead of through the shims.

Also:
- Move src/channels/telegram/ (allow-from, api) to extensions/telegram/src/
- Fix outbound adapters to use resolveOutboundSendDep (fixes 5 pre-existing TS errors)
- Update cross-extension imports (src/web/media.js → extensions/whatsapp/src/media.js)
- Update vitest, tsdown, knip, labeler, and script configs for new paths
- Update guard test allowlists for extension paths

After this, src/ has zero channel-specific implementation code — only the
generic plugin framework remains.

* fix: update raw-fetch guard allowlist line numbers after shim removal

* refactor: document direct extension channel imports

* test: mock transcript module in delivery helpers

* fix(zai): align explicit coding endpoint setup with detected model defaults (#45969)

* fix: align Z.AI coding onboarding with endpoint docs

* fix: align Z.AI coding onboarding with endpoint docs (#45969)

* docs: mark memory bootstrap change as breaking

* fix(ui): session dropdown shows label instead of key (#45130)

Merged via squash.

Prepared head SHA: 0255e3971b06b3641e6b26590eaa22a900079517
Co-authored-by: luzhidong <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Reviewed-by: @altaywtf

* feat: add --force-document to message.send for Telegram (bypass sendPhoto + image optimizer) (#45111)

* feat: add --force-document to message.send for Telegram

Adds --force-document CLI flag to bypass sendPhoto and use sendDocument
instead, avoiding Telegram image compression for PNG/image files.

- TelegramSendOpts: add forceDocument field
- send.ts: skip sendPhoto when forceDocument=true (mediaSender pattern)
- ChannelOutboundContext: add forceDocument field
- telegramOutbound.sendMedia: pass forceDocument to sendMessageTelegram
- ChannelHandlerParams / DeliverOutboundPayloadsCoreParams: add forceDocument
- createChannelOutboundContextBase: propagate forceDocument
- outbound-send-service.ts: add forceDocument to executeSendAction params
- message-action-runner.ts: read forceDocument from params
- message.ts: add forceDocument to MessageSendParams
- register.send.ts: add --force-document CLI option

* fix: pass forceDocument through telegram action dispatch path

The actual send path goes through dispatchChannelMessageAction ->
telegramMessageActions.handleAction -> handleTelegramAction, not
deliverOutboundPayloads. forceDocument was not being read in
readTelegramSendParams or passed to sendMessageTelegram.

* fix: apply forceDocument to GIF branch to avoid sendAnimation

* fix: add disable_content_type_detection=true to sendDocument for --force-document

* fix: add forceDocument to buildSendSchema for agent discoverability

* fix: scope telegram force-document detection

* test: fix heartbeat target helper typing

* fix: skip image optimization when forceDocument is set

* fix: persist forceDocument in WAL queue for crash-recovery replay

* test: tighten heartbeat target test entry typing

---------

Co-authored-by: thepagent <[email protected]>
Co-authored-by: Frank Yang <[email protected]>

* Update CONTRIBUTING.md

* ci: add dry-run gate to npm release workflow

* ci: make npm release preview more verbose

* ci: preserve manual npm release approval delays

* docs: clarify npm release preview and publish flow

* ci: switch npm release workflow to trusted publishing

* chore: add code owners for npm release paths

* ci: enforce calver freshness on npm publish

* ci: move Docker release to GitHub-hosted runners (#46247)

* ci: move docker release to GitHub-hosted runners

* ci: annotate docker release runner guardrails

* Add /btw side questions (#45444)

* feat(agent): add /btw side questions

* fix(agent): gate and log /btw reviews

* feat(btw): isolate side-question delivery

* test(reply): update route reply runtime mocks

* fix(btw): complete side-result delivery across clients

* fix(gateway): handle streamed btw side results

* fix(telegram): unblock btw side questions

* fix(reply): make external btw replies explicit

* fix(chat): keep btw side results ephemeral in internal history

* fix(btw): address remaining review feedback

* fix(chat): preserve btw history on mobile refresh

* fix(acp): keep btw replies out of prompt history

* refactor(btw): narrow side questions to live channels

* fix(btw): preserve channel typing indicators

* fix(btw): keep side questions isolated in chat

* fix(outbound): restore typed channel send deps

* fix(btw): avoid blocking replies on transcript persistence

* fix(btw): keep side questions fast

* docs(commands): document btw slash command

* docs(changelog): add btw side questions entry

* test(outbound): align session transcript mocks

* ci: add manual backfill support to Docker release (#46269)

* ci: add docker release backfill workflow

* ci: add manual backfill support to docker release

* ci: keep docker latest tags off manual backfills

* Fix configure startup stalls from outbound send-deps imports (#46301)

* fix: avoid configure startup plugin stalls

* fix: credit configure startup changelog entry

* fix(btw): stop persisting side questions (#46328)

* fix(btw): stop persisting side questions

* docs(btw): document side-question behavior

* Slack: preserve interactive reply blocks in DMs (#45890)

* Slack: forward reply blocks in DM delivery

* Slack: preserve reply blocks in preview finalization

* Slack: cover block-only DM replies

* Changelog: note Slack interactive reply fix

* docs(nav): move btw to end of built-in tools (#46416)

* Docs: sweep recent user-facing updates (#46424)

* Docs: document Telegram force-document sends

* Docs: note Telegram document send behavior

* Docs: clarify memory file precedence

* Docs: align default AGENTS memory guidance

* Docs: update workspace FAQ memory note

* Docs: document gateway status require-rpc

* Docs: add require-rpc to gateway CLI index

* docs: add ademczuk to maintainers list

* fix(feishu): clear stale streamingStartPromise on card creation failure

Fixes #43322

* fix(feishu): clear stale streamingStartPromise on card creation failure

When FeishuStreamingSession.start() throws (HTTP 400), the catch block
sets streaming = null but leaves streamingStartPromise dangling. The
guard in startStreaming() checks streamingStartPromise first, so all
future deliver() calls silently skip streaming - the session locks
permanently.

Clear streamingStartPromise in the catch block so subsequent messages
can retry streaming instead of dropping all future replies.

Fixes #43322

* test(feishu): wrap push override in try/finally for cleanup safety

* fix(gateway): skip device pairing when auth.mode=none

Fixes #42931

When gateway.auth.mode is set to "none", authentication succeeds with
method "none" but sharedAuthOk remains false because the auth-context
only recognises token/password/trusted-proxy methods. This causes all
pairing-skip conditions to fail, so Control UI browser connections get
closed with code 1008 "pairing required" despite auth being disabled.

Short-circuit the skipPairing check: if the operator explicitly
disabled authentication, device pairing (which is itself an auth
mechanism) must also be bypassed.

Fixes #42931

* fix(auth): clear stale lockout state when user re-authenticates

Fixes #43057

* fix(auth): clear stale lockout on re-login

Clear stale `auth_permanent` and `billing` disabled state for all
profiles matching the target provider when `openclaw models auth login`
is invoked, so users locked out by expired or revoked OAuth tokens can
recover by re-authenticating instead of waiting for the cooldown timer.

Uses the agent-scoped store (`loadAuthProfileStoreForRuntime`) for
correct multi-agent profile resolution and wraps the housekeeping in
try/catch so corrupt store files never block re-authentication.

Fixes #43057

* test(auth): remove unnecessary non-null assertions

oxlint no-unnecessary-type-assertion: invocationCallOrder[0]
already returns number, not number | undefined.

* fix(ci): update vitest configs after channel move to extensions/ (openclaw#46066)

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* fix(gateway/cli): relax local backend self-pairing and harden launchd restarts (#46290)

Signed-off-by: sallyom <[email protected]>

* ci: allow fallback npm correction tags (#46486)

* fix(agents): restore usage tracking for non-native openai-completions providers

Fixes #46142

Stop forcing supportsUsageInStreaming=false on non-native openai-completions
endpoints. Most OpenAI-compatible APIs (DashScope, DeepSeek, Groq, Together,
etc.) handle stream_options: { include_usage: true } correctly. The blanket
disable broke usage/cost tracking for all non-OpenAI providers.

supportsDeveloperRole is still forced off for non-native endpoints since
the developer message role is genuinely OpenAI-specific.

Users on backends that reject stream_options can opt out with
compat.supportsUsageInStreaming: false in their model config.

Fixes #46142

* Fix test environment regressions on main

* fix(node): remove debug console.log on node host startup

Fixes #46411

Fixes #46411

* style(gateway): fix oxfmt formatting and remove unused test helper

* Fix full local gate on main

* Security: add secops ownership for sensitive paths (#46440)

* Meta: add secops ownership for sensitive paths

* Docs: restrict Codeowners-managed security edits

* Meta: guide agents away from secops-owned paths

* Meta: broaden secops CODEOWNERS coverage

* Meta: narrow secops workflow ownership

* Docs: add config drift baseline statefile (#45891)

* Docs: add config drift statefile generator

* Docs: generate config drift baseline

* CI: move config docs drift runner into workflow sanity

* Docs: emit config drift baseline json

* Docs: commit config drift baseline json

* Docs: wire config baseline into release checks

* Config: fix baseline drift walker coverage

* Docs: regenerate config drift baselines

* UI: surface gateway restart reasons in dashboard disconnect state (#46580)

* UI: surface gateway shutdown reason

* UI: add gateway restart disconnect tests

* Changelog: add dashboard restart reason fix

* UI: cover reconnect shutdown state

* fix(deps): update package yauzl

* feat(browser): add headless existing-session MCP support esp for Linux/Docker/VPS (#45769)

* fix(browser): prefer managed default profile in headless mode

* test(browser): cover headless default profile fallback

* feat(browser): support headless MCP profile resolution

* feat(browser): add headless and target-url Chrome MCP modes

* feat(browser): allow MCP target URLs in profile creation

* docs(browser): document headless MCP existing-session flows

* fix(browser): restore playwright browser act helpers

* fix(browser): preserve strict selector actions

* docs(changelog): add existing-session MCP note

* revert: 9bffa3422c4dc13f5c72ab5d2813cc287499cc14

* browser: drop chrome-relay auto-creation, simplify to user profile only (#46596)

Merged via squash.

Prepared head SHA: 74becc8f7dac245a345d2c7d549f604344df33fd
Co-authored-by: odysseus0 <[email protected]>
Co-authored-by: odysseus0 <[email protected]>
Reviewed-by: @odysseus0

* chore: regenerate config baseline (#46598)

* browser: drop headless/remote MCP attach modes, simplify existing-session to autoConnect-only (#46628)

* fix(feishu): fetch thread context so AI can see bot replies in topic threads (#45254)

* fix(feishu): fetch thread context so AI can see bot replies in topic threads

When a user replies in a Feishu topic thread, the AI previously could only
see the quoted parent message but not the bot's own prior replies in the
thread. This made multi-turn conversations in threads feel broken.

- Add `threadId` (omt_xxx) to `FeishuMessageInfo` and `getMessageFeishu`
- Add `listFeishuThreadMessages()` using `container_id_type=thread` API
  to fetch all messages in a thread including bot replies
- In `handleFeishuMessage`, fetch ThreadStarterBody and ThreadHistoryBody
  for topic session modes and pass them to the AI context
- Reuse quoted message result when rootId === parentId to avoid redundant
  API calls; exclude root message from thread history to prevent duplication
- Fall back to inbound ctx.threadId when rootId is absent or API fails
- Fetch newest messages first (ByCreateTimeDesc + reverse) so long threads
  keep the most recent turns instead of the oldest

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(feishu): skip redundant thread context injection on subsequent turns

Only inject ThreadHistoryBody on the first turn of a thread session.
On subsequent turns the session already contains prior context, so
re-injecting thread history (and starter) would waste tokens.

The heuristic checks whether the current user has already sent a
non-root message in the thread — if so, the session has prior turns
and thread context injection is skipped entirely.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(feishu): handle thread_id-only events in prior-turn detection

When ctx.rootId is undefined (thread_id-only events), the starter
message exclusion check `msg.messageId !== ctx.rootId` was always
true, causing the first follow-up to be misclassified as a prior
turn. Fall back to the first message in the chronologically-sorted
thread history as the starter.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(feishu): bootstrap topic thread context via session state

* test(memory): pin remote embedding hostnames in offline suites

* fix(feishu): use plugin-safe session runtime for thread bootstrap

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* fix: persist context-engine auto-compaction counts (#42629)

Merged via squash.

Prepared head SHA: df8f292039e27edec45b8ed2ad65ab0ac7f56194
Co-authored-by: uf-hy <[email protected]>
Co-authored-by: jalehman <[email protected]>
Reviewed-by: @jalehman

* feat(feishu): add reasoning stream support to streaming cards (openclaw#46029)

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: day253 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* Heartbeat: add isolatedSession option for fresh session per heartbeat run (#46634)

Reuses the cron isolated session pattern (resolveCronSession with forceNew)
to give each heartbeat a fresh session with no prior conversation history.
Reduces per-heartbeat token cost from ~100K to ~2-5K tokens.

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>

* revert: restore supportsUsageInStreaming=false default for non-native endpoints

Reverts #46500. Breaks Ollama, LM Studio, TGI, LocalAI, Mistral API -
these backends reject stream_options with 400/422.

This reverts commit bb06dc7cc9e71fbac29d7888d64323db2acec7ca.

* Fix Codex CLI auth profile sync (#45353)

Merged via squash.

Prepared head SHA: e5432ec4e1685a78ca7251bc71f26c1f17355a15
Co-authored-by: Gugu-sugar <[email protected]>
Co-authored-by: grp06 <[email protected]>
Reviewed-by: @grp06

* fix(zalo): use plugin-sdk export for webhook client IP resolution (openclaw#46549)

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Tomáš Dinh <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* fix(gateway): remove re-introduced auth.mode=none pairing bypass

The revert of #43478 (commit 39b4185d0b) was silently undone by
3704293e6f which was based on a branch that included the original
change. This removes the auth.mode=none skipPairing condition again.

The blanket skip was too broad - it disabled pairing for ALL websocket
clients, not just Control UI behind reverse proxies.

* fix(feishu): keep sender-scoped thread bootstrap across id types (#46651)

* feat(webchat): add toggle to hide tool calls and thinking blocks (#20317) thanks @nmccready

Merged via maintainer override after review.\n\nRed required checks are unrelated to this PR; local inspection found no blocker in the diff.

* fix(zalouser): stop inheriting dm allowlist for groups (#46663)

* docs: remove dead security README nav entry (#46675)

Merged via squash.

Prepared head SHA: 63331a54b8a6d50950a6ca85774fa1d915cd4e8d
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark

* feat(provider): support new model zai glm-5-turbo, performs better for  openclaw (openclaw#46670)

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: tomsun28 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* fix: validate edge tts output file is non-empty before reporting success (#43385) thanks @Huntterxx

Merged after review.\n\nSmall, scoped fix: treat 0-byte Edge TTS output as failure so provider fallback can continue.

* Add Feishu reactions and card action support (#46692)

* Add Feishu reactions and card action support

* Tighten Feishu action handling

* feat(feishu): structured cards with identity header, note footer, and streaming enhancements (openclaw#29938)

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: nszhsl <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* Docs: fix MDX markers blocking page refreshes (#46695)

Merged via squash.

Prepared head SHA: 56b25a9fb3acc1a3befbf33c28a6d27df8aca8ef
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark

* fix(plugins): prefer explicit installs over bundled duplicates (#46722)

* fix(plugins): prefer explicit installs over bundled duplicates

* test(feishu): mock structured card sends in outbound tests

* fix(plugins): align duplicate diagnostics with loader precedence

* feat(gateway): make health monitor stale threshold and max restarts configurable (openclaw#42107)

Verified:
- pnpm exec vitest --run src/config/config-misc.test.ts -t "gateway.channelHealthCheckMinutes"
- pnpm exec vitest --run src/gateway/server-channels.test.ts -t "health monitor"
- pnpm exec vitest --run src/gateway/channel-health-monitor.test.ts src/gateway/server/readiness.test.ts
- pnpm exec vitest --run extensions/feishu/src/outbound.test.ts
- pnpm exec tsc --noEmit

Co-authored-by: rstar327 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* docker: add lsof to runtime image (#46636)

* fix(gateway): harden health monitor account gating (#46749)

* gateway: harden health monitor account gating

* gateway: tighten health monitor account-id guard

* feat(android): add dark theme (#46249)

* Android: add mobile dark theme

* Android: fix remaining dark mode card surfaces

* Android: address dark mode review comments

* fix(android): theme onboarding flow

* fix: add Android dark theme coverage (#46249) (thanks @sibbl)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* fix(android): theme popup surfaces

* docs: reorder unreleased changelog

* External content: sanitize wrapped metadata (#46816)

* fix(openrouter): silently dropped images for new OpenRouter models — runtime capability detection (#45824)

* fix: fetch OpenRouter model capabilities at runtime for unknown models

When an OpenRouter model is not in the built-in static snapshot from
pi-ai, the fallback hardcodes input: ["text"], silently dropping images.

Query the OpenRouter API at runtime to detect actual capabilities
(image support, reasoning, context window) for models not in the
built-in list. Results are cached in memory for 1 hour. On API
failure/timeout, falls back to text-only (no regression).

* feat(openrouter): add disk cache for OpenRouter model capabilities

Persist the OpenRouter model catalog to ~/.openclaw/cache/openrouter-models.json
so it survives process restarts. Cache lookup order:

1. In-memory Map (instant)
2. On-disk JSON file (avoids network on restart)
3. OpenRouter API fetch (populates both layers)

Also triggers a background refresh when a model is not found in the cache,
in case it was newly added to OpenRouter.

* refactor(openrouter): remove pre-warm, use pure lazy-load with disk cache

- Remove eager ensureOpenRouterModelCache() from run.ts
- Remove TTL — model capabilities are stable, no periodic re-fetching
- Cache lookup: in-memory → disk → API fetch (only when needed)
- API is only called when no cache exists or a model is not found
- Disk cache persists across gateway restarts

* fix(openrouter): address review feedback

- Fix timer leak: move clearTimeout to finally block
- Fix modality check: only check input side of "->" separator to avoid
  matching image-generation models (text->image)
- Use resolveStateDir() instead of hardcoded homedir()/.openclaw
- Separate cache dir and filename constants
- Add utf-8 encoding to writeFileSync for consistency
- Add data validation when reading disk cache

* ci: retrigger checks

* fix: preload unknown OpenRouter model capabilities before resolve

* fix: accept top-level OpenRouter max token metadata

* fix: update changelog for OpenRouter runtime capability lookup (#45824) (thanks @DJjjjhao)

* fix: avoid redundant OpenRouter refetches and preserve suppression guards

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* fix(context): skip eager warmup for non-model CLI commands

* Tlon: honor explicit empty allowlists and defer cite expansion (#46788)

* Tlon: fail closed on explicit empty allowlists

* Tlon: preserve cited content for owner DMs

* macOS: restrict canvas agent actions to trusted surfaces (#46790)

* macOS: restrict canvas agent actions to trusted surfaces

* Changelog: note trusted macOS canvas actions

* macOS: encode allowed canvas schemes as JSON

* feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds (#46889)

* feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds

The hardcoded 5-minute (300s) compaction timeout causes large sessions
to enter a death spiral where compaction repeatedly fails and the
session grows indefinitely. This adds agents.defaults.compaction.timeoutSeconds
to allow operators to override the compaction safety timeout.

Default raised to 900s (15min) which is sufficient for sessions up to
~400k tokens. The resolved timeout is also used for the session write
lock duration so locks don't expire before compaction completes.

Fixes #38233

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* test: add resolveCompactionTimeoutMs tests

Cover config resolution edge cases: undefined config, missing
compaction section, valid seconds, fractional values, zero,
negative, NaN, and Infinity.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: add timeoutSeconds to compaction Zod schema

The compaction object schema uses .strict(), so setting the new
timeoutSeconds config option would fail validation at startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: enforce integer constraint on compaction timeoutSeconds schema

Prevents sub-second values like 0.5 which would floor to 0ms and
cause immediate compaction timeout. Matches pattern of other
integer timeout fields in the schema.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: clamp compaction timeout to Node timer-safe maximum

Values above ~2.1B ms overflow Node's setTimeout to 1ms, causing
immediate timeout. Clamp to MAX_SAFE_TIMEOUT_MS matching the
pattern in agents/timeout.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: add FIELD_LABELS entry for compaction timeoutSeconds

Maintains label/help parity invariant enforced by
schema.help.quality.test.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: align compaction timeouts with abort handling

* fix: land compaction timeout handling (#46889) (thanks @asyncjason)

---------

Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: harden compaction timeout follow-ups

* Docs: fix stale Clawdbot branding in agent workflow file (#46963)

Co-authored-by: webdevpraveen <[email protected]>

* docs: replace outdated Clawdbot references with OpenClaw in skill docs (#41563)

Update 5 references to the old "Clawdbot" name in
skills/apple-reminders/SKILL.md and skills/imsg/SKILL.md.

Co-authored-by: imanisynapse <[email protected]>

* Docs: switch README logo to SVG assets (#47049)

* fix: Disable strict mode tools for non-native openai-completions compatible APIs (#45497)

Merged via squash.

Prepared head SHA: 20fe05fe747821455c020521e5c2072b368713d8
Co-authored-by: sahancava <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* fix: forward forceDocument through sendPayload path (follow-up to #45111) (#47119)

Merged via squash.

Prepared head SHA: d791190f8303c664cea8737046eb653c0514e939
Co-authored-by: thepagent <[email protected]>
Reviewed-by: @frankekn

* fix(android): support android node  `calllog.search` (#44073)

* fix(android): support android node  `calllog.search`

* fix(android): support android node calllog.search

* fix(android): wire callLog through shared surfaces

* fix: land Android callLog support (#44073) (thanks @lxk7280)

---------

Co-authored-by: lixuankai <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps (#42588)

Merged via squash.

Prepared head SHA: 8ce59bb7153c1717dad4022e1cfd94857be53324
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob

* fix(web): handle 515 Stream Error during WhatsApp QR pairing (#27910)

* fix(web): handle 515 Stream Error during WhatsApp QR pairing

getStatusCode() never unwrapped the lastDisconnect wrapper object,
so login.errorStatus was always undefined and the 515 restart path
in restartLoginSocket was dead code.

- Add err.error?.output?.statusCode fallback to getStatusCode()
- Export waitForCredsSaveQueue() so callers can await pending creds
- Await creds flush in restartLoginSocket before creating new socket

Fixes #3942

* test: update session mock for getStatusCode unwrap + waitForCredsSaveQueue

Mirror the getStatusCode fix (err.error?.output?.statusCode fallback)
in the test mock and export waitForCredsSaveQueue so restartLoginSocket
tests work correctly.

* fix(web): scope creds save queue per-authDir to avoid cross-account blocking

The credential save queue was a single global promise chain shared by all
WhatsApp accounts. In multi-account setups, a slow save on one account
blocked credential writes and 515 restart recovery for unrelated accounts.

Replace the global queue with a per-authDir Map so each account's creds
serialize independently. waitForCredsSaveQueue() now accepts an optional
authDir to wait on a single account's queue, or waits on all when omitted.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* test: use real Baileys v7 error shape in 515 restart test

The test was using { output: { statusCode: 515 } } which was already
handled before the fix. Updated to use the actual Baileys v7 shape
{ error: { output: { statusCode: 515 } } } to cover the new fallback
path in getStatusCode.

Co-Authored-By: Claude Code (Opus 4.6) <[email protected]>

* fix(web): bound credential-queue wait during 515 restart

Prevents restartLoginSocket from blocking indefinitely if a queued
saveCreds() promise stalls (e.g. hung filesystem write).

Co-Authored-By: Claude <[email protected]>

* fix: clear flush timeout handle and assert creds queue in test

Co-Authored-By: Claude <[email protected]>

* fix: evict settled credsSaveQueues entries to prevent unbounded growth

Co-Authored-By: Claude <[email protected]>

* fix: share WhatsApp 515 creds flush handling (#27910) (thanks @asyncjason)

---------

Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* Deduplicate repeated tool call IDs for OpenAI-compatible APIs (#40996)

Merged via squash.

Prepared head SHA: 38d80483592de63866b07cd61edc7f41ffd56021
Co-authored-by: xaeon2026 <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* fix(gateway): skip Control UI pairing when auth.mode=none (closes #42931) (#47148)

When auth is completely disabled (mode=none), requiring device pairing
for Control UI operator sessions adds friction without security value
since any client can already connect without credentials.

Add authMode parameter to shouldSkipControlUiPairing so the bypass
fires only for Control UI + operator role + auth.mode=none. This avoids
the #43478 regression where a top-level OR disabled pairing for ALL
websocket clients.

* fix: preserve Telegram word boundaries when rechunking HTML (#47274)

* fix: preserve Telegram chunk word boundaries

* fix: address Telegram chunking review feedback

* fix: preserve Telegram retry separators

* fix: preserve Telegram chunking boundaries (#47274)

* tests: stabilize sessions_spawn mock/import ordering

* chore: retrigger CI for flaky channel lane

* tests: format sessions spawn depth limits spec

* chore: retrigger CI for flaky channels lane

* chore: retrigger CI for flaky channels lane

* chore: retrigger CI for flaky channels lane

* chore: retrigger CI for flaky channels lane

* chore: retrigger CI for flaky channels lane

---------

Signed-off-by: sallyom <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: fabiaodemianyang <[email protected]>
Co-authored-by: Robin Waslander <[email protected]>
Co-authored-by: Steven <[email protected]>
Co-authored-by: ImLukeF <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Sally O'Malley <[email protected]>
Co-authored-by: sallyom <[email protected]>
Co-authored-by: Jaehoon You <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: 2233admin <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: Xinhua Gu <[email protected]>
Co-authored-by: George Zhang <[email protected]>
Co-authored-by: yunweibang <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: Muhammed Mukhthar CM <[email protected]>
Co-authored-by: Josh Avant <[email protected]>
Co-authored-by: Mainframe <[email protected]>
Co-authored-by: kkhomej33-netizen <[email protected]>
Co-authored-by: kkhomej33-netizen <[email protected]>
Co-authored-by: Catalin Lupuleti <[email protected]>
Co-authored-by: Darshil <[email protected]>
Co-authored-by: Teconomix <[email protected]>
Co-authored-by: teconomix <[email protected]>
Co-authored-by: mukhtharcm <[email protected]>
Co-authored-by: luzhidong <[email protected]>
Co-authored-by: luzhidong <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Co-authored-by: thepagent <[email protected]>
Co-authored-by: thepagent <[email protected]>
Co-authored-by: Radek Sienkiewicz <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Nimrod Gutman <[email protected]>
Co-authored-by: Andrew Demczuk <[email protected]>
Co-authored-by: odysseus0 <[email protected]>
Co-authored-by: Josh Lehman <[email protected]>
Co-authored-by: Brian Qu <[email protected]>
Co-authored-by: ufhy <[email protected]>
Co-authored-by: jalehman <[email protected]>
Co-authored-by: day253 <[email protected]>
Co-authored-by: Gugu-sugar <[email protected]>
Co-authored-by: Gugu-sugar <[email protected]>
Co-authored-by: grp06 <[email protected]>
Co-authored-by: Tomáš Dinh <[email protected]>
Co-authored-by: nmccready <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: Tomsun28 <[email protected]>
Co-authored-by: tomsun28 <[email protected]>
Co-authored-by: Hiago Silva <[email protected]>
Co-authored-by: songlei <[email protected]>
Co-authored-by: nszhsl <[email protected]>
Co-authored-by: rstar327 <[email protected]>
Co-authored-by: rstar327 <[email protected]>
Co-authored-by: Sebastian Schubotz <[email protected]>
Co-authored-by: Jinhao Dong <[email protected]>
Co-authored-by: Jason <[email protected]>
Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Praveen K  Singh <[email protected]>
Co-authored-by: webdevpraveen <[email protected]>
Co-authored-by: SkunkWorks0x <[email protected]>
Co-authored-by: imanisynapse <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Sahan <[email protected]>
Co-authored-by: frankekn <[email protected]>
Co-authored-by: thepagent <[email protected]>
Co-authored-by: Ace Lee <[email protected]>
Co-authored-by: lixuankai <[email protected]>
Co-authored-by: Ted Li <[email protected]>
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: 助爪 <[email protected]>
Co-authored-by: xaeon2026 <[email protected]>
kesor pushed a commit to kesor/openclaw that referenced this pull request Mar 15, 2026
…tor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob
sibbl pushed a commit to sibbl/clawdbot that referenced this pull request Mar 15, 2026
…tor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob
romeroej2 pushed a commit to romeroej2/openclaw that referenced this pull request Mar 16, 2026
…tor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob
guiramos added a commit to butley/openclaw that referenced this pull request Mar 22, 2026
* feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds (openclaw#46889)

* feat: make compaction timeout configurable via agents.defaults.compaction.timeoutSeconds

The hardcoded 5-minute (300s) compaction timeout causes large sessions
to enter a death spiral where compaction repeatedly fails and the
session grows indefinitely. This adds agents.defaults.compaction.timeoutSeconds
to allow operators to override the compaction safety timeout.

Default raised to 900s (15min) which is sufficient for sessions up to
~400k tokens. The resolved timeout is also used for the session write
lock duration so locks don't expire before compaction completes.

Fixes openclaw#38233

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* test: add resolveCompactionTimeoutMs tests

Cover config resolution edge cases: undefined config, missing
compaction section, valid seconds, fractional values, zero,
negative, NaN, and Infinity.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: add timeoutSeconds to compaction Zod schema

The compaction object schema uses .strict(), so setting the new
timeoutSeconds config option would fail validation at startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: enforce integer constraint on compaction timeoutSeconds schema

Prevents sub-second values like 0.5 which would floor to 0ms and
cause immediate compaction timeout. Matches pattern of other
integer timeout fields in the schema.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: clamp compaction timeout to Node timer-safe maximum

Values above ~2.1B ms overflow Node's setTimeout to 1ms, causing
immediate timeout. Clamp to MAX_SAFE_TIMEOUT_MS matching the
pattern in agents/timeout.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: add FIELD_LABELS entry for compaction timeoutSeconds

Maintains label/help parity invariant enforced by
schema.help.quality.test.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: align compaction timeouts with abort handling

* fix: land compaction timeout handling (openclaw#46889) (thanks @asyncjason)

---------

Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix: harden compaction timeout follow-ups

* Docs: fix stale Clawdbot branding in agent workflow file (openclaw#46963)

Co-authored-by: webdevpraveen <[email protected]>

* docs: replace outdated Clawdbot references with OpenClaw in skill docs (openclaw#41563)

Update 5 references to the old "Clawdbot" name in
skills/apple-reminders/SKILL.md and skills/imsg/SKILL.md.

Co-authored-by: imanisynapse <[email protected]>

* Docs: switch README logo to SVG assets (openclaw#47049)

* fix: Disable strict mode tools for non-native openai-completions compatible APIs (openclaw#45497)

Merged via squash.

Prepared head SHA: 20fe05f
Co-authored-by: sahancava <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* fix: forward forceDocument through sendPayload path (follow-up to openclaw#45111) (openclaw#47119)

Merged via squash.

Prepared head SHA: d791190
Co-authored-by: thepagent <[email protected]>
Reviewed-by: @frankekn

* fix(android): support android node  `calllog.search` (openclaw#44073)

* fix(android): support android node  `calllog.search`

* fix(android): support android node calllog.search

* fix(android): wire callLog through shared surfaces

* fix: land Android callLog support (openclaw#44073) (thanks @lxk7280)

---------

Co-authored-by: lixuankai <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob

* fix(web): handle 515 Stream Error during WhatsApp QR pairing (openclaw#27910)

* fix(web): handle 515 Stream Error during WhatsApp QR pairing

getStatusCode() never unwrapped the lastDisconnect wrapper object,
so login.errorStatus was always undefined and the 515 restart path
in restartLoginSocket was dead code.

- Add err.error?.output?.statusCode fallback to getStatusCode()
- Export waitForCredsSaveQueue() so callers can await pending creds
- Await creds flush in restartLoginSocket before creating new socket

Fixes openclaw#3942

* test: update session mock for getStatusCode unwrap + waitForCredsSaveQueue

Mirror the getStatusCode fix (err.error?.output?.statusCode fallback)
in the test mock and export waitForCredsSaveQueue so restartLoginSocket
tests work correctly.

* fix(web): scope creds save queue per-authDir to avoid cross-account blocking

The credential save queue was a single global promise chain shared by all
WhatsApp accounts. In multi-account setups, a slow save on one account
blocked credential writes and 515 restart recovery for unrelated accounts.

Replace the global queue with a per-authDir Map so each account's creds
serialize independently. waitForCredsSaveQueue() now accepts an optional
authDir to wait on a single account's queue, or waits on all when omitted.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* test: use real Baileys v7 error shape in 515 restart test

The test was using { output: { statusCode: 515 } } which was already
handled before the fix. Updated to use the actual Baileys v7 shape
{ error: { output: { statusCode: 515 } } } to cover the new fallback
path in getStatusCode.

Co-Authored-By: Claude Code (Opus 4.6) <[email protected]>

* fix(web): bound credential-queue wait during 515 restart

Prevents restartLoginSocket from blocking indefinitely if a queued
saveCreds() promise stalls (e.g. hung filesystem write).

Co-Authored-By: Claude <[email protected]>

* fix: clear flush timeout handle and assert creds queue in test

Co-Authored-By: Claude <[email protected]>

* fix: evict settled credsSaveQueues entries to prevent unbounded growth

Co-Authored-By: Claude <[email protected]>

* fix: share WhatsApp 515 creds flush handling (openclaw#27910) (thanks @asyncjason)

---------

Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>

* Deduplicate repeated tool call IDs for OpenAI-compatible APIs (openclaw#40996)

Merged via squash.

Prepared head SHA: 38d8048
Co-authored-by: xaeon2026 <[email protected]>
Co-authored-by: frankekn <[email protected]>
Reviewed-by: @frankekn

* fix(gateway): skip Control UI pairing when auth.mode=none (closes openclaw#42931) (openclaw#47148)

When auth is completely disabled (mode=none), requiring device pairing
for Control UI operator sessions adds friction without security value
since any client can already connect without credentials.

Add authMode parameter to shouldSkipControlUiPairing so the bypass
fires only for Control UI + operator role + auth.mode=none. This avoids
the openclaw#43478 regression where a top-level OR disabled pairing for ALL
websocket clients.

* fix: preserve Telegram word boundaries when rechunking HTML (openclaw#47274)

* fix: preserve Telegram chunk word boundaries

* fix: address Telegram chunking review feedback

* fix: preserve Telegram retry separators

* fix: preserve Telegram chunking boundaries (openclaw#47274)

* test(whatsapp): fix stale append inbox expectation

* chore(gateway): ignore `.test.ts` changes in `gateway:watch` (openclaw#36211)

* fix: harden remote cdp probes

* feat(feishu): add ACP and subagent session binding (openclaw#46819)

* feat(feishu): add ACP session support

* fix(feishu): preserve sender-scoped ACP rebinding

* fix(feishu): recover sender scope from bound ACP sessions

* fix(feishu): support DM ACP binding placement

* feat(feishu): add current-conversation session binding

* fix(feishu): avoid DM parent binding fallback

* fix(feishu): require canonical topic sender ids

* fix(feishu): honor sender-scoped ACP bindings

* fix(feishu): allow user-id ACP DM bindings

* fix(feishu): recover user-id ACP DM bindings

* ACP: fail closed on conflicting tool identity hints (openclaw#46817)

* ACP: fail closed on conflicting tool identity hints

* ACP: restore rawInput fallback for safe tool resolution

* ACP tests: cover rawInput-only safe tool approval

* fix: harden mention pattern regex compilation

* Nodes: recheck queued actions before delivery (openclaw#46815)

* Nodes: recheck queued actions before delivery

* Nodes tests: cover pull-time policy recheck

* Nodes tests: type node policy mocks explicitly

* refactor: drop deprecated whatsapp mention pattern sdk helper

* added a fix for memory leak on 2gb ram (openclaw#46522)

* Nodes tests: prove pull-time policy revalidation

* fix: harden device token rotation denial paths

* style: format imported model helpers

* Plugins: preserve scoped ids and reserve bundled duplicates (openclaw#47413)

* Plugins: preserve scoped ids and reserve bundled duplicates

* Changelog: add plugin scoped id note

* Plugins: harden scoped install ids

* Plugins: reserve scoped install dirs

* Plugins: migrate legacy scoped update ids

* CLI: reduce channels add startup memory (openclaw#46784)

* CLI: lazy-load channel subcommand handlers

* Channels: defer add command dependencies

* CLI: skip status JSON plugin preload

* CLI: cover status JSON route preload

* Status: trim JSON security audit path

* Status: update JSON fast-path tests

* CLI: cover root help fast path

* CLI: fast-path root help

* Status: keep JSON security parity

* Status: restore JSON security tests

* CLI: document status plugin preload

* Channels: reuse Telegram account import

* Integrations: tighten inbound callback and allowlist checks (openclaw#46787)

* Integrations: harden inbound callback and allowlist handling

* Integrations: address review follow-ups

* Update CHANGELOG.md

* Mattermost: avoid command-gating open button callbacks

* ACP: require admin scope for mutating internal actions (openclaw#46789)

* ACP: require admin scope for mutating internal actions

* ACP: cover operator admin mutating actions

* ACP: gate internal status behind admin scope

* Changelog: add missing PR credits

* Changelog: add more unreleased PR numbers

* Subagents: restrict follow-up messaging scope (openclaw#46801)

* Subagents: restrict follow-up messaging scope

* Subagents: cover foreign-session follow-up sends

* Update CHANGELOG.md

* Webhooks: tighten pre-auth body handling (openclaw#46802)

* Webhooks: tighten pre-auth body handling

* Webhooks: clean up request body guards

* Tools: revalidate workspace-only patch targets (openclaw#46803)

* Tools: revalidate workspace-only patch targets

* Tests: narrow apply-patch delete-path assertion

* CLI: trim onboarding provider startup imports (openclaw#47467)

* Scope Control UI sessions per gateway (openclaw#47453)

* Scope Control UI sessions per gateway

Signed-off-by: sallyom <[email protected]>

* Add changelog for Control UI session scoping

Signed-off-by: sallyom <[email protected]>

---------

Signed-off-by: sallyom <[email protected]>

* Gateway: scrub credentials from endpoint snapshots (openclaw#46799)

* Gateway: scrub credentials from endpoint snapshots

* Gateway: scrub raw endpoint credentials in snapshots

* Gateway: preserve config redaction round-trips

* Gateway: restore redacted endpoint URLs on apply

* fix(config): avoid failing startup on implicit memory slot (openclaw#47494)

* fix(config): avoid failing on implicit memory slot

* fix(config): satisfy build for memory slot guard

* docs(changelog): note implicit memory slot startup fix (openclaw#47494)

* CLI: lazy-load auth choice provider fallback (openclaw#47495)

* CLI: lazy-load auth choice provider fallback

* CLI: cover lazy auth choice provider fallback

* fix(ci): config drift found and documented

* Gateway: tighten forwarded client and pairing guards (openclaw#46800)

* Gateway: tighten forwarded client and pairing guards

* Gateway: make device approval scope checks atomic

* Gateway: preserve device approval baseDir compatibility

* Changelog: note CLI OOM startup fixes (openclaw#47525)

* Commands: lazy-load model picker provider runtime (openclaw#47536)

* Commands: lazy-load model picker provider runtime

* Tests: cover model picker runtime boundary

* docs: fork rebase spec + per-patch diffs for upstream v2026.3.13 merge

Generated after failed merge attempt (2026-03-15). Contains:
- FORK-PATCHES-SPEC.md: implementation instructions per patch group (249 lines)
- FORK-REBASE-SPEC.md: technical context, errors, SSE protocol (292 lines)
- fork-patches/by-patch/: 31 per-patch git diffs (consultable on demand)
- fork-patches/fork-vs-upstream-src-only.patch: full squashed diff (5813 lines)

Co-authored-by: Bob

* docs: add merge plan from feat/upstream-merge-3.13 branch

Co-authored-by: Bob

* docs: remove old merge plan — superseded by FORK-PATCHES-SPEC + FORK-REBASE-SPEC

Co-authored-by: Bob

* chore(fmt): format changes and broken types

* Commands: split static onboard auth choice help (openclaw#47545)

* Commands: split static onboard auth choice help

* Tests: cover static onboard auth choice help

* Changelog: note static onboard auth choice help

* CLI/completion: fix generator OOM and harden plugin registries (openclaw#45537)

* fix: avoid OOM during completion script generation

* CLI/completion: fix PowerShell nested command paths

* CLI/completion: cover generated shell scripts

* Changelog: note completion generator follow-up

* Plugins: reserve shared registry names

---------

Co-authored-by: Xiaoyi <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>

* fix(plugins): load bundled extensions from dist (openclaw#47560)

* fix(models): preserve stream usage compat opt-ins (openclaw#45733)

Preserves explicit `supportsUsageInStreaming` overrides from built-in provider
catalogs and user config instead of unconditionally forcing `false` on non-native
openai-completions endpoints.

Adds `applyNativeStreamingUsageCompat()` to set `supportsUsageInStreaming: true`
on ModelStudio (DashScope) and Moonshot models at config build time so their
native streaming usage works out of the box.

Closes openclaw#46142

Co-authored-by: pezy <[email protected]>

* Plugins: reserve context engine ownership

* docs(zalo): document current Marketplace bot behavior (openclaw#47552)

Verified:
- pnpm check:docs

Co-authored-by: Tomáš Dinh <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>

* Docs: move release runbook to maintainer repo (openclaw#47532)

* Docs: redact private release setup

* Docs: tighten release order

* Docs: move release runbook to maintainer repo

* Docs: delete public mac release page

* Docs: remove zh-CN mac release page

* Docs: turn release checklist into release policy

* Docs: point release policy to private docs

* Docs: regenerate zh-CN release policy pages

* Docs: preserve Doctor in zh-CN hubs

* Docs: fix zh-CN polls label

* Docs: tighten docs i18n term guardrails

* Docs: enforce zh-CN glossary coverage

* Scripts: rebuild on extension and tsdown config changes (openclaw#47571)

Merged via squash.

Prepared head SHA: edd8ed8
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras

* fix: reset chat buffer on tool-start to prevent intermediary text accumulation

The Pi SDK resets lastStreamedAssistantCleaned between tool calls, but
the gateway chatRunState.buffers was not reset — causing mergedText to
accumulate text from ALL prior turns. The SSE subscriber (which resets
lastTextLen=0 on tool-start) then re-emitted the entire conversation.

Co-authored-by: Bob

* fix(release): block oversized npm packs that regress low-memory startup (openclaw#46850)

* fix(release): guard npm pack size regressions

* fix(release): fail closed when npm omits pack size

* Plugins: reserve context engine ownership (openclaw#47595)

* Plugins: reserve context engine ownership

* Update src/context-engine/registry.ts

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

---------

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

* fix: restore Patch #4 (chat mirror) and Patch #5 (inbound push) lost in Codex merge

Patch #4: webchat-originated replies on WA-scoped sessions now mirror
to WhatsApp via sendMessageWhatsApp(). The Codex merge kept the
runContext.mirror registration but lost the delivery block.

Patch #5: inbound messages (WA/Slack/etc.) now broadcast to WS/SSE
clients via message.inbound event, restoring real-time cross-channel
message display in the webchat dashboard.

Co-authored-by: Bob

* fix: mirror delivery in emitChatFinal (embedded runner path)

The previous commit placed mirror only in the !agentRunStarted branch
of server-methods/chat.ts, but the embedded runner sets agentRunStarted=true
and delivers via emitChatFinal in server-chat.ts instead. This restores
the mirror block in the correct location — matching the alpha.

Co-authored-by: Bob

* Gateway: sync runtime post-build artifacts

* Plugins: harden context engine ownership

* fix: complete Patch #5 inbound push + fix mirror static import

- Add emitInboundMessageEvent() call in dispatch-from-config.ts (was
  only defined but never called — WA messages never reached SSE/webchat)
- Switch mirror from dynamic import() to static import (dynamic import
  failed silently in bundled build)

Co-authored-by: Bob

* fix: globalThis singleton for WA listeners to survive chunk duplication

The bundler splits active-listener.ts into a different chunk than
server-chat.ts (mirror) and auto-reply/monitor.ts (listener registration).
Static/dynamic imports resolve to different module instances, so mirror
always sees an empty listeners Map. Using globalThis ensures all chunks
share the same Map instance.

Co-authored-by: Bob

* fix(plugins): fix bundled plugin roots and skill assets (openclaw#47601)

* fix(acpx): resolve bundled plugin root correctly

* fix(plugins): copy bundled plugin skill assets

* fix(plugins): tolerate missing bundled skill paths

* chore: remove temporary mirror debug logs

Co-authored-by: Bob

* fix: globalThis singleton for inbound event listeners (chunk duplication)

Same root cause as the WA listener fix: dispatch-from-config.ts emits
inbound events in one chunk, server.impl.ts subscribes in another.
Module-level Set gets duplicated across chunks.

Co-authored-by: Bob

* fix: emit inbound events from WA process-message path (not dispatch-from-config)

dispatch-from-config.ts is NOT in the WA message processing chain.
WA messages go through process-message.ts → provider-dispatcher.ts.
Moved emitInboundMessageEvent to process-message.ts where WA messages
are actually processed.

Co-authored-by: Bob

* fix(ci): restore config baseline release-check output (openclaw#47629)

* Docs: regenerate config baseline

* Chore: ignore generated config baseline

* Update .prettierignore

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

---------

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

* CLI: support package-manager installs from GitHub main (openclaw#47630)

* CLI: resolve package-manager main install specs

* CLI: skip registry resolution for raw package specs

* CLI: support main package target updates

* CLI: document package update specs in help

* Tests: cover package install spec resolution

* Tests: cover npm main-package updates

* Tests: cover update --tag main

* Installer: support main package targets

* Installer: support main package targets on Windows

* Docs: document package-manager main updates

* Docs: document installer main targets

* Docs: document npm and pnpm main installs

* Docs: document update --tag main

* Changelog: note package-manager main installs

* Update src/infra/update-global.test.ts

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

---------

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

* fix: emit message.inbound directly on gatewayEventBus

Bypass inbound-events.ts entirely — its module-level Set suffers from
chunk duplication even with globalThis (timing/ordering issues).
gatewayEventBus already uses globalThis singleton and is proven to work
for chat/agent events. SSE listens on gatewayEventBus for message.inbound.

Co-authored-by: Bob

* fix(dev): align gateway watch with tsdown wrapper (openclaw#47636)

* Commands: lazy-load non-interactive plugin provider runtime (openclaw#47593)

* Commands: lazy-load non-interactive plugin provider runtime

* Tests: cover non-interactive plugin provider ordering

* Update src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.runtime.ts

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

---------

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

* Plugins: relocate bundled skill assets

* Plugins: skip nested node_modules in bundled skills

* Plugins: clean stale bundled skill outputs

* feat(plugins): move provider runtimes into bundled plugins

* build(plugins): add bundled provider plugin manifests

* Channels: move onboarding adapters into extensions

* Channels: use owned helper imports

* Plugins: broaden plugin surface for Codex App Server (openclaw#45318)

* Plugins: add inbound claim and Telegram interaction seams

* Plugins: add Discord interaction surface

* Chore: fix formatting after plugin rebase

* fix(hooks): preserve observers after inbound claim

* test(hooks): cover claimed inbound observer delivery

* fix(plugins): harden typing lease refreshes

* fix(discord): pass real auth to plugin interactions

* fix(plugins): remove raw session binding runtime exposure

* fix(plugins): tighten interactive callback handling

* Plugins: gate conversation binding with approvals

* Plugins: migrate legacy plugin binding records

* Plugins/phone-control: update test command context

* Plugins: migrate legacy binding ids

* Plugins: migrate legacy codex session bindings

* Discord: fix plugin interaction handling

* Discord: support direct plugin conversation binds

* Plugins: preserve Discord command bind targets

* Tests: fix plugin binding and interactive fallout

* Discord: stabilize directory lookup tests

* Discord: route bound DMs to plugins

* Discord: restore plugin bindings after restart

* Telegram: persist detached plugin bindings

* Plugins: limit binding APIs to Telegram and Discord

* Plugins: harden bound conversation routing

* Plugins: fix extension target imports

* Plugins: fix Telegram runtime extension imports

* Plugins: format rebased binding handlers

* Discord: bind group DM interactions by channel

---------

Co-authored-by: Vincent Koc <[email protected]>

* feat(plugins): add compatible bundle support

* feat(plugins): move provider runtimes into bundled plugins

* build(plugins): add bundled provider plugin packages

* fix(plugins): restore provider compatibility fallbacks

* Changelog: note plugin agent integrations

* chore: remove inbound-push debug logs

Co-authored-by: Bob

* refactor: decouple channel setup discovery

* refactor: move telegram onboarding to setup wizard

* docs: describe channel setup wizard surface

* fix: tighten setup wizard typing

* fix: deduplicate inbound events + use raw body instead of envelope

- Remove emitInboundMessageEvent from dispatch-from-config.ts (WA uses
  process-message.ts path, causing double emit)
- Use params.msg.body (clean) instead of combinedBody (with envelope
  prefix) to avoid showing [WhatsApp ...] metadata in chat UI

Co-authored-by: Bob

* Commands: lazy-load auth choice plugin provider runtime (openclaw#47692)

* Commands: lazy-load auth choice plugin provider runtime

* Tests: cover auth choice plugin provider runtime

* refactor: expand setup wizard flow

* refactor: move discord and slack to setup wizard

* refactor: drop onboarding adapter sdk exports

* docs: update setup wizard capabilities

* feat(plugins): test bundle MCP end to end

* fix(onboarding): use scoped plugin snapshots to prevent OOM on low-memory hosts (openclaw#46763)

* fix(onboarding): use scoped plugin snapshots to prevent OOM on low-memory hosts

Onboarding and channel-add flows previously loaded the full plugin registry,
which caused OOM crashes on memory-constrained hosts. This patch introduces
scoped, non-activating plugin registry snapshots that load only the selected
channel plugin without replacing the running gateway's global state.

Key changes:
- Add onlyPluginIds and activate options to loadOpenClawPlugins for scoped loads
- Add suppressGlobalCommands to plugin registry to avoid leaking commands
- Replace full registry reloads in onboarding with per-channel scoped snapshots
- Validate command definitions in snapshot loads without writing global registry
- Preload configured external plugins via scoped discovery during onboarding

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(test): add return type annotation to hoisted mock to resolve TS2322

* fix(plugins): enforce cache:false invariant for non-activating snapshot loads

* Channels: preserve lazy scoped snapshot import after rebase

* Onboarding: scope channel snapshots by plugin id

* Catalog: trust manifest ids for channel plugin mapping

* Onboarding: preserve scoped setup channel loading

* Onboarding: restore built-in adapter fallback

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>

* feat(plugins): add provider usage runtime hooks

* feat(plugins): move bundled providers behind plugin hooks

* docs(plugins): document provider runtime usage hooks

* docs(plugins): unify bundle format explainer

* fix: repair onboarding adapter registry imports

* refactor: expand setup wizard input flow

* refactor: move signal imessage mattermost to setup wizard

* docs: document richer setup wizard prompts

* feat(plugins): move anthropic and openai vendors to plugins

* fix: repair onboarding setup-wizard imports

* test(discord): cover startup phase logging

* fix: reduce plugin and discord warning noise

* chore: raise plugin registry cache cap

* build: suppress protobufjs eval warning in tsdown

* refactor: tighten setup wizard onboarding bridge

* refactor: move bluebubbles to setup wizard

* refactor: move nextcloud talk to setup wizard

* CLI: restore lightweight root help and scoped status plugin preload

* Matrix: lazy-load runtime-heavy channel paths

* CI: add CLI startup memory regression check

* MSTeams: lazy-load runtime-heavy channel paths

* refactor: expand setup wizard flow

* refactor: move whatsapp to setup wizard

* refactor: move irc to setup wizard

* refactor: move tlon to setup wizard

* refactor: move googlechat to setup wizard

* refactor: expose setup wizard sdk surfaces

* Feishu: lazy-load runtime-heavy channel paths

* Google Chat: lazy-load runtime-heavy channel paths

* fix: gate setup-only plugin side effects

* feat(web-search): add plugin-backed search providers

* fix(web-search): restore build after plugin rebase

* refactor(web-search): move providers into company plugins

* WhatsApp: lazy-load setup wizard surface

* fix: align channel adapters with plugin sdk

* fix: repair node24 ci type drift

* refactor(google): merge gemini auth into google plugin

* feat(plugins): merge openai vendor seams into one plugin

* refactor(plugins): lazy load provider runtime shims

* perf(cli): trim help startup imports

* perf(status): defer heavy startup loading

* fix(matrix): assert outbound runtime hooks

* refactor: extend setup wizard account resolution

* refactor: move feishu zalo zalouser to setup wizard

* refactor: move matrix msteams twitch to setup wizard

* refactor: drop channel onboarding fallback

* fix: quiet discord startup logs

* Slack: lazy-load setup wizard surface

* Feishu: drop stale runtime onboarding export

* Discord: lazy-load setup wizard surface

* Signal: lazy-load setup wizard surface

* perf(plugins): lazy-load setup surfaces

* fix(cli): repair preaction merge typo

* Signal: restore setup surface helper exports

* iMessage: lazy-load setup wizard surface

* Nextcloud Talk: split setup adapter helpers

* fix: remove stale dist plugin dirs

* BlueBubbles: split setup adapter helpers

* test(plugins): cover retired google auth compatibility

* refactor(tests): share plugin registration helpers

* refactor(plugins): share bundled compat transforms

* refactor(google): split oauth flow modules

* refactor(plugin-sdk): centralize entrypoint manifest

* fix(docs): harden i18n prompt failures

* docs(i18n): sync zh-CN google plugin references

* fix(docs): run i18n through a local rpc client

* build(plugin-sdk): enforce export sync in check

* docs(google): remove stale plugin references

* IRC: split setup adapter helpers

* refactor: move line to setup wizard

* refactor: trim onboarding sdk exports

* Telegram: split setup adapter helpers

* fix: allow plugin package id hints

* Tlon: split setup adapter helpers

* LINE: split setup adapter helpers

* fix: restore ci type checks

* fix: resolve line setup rebase drift

* Mattermost: split setup adapter helpers

* refactor: merge minimax bundled plugins

* docs: refresh zh-CN model providers

* perf(plugins): lazy-load channel setup entrypoints

* Google Chat: split setup adapter helpers

* Matrix: split setup adapter helpers

* MSTeams: split setup adapter helpers

* feat(telegram): add topic-edit action

* fix(telegram): normalize topic-edit targets

* fix: add Telegram topic-edit action (openclaw#47798)

* Feishu: split setup adapter helpers

* fixed main?

* Zalo: split setup adapter helpers

* refactor(plugins): split lightweight channel setup modules

* Zalouser: split setup adapter helpers

* Status: skip unused channel issue scan in JSON mode

* fix(plugins): tighten lazy setup typing

* fix: tighten outbound channel/plugin resolution

* fix(ci): repair security and route test fixtures

* secrets: harden read-only SecretRef command paths and diagnostics (openclaw#47794)

* secrets: harden read-only SecretRef resolution for status and audit

* CLI: add SecretRef degrade-safe regression coverage

* Docs: align SecretRef status and daemon probe semantics

* Security audit: close SecretRef review gaps

* Security audit: preserve source auth SecretRef configuredness

* changelog

Signed-off-by: joshavant <[email protected]>

---------

Signed-off-by: joshavant <[email protected]>

* Gateway: add presence-only probe mode for status

* refactor: move group access into setup wizard

* feat: add nostr setup and unify channel setup discovery

* fix: drop duplicate channel setup import

* feat: add openshell sandbox backend

* feat(system-prompt): replace hardcoded identity with butley-system-prompt.md

Cherry-picked from work (9e1137a). Guilherme's PR #4.
Co-authored-by: Bob

* fix: suppress SSE finalization on retryable rate-limit errors (openclaw#32)

Cherry-picked from work (456f091). Retryable provider errors (429,
overload) no longer kill the SSE stream — keeps it open during
gateway retries/failover so text flows when retry succeeds.

Co-authored-by: Bob

* Status: scope JSON plugin preload to configured channels

* feat: persist previousSessionId chain across session resets (openclaw#34)

Cherry-picked from work (b465233). Adapted: upstream already has
previousSessionEntry — only added previousSessionIdForChain for
fallback chain persistence on reset/new/idle-expiry.

Co-authored-by: Bob

* Status: lazy-load read-only account inspectors

* refactor(core): land plugin auth and startup cleanup

* chore: restore butley-api + clickup-api custom plugins from alpha

These custom extensions were missing from the rebase branch.
Copied from alpha verbatim.

Co-authored-by: Bob

* CLI: route gateway status before program registration

* feat: add remote openshell sandbox mode

* docs: expand openshell sandbox docs

* feat: add firecrawl onboarding search plugin

* Gateway: lazy-load SSH status helpers

* refactor: rename channel setup flow seam

* refactor: move setup fallback into setup registry

* feat: add synology chat setup wizard

* build: add setup entrypoints for migrated channel plugins

* docs: update channel setup docs

* fix: update feishu setup adapter import

* Status: lazy-load channel summary helpers

* Agents: skip eager context warmup for status commands

* Status: route JSON through lean command

* refactor(plugins): move auth and model policy to providers

* fix: control UI sends correct provider prefix when switching models

The model selector was using just the model ID (e.g. "gpt-5.2") as the
option value. When sent to sessions.patch, the server would fall back to
the session's current provider ("anthropic") yielding "anthropic/gpt-5.2"
instead of "openai/gpt-5.2".

Now option values use "provider/model" format, and resolveModelOverrideValue
and resolveDefaultModelValue also return the full provider-prefixed key so
selected state stays consistent.

* fix: format default model label as 'model · provider' for consistency

The default option showed 'Default (openai/gpt-5.2)' while individual
options used the friendlier 'gpt-5.2 · openai' format.

* Nostr: break setup-surface import cycle

* Tests: stabilize bundle MCP env on Windows

* Status: lazy-load channel security and summaries

* Docs: refresh generated config baseline

* test: silence vitest warning noise

* Status: lazy-load text scan helpers

* refactor: rename setup helper surfaces

* test: fix fetch mock typing

* docs: update channel setup wording

* Security: lazy-load channel audit provider helpers

* fix(ui): centralize control model ref handling

* CLI: route gateway status through daemon status

* Status: restore lazy scan runtime typing

* feat: token usage tracking via llm_output hook

* fix: remove duplicate previousSessionEntry declaration

* fix: butley-api extension imports — use openclaw/plugin-sdk instead of relative source paths

* fix: ensure llm_output hook is included in butley-api build output

- Add optional bundled cluster filtering to listBundledPluginBuildEntries()
  to skip extensions with native dependencies (matrix, whatsapp, etc.)
  that cannot be bundled by rolldown on all platforms
- Filter plugin-sdk entries for optional clusters to prevent native
  .node binary bundling failures
- Matches upstream's shouldBuildBundledCluster() pattern
- butley-api dist output now correctly contains both the tool
  registration AND the llm_output hook in the register() default export

* Revert "fix: ensure llm_output hook is included in butley-api build output"

This reverts commit 4558c9d.

* fix: revert tsdown changes, copy butley-api to dist via Dockerfile

* feat: [FORK-PATCH-37] token usage tracking via direct Convex POST

* fix: improve FORK-PATCH-37 logging and add cache token tracking

---------

Signed-off-by: sallyom <[email protected]>
Signed-off-by: joshavant <[email protected]>
Co-authored-by: Jason <[email protected]>
Co-authored-by: Jason Separovic <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Praveen K  Singh <[email protected]>
Co-authored-by: webdevpraveen <[email protected]>
Co-authored-by: SkunkWorks0x <[email protected]>
Co-authored-by: imanisynapse <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Sahan <[email protected]>
Co-authored-by: frankekn <[email protected]>
Co-authored-by: Frank Yang <[email protected]>
Co-authored-by: thepagent <[email protected]>
Co-authored-by: Ace Lee <[email protected]>
Co-authored-by: lixuankai <[email protected]>
Co-authored-by: Ted Li <[email protected]>
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: 助爪 <[email protected]>
Co-authored-by: xaeon2026 <[email protected]>
Co-authored-by: Andrew Demczuk <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Harold Hunt <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: Aditya Chaudhary <[email protected]>
Co-authored-by: Sally O'Malley <[email protected]>
Co-authored-by: Nimrod Gutman <[email protected]>
Co-authored-by: Lucas Machado <[email protected]>
Co-authored-by: xiaoyi <[email protected]>
Co-authored-by: Xiaoyi <[email protected]>
Co-authored-by: peizhe.chen <[email protected]>
Co-authored-by: Tomáš Dinh <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Mason <[email protected]>
Co-authored-by: Josh Avant <[email protected]>
Co-authored-by: Christopher Chamaletsos <[email protected]>
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 23, 2026
…tor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob

(cherry picked from commit 843e3c1)
alexey-pelykh added a commit to remoteclaw/remoteclaw that referenced this pull request Mar 23, 2026
* fix(telegram): validate webhook secret before reading request body

Refs: GHSA-jq3f-vjww-8rq7
(cherry picked from commit 7e49e98)

* fix(feishu): clear stale streamingStartPromise on card creation failure

Fixes openclaw#43322

* fix(feishu): clear stale streamingStartPromise on card creation failure

When FeishuStreamingSession.start() throws (HTTP 400), the catch block
sets streaming = null but leaves streamingStartPromise dangling. The
guard in startStreaming() checks streamingStartPromise first, so all
future deliver() calls silently skip streaming - the session locks
permanently.

Clear streamingStartPromise in the catch block so subsequent messages
can retry streaming instead of dropping all future replies.

Fixes openclaw#43322

* test(feishu): wrap push override in try/finally for cleanup safety

(cherry picked from commit c6e3283)

* fix(feishu): preserve non-ASCII filenames in file uploads (openclaw#33912) (openclaw#34262)

* fix(feishu): preserve non-ASCII filenames in file uploads (openclaw#33912)

* style(feishu): format media test file

* fix(feishu): preserve UTF-8 filenames in file uploads (openclaw#34262) thanks @fabiaodemianyang

---------

Co-authored-by: Robin Waslander <[email protected]>
(cherry picked from commit 983fecc)

* fix(feishu): fail closed on webhook signature checks

(cherry picked from commit 496ca3a)

* fix(feishu): harden webhook signature compare

(cherry picked from commit 223ae42)

* fix(whatsapp): trim leading whitespace in direct outbound sends (openclaw#43539)

Trim leading whitespace from direct WhatsApp text and media caption sends.

Also guard empty text-only web sends after trimming.

(cherry picked from commit a5ceb62)

* fix(whatsapp): restore append recency filter lost in extensions refactor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob

(cherry picked from commit 843e3c1)

* fix(whatsapp): preserve watchdog message age across reconnects

(cherry picked from commit abd948f)

* fix(whatsapp): restore implicit reply mentions for LID identities (openclaw#48494)

Threads selfLid from the Baileys socket through the inbound WhatsApp
pipeline and adds LID-format matching to the implicit mention check
in group gating, so reply-to-bot detection works when WhatsApp sends
the quoted sender in @lid format.

Also fixes the device-suffix stripping regex (was a silent no-op).

Closes openclaw#23029

Co-authored-by: sparkyrider <[email protected]>
Reviewed-by: @ademczuk
(cherry picked from commit 10ef58d)

* fix(discord): add missing autoThread to DiscordGuildChannelConfig type (openclaw#35608)

Merged via squash.

Prepared head SHA: e62b88b
Co-authored-by: ingyukoh <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Reviewed-by: @altaywtf

(cherry picked from commit 78b9384)

* fix(mattermost): prevent duplicate messages when block streaming + threading are active (openclaw#41362)

* fix(mattermost): prevent duplicate messages when block streaming + threading are active

Remove replyToId from createBlockReplyPayloadKey so identical content is
deduplicated regardless of threading target. Add explicit threading dock
to the Mattermost plugin with resolveReplyToMode reading from config
(default "all"), and add replyToMode to the Mattermost config schema.

Fixes openclaw#41219

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(mattermost): address PR review — per-account replyToMode and test clarity

Read replyToMode from the merged per-account config via
resolveMattermostAccount so account-level overrides are honored in
multi-account setups. Add replyToMode to MattermostAccountConfig type.
Rename misleading test to clarify it exercises shouldDropFinalPayloads
short-circuit, not payload key dedup.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Replies: keep block-pipeline reply targets distinct

* Tests: cover block reply target-aware dedupe

* Update CHANGELOG.md

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit e8a162d)

* fix: remove cherry-picked test files with missing fork dependencies

These tests reference modules (monitor.test-mocks, monitor-inbox.test-harness,
inbound) and import paths (openclaw/plugin-sdk/feishu) that don't exist in
the fork — they depend on upstream infrastructure not present here.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix(voice-call): resolve pre-existing TelephonyTtsRuntime type mismatch

Cast api.runtime.tts to TelephonyTtsRuntime at the call site and add
missing ttsProviders param to the type — the core textToSpeechTelephony
signature diverged from the extension's type after the RemoteClawConfig
rename.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

---------

Co-authored-by: Robin Waslander <[email protected]>
Co-authored-by: Andrew Demczuk <[email protected]>
Co-authored-by: fabiaodemianyang <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Luke <[email protected]>
Co-authored-by: Ted Li <[email protected]>
Co-authored-by: sparkyrider <[email protected]>
Co-authored-by: sparkyrider <[email protected]>
Co-authored-by: ingyukoh <[email protected]>
Co-authored-by: Mathias Nagler <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Interstellar-code pushed a commit to Interstellar-code/operator1 that referenced this pull request Mar 24, 2026
…tor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob

(cherry picked from commit 843e3c1)
sbezludny pushed a commit to sbezludny/openclaw that referenced this pull request Mar 27, 2026
…tor, handle Long timestamps (openclaw#42588)

Merged via squash.

Prepared head SHA: 8ce59bb
Co-authored-by: MonkeyLeeT <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Reviewed-by: @scoootscooob
@MonkeyLeeT MonkeyLeeT deleted the fix/whatsapp-append-recent-messages branch March 31, 2026 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: whatsapp-web Channel integration: whatsapp-web size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants