fix: add session-memory hook support for Feishu provider#31437
fix: add session-memory hook support for Feishu provider#31437Takhoffman merged 2 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR attempts to add session-memory hook support for Feishu but has critical implementation issues that prevent it from working:
The intended fix requires:
Confidence Score: 0/5
Last reviewed commit: d3b3e9d |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d3b3e9d949
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| /** | ||
| * Handle Feishu command messages and trigger appropriate hooks | ||
| */ | ||
| export async function handleFeishuCommand( |
There was a problem hiding this comment.
Wire Feishu reset-hook handler into inbound command flow
This function is added but never imported or called anywhere in the Feishu inbound path, so /new and /reset messages continue through the existing logic in extensions/feishu/src/bot.ts without executing runBeforeReset. In practice, this means the commit does not actually trigger before_reset hooks for Feishu sessions, so session-memory plugins still miss reset events in production.
Useful? React with 👍 / 👎.
| @@ -0,0 +1,49 @@ | |||
| import type { PluginHookRunner } from "openclaw/plugin-sdk"; | |||
| import { DEFAULT_RESET_TRIGGERS } from "../../../config/sessions/types.js"; | |||
There was a problem hiding this comment.
incorrect import path - /config/ directory doesn't exist at repository root. Should be:
| import { DEFAULT_RESET_TRIGGERS } from "../../../config/sessions/types.js"; | |
| import { DEFAULT_RESET_TRIGGERS } from "../../../src/config/sessions.js"; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/feishu-command-handler.ts
Line: 2
Comment:
incorrect import path - `/config/` directory doesn't exist at repository root. Should be:
```suggestion
import { DEFAULT_RESET_TRIGGERS } from "../../../src/config/sessions.js";
```
How can I resolve this? If you propose a fix, please make it concise.| await hookRunner.runBeforeReset( | ||
| { | ||
| type: "command", | ||
| action: command.replace('/', '') as "new" | "reset", | ||
| context: { | ||
| ...context, | ||
| commandSource: "feishu" | ||
| } | ||
| }, | ||
| { | ||
| agentId: "main", // or extract from sessionKey | ||
| sessionKey | ||
| } | ||
| ); |
There was a problem hiding this comment.
parameters don't match PluginHookBeforeResetEvent type. Expected: { sessionFile?: string, messages?: unknown[], reason?: string }. The current implementation passes type, action, and context which don't exist in the type definition.
Should read session file and extract messages like in src/auto-reply/reply/commands-core.ts:90-130
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/feishu-command-handler.ts
Line: 30-43
Comment:
parameters don't match `PluginHookBeforeResetEvent` type. Expected: `{ sessionFile?: string, messages?: unknown[], reason?: string }`. The current implementation passes `type`, `action`, and `context` which don't exist in the type definition.
Should read session file and extract messages like in `src/auto-reply/reply/commands-core.ts:90-130`
How can I resolve this? If you propose a fix, please make it concise.| export async function handleFeishuCommand( | ||
| messageText: string, | ||
| sessionKey: string, | ||
| hookRunner: PluginHookRunner, | ||
| context: { | ||
| cfg: any; | ||
| sessionEntry: any; | ||
| previousSessionEntry?: any; | ||
| commandSource: string; | ||
| timestamp: number; | ||
| } | ||
| ): Promise<boolean> { | ||
| // Check if message is a reset command | ||
| const trimmed = messageText.trim().toLowerCase(); | ||
| const isResetCommand = DEFAULT_RESET_TRIGGERS.some(trigger => | ||
| trimmed === trigger || trimmed.startsWith(`${trigger} `) | ||
| ); | ||
|
|
||
| if (isResetCommand) { | ||
| // Extract the actual command (without arguments) | ||
| const command = trimmed.split(' ')[0]; | ||
|
|
||
| // Trigger the before_reset hook | ||
| await hookRunner.runBeforeReset( | ||
| { | ||
| type: "command", | ||
| action: command.replace('/', '') as "new" | "reset", | ||
| context: { | ||
| ...context, | ||
| commandSource: "feishu" | ||
| } | ||
| }, | ||
| { | ||
| agentId: "main", // or extract from sessionKey | ||
| sessionKey | ||
| } | ||
| ); | ||
|
|
||
| return true; // Command was handled | ||
| } | ||
|
|
||
| return false; // Not a command we handle | ||
| } No newline at end of file |
There was a problem hiding this comment.
handleFeishuCommand is never imported or called anywhere in the codebase. The function needs to be integrated into the Feishu message flow (likely in extensions/feishu/src/bot.ts) to actually fix the issue
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/feishu-command-handler.ts
Line: 7-49
Comment:
`handleFeishuCommand` is never imported or called anywhere in the codebase. The function needs to be integrated into the Feishu message flow (likely in `extensions/feishu/src/bot.ts`) to actually fix the issue
How can I resolve this? If you propose a fix, please make it concise.| context: { | ||
| cfg: any; | ||
| sessionEntry: any; | ||
| previousSessionEntry?: any; | ||
| commandSource: string; | ||
| timestamp: number; | ||
| } |
There was a problem hiding this comment.
context parameter structure doesn't align with how other parts of the codebase trigger before_reset. The context fields like cfg, sessionEntry, previousSessionEntry are not used, and the hook needs sessionFile and messages which aren't being extracted
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/feishu-command-handler.ts
Line: 11-17
Comment:
context parameter structure doesn't align with how other parts of the codebase trigger `before_reset`. The context fields like `cfg`, `sessionEntry`, `previousSessionEntry` are not used, and the hook needs `sessionFile` and `messages` which aren't being extracted
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 32b9c5c069
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| type: "command", | ||
| action: command.replace('/', '') as "new" | "reset", | ||
| context: { |
There was a problem hiding this comment.
Emit before_reset payload in the documented schema
runBeforeReset hooks are typed to receive { sessionFile, messages, reason } (see src/plugins/types.ts) and the existing command path emits exactly that shape in src/auto-reply/reply/commands-core.ts, but this handler sends { type, action, context } instead. Any plugin that reads event.reason or event.messages (for example session-memory extraction on /new//reset) will get empty data, so the reset hook fires without the information it needs to persist conversation state.
Useful? React with 👍 / 👎.
| } | ||
| }, | ||
| { | ||
| agentId: "main", // or extract from sessionKey |
There was a problem hiding this comment.
Derive hook agentId from the Feishu session key
The hook context hardcodes agentId: "main", but Feishu can route DMs to dynamic per-user agents (extensions/feishu/src/dynamic-agent.ts creates ids like feishu-<open_id> and extensions/feishu/src/bot.ts re-resolves route.sessionKey). In those sessions, running before_reset under main mis-scopes the hook context and can cause memory/state plugins to read or write the wrong agent workspace.
Useful? React with 👍 / 👎.
Issue openclaw#31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used
32b9c5c to
e3da24d
Compare
|
Merged via autoland after rebase on latest Verification run:
Notes:
Merge commit: cad06fa |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e3da24d520
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| @@ -0,0 +1,49 @@ | |||
| import type { PluginHookRunner } from "openclaw/plugin-sdk"; | |||
| import { DEFAULT_RESET_TRIGGERS } from "../../../config/sessions/types.js"; | |||
There was a problem hiding this comment.
Correct Feishu reset-trigger import to the src config module
This handler imports DEFAULT_RESET_TRIGGERS from ../../../config/sessions/types.js, which resolves to config/sessions/types.js at repo root, but that file is not present in the repository (the source is under src/config/sessions/types.ts). Once this handler is actually wired into the Feishu inbound path, loading it will fail with a module-resolution error before any before_reset hook can run.
Useful? React with 👍 / 👎.
) * fix: add session-memory hook support for Feishu provider Issue openclaw#31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]>
) * fix: add session-memory hook support for Feishu provider Issue openclaw#31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]>
) * fix: add session-memory hook support for Feishu provider Issue openclaw#31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]>
* refactor(media): add shared ffmpeg helpers * fix(discord): harden voice ffmpeg path and opus fast-path * fix(ui): ensure GFM tables render in WebChat markdown (#20410) - Pass gfm:true + breaks:true explicitly to marked.parse() so table support is guaranteed even if global setOptions() is bypassed or reset by a future refactor (defense-in-depth) - Add display:block + overflow-x:auto to .chat-text table so wide multi-column tables scroll horizontally instead of being clipped by the parent overflow-x:hidden chat container - Add regression tests for GFM table rendering in markdown.test.ts * fix: webchat gfm table rendering and overflow (#32365) (thanks @BlueBirdBack) * refactor(tests): dedupe pi embedded test harness * refactor(tests): dedupe browser and config cli test setup * fix(test): harden discord lifecycle status sink typing * fix(gateway): harden message action channel fallback and startup grace Take the safe, tested subset from #32367:\n- per-channel startup connect grace in health monitor\n- tool-context channel-provider fallback for message actions\n\nCo-authored-by: Munem Hashmi <[email protected]> * docs(changelog): add landed notes for #32336 and #32364 * fix(test): tighten tool result typing in context pruning tests * fix(hooks): propagate run/tool IDs for tool hook correlation (#32360) * Plugin SDK: add run and tool call fields to tool hooks * Agents: propagate runId and toolCallId in before_tool_call * Agents: thread runId through tool wrapper context * Runner: pass runId into tool hook context * Compaction: pass runId into tool hook context * Agents: scope after_tool_call start data by run * Tests: cover run and tool IDs in before_tool_call hooks * Tests: add run-scoped after_tool_call collision coverage * Hooks: scope adjusted tool params by run * Tests: cover run-scoped adjusted param collisions * Hooks: preserve active tool start metadata until end * Changelog: add tool-hook correlation note * test: fix tsgo baseline test compatibility * feat(plugin-sdk): Add channelRuntime support for external channel plugins ## Overview This PR enables external channel plugins (loaded via Plugin SDK) to access advanced runtime features like AI response dispatching, which were previously only available to built-in channels. ## Changes ### src/gateway/server-channels.ts - Import PluginRuntime type - Add optional channelRuntime parameter to ChannelManagerOptions - Pass channelRuntime to channel startAccount calls via conditional spread - Ensures backward compatibility (field is optional) ### src/gateway/server.impl.ts - Import createPluginRuntime from plugins/runtime - Create and pass channelRuntime to channel manager ### src/channels/plugins/types.adapters.ts - Import PluginRuntime type - Add comprehensive documentation for channelRuntime field - Document available features, use cases, and examples - Improve type safety (use imported PluginRuntime type vs inline import) ## Benefits External channel plugins can now: - Generate AI-powered responses using dispatchReplyWithBufferedBlockDispatcher - Access routing, text processing, and session management utilities - Use command authorization and group policy resolution - Maintain feature parity with built-in channels ## Backward Compatibility - channelRuntime field is optional in ChannelGatewayContext - Conditional spread ensures it's only passed when explicitly provided - Existing channels without channelRuntime support continue to work unchanged - No breaking changes to channel plugin API ## Testing - Email channel plugin successfully uses channelRuntime for AI responses - All existing built-in channels (slack, discord, telegram, etc.) work unchanged - Gateway loads and runs without errors when channelRuntime is provided * fix: add channelRuntime regression coverage (#25462) (thanks @guxiaobo) * Feishu: cache failing probes (#29970) * Feishu: cache failing probes * Changelog: add Feishu probe failure backoff note --------- Co-authored-by: bmendonca3 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * refactor(tests): dedupe agent lock and loop detection fixtures * refactor(tests): dedupe browser and telegram tool test fixtures * refactor(tests): dedupe web fetch and embedded tool hook fixtures * refactor(tests): dedupe cron delivered status assertions * refactor(outbound): unify channel selection and action input normalization * refactor(gateway): extract channel health policy and timing aliases * refactor(feishu): expose default-account selection source * plugin-sdk: expose onAgentEvent + onSessionTranscriptUpdate via PluginRuntime.events * fix: wrap transcript event listeners in try/catch to prevent throw propagation * style: fix indentation in transcript-events * fix: add missing events property to bluebubbles PluginRuntime mock * docs: add JSDoc to onSessionTranscriptUpdate * docs: expand JSDoc for onSessionTranscriptUpdate params and return * Fix styles * fix: add runtime.events regression tests (#16044) (thanks @scifantastic) * feat(hooks): add trigger and channelId to plugin hook agent context (#28623) * feat(hooks): add trigger and channelId to plugin hook agent context Adds `trigger` and `channelId` fields to `PluginHookAgentContext` so plugins can determine what initiated the agent run and which channel it originated from, without session-key parsing or Redis bridging. trigger values: "user", "heartbeat", "cron", "memory" channelId values: "telegram", "discord", "whatsapp", etc. Both fields are threaded through run.ts and attempt.ts hookCtx so all hook phases receive them (before_model_resolve, before_prompt_build, before_agent_start, llm_input, llm_output, agent_end). channelId falls back from messageChannel to messageProvider when the former is not set. followup-runner passes originatingChannel so queued followup runs also carry channel context. * docs(changelog): note hook context parity fix for #28623 --------- Co-authored-by: Vincent Koc <[email protected]> * feat(plugins): expose requestHeartbeatNow on plugin runtime Add requestHeartbeatNow to PluginRuntime.system so extensions can trigger an immediate heartbeat wake without importing internal modules. This enables extensions to inject a system event and wake the agent in one step — useful for inbound message handlers that use the heartbeat model (e.g. agent-to-agent DMs via Nostr). Changes: - src/plugins/runtime/types.ts: add RequestHeartbeatNow type alias and requestHeartbeatNow to PluginRuntime.system - src/plugins/runtime/index.ts: import and wire requestHeartbeatNow into createPluginRuntime() * fix: add requestHeartbeatNow to bluebubbles test mock * fix: add requestHeartbeatNow runtime coverage (#19464) (thanks @AustinEral) * fix: force supportsDeveloperRole=false for non-native OpenAI endpoints (#29479) Merged via squash. Prepared head SHA: 1416c584ac4cdc48af9f224e3d870ef40900c752 Co-authored-by: akramcodez <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * test(agents): tighten pi message typing and dedupe malformed tool-call cases * refactor(test): extract cron issue-regression harness and frozen-time helper * fix(test): resolve upstream typing drift in feishu and cron suites * fix(security): pin tlon api source and secure hold music url * Plugins: add sessionKey to session lifecycle hooks * fix: add session hook context regression tests (#26394) (thanks @tempeste) * refactor(tests): dedupe isolated agent cron turn assertions * refactor(tests): dedupe cron store migration setup * refactor(tests): dedupe discord monitor e2e fixtures * refactor(tests): dedupe manifest registry link fixture setup * refactor(tests): dedupe security fix scenario helpers * refactor(tests): dedupe media transcript echo config setup * refactor(tests): dedupe tools invoke http request helpers * feat(memory): add Ollama embedding provider (#26349) Merged via /review-pr -> /prepare-pr -> /merge-pr. Prepared head SHA: ac413865431614c352c3b29f2dfccc5593f0605a Co-authored-by: nico-hoff <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * fix(sessions): preserve idle reset timestamp on inbound metadata * fix: preserve idle reset timestamp on inbound metadata writes (#32379) (thanks @romeodiaz) * fix(slack): fail fast on non-recoverable auth errors instead of retry loop When a Slack bot is removed from a workspace while still configured in OpenClaw, the gateway enters an infinite retry loop on account_inactive or invalid_auth errors, making the entire gateway unresponsive. Add isNonRecoverableSlackAuthError() to detect permanent credential failures (account_inactive, invalid_auth, token_revoked, etc.) and throw immediately instead of retrying. This mirrors how the Telegram provider already distinguishes recoverable network errors from fatal auth errors via isRecoverableTelegramNetworkError(). The check is applied in both the startup catch block and the disconnect reconnect path so stale credentials always fail fast with a clear error message. Closes #32366 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: fail fast on non-recoverable slack auth errors (#32377) (thanks @scoootscooob) * fix(cli): fail fast on unsupported Node versions in install and runtime paths Surface a clear Node 22.12+ requirement before npm/install bootstrap work so users avoid misleading downstream errors. - Add installer shell preflight to block active Node <22 and suggest NVM recovery commands - Add openclaw.mjs runtime preflight for npm/npx usage with explicit Node version guidance - Keep messaging actionable for both NVM and non-NVM environments * fix(cli): align Node 22.12 preflight checks and clean runtime guard output Tighten installer/runtime consistency so users on Node 22.0-22.11 are blocked before install/runtime drift, with cleaner CLI guidance. - Enforce Node >=22.12 in scripts/install.sh preflight checks - Align installer messages to the same 22.12+ runtime floor - Replace openclaw.mjs thrown version error with stderr+exit to avoid noisy stack traces * fix: enforce node v22.12+ preflight for installer and runtime (#32356) (thanks @jasonhargrove) * fix(webui): prevent inline code from breaking mid-token on copy/paste The parent `.chat-text` applies `overflow-wrap: anywhere; word-break: break-word;` which forces long tokens (UUIDs, hashes) inside inline `<code>` to break across visual lines. When copied, the browser injects spaces at those break points, corrupting the pasted value. Override with `overflow-wrap: normal; word-break: keep-all;` on inline `<code>` selectors so tokens stay intact. Fixes #32230 Signed-off-by: HCL <[email protected]> * fix: preserve inline code copy fidelity in web ui (#32346) (thanks @hclsys) * refactor: modularize plugin runtime and test hooks * fix(agents): treat host edit tool as success when file contains newText after upstream throw (fixes #32333) * fix(agents): only recover edit when oldText no longer in file (review feedback) * fix: recover host edit success after post-write upstream throw (#32383) (thanks @polooooo) * CLI: unify routed config positional parsing * test(agents): centralize AgentMessage fixtures and remove unsafe casts * test(security): reduce audit fixture setup overhead * test(telegram): dedupe streaming cases and tighten sequential key checks * refactor(agents): split pi-tools param and host-edit wrappers * refactor(sessions): add explicit merge activity policies * refactor(slack): extract socket reconnect policy helpers * refactor(runtime): unify node version guard parsing * refactor(ui): dedupe inline code wrap rules * fix: resolve pi-tools typing regressions * refactor(telegram): extract sequential key module * test(telegram): move preview-finalization cases to lane unit tests * perf(agents): cache per-pass context char estimates * perf(security): allow audit snapshot and summary cache reuse * fix(docker): correct awk quoting in Docker GPG fingerprint check (#32153) * fix(acp): use publishable acpx install hint * Agents: add context metadata warmup retry backoff * fix(exec): suggest increasing timeout on timeouts * fix(gemini-cli-auth): use PLATFORM_UNSPECIFIED for Linux in loadCodeAssist Google's loadCodeAssist API rejects "LINUX" as an invalid Platform enum value, causing OAuth setup to fail with 400 Bad Request on Linux systems. The pi-ai runtime already uses "PLATFORM_UNSPECIFIED" for this field. This aligns the extension's discoverProject() with that approach by returning "PLATFORM_UNSPECIFIED" for Linux (and other non-Windows/macOS platforms) instead of "LINUX". Also fixes the original resolvePlatform() which incorrectly fell through to "MACOS" as default instead of explicitly checking for "darwin". * chore: remove unreachable "LINUX" from resolvePlatform return type Address review feedback: since resolvePlatform() no longer returns "LINUX", remove it from the union type to prevent future confusion. * fix(agents): recognize connection errors as retryable timeout failures (#31697) * fix(agents): recognize connection errors as retryable timeout failures ## Problem When a model endpoint becomes unreachable (e.g., local proxy down, relay server offline), the failover system fails to switch to the next candidate model. Errors like "Connection error." are not classified as retryable, causing the session to hang on a broken endpoint instead of falling back to healthy alternatives. ## Root Cause Connection/network errors are not recognized by the current failover classifier: - Text patterns like "Connection error.", "fetch failed", "network error" - Error codes like ECONNREFUSED, ENOTFOUND, EAI_AGAIN (in message text) While `failover-error.ts` handles these as error codes (err.code), it misses them when they appear as plain text in error messages. ## Solution Extend timeout error patterns to include connection/network failures: **In `errors.ts` (ERROR_PATTERNS.timeout):** - Text: "connection error", "network error", "fetch failed", etc. - Regex: /\beconn(?:refused|reset|aborted)\b/i, /\benotfound\b/i, /\beai_again\b/i **In `failover-error.ts` (TIMEOUT_HINT_RE):** - Same patterns for non-assistant error paths ## Testing Added test cases covering: - "Connection error." - "fetch failed" - "network error: ECONNREFUSED" - "ENOTFOUND" / "EAI_AGAIN" in message text ## Impact - **Compatibility:** High - only expands retryable error detection - **Behavior:** Connection failures now trigger automatic fallback - **Risk:** Low - changes are additive and well-tested * style: fix code formatting for test file * refactor: split plugin runtime type contracts * refactor: extract session init helpers * ci: move changed-scope logic into tested script * test: consolidate extension runtime mocks and split bluebubbles webhook auth suite * fix(voice-call): prevent EADDRINUSE by guarding webhook server lifecycle Three issues caused the port to remain bound after partial failures: 1. VoiceCallWebhookServer.start() had no idempotency guard — calling it while the server was already listening would create a second server on the same port. 2. createVoiceCallRuntime() did not clean up the webhook server if a step after webhookServer.start() failed (e.g. manager.initialize). The server kept the port bound while the runtime promise rejected. 3. ensureRuntime() cached the rejected promise forever, so subsequent calls would re-throw the same error without ever retrying. Combined with (2), the port stayed orphaned until gateway restart. Fixes #32387 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(voice-call): harden webhook lifecycle cleanup and retries (#32395) (thanks @scoootscooob) * fix: unblock build type errors * ci: scale Windows CI runner and test workers * refactor(test): dedupe telegram draft-stream fixtures * refactor(telegram): split lane preview target helpers * refactor(agents): split tool-result char estimator * refactor(security): centralize audit execution context * fix(scripts/pr): SSH-first prhead remote with GraphQL fallback for fork PRs (#32126) Co-authored-by: Shakker <[email protected]> * ci: use valid Blacksmith Windows runner label * CI: add sticky-disk toggle to setup node action * CI: add sticky-disk mode to pnpm cache action * CI: enable sticky-disk pnpm cache on Linux CI jobs * CI: migrate docker release build cache to Blacksmith * CI: use Blacksmith docker builder in install smoke * CI: use Blacksmith docker builder in sandbox smoke * refactor(agents): share failover error matchers * refactor(cli): extract plugin install plan helper * refactor(voice-call): unify runtime cleanup lifecycle * refactor(acp): extract install hint resolver * refactor(tests): dedupe control ui auth pairing fixtures * refactor(tests): dedupe gateway chat history fixtures * refactor(memory): dedupe openai batch fetch flows * refactor(tests): dedupe model compat assertions * refactor(acp): dedupe runtime option command plumbing * refactor(config): dedupe repeated zod schema shapes * refactor(browser): dedupe playwright interaction helpers * refactor(tests): dedupe openresponses http fixtures * refactor(tests): dedupe agent handler test scaffolding * refactor(tests): dedupe session store route fixtures * refactor(tests): dedupe canvas host server setup * refactor(security): dedupe telegram allowlist validation loops * refactor(config): dedupe session store save error handling * refactor(gateway): dedupe agents server-method handlers * refactor(infra): dedupe exec approval allowlist evaluation flow * refactor(infra): dedupe ssrf fetch guard test fixtures * refactor(infra): dedupe update startup test setup * refactor(memory): dedupe readonly recovery test scenarios * refactor(agents): dedupe ollama provider test scaffolding * refactor(agents): dedupe steer restart test replacement flow * refactor(telegram): dedupe monitor retry test helpers * feat(secrets): expand SecretRef coverage across user-supplied credentials (#29580) * feat(secrets): expand secret target coverage and gateway tooling * docs(secrets): align gateway and CLI secret docs * chore(protocol): regenerate swift gateway models for secrets methods * fix(config): restore talk apiKey fallback and stabilize runner test * ci(windows): reduce test worker count for shard stability * ci(windows): raise node heap for test shard stability * test(feishu): make proxy env precedence assertion windows-safe * fix(gateway): resolve auth password SecretInput refs for clients * fix(gateway): resolve remote SecretInput credentials for clients * fix(secrets): skip inactive refs in command snapshot assignments * fix(secrets): scope gateway.remote refs to effective auth surfaces * fix(secrets): ignore memory defaults when enabled agents disable search * fix(secrets): honor Google Chat serviceAccountRef inheritance * fix(secrets): address tsgo errors in command and gateway collectors * fix(secrets): avoid auth-store load in providers-only configure * fix(gateway): defer local password ref resolution by precedence * fix(secrets): gate telegram webhook secret refs by webhook mode * fix(secrets): gate slack signing secret refs to http mode * fix(secrets): skip telegram botToken refs when tokenFile is set * fix(secrets): gate discord pluralkit refs by enabled flag * fix(secrets): gate discord voice tts refs by voice enabled * test(secrets): make runtime fixture modes explicit * fix(cli): resolve local qr password secret refs * fix(cli): fail when gateway leaves command refs unresolved * fix(gateway): fail when local password SecretRef is unresolved * fix(gateway): fail when required remote SecretRefs are unresolved * fix(gateway): resolve local password refs only when password can win * fix(cli): skip local password SecretRef resolution on qr token override * test(gateway): cast SecretRef fixtures to OpenClawConfig * test(secrets): activate mode-gated targets in runtime coverage fixture * fix(cron): support SecretInput webhook tokens safely * fix(bluebubbles): support SecretInput passwords across config paths * fix(msteams): make appPassword SecretInput-safe in onboarding/token paths * fix(bluebubbles): align SecretInput schema helper typing * fix(cli): clarify secrets.resolve version-skew errors * refactor(secrets): return structured inactive paths from secrets.resolve * refactor(gateway): type onboarding secret writes as SecretInput * chore(protocol): regenerate swift models for secrets.resolve * feat(secrets): expand extension credential secretref support * fix(secrets): gate web-search refs by active provider * fix(onboarding): detect SecretRef credentials in extension status * fix(onboarding): allow keeping existing ref in secret prompt * fix(onboarding): resolve gateway password SecretRefs for probe and tui * fix(onboarding): honor secret-input-mode for local gateway auth * fix(acp): resolve gateway SecretInput credentials * fix(secrets): gate gateway.remote refs to remote surfaces * test(secrets): cover pattern matching and inactive array refs * docs(secrets): clarify secrets.resolve and remote active surfaces * fix(bluebubbles): keep existing SecretRef during onboarding * fix(tests): resolve CI type errors in new SecretRef coverage * fix(extensions): replace raw fetch with SSRF-guarded fetch * test(secrets): mark gateway remote targets active in runtime coverage * test(infra): normalize home-prefix expectation across platforms * fix(cli): only resolve local qr password refs in password mode * test(cli): cover local qr token mode with unresolved password ref * docs(cli): clarify local qr password ref resolution behavior * refactor(extensions): reuse sdk SecretInput helpers * fix(wizard): resolve onboarding env-template secrets before plaintext * fix(cli): surface secrets.resolve diagnostics in memory and qr * test(secrets): repair post-rebase runtime and fixtures * fix(gateway): skip remote password ref resolution when token wins * fix(secrets): treat tailscale remote gateway refs as active * fix(gateway): allow remote password fallback when token ref is unresolved * fix(gateway): ignore stale local password refs for none and trusted-proxy * fix(gateway): skip remote secret ref resolution on local call paths * test(cli): cover qr remote tailscale secret ref resolution * fix(secrets): align gateway password active-surface with auth inference * fix(cli): resolve inferred local gateway password refs in qr * fix(gateway): prefer resolvable remote password over token ref pre-resolution * test(gateway): cover none and trusted-proxy stale password refs * docs(secrets): sync qr and gateway active-surface behavior * fix: restore stability blockers from pre-release audit * Secrets: fix collector/runtime precedence contradictions * docs: align secrets and web credential docs * fix(rebase): resolve integration regressions after main rebase * fix(node-host): resolve gateway secret refs for auth * fix(secrets): harden secretinput runtime readers * gateway: skip inactive auth secretref resolution * cli: avoid gateway preflight for inactive secret refs * extensions: allow unresolved refs in onboarding status * tests: fix qr-cli module mock hoist ordering * Security: align audit checks with SecretInput resolution * Gateway: resolve local-mode remote fallback secret refs * Node host: avoid resolving inactive password secret refs * Secrets runtime: mark Slack appToken inactive for HTTP mode * secrets: keep inactive gateway remote refs non-blocking * cli: include agent memory secret targets in runtime resolution * docs(secrets): sync docs with active-surface and web search behavior * fix(secrets): keep telegram top-level token refs active for blank account tokens * fix(daemon): resolve gateway password secret refs for probe auth * fix(secrets): skip IRC NickServ ref resolution when NickServ is disabled * fix(secrets): align token inheritance and exec timeout defaults * docs(secrets): clarify active-surface notes in cli docs * cli: require secrets.resolve gateway capability * gateway: log auth secret surface diagnostics * secrets: remove dead provider resolver module * fix(secrets): restore gateway auth precedence and fallback resolution * fix(tests): align plugin runtime mock typings --------- Co-authored-by: Peter Steinberger <[email protected]> * CI: optimize Windows lane by splitting bundle and dropping duplicate lanes * docs: reorder unreleased changelog by user interest * fix(e2e): include shared tool display resource in onboard docker build * fix(ci): tighten type signatures in gateway params validation * fix(telegram): move unchanged command-sync log to verbose * test: fix strict runtime mock types in channel tests * test: load ci changed-scope script via esm import * fix(swift): align async helper callsites across iOS and macOS * refactor(macos): simplify pairing alert and host helper paths * style(swift): apply lint and format cleanup * CI: gate Windows checks by windows-relevant scope (#32456) * CI: add windows scope output for changed-scope * Test: cover windows scope gating in changed-scope * CI: gate checks-windows by windows scope * Docs: update CI windows scope and runner label * CI: move checks-windows to 32 vCPU runner * Docs: align CI windows runner with workflow * CI: allow blacksmith 32 vCPU Windows runner in actionlint * chore(gitignore): ignore android kotlin cache * fix(ci): restore scope-test require import and sync host policy * docs(changelog): add SecretRef note for #29580 * fix(venice): retry model discovery on transient fetch failures * fix(feishu): preserve block streaming text when final payload is missing (#30663) * fix(feishu): preserve block streaming text when final payload is missing When Feishu card streaming receives block payloads without matching final/partial callbacks, keep block text in stream state so onIdle close still publishes the reply instead of an empty message. Add a regression test for block-only streaming. Closes #30628 * Feishu: preserve streaming block fallback when final text is missing --------- Co-authored-by: Tak Hoffman <[email protected]> * CI: add exact-key mode for pnpm cache restore * CI: reduce pre-test Windows setup latency * CI: increase checks-windows test shards to 3 * CI: increase checks-windows test shards to 4 * docs(feishu): Feishu docs – add verificationToken and align zh-CN with EN (openclaw#31555) thanks @xbsheng Verified: - pnpm build - pnpm test:macmini - pnpm check (blocked locally by pre-existing mainline lint issue in src/scripts/ci-changed-scope.test.ts unrelated to this PR) Co-authored-by: xbsheng <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(ci): avoid shell interpolation in changed-scope git diff * test(ci): add changed-scope shell-injection regression * fix(gateway): retry exec-read live tool probe * feat(feishu): add broadcast support for multi-agent groups (#29575) * feat(feishu): add broadcast support for multi-agent group observation When multiple agents share a Feishu group chat, only the @mentioned agent receives the message. This prevents observer agents from building session memory of group activity they weren't directly addressed in. Adds broadcast support (reusing the same cfg.broadcast schema as WhatsApp) so all configured agents receive every group message in their session transcripts. Only the @mentioned agent responds on Feishu; observer agents process silently via no-op dispatchers. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): guard sequential broadcast dispatch against single-agent failure Wrap each dispatchForAgent() call in the sequential loop with try/catch so one agent's dispatch failure doesn't abort delivery to remaining agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): avoid duplicate messages in broadcast observer mode and normalize agent IDs - Skip recordPendingHistoryEntryIfEnabled for broadcast groups when not mentioned, since the message is dispatched directly to all agents. Previously the message appeared twice in the agent prompt. - Normalize agent IDs with toLowerCase() before membership checks so config casing mismatches don't silently skip valid agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): set WasMentioned per-agent and normalize broadcast IDs - buildCtxPayloadForAgent now takes a wasMentioned parameter so active agents get WasMentioned=true and observers get false (P1 fix) - Normalize broadcastAgents to lowercase at resolution time and lowercase activeAgentId so all comparisons and session key generation use canonical IDs regardless of config casing (P2 fix) Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): canonicalize broadcast agent IDs with normalizeAgentId * fix(feishu): match ReplyDispatcher sync return types for noop dispatcher The upstream ReplyDispatcher changed sendToolResult/sendBlockReply/ sendFinalReply to synchronous (returning boolean). Update the broadcast observer noop dispatcher to match. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): deduplicate broadcast agent IDs after normalization Config entries like "Main" and "main" collapse to the same canonical ID after normalizeAgentId but were dispatched multiple times. Use Set to deduplicate after normalization. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): honor requireMention=false when selecting broadcast responder When requireMention is false, the routed agent should be active (reply on Feishu) even without an explicit @mention. Previously activeAgentId was null whenever ctx.mentionedBot was false, so all agents got the noop dispatcher and no reply was sent — silently breaking groups that disabled mention gating. Hoist requireMention out of the if(isGroup) block so it's accessible in the dispatch code. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): cross-account broadcast dedup to prevent duplicate dispatches In multi-account Feishu setups, the same message event is delivered to every bot account in a group. Without cross-account dedup, each account independently dispatches broadcast agents, causing 2×N dispatches instead of N (where N = number of broadcast agents). Two changes: 1. requireMention=true + bot not mentioned: return early instead of falling through to broadcast. The mentioned bot's handler will dispatch for all agents. Non-mentioned handlers record to history. 2. Add cross-account broadcast dedup using a shared 'broadcast' namespace (tryRecordMessagePersistent). The first handler to reach the broadcast block claims the message; subsequent accounts skip. This handles the requireMention=false multi-account case. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): strip CommandAuthorized from broadcast observer contexts Broadcast observer agents inherited CommandAuthorized from the sender, causing slash commands (e.g. /reset) to silently execute on every observer session. Now only the active agent retains CommandAuthorized; observers have it stripped before dispatch. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): use actual mention state for broadcast WasMentioned The active broadcast agent's WasMentioned was set to true whenever requireMention=false, even when the bot was not actually @mentioned. Now uses ctx.mentionedBot && agentId === activeAgentId, consistent with the single-agent path which passes ctx.mentionedBot directly. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): skip history buffer for broadcast accounts and log parallel failures 1. In requireMention groups with broadcast, non-mentioned accounts no longer buffer pending history — the mentioned handler's broadcast dispatch already writes turns into all agent sessions. Buffering caused duplicate replay via buildPendingHistoryContextFromMap. 2. Parallel broadcast dispatch now inspects Promise.allSettled results and logs rejected entries, matching the sequential path's per-agent error logging. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Changelog: note Feishu multi-agent broadcast dispatch * Changelog: restore author credit for Feishu broadcast entry --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * chore(release): prepare 2026.3.2-beta.1 * fix(ci): complete feishu route mock typing in broadcast tests * Delete changelog/fragments directory * refactor(feishu): unify Lark SDK error handling with LarkApiError (#31450) * refactor(feishu): unify Lark SDK error handling with LarkApiError - Add LarkApiError class with code, api, and context fields for better diagnostics - Add ensureLarkSuccess helper to replace 9 duplicate error check patterns - Update tool registration layer to return structured error info (code, api, context) This improves: - Observability: errors now include API name and request context for easier debugging - Maintainability: single point of change for error handling logic - Extensibility: foundation for retry strategies, error classification, etc. Affected APIs: - wiki.space.getNode - bitable.app.get - bitable.app.create - bitable.appTableField.list - bitable.appTableField.create - bitable.appTableRecord.list - bitable.appTableRecord.get - bitable.appTableRecord.create - bitable.appTableRecord.update * Changelog: note Feishu bitable error handling unification --------- Co-authored-by: echoVic <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(gateway): skip google rate limits in live suite * CI: add toggle to skip pnpm actions cache restore * CI: shard Windows tests into sixths and skip cache restore * chore(release): update appcast for 2026.3.2-beta.1 * fix(feishu): correct invalid scope name in permission grant URL (#32509) * fix(feishu): correct invalid scope name in permission grant URL The Feishu API returns error code 99991672 with an authorization URL containing the non-existent scope `contact:contact.base:readonly` when the `contact.user.get` endpoint is called without the correct permission. The valid scope is `contact:user.base:readonly`. Add a scope correction map that replaces known incorrect scope names in the extracted grant URL before presenting it to the user/agent, so the authorization link actually works. Closes #31761 * chore(changelog): note feishu scope correction --------- Co-authored-by: SidQin-cyber <[email protected]> * docs: add dedicated pdf tool docs page * feishu, line: pass per-group systemPrompt to inbound context (#31713) * feishu: pass per-group systemPrompt to inbound context The Feishu extension schema supports systemPrompt in per-group config (channels.feishu.accounts.<id>.groups.<groupId>.systemPrompt) but the value was never forwarded to the inbound context as GroupSystemPrompt. This means per-group system prompts configured for Feishu had no effect, unlike IRC, Discord, Slack, Telegram, Matrix, and other channels that already pass this field correctly. Co-authored-by: Copilot <[email protected]> * line: pass per-group systemPrompt to inbound context Same issue as feishu: the Line config schema defines systemPrompt in per-group config but the value was never forwarded as GroupSystemPrompt in the inbound context payload. Added resolveLineGroupSystemPrompt helper that mirrors the existing resolveLineGroupConfig lookup logic (groupId > roomId > wildcard). Co-authored-by: Copilot <[email protected]> * Changelog: note Feishu and LINE group systemPrompt propagation --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * docs(changelog): remove docs-only 2026.3.2 entries * CI: speed up Windows dependency warmup * CI: make node deps install optional in setup action * CI: reduce critical path for check build and windows jobs * fix(feishu): non-blocking WS ACK and preserve full streaming card content (#29616) * fix(feishu): non-blocking ws ack and preserve streaming card full content * fix(feishu): preserve fragmented streaming text without newline artifacts --------- Co-authored-by: Tak Hoffman <[email protected]> * fix: add session-memory hook support for Feishu provider (#31437) * fix: add session-memory hook support for Feishu provider Issue #31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]> * fix(feishu): guard against false-positive @mentions in multi-app groups (#30315) * fix(feishu): guard against false-positive @mentions in multi-app groups When multiple Feishu bot apps share a group chat, Feishu's WebSocket event delivery remaps the open_id in mentions[] per-app. This causes checkBotMentioned() to return true for ALL bots when only one was actually @mentioned, making requireMention ineffective. Add a botName guard: if the mention's open_id matches this bot but the mention's display name differs from this bot's configured botName, treat it as a false positive and skip. botName is already available via account.config.botName (set during onboarding). Closes #24249 * fix(feishu): support @all mention in multi-bot groups When a user sends @all (@_all in Feishu message content), treat it as mentioning every bot so all agents respond when requireMention is true. Feishu's @all does not populate the mentions[] array, so this needs explicit content-level detection. * fix(feishu): auto-fetch bot display name from API for reliable mention matching Instead of relying on the manually configured botName (which may differ from the actual Feishu bot display name), fetch the bot's display name from the Feishu API at startup via probeFeishu(). This ensures checkBotMentioned() always compares against the correct display name, even when the config botName doesn't match (e.g. config says 'Wanda' but Feishu shows '绯红女巫'). Changes: - monitor.ts: fetchBotOpenId → fetchBotInfo (returns both openId and name) - monitor.ts: store botNames map, pass botName to handleFeishuMessage - bot.ts: accept botName from params, prefer it over config fallback * Changelog: note Feishu multi-app mention false-positive guard --------- Co-authored-by: Teague Xiao <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * CI: start push test lanes earlier and drop check gating * CI: disable flaky sticky disk mount for Windows pnpm setup * 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 <[email protected]> * 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&D). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * Changelog: note Feishu inbound mention normalization --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(feishu): validate outbound renderMode routing with tests (#31562) Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> Co-authored-by: Vincent Koc <[email protected]> * 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 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> Co-authored-by: davidahmann <[email protected]> Co-authored-by: harshang03 <[email protected]> Co-authored-by: haosenwang1018 <[email protected]> Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: coygeek <[email protected]> Co-authored-by: lailoo <[email protected]> * chore: Updated Brave documentation (#26860) Merged via squash. Prepared head SHA: f8fc4bf01e0eacfb01f6ee58eea445680f7eeebd Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: Josh Lehman <[email protected]> * bug: Workaround for QMD upstream bug (#27028) Merged via squash. Prepared head SHA: 939f9f4574fcfe08762407ab9e8d6c85a77a0899 Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * feat(mattermost): add native slash command support (refresh) (#32467) Merged via squash. Prepared head SHA: 989126574ead75c0eedc185293659eb0d4fc6844 Co-authored-by: mukhtharcm <[email protected]> Co-authored-by: mukhtharcm <[email protected]> Reviewed-by: @mukhtharcm * Gateway: fix stale self version in status output (#32655) Merged via squash. Prepared head SHA: b9675d1f90ef0eabb7e68c24a72d4b2fb27def22 Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: altaywtf <[email protected]> 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 <[email protected]> * 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: grp06 <[email protected]> Reviewed-by: @grp06 * docs(contributing): require before/after screenshots for UI PRs (#32206) Merged via squash. Prepared head SHA: d7f0914873aec1c3c64c9161771ff0bcbc457c95 Co-authored-by: hydro13 <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf * fix(discord): align DiscordAccountConfig.token type with SecretInput (#32490) Merged via squash. Prepared head SHA: 233aa032f1d894b7eb6a960247baa1336f8fbc26 Co-authored-by: scoootscooob <[email protected]> Co-authored-by: joshavant <[email protected]> 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 <[email protected]> Co-authored-by: shakkernerd <[email protected]> Reviewed-by: @shakkernerd * Harden embedded run deadlock and timeout handling Co-authored-by: Copilot <[email protected]> (cherry picked from commit 6da593ad721a9c5670623205fd2e20efc26b4205) (cherry picked from commit 8f9e7f97c717b31f15e4fa1663b13e27f8f1edd6) * fix(ci): make labeler token acquisition non-blocking Co-authored-by: Copilot <[email protected]> (cherry picked from commit 811fc2da79a727bbd5a0580391ce39c303b61f98) (cherry picked from commit 632a4dc2431a2fa09ca9952507249b3f342c9411) * fix(ci): retry flaky pre-commit security hooks Co-authored-by: Copilot <[email protected]> (cherry picked from commit d55fa6f00b5313b99704a46a3e990a5365ba112d) * fix(embedded): restore type-safe history image injection build * test(plugins): align command spec expectation with default acceptsArgs --------- Signed-off-by: HCL <[email protected]> Co-authored-by: Peter Steinberger <[email protected]> Co-authored-by: Ash (Bug Lab) <ash@openclaw-lab> Co-authored-by: Vincent Koc <[email protected]> Co-authored-by: Gu XiaoBo <[email protected]> Co-authored-by: Brian Mendonca <[email protected]> Co-authored-by: bmendonca3 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> Co-authored-by: SciFantastic <[email protected]> Co-authored-by: David Rudduck <[email protected]> Co-authored-by: Austin Eral <[email protected]> Co-authored-by: Sk Akram <[email protected]> Co-authored-by: akramcodez <[email protected]> Co-authored-by: gumadeiras <[email protected]> Co-authored-by: tempeste <[email protected]> Co-authored-by: nico-hoff <[email protected]> Co-authored-by: romeodiaz <[email protected]> Co-authored-by: scoootscooob <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Jason Hargrove <[email protected]> Co-authored-by: HCL <[email protected]> Co-authored-by: 倪汉杰0668001185 <[email protected]> Co-authored-by: Gustavo Madeira Santana <[email protected]> Co-authored-by: 苏敏童0668001043 <[email protected]> Co-authored-by: john <[email protected]> Co-authored-by: AaronWander <[email protected]> Co-authored-by: riftzen-bit <[email protected]> Co-authored-by: AI南柯(KingMo) <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]> Co-authored-by: Josh Lehman <[email protected]> Co-authored-by: Shakker <[email protected]> Co-authored-by: Josh Avant <[email protected]> Co-authored-by: Sid <[email protected]> Co-authored-by: xbsheng <[email protected]> Co-authored-by: xbsheng <[email protected]> Co-authored-by: Runkun Miao <[email protected]> Co-authored-by: 青雲 <[email protected]> Co-authored-by: echoVic <[email protected]> Co-authored-by: Tian Wei <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Huaqing.Hao <[email protected]> Co-authored-by: Andy Tien <[email protected]> Co-authored-by: 挨踢小茶 <[email protected]> Co-authored-by: Teague Xiao <[email protected]> Co-authored-by: Jealous <[email protected]> Co-authored-by: dongdong <[email protected]> Co-authored-by: Viz <[email protected]> Co-authored-by: Shadow <[email protected]> Co-authored-by: davidahmann <[email protected]> Co-authored-by: harshang03 <[email protected]> Co-authored-by: haosenwang1018 <[email protected]> Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: coygeek <[email protected]> Co-authored-by: lailoo <[email protected]> Co-authored-by: Henry Loenwind <[email protected]> Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: JT <[email protected]> Co-authored-by: Eugene <[email protected]> Co-authored-by: sircrumpet <[email protected]> Co-authored-by: Muhammed Mukhthar CM <[email protected]> Co-authored-by: Sid-Qin <[email protected]> Co-authored-by: Nimrod Gutman <[email protected]> Co-authored-by: KimGLee <[email protected]> Co-authored-by: Altay <[email protected]> Co-authored-by: altaywtf <[email protected]> Co-authored-by: OpenCils <[email protected]> Co-authored-by: chengzhichao-xydt <[email protected]> Co-authored-by: chengzhichao-xydt <[email protected]> Co-authored-by: jalehman <[email protected]> Co-authored-by: Rodrigo Uroz <[email protected]> Co-authored-by: rodrigouroz <[email protected]> Co-authored-by: Shadow <[email protected]> Co-authored-by: Mylszd <[email protected]> Co-authored-by: Jason L. West, Sr. <[email protected]> Co-authored-by: jlwestsr <[email protected]> Co-authored-by: Mariano <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]> Co-authored-by: dorukardahan <[email protected]> Co-authored-by: grp06 <[email protected]> Co-authored-by: Robin Waslander <[email protected]> Co-authored-by: hydro13 <[email protected]> Co-authored-by: scoootscooob <[email protected]> Co-authored-by: shakkernerd <[email protected]>
* fix(discord): harden voice ffmpeg path and opus fast-path * fix(ui): ensure GFM tables render in WebChat markdown (#20410) - Pass gfm:true + breaks:true explicitly to marked.parse() so table support is guaranteed even if global setOptions() is bypassed or reset by a future refactor (defense-in-depth) - Add display:block + overflow-x:auto to .chat-text table so wide multi-column tables scroll horizontally instead of being clipped by the parent overflow-x:hidden chat container - Add regression tests for GFM table rendering in markdown.test.ts * fix: webchat gfm table rendering and overflow (#32365) (thanks @BlueBirdBack) * refactor(tests): dedupe pi embedded test harness * refactor(tests): dedupe browser and config cli test setup * fix(test): harden discord lifecycle status sink typing * fix(gateway): harden message action channel fallback and startup grace Take the safe, tested subset from #32367:\n- per-channel startup connect grace in health monitor\n- tool-context channel-provider fallback for message actions\n\nCo-authored-by: Munem Hashmi <[email protected]> * docs(changelog): add landed notes for #32336 and #32364 * fix(test): tighten tool result typing in context pruning tests * fix(hooks): propagate run/tool IDs for tool hook correlation (#32360) * Plugin SDK: add run and tool call fields to tool hooks * Agents: propagate runId and toolCallId in before_tool_call * Agents: thread runId through tool wrapper context * Runner: pass runId into tool hook context * Compaction: pass runId into tool hook context * Agents: scope after_tool_call start data by run * Tests: cover run and tool IDs in before_tool_call hooks * Tests: add run-scoped after_tool_call collision coverage * Hooks: scope adjusted tool params by run * Tests: cover run-scoped adjusted param collisions * Hooks: preserve active tool start metadata until end * Changelog: add tool-hook correlation note * test: fix tsgo baseline test compatibility * feat(plugin-sdk): Add channelRuntime support for external channel plugins ## Overview This PR enables external channel plugins (loaded via Plugin SDK) to access advanced runtime features like AI response dispatching, which were previously only available to built-in channels. ## Changes ### src/gateway/server-channels.ts - Import PluginRuntime type - Add optional channelRuntime parameter to ChannelManagerOptions - Pass channelRuntime to channel startAccount calls via conditional spread - Ensures backward compatibility (field is optional) ### src/gateway/server.impl.ts - Import createPluginRuntime from plugins/runtime - Create and pass channelRuntime to channel manager ### src/channels/plugins/types.adapters.ts - Import PluginRuntime type - Add comprehensive documentation for channelRuntime field - Document available features, use cases, and examples - Improve type safety (use imported PluginRuntime type vs inline import) ## Benefits External channel plugins can now: - Generate AI-powered responses using dispatchReplyWithBufferedBlockDispatcher - Access routing, text processing, and session management utilities - Use command authorization and group policy resolution - Maintain feature parity with built-in channels ## Backward Compatibility - channelRuntime field is optional in ChannelGatewayContext - Conditional spread ensures it's only passed when explicitly provided - Existing channels without channelRuntime support continue to work unchanged - No breaking changes to channel plugin API ## Testing - Email channel plugin successfully uses channelRuntime for AI responses - All existing built-in channels (slack, discord, telegram, etc.) work unchanged - Gateway loads and runs without errors when channelRuntime is provided * fix: add channelRuntime regression coverage (#25462) (thanks @guxiaobo) * Feishu: cache failing probes (#29970) * Feishu: cache failing probes * Changelog: add Feishu probe failure backoff note --------- Co-authored-by: bmendonca3 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * refactor(tests): dedupe agent lock and loop detection fixtures * refactor(tests): dedupe browser and telegram tool test fixtures * refactor(tests): dedupe web fetch and embedded tool hook fixtures * refactor(tests): dedupe cron delivered status assertions * refactor(outbound): unify channel selection and action input normalization * refactor(gateway): extract channel health policy and timing aliases * refactor(feishu): expose default-account selection source * plugin-sdk: expose onAgentEvent + onSessionTranscriptUpdate via PluginRuntime.events * fix: wrap transcript event listeners in try/catch to prevent throw propagation * style: fix indentation in transcript-events * fix: add missing events property to bluebubbles PluginRuntime mock * docs: add JSDoc to onSessionTranscriptUpdate * docs: expand JSDoc for onSessionTranscriptUpdate params and return * Fix styles * fix: add runtime.events regression tests (#16044) (thanks @scifantastic) * feat(hooks): add trigger and channelId to plugin hook agent context (#28623) * feat(hooks): add trigger and channelId to plugin hook agent context Adds `trigger` and `channelId` fields to `PluginHookAgentContext` so plugins can determine what initiated the agent run and which channel it originated from, without session-key parsing or Redis bridging. trigger values: "user", "heartbeat", "cron", "memory" channelId values: "telegram", "discord", "whatsapp", etc. Both fields are threaded through run.ts and attempt.ts hookCtx so all hook phases receive them (before_model_resolve, before_prompt_build, before_agent_start, llm_input, llm_output, agent_end). channelId falls back from messageChannel to messageProvider when the former is not set. followup-runner passes originatingChannel so queued followup runs also carry channel context. * docs(changelog): note hook context parity fix for #28623 --------- Co-authored-by: Vincent Koc <[email protected]> * feat(plugins): expose requestHeartbeatNow on plugin runtime Add requestHeartbeatNow to PluginRuntime.system so extensions can trigger an immediate heartbeat wake without importing internal modules. This enables extensions to inject a system event and wake the agent in one step — useful for inbound message handlers that use the heartbeat model (e.g. agent-to-agent DMs via Nostr). Changes: - src/plugins/runtime/types.ts: add RequestHeartbeatNow type alias and requestHeartbeatNow to PluginRuntime.system - src/plugins/runtime/index.ts: import and wire requestHeartbeatNow into createPluginRuntime() * fix: add requestHeartbeatNow to bluebubbles test mock * fix: add requestHeartbeatNow runtime coverage (#19464) (thanks @AustinEral) * fix: force supportsDeveloperRole=false for non-native OpenAI endpoints (#29479) Merged via squash. Prepared head SHA: 1416c584ac4cdc48af9f224e3d870ef40900c752 Co-authored-by: akramcodez <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * test(agents): tighten pi message typing and dedupe malformed tool-call cases * refactor(test): extract cron issue-regression harness and frozen-time helper * fix(test): resolve upstream typing drift in feishu and cron suites * fix(security): pin tlon api source and secure hold music url * Plugins: add sessionKey to session lifecycle hooks * fix: add session hook context regression tests (#26394) (thanks @tempeste) * refactor(tests): dedupe isolated agent cron turn assertions * refactor(tests): dedupe cron store migration setup * refactor(tests): dedupe discord monitor e2e fixtures * refactor(tests): dedupe manifest registry link fixture setup * refactor(tests): dedupe security fix scenario helpers * refactor(tests): dedupe media transcript echo config setup * refactor(tests): dedupe tools invoke http request helpers * feat(memory): add Ollama embedding provider (#26349) Merged via /review-pr -> /prepare-pr -> /merge-pr. Prepared head SHA: ac413865431614c352c3b29f2dfccc5593f0605a Co-authored-by: nico-hoff <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * fix(sessions): preserve idle reset timestamp on inbound metadata * fix: preserve idle reset timestamp on inbound metadata writes (#32379) (thanks @romeodiaz) * fix(slack): fail fast on non-recoverable auth errors instead of retry loop When a Slack bot is removed from a workspace while still configured in OpenClaw, the gateway enters an infinite retry loop on account_inactive or invalid_auth errors, making the entire gateway unresponsive. Add isNonRecoverableSlackAuthError() to detect permanent credential failures (account_inactive, invalid_auth, token_revoked, etc.) and throw immediately instead of retrying. This mirrors how the Telegram provider already distinguishes recoverable network errors from fatal auth errors via isRecoverableTelegramNetworkError(). The check is applied in both the startup catch block and the disconnect reconnect path so stale credentials always fail fast with a clear error message. Closes #32366 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: fail fast on non-recoverable slack auth errors (#32377) (thanks @scoootscooob) * fix(cli): fail fast on unsupported Node versions in install and runtime paths Surface a clear Node 22.12+ requirement before npm/install bootstrap work so users avoid misleading downstream errors. - Add installer shell preflight to block active Node <22 and suggest NVM recovery commands - Add openclaw.mjs runtime preflight for npm/npx usage with explicit Node version guidance - Keep messaging actionable for both NVM and non-NVM environments * fix(cli): align Node 22.12 preflight checks and clean runtime guard output Tighten installer/runtime consistency so users on Node 22.0-22.11 are blocked before install/runtime drift, with cleaner CLI guidance. - Enforce Node >=22.12 in scripts/install.sh preflight checks - Align installer messages to the same 22.12+ runtime floor - Replace openclaw.mjs thrown version error with stderr+exit to avoid noisy stack traces * fix: enforce node v22.12+ preflight for installer and runtime (#32356) (thanks @jasonhargrove) * fix(webui): prevent inline code from breaking mid-token on copy/paste The parent `.chat-text` applies `overflow-wrap: anywhere; word-break: break-word;` which forces long tokens (UUIDs, hashes) inside inline `<code>` to break across visual lines. When copied, the browser injects spaces at those break points, corrupting the pasted value. Override with `overflow-wrap: normal; word-break: keep-all;` on inline `<code>` selectors so tokens stay intact. Fixes #32230 Signed-off-by: HCL <[email protected]> * fix: preserve inline code copy fidelity in web ui (#32346) (thanks @hclsys) * refactor: modularize plugin runtime and test hooks * fix(agents): treat host edit tool as success when file contains newText after upstream throw (fixes #32333) * fix(agents): only recover edit when oldText no longer in file (review feedback) * fix: recover host edit success after post-write upstream throw (#32383) (thanks @polooooo) * CLI: unify routed config positional parsing * test(agents): centralize AgentMessage fixtures and remove unsafe casts * test(security): reduce audit fixture setup overhead * test(telegram): dedupe streaming cases and tighten sequential key checks * refactor(agents): split pi-tools param and host-edit wrappers * refactor(sessions): add explicit merge activity policies * refactor(slack): extract socket reconnect policy helpers * refactor(runtime): unify node version guard parsing * refactor(ui): dedupe inline code wrap rules * fix: resolve pi-tools typing regressions * refactor(telegram): extract sequential key module * test(telegram): move preview-finalization cases to lane unit tests * perf(agents): cache per-pass context char estimates * perf(security): allow audit snapshot and summary cache reuse * fix(docker): correct awk quoting in Docker GPG fingerprint check (#32153) * fix(acp): use publishable acpx install hint * Agents: add context metadata warmup retry backoff * fix(exec): suggest increasing timeout on timeouts * fix(gemini-cli-auth): use PLATFORM_UNSPECIFIED for Linux in loadCodeAssist Google's loadCodeAssist API rejects "LINUX" as an invalid Platform enum value, causing OAuth setup to fail with 400 Bad Request on Linux systems. The pi-ai runtime already uses "PLATFORM_UNSPECIFIED" for this field. This aligns the extension's discoverProject() with that approach by returning "PLATFORM_UNSPECIFIED" for Linux (and other non-Windows/macOS platforms) instead of "LINUX". Also fixes the original resolvePlatform() which incorrectly fell through to "MACOS" as default instead of explicitly checking for "darwin". * chore: remove unreachable "LINUX" from resolvePlatform return type Address review feedback: since resolvePlatform() no longer returns "LINUX", remove it from the union type to prevent future confusion. * fix(agents): recognize connection errors as retryable timeout failures (#31697) * fix(agents): recognize connection errors as retryable timeout failures ## Problem When a model endpoint becomes unreachable (e.g., local proxy down, relay server offline), the failover system fails to switch to the next candidate model. Errors like "Connection error." are not classified as retryable, causing the session to hang on a broken endpoint instead of falling back to healthy alternatives. ## Root Cause Connection/network errors are not recognized by the current failover classifier: - Text patterns like "Connection error.", "fetch failed", "network error" - Error codes like ECONNREFUSED, ENOTFOUND, EAI_AGAIN (in message text) While `failover-error.ts` handles these as error codes (err.code), it misses them when they appear as plain text in error messages. ## Solution Extend timeout error patterns to include connection/network failures: **In `errors.ts` (ERROR_PATTERNS.timeout):** - Text: "connection error", "network error", "fetch failed", etc. - Regex: /\beconn(?:refused|reset|aborted)\b/i, /\benotfound\b/i, /\beai_again\b/i **In `failover-error.ts` (TIMEOUT_HINT_RE):** - Same patterns for non-assistant error paths ## Testing Added test cases covering: - "Connection error." - "fetch failed" - "network error: ECONNREFUSED" - "ENOTFOUND" / "EAI_AGAIN" in message text ## Impact - **Compatibility:** High - only expands retryable error detection - **Behavior:** Connection failures now trigger automatic fallback - **Risk:** Low - changes are additive and well-tested * style: fix code formatting for test file * refactor: split plugin runtime type contracts * refactor: extract session init helpers * ci: move changed-scope logic into tested script * test: consolidate extension runtime mocks and split bluebubbles webhook auth suite * fix(voice-call): prevent EADDRINUSE by guarding webhook server lifecycle Three issues caused the port to remain bound after partial failures: 1. VoiceCallWebhookServer.start() had no idempotency guard — calling it while the server was already listening would create a second server on the same port. 2. createVoiceCallRuntime() did not clean up the webhook server if a step after webhookServer.start() failed (e.g. manager.initialize). The server kept the port bound while the runtime promise rejected. 3. ensureRuntime() cached the rejected promise forever, so subsequent calls would re-throw the same error without ever retrying. Combined with (2), the port stayed orphaned until gateway restart. Fixes #32387 Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(voice-call): harden webhook lifecycle cleanup and retries (#32395) (thanks @scoootscooob) * fix: unblock build type errors * ci: scale Windows CI runner and test workers * refactor(test): dedupe telegram draft-stream fixtures * refactor(telegram): split lane preview target helpers * refactor(agents): split tool-result char estimator * refactor(security): centralize audit execution context * fix(scripts/pr): SSH-first prhead remote with GraphQL fallback for fork PRs (#32126) Co-authored-by: Shakker <[email protected]> * ci: use valid Blacksmith Windows runner label * CI: add sticky-disk toggle to setup node action * CI: add sticky-disk mode to pnpm cache action * CI: enable sticky-disk pnpm cache on Linux CI jobs * CI: migrate docker release build cache to Blacksmith * CI: use Blacksmith docker builder in install smoke * CI: use Blacksmith docker builder in sandbox smoke * refactor(agents): share failover error matchers * refactor(cli): extract plugin install plan helper * refactor(voice-call): unify runtime cleanup lifecycle * refactor(acp): extract install hint resolver * refactor(tests): dedupe control ui auth pairing fixtures * refactor(tests): dedupe gateway chat history fixtures * refactor(memory): dedupe openai batch fetch flows * refactor(tests): dedupe model compat assertions * refactor(acp): dedupe runtime option command plumbing * refactor(config): dedupe repeated zod schema shapes * refactor(browser): dedupe playwright interaction helpers * refactor(tests): dedupe openresponses http fixtures * refactor(tests): dedupe agent handler test scaffolding * refactor(tests): dedupe session store route fixtures * refactor(tests): dedupe canvas host server setup * refactor(security): dedupe telegram allowlist validation loops * refactor(config): dedupe session store save error handling * refactor(gateway): dedupe agents server-method handlers * refactor(infra): dedupe exec approval allowlist evaluation flow * refactor(infra): dedupe ssrf fetch guard test fixtures * refactor(infra): dedupe update startup test setup * refactor(memory): dedupe readonly recovery test scenarios * refactor(agents): dedupe ollama provider test scaffolding * refactor(agents): dedupe steer restart test replacement flow * refactor(telegram): dedupe monitor retry test helpers * feat(secrets): expand SecretRef coverage across user-supplied credentials (#29580) * feat(secrets): expand secret target coverage and gateway tooling * docs(secrets): align gateway and CLI secret docs * chore(protocol): regenerate swift gateway models for secrets methods * fix(config): restore talk apiKey fallback and stabilize runner test * ci(windows): reduce test worker count for shard stability * ci(windows): raise node heap for test shard stability * test(feishu): make proxy env precedence assertion windows-safe * fix(gateway): resolve auth password SecretInput refs for clients * fix(gateway): resolve remote SecretInput credentials for clients * fix(secrets): skip inactive refs in command snapshot assignments * fix(secrets): scope gateway.remote refs to effective auth surfaces * fix(secrets): ignore memory defaults when enabled agents disable search * fix(secrets): honor Google Chat serviceAccountRef inheritance * fix(secrets): address tsgo errors in command and gateway collectors * fix(secrets): avoid auth-store load in providers-only configure * fix(gateway): defer local password ref resolution by precedence * fix(secrets): gate telegram webhook secret refs by webhook mode * fix(secrets): gate slack signing secret refs to http mode * fix(secrets): skip telegram botToken refs when tokenFile is set * fix(secrets): gate discord pluralkit refs by enabled flag * fix(secrets): gate discord voice tts refs by voice enabled * test(secrets): make runtime fixture modes explicit * fix(cli): resolve local qr password secret refs * fix(cli): fail when gateway leaves command refs unresolved * fix(gateway): fail when local password SecretRef is unresolved * fix(gateway): fail when required remote SecretRefs are unresolved * fix(gateway): resolve local password refs only when password can win * fix(cli): skip local password SecretRef resolution on qr token override * test(gateway): cast SecretRef fixtures to OpenClawConfig * test(secrets): activate mode-gated targets in runtime coverage fixture * fix(cron): support SecretInput webhook tokens safely * fix(bluebubbles): support SecretInput passwords across config paths * fix(msteams): make appPassword SecretInput-safe in onboarding/token paths * fix(bluebubbles): align SecretInput schema helper typing * fix(cli): clarify secrets.resolve version-skew errors * refactor(secrets): return structured inactive paths from secrets.resolve * refactor(gateway): type onboarding secret writes as SecretInput * chore(protocol): regenerate swift models for secrets.resolve * feat(secrets): expand extension credential secretref support * fix(secrets): gate web-search refs by active provider * fix(onboarding): detect SecretRef credentials in extension status * fix(onboarding): allow keeping existing ref in secret prompt * fix(onboarding): resolve gateway password SecretRefs for probe and tui * fix(onboarding): honor secret-input-mode for local gateway auth * fix(acp): resolve gateway SecretInput credentials * fix(secrets): gate gateway.remote refs to remote surfaces * test(secrets): cover pattern matching and inactive array refs * docs(secrets): clarify secrets.resolve and remote active surfaces * fix(bluebubbles): keep existing SecretRef during onboarding * fix(tests): resolve CI type errors in new SecretRef coverage * fix(extensions): replace raw fetch with SSRF-guarded fetch * test(secrets): mark gateway remote targets active in runtime coverage * test(infra): normalize home-prefix expectation across platforms * fix(cli): only resolve local qr password refs in password mode * test(cli): cover local qr token mode with unresolved password ref * docs(cli): clarify local qr password ref resolution behavior * refactor(extensions): reuse sdk SecretInput helpers * fix(wizard): resolve onboarding env-template secrets before plaintext * fix(cli): surface secrets.resolve diagnostics in memory and qr * test(secrets): repair post-rebase runtime and fixtures * fix(gateway): skip remote password ref resolution when token wins * fix(secrets): treat tailscale remote gateway refs as active * fix(gateway): allow remote password fallback when token ref is unresolved * fix(gateway): ignore stale local password refs for none and trusted-proxy * fix(gateway): skip remote secret ref resolution on local call paths * test(cli): cover qr remote tailscale secret ref resolution * fix(secrets): align gateway password active-surface with auth inference * fix(cli): resolve inferred local gateway password refs in qr * fix(gateway): prefer resolvable remote password over token ref pre-resolution * test(gateway): cover none and trusted-proxy stale password refs * docs(secrets): sync qr and gateway active-surface behavior * fix: restore stability blockers from pre-release audit * Secrets: fix collector/runtime precedence contradictions * docs: align secrets and web credential docs * fix(rebase): resolve integration regressions after main rebase * fix(node-host): resolve gateway secret refs for auth * fix(secrets): harden secretinput runtime readers * gateway: skip inactive auth secretref resolution * cli: avoid gateway preflight for inactive secret refs * extensions: allow unresolved refs in onboarding status * tests: fix qr-cli module mock hoist ordering * Security: align audit checks with SecretInput resolution * Gateway: resolve local-mode remote fallback secret refs * Node host: avoid resolving inactive password secret refs * Secrets runtime: mark Slack appToken inactive for HTTP mode * secrets: keep inactive gateway remote refs non-blocking * cli: include agent memory secret targets in runtime resolution * docs(secrets): sync docs with active-surface and web search behavior * fix(secrets): keep telegram top-level token refs active for blank account tokens * fix(daemon): resolve gateway password secret refs for probe auth * fix(secrets): skip IRC NickServ ref resolution when NickServ is disabled * fix(secrets): align token inheritance and exec timeout defaults * docs(secrets): clarify active-surface notes in cli docs * cli: require secrets.resolve gateway capability * gateway: log auth secret surface diagnostics * secrets: remove dead provider resolver module * fix(secrets): restore gateway auth precedence and fallback resolution * fix(tests): align plugin runtime mock typings --------- Co-authored-by: Peter Steinberger <[email protected]> * CI: optimize Windows lane by splitting bundle and dropping duplicate lanes * docs: reorder unreleased changelog by user interest * fix(e2e): include shared tool display resource in onboard docker build * fix(ci): tighten type signatures in gateway params validation * fix(telegram): move unchanged command-sync log to verbose * test: fix strict runtime mock types in channel tests * test: load ci changed-scope script via esm import * fix(swift): align async helper callsites across iOS and macOS * refactor(macos): simplify pairing alert and host helper paths * style(swift): apply lint and format cleanup * CI: gate Windows checks by windows-relevant scope (#32456) * CI: add windows scope output for changed-scope * Test: cover windows scope gating in changed-scope * CI: gate checks-windows by windows scope * Docs: update CI windows scope and runner label * CI: move checks-windows to 32 vCPU runner * Docs: align CI windows runner with workflow * CI: allow blacksmith 32 vCPU Windows runner in actionlint * chore(gitignore): ignore android kotlin cache * fix(ci): restore scope-test require import and sync host policy * docs(changelog): add SecretRef note for #29580 * fix(venice): retry model discovery on transient fetch failures * fix(feishu): preserve block streaming text when final payload is missing (#30663) * fix(feishu): preserve block streaming text when final payload is missing When Feishu card streaming receives block payloads without matching final/partial callbacks, keep block text in stream state so onIdle close still publishes the reply instead of an empty message. Add a regression test for block-only streaming. Closes #30628 * Feishu: preserve streaming block fallback when final text is missing --------- Co-authored-by: Tak Hoffman <[email protected]> * CI: add exact-key mode for pnpm cache restore * CI: reduce pre-test Windows setup latency * CI: increase checks-windows test shards to 3 * CI: increase checks-windows test shards to 4 * docs(feishu): Feishu docs – add verificationToken and align zh-CN with EN (openclaw#31555) thanks @xbsheng Verified: - pnpm build - pnpm test:macmini - pnpm check (blocked locally by pre-existing mainline lint issue in src/scripts/ci-changed-scope.test.ts unrelated to this PR) Co-authored-by: xbsheng <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(ci): avoid shell interpolation in changed-scope git diff * test(ci): add changed-scope shell-injection regression * fix(gateway): retry exec-read live tool probe * feat(feishu): add broadcast support for multi-agent groups (#29575) * feat(feishu): add broadcast support for multi-agent group observation When multiple agents share a Feishu group chat, only the @mentioned agent receives the message. This prevents observer agents from building session memory of group activity they weren't directly addressed in. Adds broadcast support (reusing the same cfg.broadcast schema as WhatsApp) so all configured agents receive every group message in their session transcripts. Only the @mentioned agent responds on Feishu; observer agents process silently via no-op dispatchers. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): guard sequential broadcast dispatch against single-agent failure Wrap each dispatchForAgent() call in the sequential loop with try/catch so one agent's dispatch failure doesn't abort delivery to remaining agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): avoid duplicate messages in broadcast observer mode and normalize agent IDs - Skip recordPendingHistoryEntryIfEnabled for broadcast groups when not mentioned, since the message is dispatched directly to all agents. Previously the message appeared twice in the agent prompt. - Normalize agent IDs with toLowerCase() before membership checks so config casing mismatches don't silently skip valid agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): set WasMentioned per-agent and normalize broadcast IDs - buildCtxPayloadForAgent now takes a wasMentioned parameter so active agents get WasMentioned=true and observers get false (P1 fix) - Normalize broadcastAgents to lowercase at resolution time and lowercase activeAgentId so all comparisons and session key generation use canonical IDs regardless of config casing (P2 fix) Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): canonicalize broadcast agent IDs with normalizeAgentId * fix(feishu): match ReplyDispatcher sync return types for noop dispatcher The upstream ReplyDispatcher changed sendToolResult/sendBlockReply/ sendFinalReply to synchronous (returning boolean). Update the broadcast observer noop dispatcher to match. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): deduplicate broadcast agent IDs after normalization Config entries like "Main" and "main" collapse to the same canonical ID after normalizeAgentId but were dispatched multiple times. Use Set to deduplicate after normalization. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): honor requireMention=false when selecting broadcast responder When requireMention is false, the routed agent should be active (reply on Feishu) even without an explicit @mention. Previously activeAgentId was null whenever ctx.mentionedBot was false, so all agents got the noop dispatcher and no reply was sent — silently breaking groups that disabled mention gating. Hoist requireMention out of the if(isGroup) block so it's accessible in the dispatch code. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): cross-account broadcast dedup to prevent duplicate dispatches In multi-account Feishu setups, the same message event is delivered to every bot account in a group. Without cross-account dedup, each account independently dispatches broadcast agents, causing 2×N dispatches instead of N (where N = number of broadcast agents). Two changes: 1. requireMention=true + bot not mentioned: return early instead of falling through to broadcast. The mentioned bot's handler will dispatch for all agents. Non-mentioned handlers record to history. 2. Add cross-account broadcast dedup using a shared 'broadcast' namespace (tryRecordMessagePersistent). The first handler to reach the broadcast block claims the message; subsequent accounts skip. This handles the requireMention=false multi-account case. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): strip CommandAuthorized from broadcast observer contexts Broadcast observer agents inherited CommandAuthorized from the sender, causing slash commands (e.g. /reset) to silently execute on every observer session. Now only the active agent retains CommandAuthorized; observers have it stripped before dispatch. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): use actual mention state for broadcast WasMentioned The active broadcast agent's WasMentioned was set to true whenever requireMention=false, even when the bot was not actually @mentioned. Now uses ctx.mentionedBot && agentId === activeAgentId, consistent with the single-agent path which passes ctx.mentionedBot directly. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): skip history buffer for broadcast accounts and log parallel failures 1. In requireMention groups with broadcast, non-mentioned accounts no longer buffer pending history — the mentioned handler's broadcast dispatch already writes turns into all agent sessions. Buffering caused duplicate replay via buildPendingHistoryContextFromMap. 2. Parallel broadcast dispatch now inspects Promise.allSettled results and logs rejected entries, matching the sequential path's per-agent error logging. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Changelog: note Feishu multi-agent broadcast dispatch * Changelog: restore author credit for Feishu broadcast entry --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * chore(release): prepare 2026.3.2-beta.1 * fix(ci): complete feishu route mock typing in broadcast tests * Delete changelog/fragments directory * refactor(feishu): unify Lark SDK error handling with LarkApiError (#31450) * refactor(feishu): unify Lark SDK error handling with LarkApiError - Add LarkApiError class with code, api, and context fields for better diagnostics - Add ensureLarkSuccess helper to replace 9 duplicate error check patterns - Update tool registration layer to return structured error info (code, api, context) This improves: - Observability: errors now include API name and request context for easier debugging - Maintainability: single point of change for error handling logic - Extensibility: foundation for retry strategies, error classification, etc. Affected APIs: - wiki.space.getNode - bitable.app.get - bitable.app.create - bitable.appTableField.list - bitable.appTableField.create - bitable.appTableRecord.list - bitable.appTableRecord.get - bitable.appTableRecord.create - bitable.appTableRecord.update * Changelog: note Feishu bitable error handling unification --------- Co-authored-by: echoVic <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(gateway): skip google rate limits in live suite * CI: add toggle to skip pnpm actions cache restore * CI: shard Windows tests into sixths and skip cache restore * chore(release): update appcast for 2026.3.2-beta.1 * fix(feishu): correct invalid scope name in permission grant URL (#32509) * fix(feishu): correct invalid scope name in permission grant URL The Feishu API returns error code 99991672 with an authorization URL containing the non-existent scope `contact:contact.base:readonly` when the `contact.user.get` endpoint is called without the correct permission. The valid scope is `contact:user.base:readonly`. Add a scope correction map that replaces known incorrect scope names in the extracted grant URL before presenting it to the user/agent, so the authorization link actually works. Closes #31761 * chore(changelog): note feishu scope correction --------- Co-authored-by: SidQin-cyber <[email protected]> * docs: add dedicated pdf tool docs page * feishu, line: pass per-group systemPrompt to inbound context (#31713) * feishu: pass per-group systemPrompt to inbound context The Feishu extension schema supports systemPrompt in per-group config (channels.feishu.accounts.<id>.groups.<groupId>.systemPrompt) but the value was never forwarded to the inbound context as GroupSystemPrompt. This means per-group system prompts configured for Feishu had no effect, unlike IRC, Discord, Slack, Telegram, Matrix, and other channels that already pass this field correctly. Co-authored-by: Copilot <[email protected]> * line: pass per-group systemPrompt to inbound context Same issue as feishu: the Line config schema defines systemPrompt in per-group config but the value was never forwarded as GroupSystemPrompt in the inbound context payload. Added resolveLineGroupSystemPrompt helper that mirrors the existing resolveLineGroupConfig lookup logic (groupId > roomId > wildcard). Co-authored-by: Copilot <[email protected]> * Changelog: note Feishu and LINE group systemPrompt propagation --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * docs(changelog): remove docs-only 2026.3.2 entries * CI: speed up Windows dependency warmup * CI: make node deps install optional in setup action * CI: reduce critical path for check build and windows jobs * fix(feishu): non-blocking WS ACK and preserve full streaming card content (#29616) * fix(feishu): non-blocking ws ack and preserve streaming card full content * fix(feishu): preserve fragmented streaming text without newline artifacts --------- Co-authored-by: Tak Hoffman <[email protected]> * fix: add session-memory hook support for Feishu provider (#31437) * fix: add session-memory hook support for Feishu provider Issue #31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]> * fix(feishu): guard against false-positive @mentions in multi-app groups (#30315) * fix(feishu): guard against false-positive @mentions in multi-app groups When multiple Feishu bot apps share a group chat, Feishu's WebSocket event delivery remaps the open_id in mentions[] per-app. This causes checkBotMentioned() to return true for ALL bots when only one was actually @mentioned, making requireMention ineffective. Add a botName guard: if the mention's open_id matches this bot but the mention's display name differs from this bot's configured botName, treat it as a false positive and skip. botName is already available via account.config.botName (set during onboarding). Closes #24249 * fix(feishu): support @all mention in multi-bot groups When a user sends @all (@_all in Feishu message content), treat it as mentioning every bot so all agents respond when requireMention is true. Feishu's @all does not populate the mentions[] array, so this needs explicit content-level detection. * fix(feishu): auto-fetch bot display name from API for reliable mention matching Instead of relying on the manually configured botName (which may differ from the actual Feishu bot display name), fetch the bot's display name from the Feishu API at startup via probeFeishu(). This ensures checkBotMentioned() always compares against the correct display name, even when the config botName doesn't match (e.g. config says 'Wanda' but Feishu shows '绯红女巫'). Changes: - monitor.ts: fetchBotOpenId → fetchBotInfo (returns both openId and name) - monitor.ts: store botNames map, pass botName to handleFeishuMessage - bot.ts: accept botName from params, prefer it over config fallback * Changelog: note Feishu multi-app mention false-positive guard --------- Co-authored-by: Teague Xiao <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * CI: start push test lanes earlier and drop check gating * CI: disable flaky sticky disk mount for Windows pnpm setup * 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 <[email protected]> * 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&D). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * Changelog: note Feishu inbound mention normalization --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(feishu): validate outbound renderMode routing with tests (#31562) Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> Co-authored-by: Vincent Koc <[email protected]> * 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 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> Co-authored-by: davidahmann <[email protected]> Co-authored-by: harshang03 <[email protected]> Co-authored-by: haosenwang1018 <[email protected]> Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: coygeek <[email protected]> Co-authored-by: lailoo <[email protected]> * chore: Updated Brave documentation (#26860) Merged via squash. Prepared head SHA: f8fc4bf01e0eacfb01f6ee58eea445680f7eeebd Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: Josh Lehman <[email protected]> * bug: Workaround for QMD upstream bug (#27028) Merged via squash. Prepared head SHA: 939f9f4574fcfe08762407ab9e8d6c85a77a0899 Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * feat(mattermost): add native slash command support (refresh) (#32467) Merged via squash. Prepared head SHA: 989126574ead75c0eedc185293659eb0d4fc6844 Co-authored-by: mukhtharcm <[email protected]> Co-authored-by: mukhtharcm <[email protected]> Reviewed-by: @mukhtharcm * Gateway: fix stale self version in status output (#32655) Merged via squash. Prepared head SHA: b9675d1f90ef0eabb7e68c24a72d4b2fb27def22 Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: altaywtf <[email protected]> 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 <[email protected]> * 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: grp06 <[email protected]> Reviewed-by: @grp06 * docs(contributing): require before/after screenshots for UI PRs (#32206) Merged via squash. Prepared head SHA: d7f0914873aec1c3c64c9161771ff0bcbc457c95 Co-authored-by: hydro13 <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf * fix(discord): align DiscordAccountConfig.token type with SecretInput (#32490) Merged via squash. Prepared head SHA: 233aa032f1d894b7eb6a960247baa1336f8fbc26 Co-authored-by: scoootscooob <[email protected]> Co-authored-by: joshavant <[email protected]> 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 <[email protected]> Co-authored-by: shakkernerd <[email protected]> Reviewed-by: @shakkernerd * Agent: unify bootstrap truncation warning handling (#32769) Merged via squash. Prepared head SHA: 5d6d4ddfa620011e267d892b402751847d5ac0c3 Co-authored-by: gumadeiras <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * fix(logging ): use local timezone for console log timestamps (#25970) Merged via squash. Prepared head SHA: 30123265b7b910b9208e8c9407c30536e46eb68f Co-authored-by: openperf <[email protected]> Co-authored-by: altaywtf <[email protected]> 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 <[email protected]> Co-authored-by: grp06 <[email protected]> Reviewed-by: @grp06 * Harden embedded run deadlock and timeout handling Co-authored-by: Copilot <[email protected]> (cherry picked from commit 6da593ad721a9c5670623205fd2e20efc26b4205) (cherry picked from commit 8f9e7f97c717b31f15e4fa1663b13e27f8f1edd6) * fix(ci): make labeler token acquisition non-blocking Co-authored-by: Copilot <[email protected]> (cherry picked from commit 811fc2da79a727bbd5a0580391ce39c303b61f98) (cherry picked from commit 632a4dc2431a2fa09ca9952507249b3f342c9411) * fix(ci): retry flaky pre-commit security hooks Co-authored-by: Copilot <[email protected]> (cherry picked from commit d55fa6f00b5313b99704a46a3e990a5365ba112d) --------- Signed-off-by: HCL <[email protected]> Co-authored-by: Peter Steinberger <[email protected]> Co-authored-by: Ash (Bug Lab) <ash@openclaw-lab> Co-authored-by: Vincent Koc <[email protected]> Co-authored-by: Gu XiaoBo <[email protected]> Co-authored-by: Brian Mendonca <[email protected]> Co-authored-by: bmendonca3 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> Co-authored-by: SciFantastic <[email protected]> Co-authored-by: David Rudduck <[email protected]> Co-authored-by: Austin Eral <[email protected]> Co-authored-by: Sk Akram <[email protected]> Co-authored-by: akramcodez <[email protected]> Co-authored-by: gumadeiras <[email protected]> Co-authored-by: tempeste <[email protected]> Co-authored-by: nico-hoff <[email protected]> Co-authored-by: romeodiaz <[email protected]> Co-authored-by: scoootscooob <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Jason Hargrove <[email protected]> Co-authored-by: HCL <[email protected]> Co-authored-by: 倪汉杰0668001185 <[email protected]> Co-authored-by: Gustavo Madeira Santana <[email protected]> Co-authored-by: 苏敏童0668001043 <[email protected]> Co-authored-by: john <[email protected]> Co-authored-by: AaronWander <[email protected]> Co-authored-by: riftzen-bit <[email protected]> Co-authored-by: AI南柯(KingMo) <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]> Co-authored-by: Josh Lehman <[email protected]> Co-authored-by: Shakker <[email protected]> Co-authored-by: Josh Avant <[email protected]> Co-authored-by: Sid <[email protected]> Co-authored-by: xbsheng <[email protected]> Co-authored-by: xbsheng <[email protected]> Co-authored-by: Runkun Miao <[email protected]> Co-authored-by: 青雲 <[email protected]> Co-authored-by: echoVic <[email protected]> Co-authored-by: Tian Wei <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Huaqing.Hao <[email protected]> Co-authored-by: Andy Tien <[email protected]> Co-authored-by: 挨踢小茶 <[email protected]> Co-authored-by: Teague Xiao <[email protected]> Co-authored-by: Jealous <[email protected]> Co-authored-by: dongdong <[email protected]> Co-authored-by: Viz <[email protected]> Co-authored-by: Shadow <[email protected]> Co-authored-by: davidahmann <[email protected]> Co-authored-by: harshang03 <[email protected]> Co-authored-by: haosenwang1018 <[email protected]> Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: coygeek <[email protected]> Co-authored-by: lailoo <[email protected]> Co-authored-by: Henry Loenwind <[email protected]> Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: JT <[email protected]> Co-authored-by: Eugene <[email protected]> Co-authored-by: sircrumpet <[email protected]> Co-authored-by: Muhammed Mukhthar CM <[email protected]> Co-authored-by: Sid-Qin <[email protected]> Co-authored-by: Nimrod Gutman <[email protected]> Co-authored-by: KimGLee <[email protected]> Co-authored-by: Altay <[email protected]> Co-authored-by: altaywtf <[email protected]> Co-authored-by: OpenCils <[email protected]> Co-authored-by: chengzhichao-xydt <[email protected]> Co-authored-by: chengzhichao-xydt <[email protected]> Co-authored-by: jalehman <[email protected]> Co-authored-by: Rodrigo Uroz <[email protected]> Co-authored-by: rodrigouroz <[email protected]> Co-authored-by: Shadow <[email protected]> Co-authored-by: Mylszd <[email protected]> Co-authored-by: Jason L. West, Sr. <[email protected]> Co-authored-by: jlwestsr <[email protected]> Co-authored-by: Mariano <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]> Co-authored-by: dorukardahan <[email protected]> Co-authored-by: grp06 <[email protected]> Co-authored-by: Robin Waslander <[email protected]> Co-authored-by: hydro13 <[email protected]> Co-authored-by: scoootscooob <[email protected]> Co-authored-by: shakkernerd <[email protected]> Co-authored-by: wangchunyue <[email protected]> Co-authored-by: 13otKmdr <[email protected]>
) * fix: add session-memory hook support for Feishu provider Issue openclaw#31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]>
) * fix: add session-memory hook support for Feishu provider Issue openclaw#31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]>
) * fix: add session-memory hook support for Feishu provider Issue openclaw#31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]>
* refactor(memory): dedupe readonly recovery test scenarios * refactor(agents): dedupe ollama provider test scaffolding * refactor(agents): dedupe steer restart test replacement flow * refactor(telegram): dedupe monitor retry test helpers * feat(secrets): expand SecretRef coverage across user-supplied credentials (#29580) * feat(secrets): expand secret target coverage and gateway tooling * docs(secrets): align gateway and CLI secret docs * chore(protocol): regenerate swift gateway models for secrets methods * fix(config): restore talk apiKey fallback and stabilize runner test * ci(windows): reduce test worker count for shard stability * ci(windows): raise node heap for test shard stability * test(feishu): make proxy env precedence assertion windows-safe * fix(gateway): resolve auth password SecretInput refs for clients * fix(gateway): resolve remote SecretInput credentials for clients * fix(secrets): skip inactive refs in command snapshot assignments * fix(secrets): scope gateway.remote refs to effective auth surfaces * fix(secrets): ignore memory defaults when enabled agents disable search * fix(secrets): honor Google Chat serviceAccountRef inheritance * fix(secrets): address tsgo errors in command and gateway collectors * fix(secrets): avoid auth-store load in providers-only configure * fix(gateway): defer local password ref resolution by precedence * fix(secrets): gate telegram webhook secret refs by webhook mode * fix(secrets): gate slack signing secret refs to http mode * fix(secrets): skip telegram botToken refs when tokenFile is set * fix(secrets): gate discord pluralkit refs by enabled flag * fix(secrets): gate discord voice tts refs by voice enabled * test(secrets): make runtime fixture modes explicit * fix(cli): resolve local qr password secret refs * fix(cli): fail when gateway leaves command refs unresolved * fix(gateway): fail when local password SecretRef is unresolved * fix(gateway): fail when required remote SecretRefs are unresolved * fix(gateway): resolve local password refs only when password can win * fix(cli): skip local password SecretRef resolution on qr token override * test(gateway): cast SecretRef fixtures to OpenClawConfig * test(secrets): activate mode-gated targets in runtime coverage fixture * fix(cron): support SecretInput webhook tokens safely * fix(bluebubbles): support SecretInput passwords across config paths * fix(msteams): make appPassword SecretInput-safe in onboarding/token paths * fix(bluebubbles): align SecretInput schema helper typing * fix(cli): clarify secrets.resolve version-skew errors * refactor(secrets): return structured inactive paths from secrets.resolve * refactor(gateway): type onboarding secret writes as SecretInput * chore(protocol): regenerate swift models for secrets.resolve * feat(secrets): expand extension credential secretref support * fix(secrets): gate web-search refs by active provider * fix(onboarding): detect SecretRef credentials in extension status * fix(onboarding): allow keeping existing ref in secret prompt * fix(onboarding): resolve gateway password SecretRefs for probe and tui * fix(onboarding): honor secret-input-mode for local gateway auth * fix(acp): resolve gateway SecretInput credentials * fix(secrets): gate gateway.remote refs to remote surfaces * test(secrets): cover pattern matching and inactive array refs * docs(secrets): clarify secrets.resolve and remote active surfaces * fix(bluebubbles): keep existing SecretRef during onboarding * fix(tests): resolve CI type errors in new SecretRef coverage * fix(extensions): replace raw fetch with SSRF-guarded fetch * test(secrets): mark gateway remote targets active in runtime coverage * test(infra): normalize home-prefix expectation across platforms * fix(cli): only resolve local qr password refs in password mode * test(cli): cover local qr token mode with unresolved password ref * docs(cli): clarify local qr password ref resolution behavior * refactor(extensions): reuse sdk SecretInput helpers * fix(wizard): resolve onboarding env-template secrets before plaintext * fix(cli): surface secrets.resolve diagnostics in memory and qr * test(secrets): repair post-rebase runtime and fixtures * fix(gateway): skip remote password ref resolution when token wins * fix(secrets): treat tailscale remote gateway refs as active * fix(gateway): allow remote password fallback when token ref is unresolved * fix(gateway): ignore stale local password refs for none and trusted-proxy * fix(gateway): skip remote secret ref resolution on local call paths * test(cli): cover qr remote tailscale secret ref resolution * fix(secrets): align gateway password active-surface with auth inference * fix(cli): resolve inferred local gateway password refs in qr * fix(gateway): prefer resolvable remote password over token ref pre-resolution * test(gateway): cover none and trusted-proxy stale password refs * docs(secrets): sync qr and gateway active-surface behavior * fix: restore stability blockers from pre-release audit * Secrets: fix collector/runtime precedence contradictions * docs: align secrets and web credential docs * fix(rebase): resolve integration regressions after main rebase * fix(node-host): resolve gateway secret refs for auth * fix(secrets): harden secretinput runtime readers * gateway: skip inactive auth secretref resolution * cli: avoid gateway preflight for inactive secret refs * extensions: allow unresolved refs in onboarding status * tests: fix qr-cli module mock hoist ordering * Security: align audit checks with SecretInput resolution * Gateway: resolve local-mode remote fallback secret refs * Node host: avoid resolving inactive password secret refs * Secrets runtime: mark Slack appToken inactive for HTTP mode * secrets: keep inactive gateway remote refs non-blocking * cli: include agent memory secret targets in runtime resolution * docs(secrets): sync docs with active-surface and web search behavior * fix(secrets): keep telegram top-level token refs active for blank account tokens * fix(daemon): resolve gateway password secret refs for probe auth * fix(secrets): skip IRC NickServ ref resolution when NickServ is disabled * fix(secrets): align token inheritance and exec timeout defaults * docs(secrets): clarify active-surface notes in cli docs * cli: require secrets.resolve gateway capability * gateway: log auth secret surface diagnostics * secrets: remove dead provider resolver module * fix(secrets): restore gateway auth precedence and fallback resolution * fix(tests): align plugin runtime mock typings --------- Co-authored-by: Peter Steinberger <[email protected]> * CI: optimize Windows lane by splitting bundle and dropping duplicate lanes * docs: reorder unreleased changelog by user interest * fix(e2e): include shared tool display resource in onboard docker build * fix(ci): tighten type signatures in gateway params validation * fix(telegram): move unchanged command-sync log to verbose * test: fix strict runtime mock types in channel tests * test: load ci changed-scope script via esm import * fix(swift): align async helper callsites across iOS and macOS * refactor(macos): simplify pairing alert and host helper paths * style(swift): apply lint and format cleanup * CI: gate Windows checks by windows-relevant scope (#32456) * CI: add windows scope output for changed-scope * Test: cover windows scope gating in changed-scope * CI: gate checks-windows by windows scope * Docs: update CI windows scope and runner label * CI: move checks-windows to 32 vCPU runner * Docs: align CI windows runner with workflow * CI: allow blacksmith 32 vCPU Windows runner in actionlint * chore(gitignore): ignore android kotlin cache * fix(ci): restore scope-test require import and sync host policy * docs(changelog): add SecretRef note for #29580 * fix(venice): retry model discovery on transient fetch failures * fix(feishu): preserve block streaming text when final payload is missing (#30663) * fix(feishu): preserve block streaming text when final payload is missing When Feishu card streaming receives block payloads without matching final/partial callbacks, keep block text in stream state so onIdle close still publishes the reply instead of an empty message. Add a regression test for block-only streaming. Closes #30628 * Feishu: preserve streaming block fallback when final text is missing --------- Co-authored-by: Tak Hoffman <[email protected]> * CI: add exact-key mode for pnpm cache restore * CI: reduce pre-test Windows setup latency * CI: increase checks-windows test shards to 3 * CI: increase checks-windows test shards to 4 * docs(feishu): Feishu docs – add verificationToken and align zh-CN with EN (openclaw#31555) thanks @xbsheng Verified: - pnpm build - pnpm test:macmini - pnpm check (blocked locally by pre-existing mainline lint issue in src/scripts/ci-changed-scope.test.ts unrelated to this PR) Co-authored-by: xbsheng <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(ci): avoid shell interpolation in changed-scope git diff * test(ci): add changed-scope shell-injection regression * fix(gateway): retry exec-read live tool probe * feat(feishu): add broadcast support for multi-agent groups (#29575) * feat(feishu): add broadcast support for multi-agent group observation When multiple agents share a Feishu group chat, only the @mentioned agent receives the message. This prevents observer agents from building session memory of group activity they weren't directly addressed in. Adds broadcast support (reusing the same cfg.broadcast schema as WhatsApp) so all configured agents receive every group message in their session transcripts. Only the @mentioned agent responds on Feishu; observer agents process silently via no-op dispatchers. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): guard sequential broadcast dispatch against single-agent failure Wrap each dispatchForAgent() call in the sequential loop with try/catch so one agent's dispatch failure doesn't abort delivery to remaining agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): avoid duplicate messages in broadcast observer mode and normalize agent IDs - Skip recordPendingHistoryEntryIfEnabled for broadcast groups when not mentioned, since the message is dispatched directly to all agents. Previously the message appeared twice in the agent prompt. - Normalize agent IDs with toLowerCase() before membership checks so config casing mismatches don't silently skip valid agents. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): set WasMentioned per-agent and normalize broadcast IDs - buildCtxPayloadForAgent now takes a wasMentioned parameter so active agents get WasMentioned=true and observers get false (P1 fix) - Normalize broadcastAgents to lowercase at resolution time and lowercase activeAgentId so all comparisons and session key generation use canonical IDs regardless of config casing (P2 fix) Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): canonicalize broadcast agent IDs with normalizeAgentId * fix(feishu): match ReplyDispatcher sync return types for noop dispatcher The upstream ReplyDispatcher changed sendToolResult/sendBlockReply/ sendFinalReply to synchronous (returning boolean). Update the broadcast observer noop dispatcher to match. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): deduplicate broadcast agent IDs after normalization Config entries like "Main" and "main" collapse to the same canonical ID after normalizeAgentId but were dispatched multiple times. Use Set to deduplicate after normalization. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): honor requireMention=false when selecting broadcast responder When requireMention is false, the routed agent should be active (reply on Feishu) even without an explicit @mention. Previously activeAgentId was null whenever ctx.mentionedBot was false, so all agents got the noop dispatcher and no reply was sent — silently breaking groups that disabled mention gating. Hoist requireMention out of the if(isGroup) block so it's accessible in the dispatch code. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): cross-account broadcast dedup to prevent duplicate dispatches In multi-account Feishu setups, the same message event is delivered to every bot account in a group. Without cross-account dedup, each account independently dispatches broadcast agents, causing 2×N dispatches instead of N (where N = number of broadcast agents). Two changes: 1. requireMention=true + bot not mentioned: return early instead of falling through to broadcast. The mentioned bot's handler will dispatch for all agents. Non-mentioned handlers record to history. 2. Add cross-account broadcast dedup using a shared 'broadcast' namespace (tryRecordMessagePersistent). The first handler to reach the broadcast block claims the message; subsequent accounts skip. This handles the requireMention=false multi-account case. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): strip CommandAuthorized from broadcast observer contexts Broadcast observer agents inherited CommandAuthorized from the sender, causing slash commands (e.g. /reset) to silently execute on every observer session. Now only the active agent retains CommandAuthorized; observers have it stripped before dispatch. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): use actual mention state for broadcast WasMentioned The active broadcast agent's WasMentioned was set to true whenever requireMention=false, even when the bot was not actually @mentioned. Now uses ctx.mentionedBot && agentId === activeAgentId, consistent with the single-agent path which passes ctx.mentionedBot directly. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(feishu): skip history buffer for broadcast accounts and log parallel failures 1. In requireMention groups with broadcast, non-mentioned accounts no longer buffer pending history — the mentioned handler's broadcast dispatch already writes turns into all agent sessions. Buffering caused duplicate replay via buildPendingHistoryContextFromMap. 2. Parallel broadcast dispatch now inspects Promise.allSettled results and logs rejected entries, matching the sequential path's per-agent error logging. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Changelog: note Feishu multi-agent broadcast dispatch * Changelog: restore author credit for Feishu broadcast entry --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * chore(release): prepare 2026.3.2-beta.1 * fix(ci): complete feishu route mock typing in broadcast tests * Delete changelog/fragments directory * refactor(feishu): unify Lark SDK error handling with LarkApiError (#31450) * refactor(feishu): unify Lark SDK error handling with LarkApiError - Add LarkApiError class with code, api, and context fields for better diagnostics - Add ensureLarkSuccess helper to replace 9 duplicate error check patterns - Update tool registration layer to return structured error info (code, api, context) This improves: - Observability: errors now include API name and request context for easier debugging - Maintainability: single point of change for error handling logic - Extensibility: foundation for retry strategies, error classification, etc. Affected APIs: - wiki.space.getNode - bitable.app.get - bitable.app.create - bitable.appTableField.list - bitable.appTableField.create - bitable.appTableRecord.list - bitable.appTableRecord.get - bitable.appTableRecord.create - bitable.appTableRecord.update * Changelog: note Feishu bitable error handling unification --------- Co-authored-by: echoVic <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(gateway): skip google rate limits in live suite * CI: add toggle to skip pnpm actions cache restore * CI: shard Windows tests into sixths and skip cache restore * chore(release): update appcast for 2026.3.2-beta.1 * fix(feishu): correct invalid scope name in permission grant URL (#32509) * fix(feishu): correct invalid scope name in permission grant URL The Feishu API returns error code 99991672 with an authorization URL containing the non-existent scope `contact:contact.base:readonly` when the `contact.user.get` endpoint is called without the correct permission. The valid scope is `contact:user.base:readonly`. Add a scope correction map that replaces known incorrect scope names in the extracted grant URL before presenting it to the user/agent, so the authorization link actually works. Closes #31761 * chore(changelog): note feishu scope correction --------- Co-authored-by: SidQin-cyber <[email protected]> * docs: add dedicated pdf tool docs page * feishu, line: pass per-group systemPrompt to inbound context (#31713) * feishu: pass per-group systemPrompt to inbound context The Feishu extension schema supports systemPrompt in per-group config (channels.feishu.accounts.<id>.groups.<groupId>.systemPrompt) but the value was never forwarded to the inbound context as GroupSystemPrompt. This means per-group system prompts configured for Feishu had no effect, unlike IRC, Discord, Slack, Telegram, Matrix, and other channels that already pass this field correctly. Co-authored-by: Copilot <[email protected]> * line: pass per-group systemPrompt to inbound context Same issue as feishu: the Line config schema defines systemPrompt in per-group config but the value was never forwarded as GroupSystemPrompt in the inbound context payload. Added resolveLineGroupSystemPrompt helper that mirrors the existing resolveLineGroupConfig lookup logic (groupId > roomId > wildcard). Co-authored-by: Copilot <[email protected]> * Changelog: note Feishu and LINE group systemPrompt propagation --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * docs(changelog): remove docs-only 2026.3.2 entries * CI: speed up Windows dependency warmup * CI: make node deps install optional in setup action * CI: reduce critical path for check build and windows jobs * fix(feishu): non-blocking WS ACK and preserve full streaming card content (#29616) * fix(feishu): non-blocking ws ack and preserve streaming card full content * fix(feishu): preserve fragmented streaming text without newline artifacts --------- Co-authored-by: Tak Hoffman <[email protected]> * fix: add session-memory hook support for Feishu provider (#31437) * fix: add session-memory hook support for Feishu provider Issue #31275: Session-memory hook not triggered when using /new command in Feishu - Added command handler to Feishu provider - Integrated with OpenClaw's before_reset hook system - Ensures session memory is saved when /new or /reset commands are used * Changelog: note Feishu session-memory hook parity --------- Co-authored-by: Tak Hoffman <[email protected]> * fix(feishu): guard against false-positive @mentions in multi-app groups (#30315) * fix(feishu): guard against false-positive @mentions in multi-app groups When multiple Feishu bot apps share a group chat, Feishu's WebSocket event delivery remaps the open_id in mentions[] per-app. This causes checkBotMentioned() to return true for ALL bots when only one was actually @mentioned, making requireMention ineffective. Add a botName guard: if the mention's open_id matches this bot but the mention's display name differs from this bot's configured botName, treat it as a false positive and skip. botName is already available via account.config.botName (set during onboarding). Closes #24249 * fix(feishu): support @all mention in multi-bot groups When a user sends @all (@_all in Feishu message content), treat it as mentioning every bot so all agents respond when requireMention is true. Feishu's @all does not populate the mentions[] array, so this needs explicit content-level detection. * fix(feishu): auto-fetch bot display name from API for reliable mention matching Instead of relying on the manually configured botName (which may differ from the actual Feishu bot display name), fetch the bot's display name from the Feishu API at startup via probeFeishu(). This ensures checkBotMentioned() always compares against the correct display name, even when the config botName doesn't match (e.g. config says 'Wanda' but Feishu shows '绯红女巫'). Changes: - monitor.ts: fetchBotOpenId → fetchBotInfo (returns both openId and name) - monitor.ts: store botNames map, pass botName to handleFeishuMessage - bot.ts: accept botName from params, prefer it over config fallback * Changelog: note Feishu multi-app mention false-positive guard --------- Co-authored-by: Teague Xiao <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * CI: start push test lanes earlier and drop check gating * CI: disable flaky sticky disk mount for Windows pnpm setup * 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 <[email protected]> * 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&D). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * Changelog: note Feishu inbound mention normalization --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * fix(feishu): validate outbound renderMode routing with tests (#31562) Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> Co-authored-by: Vincent Koc <[email protected]> * 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 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> Co-authored-by: davidahmann <[email protected]> Co-authored-by: harshang03 <[email protected]> Co-authored-by: haosenwang1018 <[email protected]> Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: coygeek <[email protected]> Co-authored-by: lailoo <[email protected]> * chore: Updated Brave documentation (#26860) Merged via squash. Prepared head SHA: f8fc4bf01e0eacfb01f6ee58eea445680f7eeebd Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: Josh Lehman <[email protected]> * bug: Workaround for QMD upstream bug (#27028) Merged via squash. Prepared head SHA: 939f9f4574fcfe08762407ab9e8d6c85a77a0899 Co-authored-by: HenryLoenwind <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * feat(mattermost): add native slash command support (refresh) (#32467) Merged via squash. Prepared head SHA: 989126574ead75c0eedc185293659eb0d4fc6844 Co-authored-by: mukhtharcm <[email protected]> Co-authored-by: mukhtharcm <[email protected]> Reviewed-by: @mukhtharcm * Gateway: fix stale self version in status output (#32655) Merged via squash. Prepared head SHA: b9675d1f90ef0eabb7e68c24a72d4b2fb27def22 Co-authored-by: liuxiaopai-ai <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: altaywtf <[email protected]> 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 <[email protected]> * 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: jalehman <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: grp06 <[email protected]> Reviewed-by: @grp06 * docs(contributing): require before/after screenshots for UI PRs (#32206) Merged via squash. Prepared head SHA: d7f0914873aec1c3c64c9161771ff0bcbc457c95 Co-authored-by: hydro13 <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf * fix(discord): align DiscordAccountConfig.token type with SecretInput (#32490) Merged via squash. Prepared head SHA: 233aa032f1d894b7eb6a960247baa1336f8fbc26 Co-authored-by: scoootscooob <[email protected]> Co-authored-by: joshavant <[email protected]> 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 <[email protected]> Co-authored-by: shakkernerd <[email protected]> Reviewed-by: @shakkernerd * Agent: unify bootstrap truncation warning handling (#32769) Merged via squash. Prepared head SHA: 5d6d4ddfa620011e267d892b402751847d5ac0c3 Co-authored-by: gumadeiras <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * fix(logging ): use local timezone for console log timestamps (#25970) Merged via squash. Prepared head SHA: 30123265b7b910b9208e8c9407c30536e46eb68f Co-authored-by: openperf <[email protected]> Co-authored-by: altaywtf <[email protected]> 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 <[email protected]> Co-authored-by: grp06 <[email protected]> Reviewed-by: @grp06 * fix(ios): guard talk TTS callbacks to active utterance (#33304) Merged via squash. Prepared head SHA: dd88886e416e5e6d6c08bf92162a5ff18c1eb229 Co-authored-by: mbelinky <[email protected]> Co-authored-by: mbelinky <[email protected]> Reviewed-by: @mbelinky * fix(ios): start incremental speech at soft boundaries (#33305) Merged via squash. Prepared head SHA: d1acf723176f8e9d89f8c229610c9400ab661ec7 Co-authored-by: mbelinky <[email protected]> Co-authored-by: mbelinky <[email protected]> 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 <[email protected]> Co-authored-by: mbelinky <[email protected]> Reviewed-by: @mbelinky * fix(ios): harden watch messaging activation concurrency (#33306) Merged via squash. Prepared head SHA: d40f8c4afbd6ddf38548c9b0c6ac6ac4359f2e54 Co-authored-by: mbelinky <[email protected]> Co-authored-by: mbelinky <[email protected]> Reviewed-by: @mbelinky * docs: fix Mintlify-incompatible links in security docs (#27698) Merged via squash. Prepared head SHA: 6078cd94ba38b49d17e9680073337885c5f9e781 Co-authored-by: clawdoo <[email protected]> Co-authored-by: grp06 <[email protected]> Reviewed-by: @grp06 * fix(security): strip partial API token from status labels (#33262) Merged via squash. Prepared head SHA: 5fe81704e678dc5b18ca9416e5eb0750cfe49fb6 Co-authored-by: cu1ch3n <[email protected]> Co-authored-by: grp06 <[email protected]> Reviewed-by: @grp06 * Security audit: suggest valid gateway.nodes.denyCommands entries (#29713) Merged via squash. Prepared head SHA: db23298f9806b8de8c4b3e816f1649c18ebc0c64 Co-authored-by: liquidhorizon88-bot <[email protected]> Co-authored-by: grp06 <[email protected]> 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 <[email protected]> Co-authored-by: grp06 <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> Reviewed-by: @gumadeiras * build: fix ineffective dynamic imports with lazy boundaries (#33690) Merged via squash. Prepared head SHA: 38b3c23d6f8f2b4c8a36a88ee65b508102f1ec36 Co-authored-by: gumadeiras <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: joshavant <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: joshavant <[email protected]> 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 <[email protected]> Co-authored-by: Theo Tarr <[email protected]> * 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 <[email protected]> * 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 <[email protected]> Signed-off-by: HCL <[email protected]> * 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 <[email protected]> Co-authored-by: Glucksberg <[email protected]> Co-authored-by: Efe Büken <[email protected]> Co-authored-by: Riccardo Marino <[email protected]> Co-authored-by: HCL <[email protected]> * 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 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> Co-authored-by: altaywtf <[email protected]> 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 <[email protected]> * fix(ios): treat ended live activities as inactive * chore(changelog): add PR reference and author thanks --------- Co-authored-by: leepokai <[email protected]> * 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 <[email protected]> * fix: relay ACP sessions_spawn parent streaming (#34310) (thanks @vincentkoc) (#34310) Co-authored-by: Onur Solmaz <[email protected]> * 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: shakkernerd <[email protected]> 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 <[email protected]> Co-authored-by: shakkernerd <[email protected]> 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 <[email protected]> Co-authored-by: gumadeiras <[email protected]> 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 <[email protected]> Co-authored-by: jalehman <[email protected]> Reviewed-by: @jalehman * feat(cron): add procedural playbook memory v0 --------- Signed-off-by: HCL <[email protected]> Co-authored-by: Peter Steinberger <[email protected]> Co-authored-by: Josh Avant <[email protected]> Co-authored-by: Vincent Koc <[email protected]> Co-authored-by: Sid <[email protected]> Co-authored-by: Tak Hoffman <[email protected]> Co-authored-by: xbsheng <[email protected]> Co-authored-by: xbsheng <[email protected]> Co-authored-by: Runkun Miao <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: 青雲 <[email protected]> Co-authored-by: echoVic <[email protected]> Co-authored-by: Tian Wei <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Huaqing.Hao <[email protected]> Co-authored-by: Andy Tien <[email protected]> Co-authored-by: 挨踢小茶 <[email protected]> Co-authored-by: Teague Xiao <[email protected]> Co-authored-by: Jealous <[email protected]> Co-authored-by: dongdong <[email protected]> Co-authored-by: Viz <[email protected]> Co-authored-by: Shadow <[email protected]> Co-authored-by: bmendonca3 <[email protected]> Co-authored-by: davidahmann <[email protected]> Co-authored-by: harshang03 <[email protected]> …
Issue #31275: Session-memory hook not triggered when using /new command in Feishu
Summary
Describe the problem and fix in 2–5 bullets:
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
List user-visible changes (including defaults/config).
If none, write
None.Security Impact (required)
Yes/No)Yes/No)Yes/No)Yes/No)Yes/No)Yes, explain risk + mitigation:Repro + Verification
Environment
Steps
Expected
Actual
Evidence
Attach at least one:
Human Verification (required)
What you personally verified (not just CI), and how:
Compatibility / Migration
Yes/No)Yes/No)Yes/No)Failure Recovery (if this breaks)
Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write
None.