Skip to content

fix(skills): deduplicate slash commands by skillName across all interfaces#27521

Merged
shakkernerd merged 4 commits intoopenclaw:mainfrom
shivama205:fix/skill-command-dedup
Mar 5, 2026
Merged

fix(skills): deduplicate slash commands by skillName across all interfaces#27521
shakkernerd merged 4 commits intoopenclaw:mainfrom
shivama205:fix/skill-command-dedup

Conversation

@shivama205
Copy link

Problem

When multiple agents have a skill with the same name, listSkillCommandsForAgents emits both a primary entry (github) and a suffix-renamed duplicate (github_2). Previously only Discord collapsed these via a local dedupeSkillCommandsForDiscord wrapper — every other interface (TUI autocomplete, Slack, text) could see the duplicate entries.

Fix

Move the dedup-by-skillName logic into listSkillCommandsForAgents itself so the canonical function always returns one entry per skill name regardless of which interface calls it. The Discord-only wrapper is removed.

Changes

  • src/auto-reply/skill-commands.ts — add dedupeBySkillName helper; apply it at the end of listSkillCommandsForAgents; export via __testing
  • src/discord/monitor/provider.ts — remove dedupeSkillCommandsForDiscord and its __testing export; update call site
  • src/discord/monitor/provider.skill-dedupe.test.ts — remove now-deleted dedupe tests (thread-binding tests kept)
  • src/auto-reply/skill-commands.test.ts — add dedupeBySkillName unit tests; update listSkillCommandsForAgents test to assert the duplicate is dropped

Test plan

  • pnpm test -- src/auto-reply/skill-commands.test.ts — 9 tests pass
  • pnpm test -- src/discord/monitor/provider.skill-dedupe.test.ts — 3 tests pass

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle bot added channel: discord Channel integration: discord size: S labels Feb 26, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 26, 2026

Greptile Summary

Moves skill command deduplication from a Discord-only wrapper (dedupeSkillCommandsForDiscord) into the canonical listSkillCommandsForAgents function, so all interfaces (TUI, Slack, Telegram, text) consistently receive a deduplicated command list instead of seeing suffix-renamed duplicates like github_2.

  • Added dedupeBySkillName helper in src/auto-reply/skill-commands.ts that deduplicates by case-insensitive skillName, keeping the first registration and passing through commands with empty skillName
  • Applied dedup at the end of listSkillCommandsForAgents so all callers (Discord, Slack, Telegram, TUI) benefit automatically
  • Removed the Discord-specific dedupeSkillCommandsForDiscord wrapper and its tests from src/discord/monitor/provider.ts
  • Added comprehensive unit tests for the new dedupeBySkillName helper and updated the listSkillCommandsForAgents integration test to assert duplicates are dropped

Confidence Score: 5/5

  • This PR is safe to merge — it centralizes existing dedup logic with no behavioral change for Discord and fixes a real bug for all other interfaces.
  • The change is straightforward: an existing dedup function is moved from a Discord-only wrapper into the canonical listSkillCommandsForAgents function. The logic is identical (case-insensitive skillName dedup, first-wins, empty skillName passthrough). All callers (Discord, Slack, Telegram, TUI) now benefit. Tests are comprehensive and cover edge cases. No new dependencies, no API changes, no risk of regression.
  • No files require special attention

Last reviewed commit: 976d8e9

@openclaw-barnacle
Copy link

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle bot added the stale Marked as stale due to inactivity label Mar 4, 2026
@shivama205
Copy link
Author

Reviving this — the fix is ready and the Greptile review gave it a 5/5 (safe to merge). Moving dedupeBySkillName into listSkillCommandsForAgents fixes the duplicate slash commands bug for all interfaces, not just Discord.

If anyone on the Discord can help bring attention to this, would really appreciate it! @shakkernerd happy to make any tweaks needed. 🙏

@shivama205 shivama205 force-pushed the fix/skill-command-dedup branch from 6087151 to f76639e Compare March 4, 2026 18:48
@openclaw-barnacle openclaw-barnacle bot removed the stale Marked as stale due to inactivity label Mar 5, 2026
@shakkernerd shakkernerd self-assigned this Mar 5, 2026
@shakkernerd shakkernerd force-pushed the fix/skill-command-dedup branch from f76639e to 12b6661 Compare March 5, 2026 05:03
shakkernerd added a commit to shivama205/openclaw that referenced this pull request Mar 5, 2026
Shivam and others added 4 commits March 5, 2026 05:03
…faces

Move skill-command deduplication by skillName from the Discord-only
`dedupeSkillCommandsForDiscord` into `listSkillCommandsForAgents` so
every interface (TUI, Slack, text) consistently sees a clean command
list without platform-specific workarounds.

When multiple agents share a skill with the same name the old code
emitted `github` + `github_2` and relied on Discord to collapse them.
Now `listSkillCommandsForAgents` returns only the first registration
per skillName, and the Discord-specific wrapper is removed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@shakkernerd shakkernerd force-pushed the fix/skill-command-dedup branch from 12b6661 to 0f10829 Compare March 5, 2026 05:04
@shakkernerd shakkernerd merged commit 1805735 into openclaw:main Mar 5, 2026
28 checks passed
@shakkernerd
Copy link
Member

shakkernerd commented Mar 5, 2026

Merged via rebase.

Thanks @shivama205!

@shivama205
Copy link
Author

Thanks @shakkernerd !

Kansodata pushed a commit to Kansodata/openclaw that referenced this pull request Mar 5, 2026
krakenhavoc added a commit to krakenhavoc/openclaw that referenced this pull request Mar 5, 2026
* fix(gateway): flush throttled delta before emitChatFinal (#24856)

* fix(gateway): flush throttled delta before emitChatFinal

The 150ms throttle in emitChatDelta can suppress the last text chunk
before emitChatFinal fires, causing streaming clients (e.g. ACP) to
receive truncated responses. The final event carries the complete text,
but clients that build responses incrementally from deltas miss the
tail end.

Flush one last unthrottled delta with the complete buffered text
immediately before sending the final event. This ensures all streaming
consumers have the full response without needing to reconcile deltas
against the final payload.

* fix(gateway): avoid duplicate delta flush when buffer unchanged

Track the text length at the time of the last broadcast. The flush in
emitChatFinal now only sends a delta if the buffer has grown since the
last broadcast, preventing duplicate sends when the final delta passed
the 150ms throttle and was already broadcast.

* fix(gateway): honor heartbeat suppression in final delta flush

* test(gateway): add final delta flush and dedupe coverage

* fix(gateway): skip final flush for silent lead fragments

* docs(changelog): note gateway final-delta flush fix credits

---------

Co-authored-by: Jonathan Taylor <visionik@pobox.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

* fix: repair Feishu reset hook typing and stabilize secret resolver timeout

* chore(release): bump to 2026.3.3 and seed changelog

* ci: enable stale workflow

* fix: scope extension runtime deps to plugin manifests

* docs(changelog): reattribute duplicated PR credits

* fix(gateway+acp): thread stopReason through final event to ACP bridge (#24867)

Complete the stop reason propagation chain so ACP clients can
distinguish end_turn from max_tokens:

- server-chat.ts: emitChatFinal accepts optional stopReason param,
  includes it in the final payload, reads it from lifecycle event data
- translator.ts: read stopReason from the final payload instead of
  hardcoding end_turn

Chain: LLM API → run.ts (meta.stopReason) → agent.ts (lifecycle event)
→ server-chat.ts (final payload) → ACP translator (PromptResponse)

* fix(line): synthesize media/auth/routing webhook regressions (openclaw#32546) thanks @Takhoffman

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

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(test): stabilize appcast version assertion

* fix(ci): handle disabled systemd units in docker doctor flow

* test(live): harden gateway model profile probes

* fix(telegram): debounce forwarded media-only bursts

* test(e2e): isolate module mocks across harnesses

* security(line): synthesize strict LINE auth boundary hardening

LINE auth boundary hardening synthesis for inbound webhook authn/z/authz:
- account-scoped pairing-store access
- strict DM/group allowlist boundary separation
- fail-closed webhook auth/runtime behavior
- replay and duplicate handling with in-flight continuity for concurrent redeliveries

Source PRs: #26701, #26683, #25978, #17593, #16619, #31990, #26047, #30584, #18777
Related continuity context: #21955

Co-authored-by: bmendonca3 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: davidahmann <46606159+davidahmann@users.noreply.github.com>
Co-authored-by: harshang03 <58983401+harshang03@users.noreply.github.com>
Co-authored-by: haosenwang1018 <167664334+haosenwang1018@users.noreply.github.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: coygeek <65363919+coygeek@users.noreply.github.com>
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>

* chore: Updated Brave documentation (#26860)

Merged via squash.

Prepared head SHA: f8fc4bf01e0eacfb01f6ee58eea445680f7eeebd
Co-authored-by: HenryLoenwind <1485873+HenryLoenwind@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix: improve compaction summary instructions to preserve active work (#8903)

fix: improve compaction summary instructions to preserve active work

Expand staged-summary merge instructions to preserve active task status, batch progress, latest user request, and follow-up commitments so compaction handoffs retain in-flight work context.

Co-authored-by: joetomasone <56984887+joetomasone@users.noreply.github.com>
Co-authored-by: Josh Lehman <josh@martian.engineering>

* bug: Workaround for QMD upstream bug (#27028)

Merged via squash.

Prepared head SHA: 939f9f4574fcfe08762407ab9e8d6c85a77a0899
Co-authored-by: HenryLoenwind <1485873+HenryLoenwind@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* Diffs: Migrate tool usage guidance from before_prompt_build to a plugin skill (#32630)

Merged via squash.

Prepared head SHA: 585697a4e1556baa2cd79a7b449b120c4fd87e17
Co-authored-by: sircrumpet <4436535+sircrumpet@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* feat(mattermost): add native slash command support (refresh) (#32467)

Merged via squash.

Prepared head SHA: 989126574ead75c0eedc185293659eb0d4fc6844
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm

* Gateway: fix stale self version in status output (#32655)

Merged via squash.

Prepared head SHA: b9675d1f90ef0eabb7e68c24a72d4b2fb27def22
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* agents: propagate config for embedded skill loading

* fix(telegram): warn when accounts.default is missing in multi-account setup (#32544)

Merged via squash.

Prepared head SHA: 7ebc3f65b21729137d352fa76bc31f2f849934c0
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* macOS: add tailscale serve discovery fallback for remote gateways (#32860)

* feat(macos): add tailscale serve gateway discovery fallback

* fix: add changelog note for tailscale serve discovery fallback (#32860) (thanks @ngutman)

* fix(telegram): run outbound message hooks in reply delivery path

* fix(telegram): mark message_sent success only when delivery occurred

* fix(telegram): include reply hook metadata

* docs: update changelog for telegram message_sent fix (#32649)

* fix: guard malformed Telegram replies and pass hook accountId

* fix(heartbeat): scope exec wake dispatch to session key (#32724)

Merged via squash.

Prepared head SHA: 563fee0e65af07575f3df540cab2e1e5d5589f06
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* fix(telegram): prevent duplicate messages in DM draft streaming mode (#32118)

* fix(telegram): prevent duplicate messages in DM draft streaming mode

When using sendMessageDraft for DM streaming (streaming: 'partial'),
the draft bubble auto-converts to the final message. The code was
incorrectly falling through to sendPayload() after the draft was
finalized, causing a duplicate message.

This fix checks if we're in draft preview mode with hasStreamedMessage
and skips the sendPayload call, returning "preview-finalized" directly.

Key changes:
- Use hasStreamedMessage flag instead of previewRevision comparison
- Avoids double stopDraftLane calls by returning early
- Prevents duplicate messages when final text equals last streamed text

Root cause: In lane-delivery.ts, the final message handling logic
did not properly handle the DM draft flow where sendMessageDraft
creates a transient bubble that doesn't need a separate final send.

* fix(telegram): harden DM draft finalization path

* fix(telegram): require emitted draft preview for unchanged finals

* fix(telegram): require final draft text emission before finalize

* fix: update changelog for telegram draft finalization (#32118) (thanks @OpenCils)

---------

Co-authored-by: Ayaan Zaidi <zaidi@uplause.io>

* fix: substitute YYYY-MM-DD at session startup and post-compaction (#32363) (#32381)

Merged via squash.

Prepared head SHA: aee998a2c1a911d3fef771aa891ac315a2f7dc53
Co-authored-by: chengzhichao-xydt <264300353+chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* chore: note about pagination

* Compaction/Safeguard: preserve recent turns verbatim (#25554)

Merged via squash.

Prepared head SHA: 7fb33c411c4aaea2795e490fcd0e647cf7ea6fb8
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* fix: ignore discord wildcard audit keys (#33125) (thanks @thewilloftheshadow) (#33125)

* fix: Discord acp inline actions + bound-thread filter (#33136) (thanks @thewilloftheshadow) (#33136)

* fix: harden Discord channel resolution (#33142) (thanks @thewilloftheshadow) (#33142)

* docs(loop-detection): fix config keys to match schema (#33182)

Merged via squash.

Prepared head SHA: 612ecc00d36cbbefb0657f0a2ac0898d53a5ed73
Co-authored-by: Mylszd <23611557+Mylszd@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* feat(tool-truncation): use head+tail strategy to preserve errors during truncation (#20076)

Merged via squash.

Prepared head SHA: 6edebf22b1666807b1ea5cba5afb614c41dc3dd1
Co-authored-by: jlwestsr <52389+jlwestsr@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* iOS Security Stack 1/5: Keychain Migrations + Tests (#33029)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: da2f8f614155989345d0d3efd0c0f29ef410c187
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix: improve discord chunk delivery (#33226) (thanks @thewilloftheshadow) (#33226)

* fix(discord): default presence online when unconfigured

* fix(discord): stop typing after silent runs

* fix(discord): use fetch for voice upload slots

* test(discord): align bound-thread target kind

* iOS Security Stack 2/5: Concurrency Locks (#33241)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: b99ad804fbcc20bfb3042ac1da9050a7175f009c
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* iOS Security Stack 3/5: Runtime Security Guards (#33031)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 99171654014d1960edcaca8312ef6a47d3c08399
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix: discord mention handling (#33224) (thanks @thewilloftheshadow) (#33224)

* iOS Security Stack 4/5: TTS PCM->MP3 Fallback (#30885) (#33032)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: f77e3d764425a23ab5d5b55593d9e14622f1ef95
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix: allowlist Discord CDN hostnames for SSRF media (#33275) (thanks @thewilloftheshadow) (#33275)

* fix: stabilize Telegram draft boundaries and suppress NO_REPLY lead leaks (#33169)

* fix: stabilize telegram draft stream message boundaries

* fix: suppress NO_REPLY lead-fragment leaks

* fix: keep underscore guard for non-NO_REPLY prefixes

* fix: skip assistant-start rotation only after real lane rotation

* fix: preserve finalized state when pre-rotation does not force

* fix: reset finalized preview state on message-start boundary

* fix: document Telegram draft boundary + NO_REPLY reliability updates (#33169) (thanks @obviyus)

* fix: discord auto presence health signal (#33277) (thanks @thewilloftheshadow) (#33277)

* docs: document discord ignoreOtherMentions

* fix(discord): skip bot messages before debounce

* fix(discord): honor agent media roots in replies

* fix(discord): harden slash command routing

* fix(discord): reset thread sessions on archive

* fix: drop discord opus dependency

* feat(discord): add allowBots mention gating

* fix(docs): use MDX-safe secretref markers

* fix(docs): avoid MDX regex markers in secretref page

* docs(security): document Docker UFW hardening via DOCKER-USER (#27613)

Merged via squash.

Prepared head SHA: 31ddd433265d8a7efbf932c941678598bf6be30c
Co-authored-by: dorukardahan <35905596+dorukardahan@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* docs(contributing): require before/after screenshots for UI PRs (#32206)

Merged via squash.

Prepared head SHA: d7f0914873aec1c3c64c9161771ff0bcbc457c95
Co-authored-by: hydro13 <6640526+hydro13@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* fix(discord): align DiscordAccountConfig.token type with SecretInput (#32490)

Merged via squash.

Prepared head SHA: 233aa032f1d894b7eb6a960247baa1336f8fbc26
Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant

* docs: fix secretref marker rendering in credential surface

* fix: harden pr review artifact validation

* fix(gateway): include disk-scanned agent IDs in listConfiguredAgentIds (#32831)

Merged via squash.

Prepared head SHA: 2aa58f6afd6e7766119575648483de6b5f50da6f
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* Agent: unify bootstrap truncation warning handling (#32769)

Merged via squash.

Prepared head SHA: 5d6d4ddfa620011e267d892b402751847d5ac0c3
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix(logging ): use local timezone for console log timestamps (#25970)

Merged via squash.

Prepared head SHA: 30123265b7b910b9208e8c9407c30536e46eb68f
Co-authored-by: openperf <80630709+openperf@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* security: add X-Content-Type-Options nosniff header to media route (#30356)

Merged via squash.

Prepared head SHA: b14f9ad7ca7017c6e31fb18c8032a81f49686ea4
Co-authored-by: 13otKmdr <154699144+13otKmdr@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* fix(ios): guard talk TTS callbacks to active utterance (#33304)

Merged via squash.

Prepared head SHA: dd88886e416e5e6d6c08bf92162a5ff18c1eb229
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix(ios): start incremental speech at soft boundaries (#33305)

Merged via squash.

Prepared head SHA: d1acf723176f8e9d89f8c229610c9400ab661ec7
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* Telegram/device-pair: auto-arm one-shot notify on /pair qr with manual fallback (#33299)

Merged via squash.

Prepared head SHA: 0986691fd4d2f37dd2de7ac601b1e5480602c829
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix(ios): harden watch messaging activation concurrency (#33306)

Merged via squash.

Prepared head SHA: d40f8c4afbd6ddf38548c9b0c6ac6ac4359f2e54
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* docs: fix Mintlify-incompatible links in security docs (#27698)

Merged via squash.

Prepared head SHA: 6078cd94ba38b49d17e9680073337885c5f9e781
Co-authored-by: clawdoo <65667097+clawdoo@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* fix(security): strip partial API token from status labels (#33262)

Merged via squash.

Prepared head SHA: 5fe81704e678dc5b18ca9416e5eb0750cfe49fb6
Co-authored-by: cu1ch3n <80438676+cu1ch3n@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* Security audit: suggest valid gateway.nodes.denyCommands entries (#29713)

Merged via squash.

Prepared head SHA: db23298f9806b8de8c4b3e816f1649c18ebc0c64
Co-authored-by: liquidhorizon88-bot <257047709+liquidhorizon88-bot@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* Agents: preserve bootstrap warning dedupe across followup runs

* feat(gateway): add Permissions-Policy header to default security headers (#30186)

Merged via squash.

Prepared head SHA: 0dac89283f54840ec2244007ff5a6178ce8b2ba9
Co-authored-by: habakan <12531644+habakan@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* fix(plugins): lazily initialize runtime and split plugin-sdk startup imports (#28620)

Merged via squash.

Prepared head SHA: 8bd7d6c13b070f86bd4d5c45286c1ceb1a3f9f80
Co-authored-by: hmemcpy <601206+hmemcpy@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* build: fix ineffective dynamic imports with lazy boundaries (#33690)

Merged via squash.

Prepared head SHA: 38b3c23d6f8f2b4c8a36a88ee65b508102f1ec36
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix(feishu): support SecretRef-style env credentials in account resolver (#30903)

Merged via squash.

Prepared head SHA: d3d0a18f173e999070dae4ff01423dadd2804a9c
Co-authored-by: LiaoyuanNing <259494737+LiaoyuanNing@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant

* fix(config): detect top-level heartbeat as invalid config path (#30894) (#32706)

Merged via squash.

Prepared head SHA: 1714ffe6fc1e3ed6a8a120d01d074f1be83c62d3
Co-authored-by: xiwan <931632+xiwan@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* Config: harden legacy heartbeat key migration

* build: prevent mixed static/dynamic pi-model-discovery imports

* follow-up: align ingress, atomic paths, and channel tests with credential semantics (#33733)

Merged via squash.

Prepared head SHA: c290c2ab6a3c3309adcbc4dc834f3c10d2ae1039
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant

* plugin-sdk: add channel subpaths and migrate bundled plugins

* fix(tlon): use HTTPS git URL for api-beta

* fix: stabilize telegram draft boundary previews (#33842) (thanks @ngutman)

* Runtime: stabilize tool/run state transitions under compaction and backpressure

Synthesize runtime state transition fixes for compaction tool-use integrity and long-running handler backpressure.

Sources: #33630, #33583

Co-authored-by: Kevin Shenghui <shenghuikevin@gmail.com>
Co-authored-by: Theo Tarr <theodore@tarr.com>

* fix(extensions): synthesize mediaLocalRoots propagation across sendMedia adapters

Restore deterministic mediaLocalRoots propagation through extension sendMedia adapters and add coverage for local/remote media handling in Google Chat.

Synthesis of #33581, #33545, #33540, #33536, #33528.

Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>

* fix(gateway): synthesize lifecycle robustness for restart and startup probes (#33831)

* fix(gateway): correct launchctl command sequence for gateway restart (closes #20030)

* fix(restart): expand HOME and escape label in launchctl plist path

* fix(restart): poll port free after SIGKILL to prevent EADDRINUSE restart loop

When cleanStaleGatewayProcessesSync() kills a stale gateway process,
the kernel may not immediately release the TCP port. Previously the
function returned after a fixed 500ms sleep (300ms SIGTERM + 200ms
SIGKILL), allowing triggerOpenClawRestart() to hand off to systemd
before the port was actually free. The new systemd process then raced
the dying socket for port 18789, hit EADDRINUSE, and exited with
status 1, causing systemd to retry indefinitely — the zombie restart
loop reported in #33103.

Fix: add waitForPortFreeSync() that polls lsof at 50ms intervals for
up to 2 seconds after SIGKILL. cleanStaleGatewayProcessesSync() now
blocks until the port is confirmed free (or the budget expires with a
warning) before returning. The increased SIGTERM/SIGKILL wait budgets
(600ms / 400ms) also give slow processes more time to exit cleanly.

Fixes #33103
Related: #28134

* fix: add EADDRINUSE retry and TIME_WAIT port-bind checks for gateway startup

* fix(ports): treat EADDRNOTAVAIL as non-retryable and fix flaky test

* fix(gateway): hot-reload agents.defaults.models allowlist changes

The reload plan had a rule for `agents.defaults.model` (singular) but
not `agents.defaults.models` (plural — the allowlist array).  Because
`agents.defaults.models` does not prefix-match `agents.defaults.model.`,
it fell through to the catch-all `agents` tail rule (kind=none), so
allowlist edits in openclaw.json were silently ignored at runtime.

Add a dedicated reload rule so changes to the models allowlist trigger
a heartbeat restart, which re-reads the config and serves the updated
list to clients.

Fixes #33600

Co-authored-by: HCL <chenglunhu@gmail.com>
Signed-off-by: HCL <chenglunhu@gmail.com>

* test(restart): 100% branch coverage — audit round 2

Audit findings fixed:
- remove dead guard: terminateStaleProcessesSync pids.length===0 check was
  unreachable (only caller cleanStaleGatewayProcessesSync already guards)
- expose __testing.callSleepSyncRaw so sleepSync's real Atomics.wait path
  can be unit-tested directly without going through the override
- fix broken sleepSync Atomics.wait test: previous test set override=null
  but cleanStaleGatewayProcessesSync returned before calling sleepSync —
  replaced with direct callSleepSyncRaw calls that actually exercise L36/L42-47
- fix pid collision: two tests used process.pid+304 (EPERM + dead-at-SIGTERM);
  EPERM test changed to process.pid+305
- fix misindented tests: 'deduplicates pids' and 'lsof status 1 container
  edge case' were outside their intended describe blocks; moved to correct
  scopes (findGatewayPidsOnPortSync and pollPortOnce respectively)
- add missing branch tests:
  - status 1 + non-empty stdout with zero openclaw pids → free:true (L145)
  - mid-loop non-openclaw cmd in &&-chain (L67)
  - consecutive p-lines without c-line between them (L67)
  - invalid PID in p-line (p0 / pNaN) — ternary false branch (L67)
  - unknown lsof output line (else-if false branch L69)

Coverage: 100% stmts / 100% branch / 100% funcs / 100% lines (36 tests)

* test(restart): fix stale-pid test typing for tsgo

* fix(gateway): address lifecycle review findings

* test(update): make restart-helper path assertions windows-safe

---------

Signed-off-by: HCL <chenglunhu@gmail.com>
Co-authored-by: Glucksberg <markuscontasul@gmail.com>
Co-authored-by: Efe Büken <efe@arven.digital>
Co-authored-by: Riccardo Marino <rmarino@apple.com>
Co-authored-by: HCL <chenglunhu@gmail.com>

* fix(routing): unify session delivery invariants for duplicate suppression (#33786)

* Routing: unify session delivery invariants

* Routing: address PR review feedback

* Routing: tighten topic and session-scope suppression

* fix(chat): inherit routes for per-account channel-peer sessions

* feat(telegram): add per-topic agent routing for forum groups [AI-assisted]

This feature allows different topics within a Telegram forum supergroup to route
to different agents, each with isolated workspace, memory, and sessions.

Key changes:
- Add agentId field to TelegramTopicConfig type for per-topic routing
- Add zod validation for agentId in topic config schema
- Implement routing logic to re-derive session key with topic's agent
- Add debug logging for topic agent overrides
- Add unit tests for routing behavior (forum topics + DM topics)
- Add config validation tests
- Document feature in docs/channels/telegram.md

This builds on the approach from PR #31513 by @Sid-Qin with additional fixes
for security (preserved account fail-closed guard) and test coverage.

Closes #31473

* fix(telegram): address PR review comments

- Export pickFirstExistingAgentId and use it to validate topic agentId
- Properly update mainSessionKey when overriding route agent
- Fix docs example showing incorrect session key for topic 3

Fixes issue where non-existent agentId would create orphaned sessions.
Fixes issue where DM topic replies would route to wrong agent.

* fix: tighten telegram topic-agent docs + fallback tests (#33647) (thanks @kesor)

* fix(gateway): preserve route inheritance for legacy channel session keys (openclaw#33919) thanks @Takhoffman

Verified:
- pnpm build
- pnpm check
- pnpm test src/gateway/server-methods/chat.directive-tags.test.ts
- pnpm test:macmini

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(sessions-spawn): remove maxLength from attachment content schema to fix llama.cpp GBNF grammar overflow

* fix: harden sessions_spawn attachment schema landing (#33648) (thanks @anisoptera)

* Deps: fix pnpm audit vulnerabilities in Google extension path (#33939)

* extensions/googlechat: require openclaw 2026.3.2+

* extensions/memory-core: require openclaw 2026.3.2+

* deps: bump fast-xml-parser override to 5.3.8

* deps: refresh lockfile for audit vulnerability fixes

* fix(gateway): narrow legacy route inheritance for custom session keys (openclaw#33932) thanks @Takhoffman

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

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* docs(changelog): credit #31513 in #33647 entry

* feat(web-search): switch Perplexity to native Search API (#33822)

* feat: Add Perplexity Search API as web_search provider

* docs fixes

* domain_filter validation

* address comments

* provider-specific options in cache key

* add validation for unsupported date filters

* legacy fields

* unsupported_language guard

* cache key matches the request's precedence order

* conflicting_time_filters guard

* unsupported_country guard

* invalid_date_range guard

* pplx validate for ISO 639-1 format

* docs: add Perplexity Search API changelog entry

* unsupported_domain_filter guard

---------

Co-authored-by: Shadow <hi@shadowing.dev>

* feat(slack): add typingReaction config for DM typing indicator fallback (#19816)

* feat(slack): add typingReaction config for DM typing indicator fallback

Adds a reaction-based typing indicator for Slack DMs that works without
assistant mode. When `channels.slack.typingReaction` is set (e.g.
"hourglass_flowing_sand"), the emoji is added to the user's message when
processing starts and removed when the reply is sent.

Addresses #19809

* test(slack): add typingReaction to createSlackMonitorContext test callers

* test(slack): add typingReaction to test context callers

* test(slack): add typingReaction to context fixture

* docs(changelog): credit Slack typingReaction feature

* test(slack): align existing-thread history expectation

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

* plugins: avoid peer auto-install dependency bloat (#34017)

* plugins/install: omit peer deps during plugin npm install

* tests: assert plugin install omits peer deps

* extensions/googlechat: mark openclaw peer optional

* extensions/memory-core: mark openclaw peer optional

* fix: code/cli acpx reliability 20260304 (#34020)

* agents: switch claude-cli defaults to bypassPermissions

* agents: add claude-cli default args coverage

* agents: emit watchdog stall system event for cli runs

* agents: test cli watchdog stall system event

* acpx: fallback to sessions new when ensure returns no ids

* acpx tests: mock sessions new fallback path

* acpx tests: cover ensure-empty fallback flow

* skills: clarify claude print mode without pty

* docs: update cli-backends claude default args

* docs: refresh cli live test default args

* gateway tests: align live claude args defaults

* changelog: credit claude/acpx reliability fixes

* Agents: normalize legacy Claude permission flag overrides

* Tests: cover legacy Claude permission override normalization

* Changelog: note legacy Claude permission flag auto-normalization

* ACPX: fail fast when ensure/new return no session IDs

* ACPX tests: support empty sessions new fixture output

* ACPX tests: assert ensureSession failure when IDs missing

* CLI runner: scope watchdog heartbeat wake to session

* CLI runner tests: assert session-scoped watchdog wake

* Update CHANGELOG.md

* fix(outbound): unify resolved cfg threading across send paths (#33987)

* Plugins: add root-alias shim and cache/docs updates

* Extensions: migrate bluebubbles plugin-sdk imports

* Extensions: migrate device-pair plugin-sdk imports

* Extensions: migrate diagnostics-otel plugin-sdk imports

* Extensions: migrate diffs plugin-sdk imports

* Extensions: migrate feishu plugin-sdk imports

* Extensions: migrate google-gemini-cli-auth plugin-sdk imports

* Extensions: migrate googlechat plugin-sdk imports

* Extensions: migrate irc plugin-sdk imports

* Extensions: migrate llm-task plugin-sdk imports

* Extensions: migrate lobster plugin-sdk imports

* Extensions: migrate matrix plugin-sdk imports

* Extensions: migrate mattermost plugin-sdk imports

* Extensions: migrate minimax-portal-auth plugin-sdk imports

* Extensions: migrate msteams plugin-sdk imports

* Extensions: migrate nextcloud-talk plugin-sdk imports

* Extensions: migrate nostr plugin-sdk imports

* Extensions: migrate qwen-portal-auth plugin-sdk imports

* Extensions: migrate synology-chat plugin-sdk imports

* Extensions: migrate telegram plugin-sdk imports

* Extensions: migrate test-utils plugin-sdk imports

* Extensions: migrate tlon plugin-sdk imports

* Extensions: migrate twitch plugin-sdk imports

* Extensions: migrate voice-call plugin-sdk imports

* Extensions: migrate zalo plugin-sdk imports

* Extensions: migrate zalouser plugin-sdk imports

* Extensions: migrate acpx plugin-sdk imports

* fix(tui): normalize session key to lowercase to match gateway canonicalization (#34013)

Merged via squash.

Prepared head SHA: cfe06ca131661d1fd669270345c549ee8141cc46
Co-authored-by: lynnzc <6257996+lynnzc@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* Plugin SDK: add full bundled subpath wiring

* Plugins/acpx: migrate to scoped plugin-sdk imports

* Plugins/bluebubbles: migrate to scoped plugin-sdk imports

* Plugins/copilot-proxy: migrate to scoped plugin-sdk imports

* Plugins/device-pair: migrate to scoped plugin-sdk imports

* Plugins/diagnostics-otel: migrate to scoped plugin-sdk imports

* Plugins/diffs: migrate to scoped plugin-sdk imports

* Plugins/feishu: migrate to scoped plugin-sdk imports

* Plugins/google-gemini-cli-auth: migrate to scoped plugin-sdk imports

* Plugins/googlechat: migrate to scoped plugin-sdk imports

* Plugins/irc: migrate to scoped plugin-sdk imports

* Plugins/llm-task: migrate to scoped plugin-sdk imports

* Plugins/lobster: migrate to scoped plugin-sdk imports

* Plugins/matrix: migrate to scoped plugin-sdk imports

* Plugins/mattermost: migrate to scoped plugin-sdk imports

* Plugins/memory-core: migrate to scoped plugin-sdk imports

* Plugins/memory-lancedb: migrate to scoped plugin-sdk imports

* Plugins/minimax-portal-auth: migrate to scoped plugin-sdk imports

* Plugins/msteams: migrate to scoped plugin-sdk imports

* Plugins/nextcloud-talk: migrate to scoped plugin-sdk imports

* Plugins/nostr: migrate to scoped plugin-sdk imports

* Plugins/open-prose: migrate to scoped plugin-sdk imports

* Plugins/phone-control: migrate to scoped plugin-sdk imports

* Plugins/qwen-portal-auth: migrate to scoped plugin-sdk imports

* Plugins/synology-chat: migrate to scoped plugin-sdk imports

* Plugins/talk-voice: migrate to scoped plugin-sdk imports

* Plugins/test-utils: migrate to scoped plugin-sdk imports

* Plugins/thread-ownership: migrate to scoped plugin-sdk imports

* Plugins/tlon: migrate to scoped plugin-sdk imports

* Plugins/twitch: migrate to scoped plugin-sdk imports

* Plugins/voice-call: migrate to scoped plugin-sdk imports

* Plugins/whatsapp: migrate to scoped plugin-sdk imports

* Plugins/zalo: migrate to scoped plugin-sdk imports

* Plugins/zalouser: migrate to scoped plugin-sdk imports

* Chore: remove accidental .DS_Store artifact

* chore(docs): add plugins refactor changelog entry

* feat(ios): add Live Activity connection status + stale cleanup (#33591)

* feat(ios): add live activity connection status and cleanup

Add lock-screen/Dynamic Island connection health states and prune duplicate/stale activities before reuse. This intentionally excludes AI/title generation and heavier UX rewrites from #27488.

Co-authored-by: leepokai <1663017+leepokai@users.noreply.github.com>

* fix(ios): treat ended live activities as inactive

* chore(changelog): add PR reference and author thanks

---------

Co-authored-by: leepokai <1663017+leepokai@users.noreply.github.com>

* fix: kill stuck ACP child processes on startup and harden sessions in discord threads (#33699)

* Gateway: resolve agent.wait for chat.send runs

* Discord: harden ACP thread binding + listener timeout

* ACPX: handle already-exited child wait

* Gateway/Discord: address PR review findings

* Discord: keep ACP error-state thread bindings on startup

* gateway: make agent.wait dedupe bridge event-driven

* discord: harden ACP probe classification and cap startup fan-out

* discord: add cooperative timeout cancellation

* discord: fix startup probe concurrency helper typing

* plugin-sdk: avoid Windows root-alias shard timeout

* plugin-sdk: keep root alias reflection path non-blocking

* discord+gateway: resolve remaining PR review findings

* gateway+discord: fix codex review regressions

* Discord/Gateway: address Codex review findings

* Gateway: keep agent.wait lifecycle active with shared run IDs

* Discord: clean up status reactions on aborted runs

* fix: add changelog note for ACP/Discord startup hardening (#33699) (thanks @dutifulbob)

---------

Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>

* fix: relay ACP sessions_spawn parent streaming (#34310) (thanks @vincentkoc) (#34310)

Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>

* fix(telegram): materialize dm draft final to avoid duplicates

* docs(changelog): credit @Brotherinlaw-13 for #34318

* fix: prevent nodes media base64 context bloat (#34332)

* fix: preserve raw media invoke for HTTP tool clients (#34365)

* fix(slack): route system events to bound agent sessions (#34045)

* fix(slack): route system events via binding-aware session keys

* fix(slack): pass sender to system event session resolver

* fix(slack): include sender context for interaction session routing

* fix(slack): include modal submitter in session routing

* test(slack): cover binding-aware system event routing

* test(slack): update interaction session key assertions

* test(slack): assert reaction session routing carries sender

* docs(changelog): note slack system event routing fix

* Update CHANGELOG.md

* Delete changelog/fragments directory

* fix(memory): serialize local embedding initialization to avoid duplicate model loads (#15639)

Merged via squash.

Prepared head SHA: a085fc21a8ba7163fffdb5de640dd4dc1ff5a88e
Co-authored-by: SubtleSpark <43933609+SubtleSpark@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix(model): propagate custom provider headers to model objects (#27490)

Merged via squash.

Prepared head SHA: e4183b398fc7eb4c18b2b691cb0dd882ec993608
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(daemon): handle systemctl is-enabled exit 4 (not-found) on Ubuntu (#33634)

Merged via squash.

Prepared head SHA: 67dffc3ee239cd7b813cb200c3dd5475d9e203a6
Co-authored-by: Yuandiaodiaodiao <33371662+Yuandiaodiaodiao@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(node-host): sync rawCommand with hardened argv after executable path pinning (#33137)

Merged via squash.

Prepared head SHA: a7987905f7ad6cf5fee286ffa81ceaad8297174f
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* Agents: add generic poll-vote action support

* fix(ollama): pass provider headers to Ollama stream function (#24285)

createOllamaStreamFn() only accepted baseUrl, ignoring custom headers
configured in models.providers.<provider>.headers. This caused 403
errors when Ollama endpoints are behind reverse proxies that require
auth headers (e.g. X-OLLAMA-KEY via HAProxy).

Add optional defaultHeaders parameter to createOllamaStreamFn() and
merge them into every fetch request. Provider headers from config are
now passed through at the call site in the embedded runner.

Fixes #24285

* test(ollama): add default header precedence coverage

* chore(changelog): add PR entry openclaw#24337 thanks @echoVic

* Outbound: allow text-only plugin adapters

* Outbound: avoid empty multi-media fallback sends

* chore(changelog): align outbound adapter entry openclaw#32788 thanks @liuxiaopai-ai

* fix(outbound): fail media-only text-only adapter fallback

* chore(changelog): clarify outbound media-only fallback openclaw#32788 thanks @liuxiaopai-ai

* fix(review): enforce behavioral sweep validation

* Fix gateway restart false timeouts on Debian/systemd (#34874)

* daemon(systemd): target sudo caller user scope

* test(systemd): cover sudo user scope commands

* infra(ports): fall back to ss when lsof missing

* test(ports): verify ss fallback listener detection

* cli(gateway): use probe fallback for restart health

* test(gateway): cover restart-health probe fallback

* Compaction/Safeguard: require structured summary headings (#25555)

Merged via squash.

Prepared head SHA: 0b1df34806a7b788261290be55760fd89220de53
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* Fix Linux daemon install checks when systemd user bus env is missing (#34884)

* daemon(systemd): fall back to machine user scope when user bus is missing

* test(systemd): cover machine scope fallback for user-bus errors

* test(systemd): reset execFile mock state across cases

* test(systemd): make machine-user fallback assertion portable

* fix(daemon): keep root sudo path on direct user scope

* test(systemd): cover sudo root user-scope behavior

* ci: use resolvable bun version in setup-node-env

* agents: preserve totalTokens on request failure instead of using contextWindow (#34275)

Merged via squash.

Prepared head SHA: f9d111d0a79a07815d476356e98a28df3a0000ba
Co-authored-by: RealKai42 <44634134+RealKai42@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* fix: align AGENTS.md template section names with post-compaction extraction (#25029) (#25098)

Merged via squash.

Prepared head SHA: 8cd6cc8049aab5a94d8a9d5fb08f2e792c4ac5fd
Co-authored-by: echoVic <16428813+echoVic@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* Changelog: add daemon systemd user-bus fallback entry (#34884)

* Changelog: add gateway restart health entry (#34874)

* fix: finalize spanish locale support

* fix: add spanish locale support (#35038) (thanks @DaoPromociones)

* fix(deps): patch hono transitive audit vulnerabilities

* fix(security): avoid prototype-chain account path checks (#34982)

Merged via squash.

Prepared head SHA: f89cc6a649959997fe1dec1e1c1bff9a61b2de98
Co-authored-by: HOYALIM <166576253+HOYALIM@users.noreply.github.com>
Co-authored-by: dvrshil <81693876+dvrshil@users.noreply.github.com>
Reviewed-by: @dvrshil

* fix(deps): bump tar to 7.5.10

* docs(changelog): document dependency security fixes

* fix: restore auto-reply system events timeline (#34794) (thanks @anisoptera) (#34794)

Co-authored-by: Ayaan Zaidi <zaidi@uplause.io>

* fix(feishu): comprehensive reply mechanism — outbound replyToId forwarding + topic-aware reply targeting (#33789)

* fix(feishu): comprehensive reply mechanism fix — outbound replyToId forwarding + topic-aware reply targeting

- Forward replyToId from ChannelOutboundContext through sendText/sendMedia
  to sendMessageFeishu/sendMarkdownCardFeishu/sendMediaFeishu, enabling
  reply-to-message via the message tool.

- Fix group reply targeting: use ctx.messageId (triggering message) in
  normal groups to prevent silent topic thread creation (#32980). Preserve
  ctx.rootId targeting for topic-mode groups (group_topic/group_topic_sender)
  and groups with explicit replyInThread config.

- Add regression tests for both fixes.

Fixes #32980
Fixes #32958
Related #19784

* fix: normalize Feishu delivery.to before comparing with messaging tool targets

- Add normalizeDeliveryTarget helper to strip user:/chat: prefixes for Feishu
- Apply normalization in matchesMessagingToolDeliveryTarget before comparison
- This ensures cron duplicate suppression works when session uses prefixed targets
  (user:ou_xxx) but messaging tool extract uses normalized bare IDs (ou_xxx)

Fixes review comment on PR #32755

(cherry picked from commit fc20106f16ccc88a5f02e58922bb7b7999fe9dcd)

* fix(feishu): catch thrown SDK errors for withdrawn reply targets

The Feishu Lark SDK can throw exceptions (SDK errors with .code or
AxiosErrors with .response.data.code) for withdrawn/deleted reply
targets, in addition to returning error codes in the response object.

Wrap reply calls in sendMessageFeishu and sendCardFeishu with
try-catch to handle thrown withdrawn/not-found errors (230011,
231003) and fall back to client.im.message.create, matching the
existing response-level fallback behavior.

Also extract sendFallbackDirect helper to deduplicate the
direct-send fallback block across both functions.

Closes #33496

(cherry picked from commit ad0901aec103a2c52f186686cfaf5f8ba54b4a48)

* feishu: forward outbound reply target context

(cherry picked from commit c129a691fcf552a1cebe1e8a22ea8611ffc3b377)

* feishu extension: tighten reply target fallback semantics

(cherry picked from commit f85ec610f267020b66713c09e648ec004b2e26f1)

* fix(feishu): align synthesized fallback typing and changelog attribution

* test(feishu): cover group_topic_sender reply targeting

---------

Co-authored-by: Xu Zimo <xuzimojimmy@163.com>
Co-authored-by: Munem Hashmi <munem.hashmi@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(feishu): use msg_type media for mp4 video (fixes #33674) (#33720)

* fix(feishu): use msg_type media for mp4 video (fixes #33674)

* Feishu: harden streaming merge semantics and final reply dedupe

Use explicit streaming update semantics in the Feishu reply dispatcher:
treat onPartialReply payloads as snapshot updates and block fallback payloads
as delta chunks, then merge final text with the shared overlap-aware
mergeStreamingText helper before closing the stream.

Prevent duplicate final text delivery within the same dispatch cycle, and add
regression tests covering overlap snapshot merge, duplicate final suppression,
and block-as-delta behavior to guard against repeated/truncated output.

* fix(feishu): prefer message.reply for streaming cards in topic threads

* fix: reduce Feishu streaming card print_step to avoid duplicate rendering

Fixes openclaw/openclaw#33751

* Feishu: preserve media sends on duplicate finals and add media synthesis changelog

* Feishu: only dedupe exact duplicate final replies

* Feishu: use scoped plugin-sdk import in streaming-card tests

---------

Co-authored-by: 倪汉杰0668001185 <ni.hanjie@xydigit.com>
Co-authored-by: zhengquanliu <zhengquanliu@bytedance.com>
Co-authored-by: nick <nickzj@qq.com>
Co-authored-by: linhey <linhey@mini.local>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(agents): bypass pendingDescendantRuns guard for cron announce delivery (#35185)

* fix(agents): bypass pendingDescendantRuns guard for cron announce delivery

Standalone cron job completions were blocked from direct channel delivery
when the cron run had spawned subagents that were still registered as
pending. The pendingDescendantRuns guard exists for live orchestration
coordination and should not apply to fire-and-forget cron announce sends.

Thread the announceType through the delivery chain and skip both the
child-descendant and requester-descendant pending-run guards when the
announce originates from a cron job.

Closes #34966

* fix: ensure outbound session entry for cron announce with named agents (#32432)

Named agents may not have a session entry for their delivery target,
causing the announce flow to silently fail (delivered=false, no error).

Two fixes:
1. Call ensureOutboundSessionEntry when resolving the cron announce
   session key so downstream delivery can find channel metadata.
2. Fall back to direct outbound delivery when announce delivery fails
   to ensure cron output reaches the target channel.

Closes #32432

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

* fix: guard announce direct-delivery fallback against suppression leaks (#32432)

The `!delivered` fallback condition was too broad — it caught intentional
suppressions (active subagents, interim messages, SILENT_REPLY_TOKEN) in
addition to actual announce delivery failures.  Add an
`announceDeliveryWasAttempted` flag so the direct-delivery fallback only
fires when `runSubagentAnnounceFlow` was actually called and failed.

Also remove the redundant `if (route)` guard in
`resolveCronAnnounceSessionKey` since `resolved` being truthy guarantees
`route` is non-null.

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

* fix(cron): harden announce synthesis follow-ups

---------

Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* Feishu: harden streaming merge semantics and final reply dedupe (#33245)

* Feishu: close duplicate final gap and cover routing precedence

* Feishu: resolve reviewer duplicate-final and routing feedback

* Feishu: tighten streaming send-mode option typing

* Feishu: fix reverse-overlap streaming merge ordering

* Feishu: align streaming final dedupe test expectation

* Feishu: allow distinct streaming finals while deduping repeats

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix: cron backup should preserve pre-edit snapshot (#35195) (#35234)

* fix(cron): avoid overwriting .bak during normalization

Fixes openclaw/openclaw#35195

* test(cron): preserve pre-edit bak snapshot in normalization path

---------

Co-authored-by: 0xsline <sline@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(cron): stabilize restart catch-up replay semantics (#35351)

* Cron: stabilize restart catch-up replay semantics

* Cron: respect backoff in startup missed-run replay

* cron: narrow startup replay backoff guard (#35391)

* cron: unify stale-run recovery and preserve manual-run every anchors (#35363)

* cron: unify stale-run recovery and preserve manual every anchors

* cron: address unresolved review threads on recovery paths

* cron: remove duplicate timestamp helper after rebase

* refactor(telegram): remove unused webhook callback helper (#27816)

* fix(pr): make review claim step required

* fix(skills): deduplicate slash commands by skillName across all interfaces

Move skill-command deduplication by skillName from the Discord-only
`dedupeSkillCommandsForDiscord` into `listSkillCommandsForAgents` so
every interface (TUI, Slack, text) consistently sees a clean command
list without platform-specific workarounds.

When multiple agents share a skill with the same name the old code
emitted `github` + `github_2` and relied on Discord to collapse them.
Now `listSkillCommandsForAgents` returns only the first registration
per skillName, and the Discord-specific wrapper is removed.

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

* style: fix formatting in skill-commands.test.ts and provider.ts

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

* style(skills): align formatting cleanup for dedupe changes

* chore(changelog): add dedupe note openclaw#27521 thanks @shivama205

* fix(agents): detect Venice provider proxying xAI/Grok models for schema cleaning (#35355)

Merged via squash.

Prepared head SHA: 8bfdec257bb6a6025cb69a0a213a433da32b15db
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(agents): decode HTML entities in xAI/Grok tool call arguments (#35276)

Merged via squash.

Prepared head SHA: c4445d2938898ded9c046614f9315dbda65ec573
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(agents): guard promoteThinkingTagsToBlocks against malformed content entries (#35143)

Merged via squash.

Prepared head SHA: 3971122f5fd27c66c8c9c5ce783f00e113b1f47b
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(web-ui): render Accounts schema node properly (#35380)

Co-authored-by: stakeswky <64798754+stakeswky@users.noreply.github.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(agents): guard context pruning against malformed thinking blocks (#35146)

Merged via squash.

Prepared head SHA: a196a565b1b8e806ffbf85172bcf1128796b45a2
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(gateway): prevent internal route leakage in chat.send

Synthesis of routing fixes from #35321, #34635, and #35356 for internal-client reply safety.

- Require explicit `deliver: true` before inheriting any external delivery route.
- Keep webchat/TUI/UI-origin traffic on internal routing by default.
- Allow configured-main session inheritance only for non-Webchat/UI clients, and honor `session.mainKey`.
- Add regression tests for UI no-inherit, configured-main CLI inherit, and deliver-flag behavior.

Co-authored-by: alexyyyander <alexyyyander@users.noreply.github.com>
Co-authored-by: Octane0411 <88922959+Octane0411@users.noreply.github.com>
Co-authored-by: Linux2010 <35169750+Linux2010@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(gateway): pass actual version to Control UI client instead of dev (#35230)

* fix(gateway): pass actual version to Control UI client instead of "dev"

The GatewayClient, CLI WS client, and browser Control UI all sent
"dev" as their clientVersion during handshake, making it impossible
to distinguish builds in gateway logs and health snapshots.

- GatewayClient and CLI WS client now use the resolved VERSION constant
- Control UI reads serverVersion from the bootstrap endpoint and
  forwards it when connecting
- Bootstrap contract extended with serverVersion field

Closes #35209

* Gateway: fix control-ui version version-reporting consistency

* Control UI: guard deferred bootstrap connect after disconnect

* fix(ui): accept same-origin http and relative gateway URLs for client version

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* chore(pr): enforce changelog placement and reduce merge sync churn

* TTS: add baseUrl support to OpenAI TTS config (#34321)

Merged via squash.

Prepared head SHA: e9a10cf81d2021cf81091dfa81e13ffdbb6a540a
Co-authored-by: RealKai42 <44634134+RealKai42@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* ACP: add persistent Discord channel and Telegram topic bindings (#34873)

* docs: add ACP persistent binding experiment plan

* docs: align ACP persistent binding spec to channel-local config

* docs: scope Telegram ACP bindings to forum topics only

* docs: lock bound /new and /reset behavior to in-place ACP reset

* ACP: add persistent discord/telegram conversation bindings

* ACP: fix persistent binding reuse and discord thread parent context

* docs: document channel-specific persistent ACP bindings

* ACP: split persistent bindings and share conversation id helpers

* ACP: defer configured binding init until preflight passes

* ACP: fix discord thread parent fallback and explicit disable inheritance

* ACP: keep bound /new and /reset in-place

* ACP: honor configured bindings in native command flows

* ACP: avoid configured fallback after runtime bind failure

* docs: refine ACP bindings experiment config examples

* acp: cut over to typed top-level persistent bindings

* ACP bindings: harden reset recovery and native command auth

* Docs: add ACP bound command auth proposal

* Tests: normalize i18n registry zh-CN assertion encoding

* ACP bindings: address review findings for reset and fallback routing

* ACP reset: gate hooks on success and preserve /new arguments

* ACP bindings: fix auth and binding-priority review findings

* Telegram ACP: gate ensure on auth and accepted messages

* ACP bindings: fix session-key precedence and unavailable handling

* ACP reset/native commands: honor fallback targets and abort on bootstrap failure

* Config schema: validate ACP binding channel and Telegram topic IDs

* Discord ACP: apply configured DM bindings to native commands

* ACP reset tails: dispatch through ACP after command handling

* ACP tails/native reset auth: fix target dispatch and restore full auth

* ACP reset detection: fallback to active ACP keys for DM contexts

* Tests: type runTurn mock input in ACP dispatch test

* ACP: dedup binding route bootstrap and reset target resolution

* reply: align ACP reset hooks with bound session key

* docs: replace personal discord ids with placeholders

* fix: add changelog entry for ACP persistent bindings (#34873) (thanks @dutifulbob)

---------

Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>

* docs(telegram): recommend allowlist for single-user DM policy (#34841)

* docs(telegram): recommend allowlist for single-user bots

* docs(telegram): condense single-user allowlist note

---------

Co-authored-by: echoVic <echovic@163.com>

* fix(feishu): check response.ok before calling response.json() in streaming card (#35628)

Merged via squash.

Prepared head SHA: 62c3fec80d97cea9be344c0bef5358a0a5dc5560
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* Mattermost: honor onmessage mention override and add gating diagnostics tests (#27160)

Merged via squash.

Prepared head SHA: 6cefb1d5bf3d6dfcec36c1cee3f9ea887f10c890
Co-authored-by: turian <65918+turian@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm

* fix(subagents): strip leaked [[reply_to]] tags from completion announces (#34503)

* fix(subagents): strip reply tags from completion delivery text

* test(subagents): cover reply-tag stripping in cron completion sends

* changelog: note iMessage reply-tag stripping in completion announces

* Update CHANGELOG.md

* Apply suggestion from @greptile-apps[bot]

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(cron): restore direct fallback after announce failure in best-effort mode (openclaw#36177)

Verified:
- pnpm build
- pnpm check (fails on pre-existing origin/main lint debt in extensions/mattermost imports)
- pnpm test:macmini

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* test(cron): add cross-channel announce fallback regression coverage (openclaw#36197)

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check (fails on pre-existing origin/main lint debt in extensions/mattermost imports)
- pnpm test:macmini

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* feat(mattermost): add interactive buttons support (#19957)

Merged via squash.

Prepared head SHA: 8a25e608729d0b9fd07bb0ee4219d199d9796dbe
Co-authored-by: tonydehnke <36720180+tonydehnke@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm

* fix(browser): remove deprecated --disable-blink-features=AutomationControlled flag

- Removes OpenClaw's default `--disable-blink-features=AutomationControlled` Chrome launch switch to avoid unsupported-flag warnings in newer Chrome (#35721).
- Preserves compatibility for older Chrome via `browser.extraArgs` override behavior (source analysis: #35770, #35728, #35727, #35885).
- Synthesis attribution: thanks @Sid-Qin, @kevinWangSheng, @ningding97, @Naylenv, @clawbie.

Source PR refs: #35734, #35770, #35728, #35727, #35885

Co-authored-by: Sid-Qin <Sid-Qin@users.noreply.github.com>
Co-authored-by: kevinWangSheng <kevinWangSheng@users.noreply.github.com>
Co-authored-by: ningding97 <ningding97@users.noreply.github.com>
Co-authored-by: Naylenv <Naylenv@users.noreply.github.com>
Co-authored-by: clawbie <clawbie@users.noreply.github.com>
Co-authored-by: Takhoffman <Takhoffman@users.noreply.github.com>

* fix(feishu): add HTTP timeout to prevent per-chat queue deadlocks (#36430)

When the Feishu API hangs or responds slowly, the sendChain never settles,
causing the per-chat queue to remain in a processing state forever and
blocking all subsequent messages in that thread. This adds a 30-second
default timeout to all Feishu HTTP requests by providing a timeout-aware
httpInstance to the Lark SDK client.

Closes #36412

Co-authored-by: Ayane <wangruofei@soulapp.cn>

* fix(feishu): use probed botName for mention checks (#36391)

* Feishu: honor bot mentions by ID despite aliases (Fixes #36317) (#36333)

* Mattermost: switch plugin-sdk imports to scoped subpaths (openclaw#36480)

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

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(feishu): accept groupPolicy "allowall" as alias for "open" (#36358)

* fix(feishu): accept groupPolicy "allowall" as alias for "open"

When users configure groupPolicy: "allowall" in Feishu channel config,
the Zod schema rejects the value and the runtime policy check falls
through to the allowlist path.  With an empty allowFrom array, all group
messages are silently dropped despite the intended "allow all" semantics.

Accept "allowall" at the schema level (transform to "open") and add a
runtime guard in isFeishuGroupAllowed so the value is handled even if it
bypasses schema validation.

Closes #36312

Made-with: Cursor

* Feishu: tighten allowall alias handling and coverage

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* synthesis: fix Feishu group mention slash parsing

## Summary\n\nFeishu group slash command parsing is fixed for mentions and command probes across authorization paths.\n\nThis includes:\n- Normalizing bot mention text in group context for reliable slash detection in message parsing.\n- Adding command-probe normalization for group slash invocations.\n\nCo-authored-by: Sid Qin <sidqin0410@gmail.com>\nCo-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* Feishu: normalize group slash command probing

- Feishu/group slash command detection: normalize group mention wrappers before command-authorization probing so mention-prefixed commands are recognized in group routing.\n- Source PR: #36011\n- Contributor: @liuxiaopai-ai\n\nCo-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>\nCo-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>

* add prependSystemContext and appendSystemContext to before_prompt_build (fixes #35131) (#35177)

Merged via squash.

Prepared head SHA: d9a2869ad69db9449336a2e2846bd9de0e647ac6
Co-authored-by: maweibin <18023423+maweibin@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix(feishu): avoid media regressions from global HTTP timeout (#36500)

* fix(feishu): avoid media regressions from global http timeout

* fix(feishu): source HTTP timeout from config

* fix(feishu): apply media timeout override to image uploads

* fix(feishu): invalidate cached client when timeout changes

* fix(feishu): clamp timeout values and cover image download

* chore(devcontainer): add claude-code extension and set Ollama context window to 16384

---------

Signed-off-by: HCL <chenglunhu@gmail.com>
Co-authored-by: Viz <visionik@pobox.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Shadow <hi@shadowing.dev>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: bmendonca3 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: davidahmann <46606159+davidahmann@users.noreply.github.com>
Co-authored-by: harshang03 <58983401+harshang03@users.noreply.github.com>
Co-authored-by: haosenwang1018 <167664334+haosenwang1018@users.noreply.github.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: coygeek <65363919+coygeek@users.noreply.github.com>
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: Henry Loenwind <henry@loenwind.info>
Co-authored-by: HenryLoenwind <1485873+HenryLoenwind@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: JT <56984887+joetomasone@users.noreply.github.com>
Co-authored-by: Josh Lehman <josh@martian.engineering>
Co-authored-by: Eugene <eugene@eagerdesigns.com.au>
Co-authored-by: sircrumpet <4436535+sircrumpet@users.noreply.github.com>
Co-authored-by: Muhammed Mukhthar CM <56378562+mukhtharcm@users.noreply.github.com>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
Co-authored-by: Sid <sidqin0410@gmail.com>
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: Nimrod Gutman <nimrod.g@singular.net>
Co-authored-by: KimGLee <05_bolster_inkling@icloud.com>
Co-authored-by: Ayaan Zaidi <zaidi@uplause.io>
Co-authored-by: Altay <altay@hey.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Co-authored-by: OpenCils <114985039+OpenCils@users.noreply.github.com>
Co-authored-by: chengzhichao-xydt <cheng.zhichao@xydigit.com>
Co-authored-by: chengzhichao-xydt <264300353+chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: Rodrigo Uroz <rodrigo.uroz@classdojo.com>
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: Shadow <shadow@openclaw.ai>
Co-authored-by: Mylszd <23611557+Mylszd@users.noreply.github.com>
Co-authored-by: Jason L. West, Sr. <jlwestsr@gmail.com>
Co-authored-by: jlwestsr <52389+jlwestsr@users.noreply.g…
@shivama205
Copy link
Author

Hi @steipete 👋 — quick note on the CI status for this PR:

All checks directly related to this change passcheck (format/tsgo/lint), protocol:check, tests (node), tests (bun), android, all green. ✅

The 2 remaining failures are pre-existing Windows test failures unrelated to this PR:

  • src/infra/restart.test.ts — lsof-based tests fail on Windows (no lsof, mock incomplete for that platform)
  • src/plugins/loader.test.ts — bundled memory plugin metadata returns undefined on Windows
  • src/commands/onboard.test.ts — workspace reset assertion mismatch on Windows
  • src/plugins/discovery.test.ts — plugin directory scanning (likely path separator issue)
  • src/agents/sandbox/validate-sandbox-security.test.ts — Unix bind mount paths

None of these files are touched by this PR (src/auto-reply/skill-commands.ts, src/discord/monitor/provider.ts, and their tests). The same failures appear on the same files across other recent PRs on main.

Happy to open a separate issue/PR to track and fix the Windows test suite if that would be helpful. Let me know if you'd like any changes to this PR.

Takhoffman pushed a commit to Sid-Qin/openclaw that referenced this pull request Mar 5, 2026
bh-rat added a commit to bh-rat/storeclaw that referenced this pull request Mar 6, 2026
* chore(release): cut 2026.3.2

* fix(feishu): normalize all mentions in inbound agent context (#30252)

* fix(feishu): normalize all mentions in inbound agent context

Convert Feishu mention placeholders to explicit <at user_id="..."> tags (including bot mentions), add mention semantics hints for the model, and remove unused mentionMessageBody parsing to keep context handling consistent.

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

* fix(feishu): use replacer callback and escape only < > in normalizeMentions

Switch String.replace to a function replacer to prevent $ sequences in
display names from being interpolated as replacement patterns. Narrow
escaping to < and > only — & does not need escaping in LLM prompt tag
bodies and escaping it degrades readability (e.g. R&D → R&amp;D).

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

* fix(feishu): only use open_id in normalizeMentions tag, drop user_id fallback

When a mention has no open_id, degrade to @name instead of emitting
<at user_id="uid_...">. This keeps the tag user_id space exclusively
open_id, so the bot self-reference hint (which uses botOpenId) is
always consistent with what appears in the tags.

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

* fix(feishu): register mention strip pattern for <at> tags in channel dock

Add mentions.stripPatterns to feishuPlugin so that normalizeCommandBody
receives a slash-clean string after normalizeMentions replaces Feishu
placeholders with <at user_id="...">name</at> tags. Without this,
group slash commands like @Bot /help had their leading / obscured by
the tag prefix and no longer triggered command handlers.

Pattern mirrors the approach used by Slack (<@[^>]+>) and Discord (<@!?\d+>).

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

* fix(feishu): strip bot mention in p2p to preserve DM slash commands

In p2p messages the bot mention is a pure addressing prefix; converting
it to <at user_id="..."> breaks slash commands because buildCommandContext
skips stripMentions for DMs. Extend normalizeMentions with a stripKeys
set and populate it with bot mention keys in p2p, so @Bot /help arrives
as /help. Non-bot mentions (mention-forward targets) are still normalized
to <at> tags in both p2p and group contexts.

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

* Changelog: note Feishu inbound mention normalization

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(feishu): validate outbound renderMode routing with tests (#31562)

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(gateway): flush throttled delta before emitChatFinal (#24856)

* fix(gateway): flush throttled delta before emitChatFinal

The 150ms throttle in emitChatDelta can suppress the last text chunk
before emitChatFinal fires, causing streaming clients (e.g. ACP) to
receive truncated responses. The final event carries the complete text,
but clients that build responses incrementally from deltas miss the
tail end.

Flush one last unthrottled delta with the complete buffered text
immediately before sending the final event. This ensures all streaming
consumers have the full response without needing to reconcile deltas
against the final payload.

* fix(gateway): avoid duplicate delta flush when buffer unchanged

Track the text length at the time of the last broadcast. The flush in
emitChatFinal now only sends a delta if the buffer has grown since the
last broadcast, preventing duplicate sends when the final delta passed
the 150ms throttle and was already broadcast.

* fix(gateway): honor heartbeat suppression in final delta flush

* test(gateway): add final delta flush and dedupe coverage

* fix(gateway): skip final flush for silent lead fragments

* docs(changelog): note gateway final-delta flush fix credits

---------

Co-authored-by: Jonathan Taylor <visionik@pobox.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

* fix: repair Feishu reset hook typing and stabilize secret resolver timeout

* chore(release): bump to 2026.3.3 and seed changelog

* ci: enable stale workflow

* fix: scope extension runtime deps to plugin manifests

* docs(changelog): reattribute duplicated PR credits

* fix(gateway+acp): thread stopReason through final event to ACP bridge (#24867)

Complete the stop reason propagation chain so ACP clients can
distinguish end_turn from max_tokens:

- server-chat.ts: emitChatFinal accepts optional stopReason param,
  includes it in the final payload, reads it from lifecycle event data
- translator.ts: read stopReason from the final payload instead of
  hardcoding end_turn

Chain: LLM API → run.ts (meta.stopReason) → agent.ts (lifecycle event)
→ server-chat.ts (final payload) → ACP translator (PromptResponse)

* fix(line): synthesize media/auth/routing webhook regressions (openclaw#32546) thanks @Takhoffman

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

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(test): stabilize appcast version assertion

* fix(ci): handle disabled systemd units in docker doctor flow

* test(live): harden gateway model profile probes

* fix(telegram): debounce forwarded media-only bursts

* test(e2e): isolate module mocks across harnesses

* security(line): synthesize strict LINE auth boundary hardening

LINE auth boundary hardening synthesis for inbound webhook authn/z/authz:
- account-scoped pairing-store access
- strict DM/group allowlist boundary separation
- fail-closed webhook auth/runtime behavior
- replay and duplicate handling with in-flight continuity for concurrent redeliveries

Source PRs: #26701, #26683, #25978, #17593, #16619, #31990, #26047, #30584, #18777
Related continuity context: #21955

Co-authored-by: bmendonca3 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: davidahmann <46606159+davidahmann@users.noreply.github.com>
Co-authored-by: harshang03 <58983401+harshang03@users.noreply.github.com>
Co-authored-by: haosenwang1018 <167664334+haosenwang1018@users.noreply.github.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: coygeek <65363919+coygeek@users.noreply.github.com>
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>

* chore: Updated Brave documentation (#26860)

Merged via squash.

Prepared head SHA: f8fc4bf01e0eacfb01f6ee58eea445680f7eeebd
Co-authored-by: HenryLoenwind <1485873+HenryLoenwind@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix: improve compaction summary instructions to preserve active work (#8903)

fix: improve compaction summary instructions to preserve active work

Expand staged-summary merge instructions to preserve active task status, batch progress, latest user request, and follow-up commitments so compaction handoffs retain in-flight work context.

Co-authored-by: joetomasone <56984887+joetomasone@users.noreply.github.com>
Co-authored-by: Josh Lehman <josh@martian.engineering>

* bug: Workaround for QMD upstream bug (#27028)

Merged via squash.

Prepared head SHA: 939f9f4574fcfe08762407ab9e8d6c85a77a0899
Co-authored-by: HenryLoenwind <1485873+HenryLoenwind@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* Diffs: Migrate tool usage guidance from before_prompt_build to a plugin skill (#32630)

Merged via squash.

Prepared head SHA: 585697a4e1556baa2cd79a7b449b120c4fd87e17
Co-authored-by: sircrumpet <4436535+sircrumpet@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* feat(mattermost): add native slash command support (refresh) (#32467)

Merged via squash.

Prepared head SHA: 989126574ead75c0eedc185293659eb0d4fc6844
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm

* Gateway: fix stale self version in status output (#32655)

Merged via squash.

Prepared head SHA: b9675d1f90ef0eabb7e68c24a72d4b2fb27def22
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* agents: propagate config for embedded skill loading

* fix(telegram): warn when accounts.default is missing in multi-account setup (#32544)

Merged via squash.

Prepared head SHA: 7ebc3f65b21729137d352fa76bc31f2f849934c0
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* macOS: add tailscale serve discovery fallback for remote gateways (#32860)

* feat(macos): add tailscale serve gateway discovery fallback

* fix: add changelog note for tailscale serve discovery fallback (#32860) (thanks @ngutman)

* fix(telegram): run outbound message hooks in reply delivery path

* fix(telegram): mark message_sent success only when delivery occurred

* fix(telegram): include reply hook metadata

* docs: update changelog for telegram message_sent fix (#32649)

* fix: guard malformed Telegram replies and pass hook accountId

* fix(heartbeat): scope exec wake dispatch to session key (#32724)

Merged via squash.

Prepared head SHA: 563fee0e65af07575f3df540cab2e1e5d5589f06
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* fix(telegram): prevent duplicate messages in DM draft streaming mode (#32118)

* fix(telegram): prevent duplicate messages in DM draft streaming mode

When using sendMessageDraft for DM streaming (streaming: 'partial'),
the draft bubble auto-converts to the final message. The code was
incorrectly falling through to sendPayload() after the draft was
finalized, causing a duplicate message.

This fix checks if we're in draft preview mode with hasStreamedMessage
and skips the sendPayload call, returning "preview-finalized" directly.

Key changes:
- Use hasStreamedMessage flag instead of previewRevision comparison
- Avoids double stopDraftLane calls by returning early
- Prevents duplicate messages when final text equals last streamed text

Root cause: In lane-delivery.ts, the final message handling logic
did not properly handle the DM draft flow where sendMessageDraft
creates a transient bubble that doesn't need a separate final send.

* fix(telegram): harden DM draft finalization path

* fix(telegram): require emitted draft preview for unchanged finals

* fix(telegram): require final draft text emission before finalize

* fix: update changelog for telegram draft finalization (#32118) (thanks @OpenCils)

---------

Co-authored-by: Ayaan Zaidi <zaidi@uplause.io>

* fix: substitute YYYY-MM-DD at session startup and post-compaction (#32363) (#32381)

Merged via squash.

Prepared head SHA: aee998a2c1a911d3fef771aa891ac315a2f7dc53
Co-authored-by: chengzhichao-xydt <264300353+chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* chore: note about pagination

* Compaction/Safeguard: preserve recent turns verbatim (#25554)

Merged via squash.

Prepared head SHA: 7fb33c411c4aaea2795e490fcd0e647cf7ea6fb8
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* fix: ignore discord wildcard audit keys (#33125) (thanks @thewilloftheshadow) (#33125)

* fix: Discord acp inline actions + bound-thread filter (#33136) (thanks @thewilloftheshadow) (#33136)

* fix: harden Discord channel resolution (#33142) (thanks @thewilloftheshadow) (#33142)

* docs(loop-detection): fix config keys to match schema (#33182)

Merged via squash.

Prepared head SHA: 612ecc00d36cbbefb0657f0a2ac0898d53a5ed73
Co-authored-by: Mylszd <23611557+Mylszd@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* feat(tool-truncation): use head+tail strategy to preserve errors during truncation (#20076)

Merged via squash.

Prepared head SHA: 6edebf22b1666807b1ea5cba5afb614c41dc3dd1
Co-authored-by: jlwestsr <52389+jlwestsr@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* iOS Security Stack 1/5: Keychain Migrations + Tests (#33029)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: da2f8f614155989345d0d3efd0c0f29ef410c187
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix: improve discord chunk delivery (#33226) (thanks @thewilloftheshadow) (#33226)

* fix(discord): default presence online when unconfigured

* fix(discord): stop typing after silent runs

* fix(discord): use fetch for voice upload slots

* test(discord): align bound-thread target kind

* iOS Security Stack 2/5: Concurrency Locks (#33241)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: b99ad804fbcc20bfb3042ac1da9050a7175f009c
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* iOS Security Stack 3/5: Runtime Security Guards (#33031)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 99171654014d1960edcaca8312ef6a47d3c08399
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix: discord mention handling (#33224) (thanks @thewilloftheshadow) (#33224)

* iOS Security Stack 4/5: TTS PCM->MP3 Fallback (#30885) (#33032)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: f77e3d764425a23ab5d5b55593d9e14622f1ef95
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix: allowlist Discord CDN hostnames for SSRF media (#33275) (thanks @thewilloftheshadow) (#33275)

* fix: stabilize Telegram draft boundaries and suppress NO_REPLY lead leaks (#33169)

* fix: stabilize telegram draft stream message boundaries

* fix: suppress NO_REPLY lead-fragment leaks

* fix: keep underscore guard for non-NO_REPLY prefixes

* fix: skip assistant-start rotation only after real lane rotation

* fix: preserve finalized state when pre-rotation does not force

* fix: reset finalized preview state on message-start boundary

* fix: document Telegram draft boundary + NO_REPLY reliability updates (#33169) (thanks @obviyus)

* fix: discord auto presence health signal (#33277) (thanks @thewilloftheshadow) (#33277)

* docs: document discord ignoreOtherMentions

* fix(discord): skip bot messages before debounce

* fix(discord): honor agent media roots in replies

* fix(discord): harden slash command routing

* fix(discord): reset thread sessions on archive

* fix: drop discord opus dependency

* feat(discord): add allowBots mention gating

* fix(docs): use MDX-safe secretref markers

* fix(docs): avoid MDX regex markers in secretref page

* docs(security): document Docker UFW hardening via DOCKER-USER (#27613)

Merged via squash.

Prepared head SHA: 31ddd433265d8a7efbf932c941678598bf6be30c
Co-authored-by: dorukardahan <35905596+dorukardahan@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* docs(contributing): require before/after screenshots for UI PRs (#32206)

Merged via squash.

Prepared head SHA: d7f0914873aec1c3c64c9161771ff0bcbc457c95
Co-authored-by: hydro13 <6640526+hydro13@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* fix(discord): align DiscordAccountConfig.token type with SecretInput (#32490)

Merged via squash.

Prepared head SHA: 233aa032f1d894b7eb6a960247baa1336f8fbc26
Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant

* docs: fix secretref marker rendering in credential surface

* fix: harden pr review artifact validation

* fix(gateway): include disk-scanned agent IDs in listConfiguredAgentIds (#32831)

Merged via squash.

Prepared head SHA: 2aa58f6afd6e7766119575648483de6b5f50da6f
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* Agent: unify bootstrap truncation warning handling (#32769)

Merged via squash.

Prepared head SHA: 5d6d4ddfa620011e267d892b402751847d5ac0c3
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix(logging ): use local timezone for console log timestamps (#25970)

Merged via squash.

Prepared head SHA: 30123265b7b910b9208e8c9407c30536e46eb68f
Co-authored-by: openperf <80630709+openperf@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* security: add X-Content-Type-Options nosniff header to media route (#30356)

Merged via squash.

Prepared head SHA: b14f9ad7ca7017c6e31fb18c8032a81f49686ea4
Co-authored-by: 13otKmdr <154699144+13otKmdr@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* fix(ios): guard talk TTS callbacks to active utterance (#33304)

Merged via squash.

Prepared head SHA: dd88886e416e5e6d6c08bf92162a5ff18c1eb229
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix(ios): start incremental speech at soft boundaries (#33305)

Merged via squash.

Prepared head SHA: d1acf723176f8e9d89f8c229610c9400ab661ec7
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* Telegram/device-pair: auto-arm one-shot notify on /pair qr with manual fallback (#33299)

Merged via squash.

Prepared head SHA: 0986691fd4d2f37dd2de7ac601b1e5480602c829
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* fix(ios): harden watch messaging activation concurrency (#33306)

Merged via squash.

Prepared head SHA: d40f8c4afbd6ddf38548c9b0c6ac6ac4359f2e54
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky

* docs: fix Mintlify-incompatible links in security docs (#27698)

Merged via squash.

Prepared head SHA: 6078cd94ba38b49d17e9680073337885c5f9e781
Co-authored-by: clawdoo <65667097+clawdoo@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* fix(security): strip partial API token from status labels (#33262)

Merged via squash.

Prepared head SHA: 5fe81704e678dc5b18ca9416e5eb0750cfe49fb6
Co-authored-by: cu1ch3n <80438676+cu1ch3n@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* Security audit: suggest valid gateway.nodes.denyCommands entries (#29713)

Merged via squash.

Prepared head SHA: db23298f9806b8de8c4b3e816f1649c18ebc0c64
Co-authored-by: liquidhorizon88-bot <257047709+liquidhorizon88-bot@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* Agents: preserve bootstrap warning dedupe across followup runs

* feat(gateway): add Permissions-Policy header to default security headers (#30186)

Merged via squash.

Prepared head SHA: 0dac89283f54840ec2244007ff5a6178ce8b2ba9
Co-authored-by: habakan <12531644+habakan@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06

* fix(plugins): lazily initialize runtime and split plugin-sdk startup imports (#28620)

Merged via squash.

Prepared head SHA: 8bd7d6c13b070f86bd4d5c45286c1ceb1a3f9f80
Co-authored-by: hmemcpy <601206+hmemcpy@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* build: fix ineffective dynamic imports with lazy boundaries (#33690)

Merged via squash.

Prepared head SHA: 38b3c23d6f8f2b4c8a36a88ee65b508102f1ec36
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix(feishu): support SecretRef-style env credentials in account resolver (#30903)

Merged via squash.

Prepared head SHA: d3d0a18f173e999070dae4ff01423dadd2804a9c
Co-authored-by: LiaoyuanNing <259494737+LiaoyuanNing@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant

* fix(config): detect top-level heartbeat as invalid config path (#30894) (#32706)

Merged via squash.

Prepared head SHA: 1714ffe6fc1e3ed6a8a120d01d074f1be83c62d3
Co-authored-by: xiwan <931632+xiwan@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* Config: harden legacy heartbeat key migration

* build: prevent mixed static/dynamic pi-model-discovery imports

* follow-up: align ingress, atomic paths, and channel tests with credential semantics (#33733)

Merged via squash.

Prepared head SHA: c290c2ab6a3c3309adcbc4dc834f3c10d2ae1039
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant

* plugin-sdk: add channel subpaths and migrate bundled plugins

* fix(tlon): use HTTPS git URL for api-beta

* fix: stabilize telegram draft boundary previews (#33842) (thanks @ngutman)

* Runtime: stabilize tool/run state transitions under compaction and backpressure

Synthesize runtime state transition fixes for compaction tool-use integrity and long-running handler backpressure.

Sources: #33630, #33583

Co-authored-by: Kevin Shenghui <shenghuikevin@gmail.com>
Co-authored-by: Theo Tarr <theodore@tarr.com>

* fix(extensions): synthesize mediaLocalRoots propagation across sendMedia adapters

Restore deterministic mediaLocalRoots propagation through extension sendMedia adapters and add coverage for local/remote media handling in Google Chat.

Synthesis of #33581, #33545, #33540, #33536, #33528.

Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>

* fix(gateway): synthesize lifecycle robustness for restart and startup probes (#33831)

* fix(gateway): correct launchctl command sequence for gateway restart (closes #20030)

* fix(restart): expand HOME and escape label in launchctl plist path

* fix(restart): poll port free after SIGKILL to prevent EADDRINUSE restart loop

When cleanStaleGatewayProcessesSync() kills a stale gateway process,
the kernel may not immediately release the TCP port. Previously the
function returned after a fixed 500ms sleep (300ms SIGTERM + 200ms
SIGKILL), allowing triggerOpenClawRestart() to hand off to systemd
before the port was actually free. The new systemd process then raced
the dying socket for port 18789, hit EADDRINUSE, and exited with
status 1, causing systemd to retry indefinitely — the zombie restart
loop reported in #33103.

Fix: add waitForPortFreeSync() that polls lsof at 50ms intervals for
up to 2 seconds after SIGKILL. cleanStaleGatewayProcessesSync() now
blocks until the port is confirmed free (or the budget expires with a
warning) before returning. The increased SIGTERM/SIGKILL wait budgets
(600ms / 400ms) also give slow processes more time to exit cleanly.

Fixes #33103
Related: #28134

* fix: add EADDRINUSE retry and TIME_WAIT port-bind checks for gateway startup

* fix(ports): treat EADDRNOTAVAIL as non-retryable and fix flaky test

* fix(gateway): hot-reload agents.defaults.models allowlist changes

The reload plan had a rule for `agents.defaults.model` (singular) but
not `agents.defaults.models` (plural — the allowlist array).  Because
`agents.defaults.models` does not prefix-match `agents.defaults.model.`,
it fell through to the catch-all `agents` tail rule (kind=none), so
allowlist edits in openclaw.json were silently ignored at runtime.

Add a dedicated reload rule so changes to the models allowlist trigger
a heartbeat restart, which re-reads the config and serves the updated
list to clients.

Fixes #33600

Co-authored-by: HCL <chenglunhu@gmail.com>
Signed-off-by: HCL <chenglunhu@gmail.com>

* test(restart): 100% branch coverage — audit round 2

Audit findings fixed:
- remove dead guard: terminateStaleProcessesSync pids.length===0 check was
  unreachable (only caller cleanStaleGatewayProcessesSync already guards)
- expose __testing.callSleepSyncRaw so sleepSync's real Atomics.wait path
  can be unit-tested directly without going through the override
- fix broken sleepSync Atomics.wait test: previous test set override=null
  but cleanStaleGatewayProcessesSync returned before calling sleepSync —
  replaced with direct callSleepSyncRaw calls that actually exercise L36/L42-47
- fix pid collision: two tests used process.pid+304 (EPERM + dead-at-SIGTERM);
  EPERM test changed to process.pid+305
- fix misindented tests: 'deduplicates pids' and 'lsof status 1 container
  edge case' were outside their intended describe blocks; moved to correct
  scopes (findGatewayPidsOnPortSync and pollPortOnce respectively)
- add missing branch tests:
  - status 1 + non-empty stdout with zero openclaw pids → free:true (L145)
  - mid-loop non-openclaw cmd in &&-chain (L67)
  - consecutive p-lines without c-line between them (L67)
  - invalid PID in p-line (p0 / pNaN) — ternary false branch (L67)
  - unknown lsof output line (else-if false branch L69)

Coverage: 100% stmts / 100% branch / 100% funcs / 100% lines (36 tests)

* test(restart): fix stale-pid test typing for tsgo

* fix(gateway): address lifecycle review findings

* test(update): make restart-helper path assertions windows-safe

---------

Signed-off-by: HCL <chenglunhu@gmail.com>
Co-authored-by: Glucksberg <markuscontasul@gmail.com>
Co-authored-by: Efe Büken <efe@arven.digital>
Co-authored-by: Riccardo Marino <rmarino@apple.com>
Co-authored-by: HCL <chenglunhu@gmail.com>

* fix(routing): unify session delivery invariants for duplicate suppression (#33786)

* Routing: unify session delivery invariants

* Routing: address PR review feedback

* Routing: tighten topic and session-scope suppression

* fix(chat): inherit routes for per-account channel-peer sessions

* feat(telegram): add per-topic agent routing for forum groups [AI-assisted]

This feature allows different topics within a Telegram forum supergroup to route
to different agents, each with isolated workspace, memory, and sessions.

Key changes:
- Add agentId field to TelegramTopicConfig type for per-topic routing
- Add zod validation for agentId in topic config schema
- Implement routing logic to re-derive session key with topic's agent
- Add debug logging for topic agent overrides
- Add unit tests for routing behavior (forum topics + DM topics)
- Add config validation tests
- Document feature in docs/channels/telegram.md

This builds on the approach from PR #31513 by @Sid-Qin with additional fixes
for security (preserved account fail-closed guard) and test coverage.

Closes #31473

* fix(telegram): address PR review comments

- Export pickFirstExistingAgentId and use it to validate topic agentId
- Properly update mainSessionKey when overriding route agent
- Fix docs example showing incorrect session key for topic 3

Fixes issue where non-existent agentId would create orphaned sessions.
Fixes issue where DM topic replies would route to wrong agent.

* fix: tighten telegram topic-agent docs + fallback tests (#33647) (thanks @kesor)

* fix(gateway): preserve route inheritance for legacy channel session keys (openclaw#33919) thanks @Takhoffman

Verified:
- pnpm build
- pnpm check
- pnpm test src/gateway/server-methods/chat.directive-tags.test.ts
- pnpm test:macmini

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(sessions-spawn): remove maxLength from attachment content schema to fix llama.cpp GBNF grammar overflow

* fix: harden sessions_spawn attachment schema landing (#33648) (thanks @anisoptera)

* Deps: fix pnpm audit vulnerabilities in Google extension path (#33939)

* extensions/googlechat: require openclaw 2026.3.2+

* extensions/memory-core: require openclaw 2026.3.2+

* deps: bump fast-xml-parser override to 5.3.8

* deps: refresh lockfile for audit vulnerability fixes

* fix(gateway): narrow legacy route inheritance for custom session keys (openclaw#33932) thanks @Takhoffman

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

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* docs(changelog): credit #31513 in #33647 entry

* feat(web-search): switch Perplexity to native Search API (#33822)

* feat: Add Perplexity Search API as web_search provider

* docs fixes

* domain_filter validation

* address comments

* provider-specific options in cache key

* add validation for unsupported date filters

* legacy fields

* unsupported_language guard

* cache key matches the request's precedence order

* conflicting_time_filters guard

* unsupported_country guard

* invalid_date_range guard

* pplx validate for ISO 639-1 format

* docs: add Perplexity Search API changelog entry

* unsupported_domain_filter guard

---------

Co-authored-by: Shadow <hi@shadowing.dev>

* feat(slack): add typingReaction config for DM typing indicator fallback (#19816)

* feat(slack): add typingReaction config for DM typing indicator fallback

Adds a reaction-based typing indicator for Slack DMs that works without
assistant mode. When `channels.slack.typingReaction` is set (e.g.
"hourglass_flowing_sand"), the emoji is added to the user's message when
processing starts and removed when the reply is sent.

Addresses #19809

* test(slack): add typingReaction to createSlackMonitorContext test callers

* test(slack): add typingReaction to test context callers

* test(slack): add typingReaction to context fixture

* docs(changelog): credit Slack typingReaction feature

* test(slack): align existing-thread history expectation

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

* plugins: avoid peer auto-install dependency bloat (#34017)

* plugins/install: omit peer deps during plugin npm install

* tests: assert plugin install omits peer deps

* extensions/googlechat: mark openclaw peer optional

* extensions/memory-core: mark openclaw peer optional

* fix: code/cli acpx reliability 20260304 (#34020)

* agents: switch claude-cli defaults to bypassPermissions

* agents: add claude-cli default args coverage

* agents: emit watchdog stall system event for cli runs

* agents: test cli watchdog stall system event

* acpx: fallback to sessions new when ensure returns no ids

* acpx tests: mock sessions new fallback path

* acpx tests: cover ensure-empty fallback flow

* skills: clarify claude print mode without pty

* docs: update cli-backends claude default args

* docs: refresh cli live test default args

* gateway tests: align live claude args defaults

* changelog: credit claude/acpx reliability fixes

* Agents: normalize legacy Claude permission flag overrides

* Tests: cover legacy Claude permission override normalization

* Changelog: note legacy Claude permission flag auto-normalization

* ACPX: fail fast when ensure/new return no session IDs

* ACPX tests: support empty sessions new fixture output

* ACPX tests: assert ensureSession failure when IDs missing

* CLI runner: scope watchdog heartbeat wake to session

* CLI runner tests: assert session-scoped watchdog wake

* Update CHANGELOG.md

* fix(outbound): unify resolved cfg threading across send paths (#33987)

* Plugins: add root-alias shim and cache/docs updates

* Extensions: migrate bluebubbles plugin-sdk imports

* Extensions: migrate device-pair plugin-sdk imports

* Extensions: migrate diagnostics-otel plugin-sdk imports

* Extensions: migrate diffs plugin-sdk imports

* Extensions: migrate feishu plugin-sdk imports

* Extensions: migrate google-gemini-cli-auth plugin-sdk imports

* Extensions: migrate googlechat plugin-sdk imports

* Extensions: migrate irc plugin-sdk imports

* Extensions: migrate llm-task plugin-sdk imports

* Extensions: migrate lobster plugin-sdk imports

* Extensions: migrate matrix plugin-sdk imports

* Extensions: migrate mattermost plugin-sdk imports

* Extensions: migrate minimax-portal-auth plugin-sdk imports

* Extensions: migrate msteams plugin-sdk imports

* Extensions: migrate nextcloud-talk plugin-sdk imports

* Extensions: migrate nostr plugin-sdk imports

* Extensions: migrate qwen-portal-auth plugin-sdk imports

* Extensions: migrate synology-chat plugin-sdk imports

* Extensions: migrate telegram plugin-sdk imports

* Extensions: migrate test-utils plugin-sdk imports

* Extensions: migrate tlon plugin-sdk imports

* Extensions: migrate twitch plugin-sdk imports

* Extensions: migrate voice-call plugin-sdk imports

* Extensions: migrate zalo plugin-sdk imports

* Extensions: migrate zalouser plugin-sdk imports

* Extensions: migrate acpx plugin-sdk imports

* fix(tui): normalize session key to lowercase to match gateway canonicalization (#34013)

Merged via squash.

Prepared head SHA: cfe06ca131661d1fd669270345c549ee8141cc46
Co-authored-by: lynnzc <6257996+lynnzc@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf

* Plugin SDK: add full bundled subpath wiring

* Plugins/acpx: migrate to scoped plugin-sdk imports

* Plugins/bluebubbles: migrate to scoped plugin-sdk imports

* Plugins/copilot-proxy: migrate to scoped plugin-sdk imports

* Plugins/device-pair: migrate to scoped plugin-sdk imports

* Plugins/diagnostics-otel: migrate to scoped plugin-sdk imports

* Plugins/diffs: migrate to scoped plugin-sdk imports

* Plugins/feishu: migrate to scoped plugin-sdk imports

* Plugins/google-gemini-cli-auth: migrate to scoped plugin-sdk imports

* Plugins/googlechat: migrate to scoped plugin-sdk imports

* Plugins/irc: migrate to scoped plugin-sdk imports

* Plugins/llm-task: migrate to scoped plugin-sdk imports

* Plugins/lobster: migrate to scoped plugin-sdk imports

* Plugins/matrix: migrate to scoped plugin-sdk imports

* Plugins/mattermost: migrate to scoped plugin-sdk imports

* Plugins/memory-core: migrate to scoped plugin-sdk imports

* Plugins/memory-lancedb: migrate to scoped plugin-sdk imports

* Plugins/minimax-portal-auth: migrate to scoped plugin-sdk imports

* Plugins/msteams: migrate to scoped plugin-sdk imports

* Plugins/nextcloud-talk: migrate to scoped plugin-sdk imports

* Plugins/nostr: migrate to scoped plugin-sdk imports

* Plugins/open-prose: migrate to scoped plugin-sdk imports

* Plugins/phone-control: migrate to scoped plugin-sdk imports

* Plugins/qwen-portal-auth: migrate to scoped plugin-sdk imports

* Plugins/synology-chat: migrate to scoped plugin-sdk imports

* Plugins/talk-voice: migrate to scoped plugin-sdk imports

* Plugins/test-utils: migrate to scoped plugin-sdk imports

* Plugins/thread-ownership: migrate to scoped plugin-sdk imports

* Plugins/tlon: migrate to scoped plugin-sdk imports

* Plugins/twitch: migrate to scoped plugin-sdk imports

* Plugins/voice-call: migrate to scoped plugin-sdk imports

* Plugins/whatsapp: migrate to scoped plugin-sdk imports

* Plugins/zalo: migrate to scoped plugin-sdk imports

* Plugins/zalouser: migrate to scoped plugin-sdk imports

* Chore: remove accidental .DS_Store artifact

* chore(docs): add plugins refactor changelog entry

* feat(ios): add Live Activity connection status + stale cleanup (#33591)

* feat(ios): add live activity connection status and cleanup

Add lock-screen/Dynamic Island connection health states and prune duplicate/stale activities before reuse. This intentionally excludes AI/title generation and heavier UX rewrites from #27488.

Co-authored-by: leepokai <1663017+leepokai@users.noreply.github.com>

* fix(ios): treat ended live activities as inactive

* chore(changelog): add PR reference and author thanks

---------

Co-authored-by: leepokai <1663017+leepokai@users.noreply.github.com>

* fix: kill stuck ACP child processes on startup and harden sessions in discord threads (#33699)

* Gateway: resolve agent.wait for chat.send runs

* Discord: harden ACP thread binding + listener timeout

* ACPX: handle already-exited child wait

* Gateway/Discord: address PR review findings

* Discord: keep ACP error-state thread bindings on startup

* gateway: make agent.wait dedupe bridge event-driven

* discord: harden ACP probe classification and cap startup fan-out

* discord: add cooperative timeout cancellation

* discord: fix startup probe concurrency helper typing

* plugin-sdk: avoid Windows root-alias shard timeout

* plugin-sdk: keep root alias reflection path non-blocking

* discord+gateway: resolve remaining PR review findings

* gateway+discord: fix codex review regressions

* Discord/Gateway: address Codex review findings

* Gateway: keep agent.wait lifecycle active with shared run IDs

* Discord: clean up status reactions on aborted runs

* fix: add changelog note for ACP/Discord startup hardening (#33699) (thanks @dutifulbob)

---------

Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>

* fix: relay ACP sessions_spawn parent streaming (#34310) (thanks @vincentkoc) (#34310)

Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>

* fix(telegram): materialize dm draft final to avoid duplicates

* docs(changelog): credit @Brotherinlaw-13 for #34318

* fix: prevent nodes media base64 context bloat (#34332)

* fix: preserve raw media invoke for HTTP tool clients (#34365)

* fix(slack): route system events to bound agent sessions (#34045)

* fix(slack): route system events via binding-aware session keys

* fix(slack): pass sender to system event session resolver

* fix(slack): include sender context for interaction session routing

* fix(slack): include modal submitter in session routing

* test(slack): cover binding-aware system event routing

* test(slack): update interaction session key assertions

* test(slack): assert reaction session routing carries sender

* docs(changelog): note slack system event routing fix

* Update CHANGELOG.md

* Delete changelog/fragments directory

* fix(memory): serialize local embedding initialization to avoid duplicate model loads (#15639)

Merged via squash.

Prepared head SHA: a085fc21a8ba7163fffdb5de640dd4dc1ff5a88e
Co-authored-by: SubtleSpark <43933609+SubtleSpark@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* fix(model): propagate custom provider headers to model objects (#27490)

Merged via squash.

Prepared head SHA: e4183b398fc7eb4c18b2b691cb0dd882ec993608
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(daemon): handle systemctl is-enabled exit 4 (not-found) on Ubuntu (#33634)

Merged via squash.

Prepared head SHA: 67dffc3ee239cd7b813cb200c3dd5475d9e203a6
Co-authored-by: Yuandiaodiaodiao <33371662+Yuandiaodiaodiao@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(node-host): sync rawCommand with hardened argv after executable path pinning (#33137)

Merged via squash.

Prepared head SHA: a7987905f7ad6cf5fee286ffa81ceaad8297174f
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras

* Agents: add generic poll-vote action support

* fix(ollama): pass provider headers to Ollama stream function (#24285)

createOllamaStreamFn() only accepted baseUrl, ignoring custom headers
configured in models.providers.<provider>.headers. This caused 403
errors when Ollama endpoints are behind reverse proxies that require
auth headers (e.g. X-OLLAMA-KEY via HAProxy).

Add optional defaultHeaders parameter to createOllamaStreamFn() and
merge them into every fetch request. Provider headers from config are
now passed through at the call site in the embedded runner.

Fixes #24285

* test(ollama): add default header precedence coverage

* chore(changelog): add PR entry openclaw#24337 thanks @echoVic

* Outbound: allow text-only plugin adapters

* Outbound: avoid empty multi-media fallback sends

* chore(changelog): align outbound adapter entry openclaw#32788 thanks @liuxiaopai-ai

* fix(outbound): fail media-only text-only adapter fallback

* chore(changelog): clarify outbound media-only fallback openclaw#32788 thanks @liuxiaopai-ai

* fix(review): enforce behavioral sweep validation

* Fix gateway restart false timeouts on Debian/systemd (#34874)

* daemon(systemd): target sudo caller user scope

* test(systemd): cover sudo user scope commands

* infra(ports): fall back to ss when lsof missing

* test(ports): verify ss fallback listener detection

* cli(gateway): use probe fallback for restart health

* test(gateway): cover restart-health probe fallback

* Compaction/Safeguard: require structured summary headings (#25555)

Merged via squash.

Prepared head SHA: 0b1df34806a7b788261290be55760fd89220de53
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* Fix Linux daemon install checks when systemd user bus env is missing (#34884)

* daemon(systemd): fall back to machine user scope when user bus is missing

* test(systemd): cover machine scope fallback for user-bus errors

* test(systemd): reset execFile mock state across cases

* test(systemd): make machine-user fallback assertion portable

* fix(daemon): keep root sudo path on direct user scope

* test(systemd): cover sudo root user-scope behavior

* ci: use resolvable bun version in setup-node-env

* agents: preserve totalTokens on request failure instead of using contextWindow (#34275)

Merged via squash.

Prepared head SHA: f9d111d0a79a07815d476356e98a28df3a0000ba
Co-authored-by: RealKai42 <44634134+RealKai42@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* fix: align AGENTS.md template section names with post-compaction extraction (#25029) (#25098)

Merged via squash.

Prepared head SHA: 8cd6cc8049aab5a94d8a9d5fb08f2e792c4ac5fd
Co-authored-by: echoVic <16428813+echoVic@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* Changelog: add daemon systemd user-bus fallback entry (#34884)

* Changelog: add gateway restart health entry (#34874)

* fix: finalize spanish locale support

* fix: add spanish locale support (#35038) (thanks @DaoPromociones)

* fix(deps): patch hono transitive audit vulnerabilities

* fix(security): avoid prototype-chain account path checks (#34982)

Merged via squash.

Prepared head SHA: f89cc6a649959997fe1dec1e1c1bff9a61b2de98
Co-authored-by: HOYALIM <166576253+HOYALIM@users.noreply.github.com>
Co-authored-by: dvrshil <81693876+dvrshil@users.noreply.github.com>
Reviewed-by: @dvrshil

* fix(deps): bump tar to 7.5.10

* docs(changelog): document dependency security fixes

* fix: restore auto-reply system events timeline (#34794) (thanks @anisoptera) (#34794)

Co-authored-by: Ayaan Zaidi <zaidi@uplause.io>

* fix(feishu): comprehensive reply mechanism — outbound replyToId forwarding + topic-aware reply targeting (#33789)

* fix(feishu): comprehensive reply mechanism fix — outbound replyToId forwarding + topic-aware reply targeting

- Forward replyToId from ChannelOutboundContext through sendText/sendMedia
  to sendMessageFeishu/sendMarkdownCardFeishu/sendMediaFeishu, enabling
  reply-to-message via the message tool.

- Fix group reply targeting: use ctx.messageId (triggering message) in
  normal groups to prevent silent topic thread creation (#32980). Preserve
  ctx.rootId targeting for topic-mode groups (group_topic/group_topic_sender)
  and groups with explicit replyInThread config.

- Add regression tests for both fixes.

Fixes #32980
Fixes #32958
Related #19784

* fix: normalize Feishu delivery.to before comparing with messaging tool targets

- Add normalizeDeliveryTarget helper to strip user:/chat: prefixes for Feishu
- Apply normalization in matchesMessagingToolDeliveryTarget before comparison
- This ensures cron duplicate suppression works when session uses prefixed targets
  (user:ou_xxx) but messaging tool extract uses normalized bare IDs (ou_xxx)

Fixes review comment on PR #32755

(cherry picked from commit fc20106f16ccc88a5f02e58922bb7b7999fe9dcd)

* fix(feishu): catch thrown SDK errors for withdrawn reply targets

The Feishu Lark SDK can throw exceptions (SDK errors with .code or
AxiosErrors with .response.data.code) for withdrawn/deleted reply
targets, in addition to returning error codes in the response object.

Wrap reply calls in sendMessageFeishu and sendCardFeishu with
try-catch to handle thrown withdrawn/not-found errors (230011,
231003) and fall back to client.im.message.create, matching the
existing response-level fallback behavior.

Also extract sendFallbackDirect helper to deduplicate the
direct-send fallback block across both functions.

Closes #33496

(cherry picked from commit ad0901aec103a2c52f186686cfaf5f8ba54b4a48)

* feishu: forward outbound reply target context

(cherry picked from commit c129a691fcf552a1cebe1e8a22ea8611ffc3b377)

* feishu extension: tighten reply target fallback semantics

(cherry picked from commit f85ec610f267020b66713c09e648ec004b2e26f1)

* fix(feishu): align synthesized fallback typing and changelog attribution

* test(feishu): cover group_topic_sender reply targeting

---------

Co-authored-by: Xu Zimo <xuzimojimmy@163.com>
Co-authored-by: Munem Hashmi <munem.hashmi@gmail.com>
Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(feishu): use msg_type media for mp4 video (fixes #33674) (#33720)

* fix(feishu): use msg_type media for mp4 video (fixes #33674)

* Feishu: harden streaming merge semantics and final reply dedupe

Use explicit streaming update semantics in the Feishu reply dispatcher:
treat onPartialReply payloads as snapshot updates and block fallback payloads
as delta chunks, then merge final text with the shared overlap-aware
mergeStreamingText helper before closing the stream.

Prevent duplicate final text delivery within the same dispatch cycle, and add
regression tests covering overlap snapshot merge, duplicate final suppression,
and block-as-delta behavior to guard against repeated/truncated output.

* fix(feishu): prefer message.reply for streaming cards in topic threads

* fix: reduce Feishu streaming card print_step to avoid duplicate rendering

Fixes openclaw/openclaw#33751

* Feishu: preserve media sends on duplicate finals and add media synthesis changelog

* Feishu: only dedupe exact duplicate final replies

* Feishu: use scoped plugin-sdk import in streaming-card tests

---------

Co-authored-by: 倪汉杰0668001185 <ni.hanjie@xydigit.com>
Co-authored-by: zhengquanliu <zhengquanliu@bytedance.com>
Co-authored-by: nick <nickzj@qq.com>
Co-authored-by: linhey <linhey@mini.local>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(agents): bypass pendingDescendantRuns guard for cron announce delivery (#35185)

* fix(agents): bypass pendingDescendantRuns guard for cron announce delivery

Standalone cron job completions were blocked from direct channel delivery
when the cron run had spawned subagents that were still registered as
pending. The pendingDescendantRuns guard exists for live orchestration
coordination and should not apply to fire-and-forget cron announce sends.

Thread the announceType through the delivery chain and skip both the
child-descendant and requester-descendant pending-run guards when the
announce originates from a cron job.

Closes #34966

* fix: ensure outbound session entry for cron announce with named agents (#32432)

Named agents may not have a session entry for their delivery target,
causing the announce flow to silently fail (delivered=false, no error).

Two fixes:
1. Call ensureOutboundSessionEntry when resolving the cron announce
   session key so downstream delivery can find channel metadata.
2. Fall back to direct outbound delivery when announce delivery fails
   to ensure cron output reaches the target channel.

Closes #32432

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

* fix: guard announce direct-delivery fallback against suppression leaks (#32432)

The `!delivered` fallback condition was too broad — it caught intentional
suppressions (active subagents, interim messages, SILENT_REPLY_TOKEN) in
addition to actual announce delivery failures.  Add an
`announceDeliveryWasAttempted` flag so the direct-delivery fallback only
fires when `runSubagentAnnounceFlow` was actually called and failed.

Also remove the redundant `if (route)` guard in
`resolveCronAnnounceSessionKey` since `resolved` being truthy guarantees
`route` is non-null.

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

* fix(cron): harden announce synthesis follow-ups

---------

Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* Feishu: harden streaming merge semantics and final reply dedupe (#33245)

* Feishu: close duplicate final gap and cover routing precedence

* Feishu: resolve reviewer duplicate-final and routing feedback

* Feishu: tighten streaming send-mode option typing

* Feishu: fix reverse-overlap streaming merge ordering

* Feishu: align streaming final dedupe test expectation

* Feishu: allow distinct streaming finals while deduping repeats

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix: cron backup should preserve pre-edit snapshot (#35195) (#35234)

* fix(cron): avoid overwriting .bak during normalization

Fixes openclaw/openclaw#35195

* test(cron): preserve pre-edit bak snapshot in normalization path

---------

Co-authored-by: 0xsline <sline@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(cron): stabilize restart catch-up replay semantics (#35351)

* Cron: stabilize restart catch-up replay semantics

* Cron: respect backoff in startup missed-run replay

* cron: narrow startup replay backoff guard (#35391)

* cron: unify stale-run recovery and preserve manual-run every anchors (#35363)

* cron: unify stale-run recovery and preserve manual every anchors

* cron: address unresolved review threads on recovery paths

* cron: remove duplicate timestamp helper after rebase

* refactor(telegram): remove unused webhook callback helper (#27816)

* fix(pr): make review claim step required

* fix(skills): deduplicate slash commands by skillName across all interfaces

Move skill-command deduplication by skillName from the Discord-only
`dedupeSkillCommandsForDiscord` into `listSkillCommandsForAgents` so
every interface (TUI, Slack, text) consistently sees a clean command
list without platform-specific workarounds.

When multiple agents share a skill with the same name the old code
emitted `github` + `github_2` and relied on Discord to collapse them.
Now `listSkillCommandsForAgents` returns only the first registration
per skillName, and the Discord-specific wrapper is removed.

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

* style: fix formatting in skill-commands.test.ts and provider.ts

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

* style(skills): align formatting cleanup for dedupe changes

* chore(changelog): add dedupe note openclaw#27521 thanks @shivama205

* fix(agents): detect Venice provider proxying xAI/Grok models for schema cleaning (#35355)

Merged via squash.

Prepared head SHA: 8bfdec257bb6a6025cb69a0a213a433da32b15db
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(agents): decode HTML entities in xAI/Grok tool call arguments (#35276)

Merged via squash.

Prepared head SHA: c4445d2938898ded9c046614f9315dbda65ec573
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(agents): guard promoteThinkingTagsToBlocks against malformed content entries (#35143)

Merged via squash.

Prepared head SHA: 3971122f5fd27c66c8c9c5ce783f00e113b1f47b
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(web-ui): render Accounts schema node properly (#35380)

Co-authored-by: stakeswky <64798754+stakeswky@users.noreply.github.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(agents): guard context pruning against malformed thinking blocks (#35146)

Merged via squash.

Prepared head SHA: a196a565b1b8e806ffbf85172bcf1128796b45a2
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* fix(gateway): prevent internal route leakage in chat.send

Synthesis of routing fixes from #35321, #34635, and #35356 for internal-client reply safety.

- Require explicit `deliver: true` before inheriting any external delivery route.
- Keep webchat/TUI/UI-origin traffic on internal routing by default.
- Allow configured-main session inheritance only for non-Webchat/UI clients, and honor `session.mainKey`.
- Add regression tests for UI no-inherit, configured-main CLI inherit, and deliver-flag behavior.

Co-authored-by: alexyyyander <alexyyyander@users.noreply.github.com>
Co-authored-by: Octane0411 <88922959+Octane0411@users.noreply.github.com>
Co-authored-by: Linux2010 <35169750+Linux2010@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(gateway): pass actual version to Control UI client instead of dev (#35230)

* fix(gateway): pass actual version to Control UI client instead of "dev"

The GatewayClient, CLI WS client, and browser Control UI all sent
"dev" as their clientVersion during handshake, making it impossible
to distinguish builds in gateway logs and health snapshots.

- GatewayClient and CLI WS client now use the resolved VERSION constant
- Control UI reads serverVersion from the bootstrap endpoint and
  forwards it when connecting
- Bootstrap contract extended with serverVersion field

Closes #35209

* Gateway: fix control-ui version version-reporting consistency

* Control UI: guard deferred bootstrap connect after disconnect

* fix(ui): accept same-origin http and relative gateway URLs for client version

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* chore(pr): enforce changelog placement and reduce merge sync churn

* TTS: add baseUrl support to OpenAI TTS config (#34321)

Merged via squash.

Prepared head SHA: e9a10cf81d2021cf81091dfa81e13ffdbb6a540a
Co-authored-by: RealKai42 <44634134+RealKai42@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd

* ACP: add persistent Discord channel and Telegram topic bindings (#34873)

* docs: add ACP persistent binding experiment plan

* docs: align ACP persistent binding spec to channel-local config

* docs: scope Telegram ACP bindings to forum topics only

* docs: lock bound /new and /reset behavior to in-place ACP reset

* ACP: add persistent discord/telegram conversation bindings

* ACP: fix persistent binding reuse and discord thread parent context

* docs: document channel-specific persistent ACP bindings

* ACP: split persistent bindings and share conversation id helpers

* ACP: defer configured binding init until preflight passes

* ACP: fix discord thread parent fallback and explicit disable inheritance

* ACP: keep bound /new and /reset in-place

* ACP: honor configured bindings in native command flows

* ACP: avoid configured fallback after runtime bind failure

* docs: refine ACP bindings experiment config examples

* acp: cut over to typed top-level persistent bindings

* ACP bindings: harden reset recovery and native command auth

* Docs: add ACP bound command auth proposal

* Tests: normalize i18n registry zh-CN assertion encoding

* ACP bindings: address review findings for reset and fallback routing

* ACP reset: gate hooks on success and preserve /new arguments

* ACP bindings: fix auth and binding-priority review findings

* Telegram ACP: gate ensure on auth and accepted messages

* ACP bindings: fix session-key precedence and unavailable handling

* ACP reset/native commands: honor fallback targets and abort on bootstrap failure

* Config schema: validate ACP binding channel and Telegram topic IDs

* Discord ACP: apply configured DM bindings to native commands

* ACP reset tails: dispatch through ACP after command handling

* ACP tails/native reset auth: fix target dispatch and restore full auth

* ACP reset detection: fallback to active ACP keys for DM contexts

* Tests: type runTurn mock input in ACP dispatch test

* ACP: dedup binding route bootstrap and reset target resolution

* reply: align ACP reset hooks with bound session key

* docs: replace personal discord ids with placeholders

* fix: add changelog entry for ACP persistent bindings (#34873) (thanks @dutifulbob)

---------

Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>

* docs(telegram): recommend allowlist for single-user DM policy (#34841)

* docs(telegram): recommend allowlist for single-user bots

* docs(telegram): condense single-user allowlist note

---------

Co-authored-by: echoVic <echovic@163.com>

* fix(feishu): check response.ok before calling response.json() in streaming card (#35628)

Merged via squash.

Prepared head SHA: 62c3fec80d97cea9be344c0bef5358a0a5dc5560
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman

* Mattermost: honor onmessage mention override and add gating diagnostics tests (#27160)

Merged via squash.

Prepared head SHA: 6cefb1d5bf3d6dfcec36c1cee3f9ea887f10c890
Co-authored-by: turian <65918+turian@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm

* fix(subagents): strip leaked [[reply_to]] tags from completion announces (#34503)

* fix(subagents): strip reply tags from completion delivery text

* test(subagents): cover reply-tag stripping in cron completion sends

* changelog: note iMessage reply-tag stripping in completion announces

* Update CHANGELOG.md

* Apply suggestion from @greptile-apps[bot]

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(cron): restore direct fallback after announce failure in best-effort mode (openclaw#36177)

Verified:
- pnpm build
- pnpm check (fails on pre-existing origin/main lint debt in extensions/mattermost imports)
- pnpm test:macmini

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* test(cron): add cross-channel announce fallback regression coverage (openclaw#36197)

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check (fails on pre-existing origin/main lint debt in extensions/mattermost imports)
- pnpm test:macmini

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* feat(mattermost): add interactive buttons support (#19957)

Merged via squash.

Prepared head SHA: 8a25e608729d0b9fd07bb0ee4219d199d9796dbe
Co-authored-by: tonydehnke <36720180+tonydehnke@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm

* fix(browser): remove deprecated --disable-blink-features=AutomationControlled flag

- Removes OpenClaw's default `--disable-blink-features=AutomationControlled` Chrome launch switch to avoid unsupported-flag warnings in newer Chrome (#35721).
- Preserves compatibility for older Chrome via `browser.extraArgs` override behavior (source analysis: #35770, #35728, #35727, #35885).
- Synthesis attribution: thanks @Sid-Qin, @kevinWangSheng, @ningding97, @Naylenv, @clawbie.

Source PR refs: #35734, #35770, #35728, #35727, #35885

Co-authored-by: Sid-Qin <Sid-Qin@users.noreply.github.com>
Co-authored-by: kevinWangSheng <kevinWangSheng@users.noreply.github.com>
Co-authored-by: ningding97 <ningding97@users.noreply.github.com>
Co-authored-by: Naylenv <Naylenv@users.noreply.github.com>
Co-authored-by: clawbie <clawbie@users.noreply.github.com>
Co-authored-by: Takhoffman <Takhoffman@users.noreply.github.com>

* fix(feishu): add HTTP timeout to prevent per-chat queue deadlocks (#36430)

When the Feishu API hangs or responds slowly, the sendChain never settles,
causing the per-chat queue to remain in a processing state forever and
blocking all subsequent messages in that thread. This adds a 30-second
default timeout to all Feishu HTTP requests by providing a timeout-aware
httpInstance to the Lark SDK client.

Closes #36412

Co-authored-by: Ayane <wangruofei@soulapp.cn>

* fix(feishu): use probed botName for mention checks (#36391)

* Feishu: honor bot mentions by ID despite aliases (Fixes #36317) (#36333)

* Mattermost: switch plugin-sdk imports to scoped subpaths (openclaw#36480)

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

Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix(feishu): accept groupPolicy "allowall" as alias for "open" (#36358)

* fix(feishu): accept groupPolicy "allowall" as alias for "open"

When users configure groupPolicy: "allowall" in Feishu channel config,
the Zod schema rejects the value and the runtime policy check falls
through to the allowlist path.  With an empty allowFrom array, all group
messages are silently dropped despite the intended "allow all" semantics.

Accept "allowall" at the schema level (transform to "open") and add a
runtime guard in isFeishuGroupAllowed so the value is handled even if it
bypasses schema validation.

Closes #36312

Made-with: Cursor

* Feishu: tighten allowall alias handling and coverage

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* synthesis: fix Feishu group mention slash parsing

## Summary\n\nFeishu group slash command parsing is fixed for mentions and command probes across authorization paths.\n\nThis includes:\n- Normalizing bot mention text in group context for reliable slash detection in message parsing.\n- Adding command-probe normalization for group slash invocations.\n\nCo-authored-by: Sid Qin <sidqin0410@gmail.com>\nCo-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* Feishu: normalize group slash command probing

- Feishu/group slash command detection: normalize group mention wrappers before command-authorization probing so mention-prefixed commands are recognized in group routing.\n- Source PR: #36011\n- Contributor: @liuxiaopai-ai\n\nCo-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>\nCo-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>

---------

Signed-off-by: HCL <chenglunhu@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: dongdong <42494191+arkyu2077@users.noreply.github.com>
Co-authored-by: Viz <visionik@pobox.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Shadow <hi@shadowing.dev>
Co-authored-by: bmendonca3 <208517100+bmendonca3@users.norepl…
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
thinstripe pushed a commit to thinstripe/openclaw that referenced this pull request Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: discord Channel integration: discord size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants