Skip to content

Plugins: add sessionKey to session lifecycle hooks#26394

Merged
steipete merged 2 commits intoopenclaw:mainfrom
tempeste:plugins-session-hooks-sessionkey-correlation
Mar 3, 2026
Merged

Plugins: add sessionKey to session lifecycle hooks#26394
steipete merged 2 commits intoopenclaw:mainfrom
tempeste:plugins-session-hooks-sessionkey-correlation

Conversation

@tempeste
Copy link
Contributor

@tempeste tempeste commented Feb 25, 2026

Summary

  • Problem: plugins can see sessionId in session lifecycle hooks and childSessionKey in subagent hooks, but cannot deterministically correlate them because session_start/session_end omit sessionKey.
  • Why it matters: channel/plugin integrations (including cancellation flows) need a stable sessionKey -> sessionId mapping to target internal abort paths without heuristics.
  • What changed: added optional sessionKey to session_start and session_end hook event payloads and PluginHookSessionContext, and passed it through from initSessionState.
  • What did NOT change (scope boundary): no new abort API, no behavior changes to hook ordering/execution, no subagent hook payload changes in this PR.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #

User-visible / Behavior Changes

Plugin session lifecycle hooks (session_start, session_end) now include optional sessionKey in both event payload and hook context. Existing plugins remain compatible.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: Linux (dev shell)
  • Runtime/container: Node v24.13.0, pnpm 10.23.0 (via Corepack)
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Register a plugin with session_start / session_end hooks.
  2. Trigger session initialization / replacement.
  3. Observe hook payload/context fields.

Expected

  • Hooks receive sessionId and sessionKey together for deterministic correlation.

Actual

  • Hooks now receive optional sessionKey in event + context (backward-compatible additive field).

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • pnpm vitest run src/plugins/wired-hooks-session.test.ts src/plugins/wired-hooks-subagent.test.ts
    • pnpm vitest run src/auto-reply/reply/session.test.ts
  • Edge cases checked:
    • Existing hook wiring remains compatible with additive optional fields.
  • What you did not verify:
    • End-to-end plugin cancellation flow against a live KanbanThing plugin.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this commit.
  • Files/config to restore: src/plugins/types.ts, src/auto-reply/reply/session.ts, src/plugins/wired-hooks-session.test.ts
  • Known bad symptoms reviewers should watch for: plugin tests or session hook consumers assuming exact object shape in assertions.

Risks and Mitigations

  • Risk: Some tests/plugins may perform strict deep-equality against hook payloads/contexts and need updates for the new field.
    • Mitigation: fields are optional/additive; existing runtime behavior is unchanged aside from extra metadata.

Greptile Summary

Adds optional sessionKey to session_start and session_end hook event payloads and PluginHookSessionContext, threading the existing in-scope sessionKey from initSessionState through to plugin hooks. This enables plugins to deterministically correlate sessionKey with sessionId without heuristics.

  • Added sessionKey?: string to PluginHookSessionContext, PluginHookSessionStartEvent, and PluginHookSessionEndEvent type definitions
  • Passed sessionKey in both event and context objects at the two runSessionEnd / runSessionStart call sites in initSessionState
  • Updated unit tests to include sessionKey in hook invocation and assertion payloads
  • All new fields are optional, maintaining backward compatibility with existing plugin consumers

Confidence Score: 5/5

  • This PR is safe to merge — it's a minimal, additive change with no behavior modifications.
  • The change adds optional fields to three type definitions and passes an already-in-scope variable through to hook payloads at two call sites. All fields are optional (backward compatible). The only production code change is threading an existing variable. Tests are updated and cover the new fields. No new dependencies, no behavioral changes, no security surface changes. There are no other callers of runSessionStart/runSessionEnd in production code that were missed.
  • No files require special attention.

Last reviewed commit: a6ef9c7

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

@tempeste
Copy link
Contributor Author

Related to #23525.

That PR adds sessionKey to PluginHookSessionContext for session_start/session_end, which helps immediate in-hook actions that use ctx.

This PR intentionally also adds sessionKey to the session_start/session_end event payloads so plugins that persist/queue lifecycle events (and do not retain ctx) can deterministically correlate sessionKey -> sessionId for cancellation/abort flows.

If maintainers prefer, I can narrow this PR to the event-payload delta on top of #23525.

@tempeste
Copy link
Contributor Author

@vincentkoc quick nudge on this when you have a moment, mainly because it’s in the hooks/plugin area and I’d value your read

Brief context: the PR adds deterministic sessionKey <-> sessionId correlation in hook data so plugins can reliably map external dispatches to the exact internal run/session. ctx helps for in-process handling; including it on events also keeps persisted/queued flows deterministic

Would appreciate your eyes on this so plugins can handle session lifecycles reliably end-to-end

@steipete steipete force-pushed the plugins-session-hooks-sessionkey-correlation branch from 45e9a72 to 7c559cb Compare March 3, 2026 01:44
@steipete steipete merged commit c0715db into openclaw:main Mar 3, 2026
34 of 36 checks passed
@steipete
Copy link
Contributor

steipete commented Mar 3, 2026

Landed via temp rebase onto main.

  • Gate: pnpm vitest run src/plugins/wired-hooks-session.test.ts src/auto-reply/reply/session-hooks-context.test.ts
  • Land commit: 7c559cb
  • Merge commit: c0715db

Thanks @tempeste!

dawi369 pushed a commit to dawi369/davis that referenced this pull request Mar 3, 2026
MillionthOdin16 added a commit to MillionthOdin16/openclaw that referenced this pull request Mar 3, 2026
* 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 <munem.hashmi@gmail.com>

* 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 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <vincentkoc@ieee.org>

* 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 <179671552+akramcodez@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
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 <43175972+nico-hoff@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
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 <noreply@anthropic.com>

* 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 <chenglunhu@gmail.com>

* 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 <noreply@anthropic.com>

* 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 <shakkerdroid@gmail.com>

* 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 <steipete@gmail.com>

* 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 <781889+Takhoffman@users.noreply.github.com>

* 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 <56357338+xbsheng@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* Changelog: note Feishu multi-agent broadcast dispatch

* Changelog: restore author credit for Feishu broadcast entry

---------

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

* 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 <echovic@163.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <sidqin0410@gmail.com>

* 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 <223556219+Copilot@users.noreply.github.com>

* 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 <223556219+Copilot@users.noreply.github.com>

* Changelog: note Feishu and LINE group systemPrompt propagation

---------

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

* 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 <781889+Takhoffman@users.noreply.github.com>

* 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 <781889+Takhoffman@users.noreply.github.com>

* 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 <teaguexiao@TeaguedeMac-mini.local>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <noreply@anthropic.com>

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

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

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

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

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

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

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

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

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

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

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

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

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

* Changelog: note Feishu inbound mention normalization

---------

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

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

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

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

* fix(gateway): flush throttled delta before emitChatFinal

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

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

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

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

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

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

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

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

---------

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

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

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

* ci: enable stale workflow

* fix: scope extension runtime deps to plugin manifests

* docs(changelog): reattribute duplicated PR credits

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

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

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

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

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

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

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

* fix(test): stabilize appcast version assertion

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

* test(live): harden gateway model profile probes

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

* test(e2e): isolate module mocks across harnesses

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

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

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

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

* chore: Updated Brave documentation (#26860)

Merged via squash.

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

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

fix: improve compaction summary instructions to preserve active work

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

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

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

* agents: propagate config for embedded skill loading

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

Merged via squash.

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

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

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

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

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

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

* fix(telegram): include reply hook metadata

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

* fix: guard malformed Telegram replies and pass hook accountId

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

Merged via squash.

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

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

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

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

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

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

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

* fix(telegram): harden DM draft finalization path

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

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

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

---------

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

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

Merged via squash.

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

* chore: note about pagination

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

Merged via squash.

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

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

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

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

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

Merged via squash.

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

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

Merged via squash.

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

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

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

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

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

* fix(discord): default presence online when unconfigured

* fix(discord): stop typing after silent runs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix: stabilize telegram draft stream message boundaries

* fix: suppress NO_REPLY lead-fragment leaks

* fix: keep underscore guard for non-NO_REPLY prefixes

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

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

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

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

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

* docs: document discord ignoreOtherMentions

* fix(discord): skip bot messages before debounce

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

* fix(discord): harden slash command routing

* fix(discord): reset thread sessions on archive

* fix: drop discord opus dependency

* feat(discord): add allowBots mention gating

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

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

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

* docs: fix secretref marker rendering in credential surface

* fix: harden pr review artifact validation

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

Merged via squash.

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

* Harden embedded run deadlock and timeout handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 6da593ad721a9c5670623205fd2e20efc26b4205)
(cherry picked from commit 8f9e7f97c717b31f15e4fa1663b13e27f8f1edd6)

* fix(ci): make labeler token acquisition non-blocking

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 811fc2da79a727bbd5a0580391ce39c303b61f98)
(cherry picked from commit 632a4dc2431a2fa09ca9952507249b3f342c9411)

* fix(ci): retry flaky pre-commit security hooks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(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 <chenglunhu@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Ash (Bug Lab) <ash@openclaw-lab>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Gu XiaoBo <guxiaobo@example.com>
Co-authored-by: Brian Mendonca <bmendonca3@gatech.edu>
Co-authored-by: bmendonca3 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: SciFantastic <scifan@percip.io>
Co-authored-by: David Rudduck <47308254+davidrudduck@users.noreply.github.com>
Co-authored-by: Austin Eral <austin@austineral.com>
Co-authored-by: Sk Akram <skcodewizard786@gmail.com>
Co-authored-by: akramcodez <179671552+akramcodez@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: tempeste <tempeste@users.noreply.github.com>
Co-authored-by: nico-hoff <43175972+nico-hoff@users.noreply.github.com>
Co-authored-by: romeodiaz <romeo@justdothings.dev>
Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jason Hargrove <285708+jasonhargrove@users.noreply.github.com>
Co-authored-by: HCL <chenglunhu@gmail.com>
Co-authored-by: 倪汉杰0668001185 <ni.hanjie@xydigit.com>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
Co-authored-by: 苏敏童0668001043 <su.mintong@xydigit.com>
Co-authored-by: john <john.j@min123.net>
Co-authored-by: AaronWander <siralonne@163.com>
Co-authored-by: riftzen-bit <binb53339@gmail.com>
Co-authored-by: AI南柯(KingMo) <haotian2546@163.com>
Co-authored-by: Ayaan Zaidi <zaidi@uplause.io>
Co-authored-by: Josh Lehman <josh@martian.engineering>
Co-authored-by: Shakker <shakkerdroid@gmail.com>
Co-authored-by: Josh Avant <830519+joshavant@users.noreply.github.com>
Co-authored-by: Sid <sidqin0410@gmail.com>
Co-authored-by: xbsheng <xxbsheng@gmail.com>
Co-authored-by: xbsheng <56357338+xbsheng@users.noreply.github.com>
Co-authored-by: Runkun Miao <miaorunkun@gmail.com>
Co-authored-by: 青雲 <137844255@qq.com>
Co-authored-by: echoVic <echovic@163.com>
Co-authored-by: Tian Wei <12080578+whiskyboy@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Huaqing.Hao <534390598@qq.com>
Co-authored-by: Andy Tien <35169750+Linux2010@users.noreply.github.com>
Co-authored-by: 挨踢小茶 <xiaopeiqing@gmail.com>
Co-authored-by: Teague Xiao <teaguexiao@TeaguedeMac-mini.local>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: dongdong <42494191+arkyu2077@users.noreply.github.com>
Co-authored-by: Viz <visionik@pobox.com>
Co-authored-by: Shadow <hi@shadowing.dev>
Co-authored-by: davidahmann <46606159+davidahmann@users.noreply.github.com>
Co-authored-by: harshang03 <58983401+harshang03@users.noreply.github.com>
Co-authored-by: haosenwang1018 <167664334+haosenwang1018@users.noreply.github.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: coygeek <65363919+coygeek@users.noreply.github.com>
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: Henry Loenwind <henry@loenwind.info>
Co-authored-by: HenryLoenwind <1485873+HenryLoenwind@users.noreply.github.com>
Co-authored-by: JT <56984887+joetomasone@users.noreply.github.com>
Co-authored-by: Eugene <eugene@eagerdesigns.com.au>
Co-authored-by: sircrumpet <4436535+sircrumpet@users.noreply.github.com>
Co-authored-by: Muhammed Mukhthar CM <56378562+mukhtharcm@users.noreply.github.com>
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: Nimrod Gutman <nimrod.g@singular.net>
Co-authored-by: KimGLee <05_bolster_inkling@icloud.com>
Co-authored-by: Altay <altay@hey.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Co-authored-by: OpenCils <114985039+OpenCils@users.noreply.github.com>
Co-authored-by: chengzhichao-xydt <cheng.zhichao@xydigit.com>
Co-authored-by: chengzhichao-xydt <264300353+chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: Rodrigo Uroz <rodrigo.uroz@classdojo.com>
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: Shadow <shadow@openclaw.ai>
Co-authored-by: Mylszd <23611557+Mylszd@users.noreply.github.com>
Co-authored-by: Jason L. West, Sr. <jlwestsr@gmail.com>
Co-authored-by: jlwestsr <52389+jlwestsr@users.noreply.github.com>
Co-authored-by: Mariano <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: dorukardahan <35905596+dorukardahan@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Co-authored-by: Robin Waslander <r.waslander@gmail.com>
Co-authored-by: hydro13 <6640526+hydro13@users.noreply.github.com>
Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
MillionthOdin16 added a commit to MillionthOdin16/openclaw that referenced this pull request Mar 3, 2026
* 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 <munem.hashmi@gmail.com>

* 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 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <vincentkoc@ieee.org>

* 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 <179671552+akramcodez@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
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 <43175972+nico-hoff@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
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 <noreply@anthropic.com>

* 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 <chenglunhu@gmail.com>

* 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 <noreply@anthropic.com>

* 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 <shakkerdroid@gmail.com>

* 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 <steipete@gmail.com>

* 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 <781889+Takhoffman@users.noreply.github.com>

* 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 <56357338+xbsheng@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* Changelog: note Feishu multi-agent broadcast dispatch

* Changelog: restore author credit for Feishu broadcast entry

---------

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

* 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 <echovic@163.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <sidqin0410@gmail.com>

* 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 <223556219+Copilot@users.noreply.github.com>

* 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 <223556219+Copilot@users.noreply.github.com>

* Changelog: note Feishu and LINE group systemPrompt propagation

---------

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

* 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 <781889+Takhoffman@users.noreply.github.com>

* 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 <781889+Takhoffman@users.noreply.github.com>

* 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 <teaguexiao@TeaguedeMac-mini.local>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* 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 <noreply@anthropic.com>

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

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

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

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

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

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

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

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

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

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

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

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

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

* Changelog: note Feishu inbound mention normalization

---------

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

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

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

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

* fix(gateway): flush throttled delta before emitChatFinal

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

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

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

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

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

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

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

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

---------

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

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

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

* ci: enable stale workflow

* fix: scope extension runtime deps to plugin manifests

* docs(changelog): reattribute duplicated PR credits

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

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

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

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

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

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

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

* fix(test): stabilize appcast version assertion

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

* test(live): harden gateway model profile probes

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

* test(e2e): isolate module mocks across harnesses

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

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

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

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

* chore: Updated Brave documentation (#26860)

Merged via squash.

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

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

fix: improve compaction summary instructions to preserve active work

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

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

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

* agents: propagate config for embedded skill loading

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

Merged via squash.

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

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

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

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

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

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

* fix(telegram): include reply hook metadata

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

* fix: guard malformed Telegram replies and pass hook accountId

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

Merged via squash.

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

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

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

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

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

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

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

* fix(telegram): harden DM draft finalization path

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

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

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

---------

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

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

Merged via squash.

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

* chore: note about pagination

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

Merged via squash.

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

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

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

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

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

Merged via squash.

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

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

Merged via squash.

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

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

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

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

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

* fix(discord): default presence online when unconfigured

* fix(discord): stop typing after silent runs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix: stabilize telegram draft stream message boundaries

* fix: suppress NO_REPLY lead-fragment leaks

* fix: keep underscore guard for non-NO_REPLY prefixes

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

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

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

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

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

* docs: document discord ignoreOtherMentions

* fix(discord): skip bot messages before debounce

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

* fix(discord): harden slash command routing

* fix(discord): reset thread sessions on archive

* fix: drop discord opus dependency

* feat(discord): add allowBots mention gating

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

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

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

* docs: fix secretref marker rendering in credential surface

* fix: harden pr review artifact validation

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

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

Merged via squash.

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

* Harden embedded run deadlock and timeout handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 6da593ad721a9c5670623205fd2e20efc26b4205)
(cherry picked from commit 8f9e7f97c717b31f15e4fa1663b13e27f8f1edd6)

* fix(ci): make labeler token acquisition non-blocking

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 811fc2da79a727bbd5a0580391ce39c303b61f98)
(cherry picked from commit 632a4dc2431a2fa09ca9952507249b3f342c9411)

* fix(ci): retry flaky pre-commit security hooks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit d55fa6f00b5313b99704a46a3e990a5365ba112d)

---------

Signed-off-by: HCL <chenglunhu@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Ash (Bug Lab) <ash@openclaw-lab>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Gu XiaoBo <guxiaobo@example.com>
Co-authored-by: Brian Mendonca <bmendonca3@gatech.edu>
Co-authored-by: bmendonca3 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Co-authored-by: SciFantastic <scifan@percip.io>
Co-authored-by: David Rudduck <47308254+davidrudduck@users.noreply.github.com>
Co-authored-by: Austin Eral <austin@austineral.com>
Co-authored-by: Sk Akram <skcodewizard786@gmail.com>
Co-authored-by: akramcodez <179671552+akramcodez@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: tempeste <tempeste@users.noreply.github.com>
Co-authored-by: nico-hoff <43175972+nico-hoff@users.noreply.github.com>
Co-authored-by: romeodiaz <romeo@justdothings.dev>
Co-authored-by: scoootscooob <zhentongfan@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jason Hargrove <285708+jasonhargrove@users.noreply.github.com>
Co-authored-by: HCL <chenglunhu@gmail.com>
Co-authored-by: 倪汉杰0668001185 <ni.hanjie@xydigit.com>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
Co-authored-by: 苏敏童0668001043 <su.mintong@xydigit.com>
Co-authored-by: john <john.j@min123.net>
Co-authored-by: AaronWander <siralonne@163.com>
Co-authored-by: riftzen-bit <binb53339@gmail.com>
Co-authored-by: AI南柯(KingMo) <haotian2546@163.com>
Co-authored-by: Ayaan Zaidi <zaidi@uplause.io>
Co-authored-by: Josh Lehman <josh@martian.engineering>
Co-authored-by: Shakker <shakkerdroid@gmail.com>
Co-authored-by: Josh Avant <830519+joshavant@users.noreply.github.com>
Co-authored-by: Sid <sidqin0410@gmail.com>
Co-authored-by: xbsheng <xxbsheng@gmail.com>
Co-authored-by: xbsheng <56357338+xbsheng@users.noreply.github.com>
Co-authored-by: Runkun Miao <miaorunkun@gmail.com>
Co-authored-by: 青雲 <137844255@qq.com>
Co-authored-by: echoVic <echovic@163.com>
Co-authored-by: Tian Wei <12080578+whiskyboy@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Huaqing.Hao <534390598@qq.com>
Co-authored-by: Andy Tien <35169750+Linux2010@users.noreply.github.com>
Co-authored-by: 挨踢小茶 <xiaopeiqing@gmail.com>
Co-authored-by: Teague Xiao <teaguexiao@TeaguedeMac-mini.local>
Co-authored-by: Jealous <CooLanfei@163.com>
Co-authored-by: dongdong <42494191+arkyu2077@users.noreply.github.com>
Co-authored-by: Viz <visionik@pobox.com>
Co-authored-by: Shadow <hi@shadowing.dev>
Co-authored-by: davidahmann <46606159+davidahmann@users.noreply.github.com>
Co-authored-by: harshang03 <58983401+harshang03@users.noreply.github.com>
Co-authored-by: haosenwang1018 <167664334+haosenwang1018@users.noreply.github.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: coygeek <65363919+coygeek@users.noreply.github.com>
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: Henry Loenwind <henry@loenwind.info>
Co-authored-by: HenryLoenwind <1485873+HenryLoenwind@users.noreply.github.com>
Co-authored-by: JT <56984887+joetomasone@users.noreply.github.com>
Co-authored-by: Eugene <eugene@eagerdesigns.com.au>
Co-authored-by: sircrumpet <4436535+sircrumpet@users.noreply.github.com>
Co-authored-by: Muhammed Mukhthar CM <56378562+mukhtharcm@users.noreply.github.com>
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: Nimrod Gutman <nimrod.g@singular.net>
Co-authored-by: KimGLee <05_bolster_inkling@icloud.com>
Co-authored-by: Altay <altay@hey.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Co-authored-by: OpenCils <114985039+OpenCils@users.noreply.github.com>
Co-authored-by: chengzhichao-xydt <cheng.zhichao@xydigit.com>
Co-authored-by: chengzhichao-xydt <264300353+chengzhichao-xydt@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: Rodrigo Uroz <rodrigo.uroz@classdojo.com>
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: Shadow <shadow@openclaw.ai>
Co-authored-by: Mylszd <23611557+Mylszd@users.noreply.github.com>
Co-authored-by: Jason L. West, Sr. <jlwestsr@gmail.com>
Co-authored-by: jlwestsr <52389+jlwestsr@users.noreply.github.com>
Co-authored-by: Mariano <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: dorukardahan <35905596+dorukardahan@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Co-authored-by: Robin Waslander <r.waslander@gmail.com>
Co-authored-by: hydro13 <6640526+hydro13@users.noreply.github.com>
Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Co-authored-by: wangchunyue <80630709+openperf@users.noreply.github.com>
Co-authored-by: 13otKmdr <154699144+13otKmdr@users.noreply.github.com>
OWALabuy pushed a commit to kcinzgg/openclaw that referenced this pull request Mar 4, 2026
zeroaltitude added a commit to zeroaltitude/openclaw that referenced this pull request Mar 4, 2026
…gent context

Enrich PluginHookAgentContext with security-relevant sender identity
fields that enable plugins to make trust decisions based on who
triggered an agent run:

- sourceProvider: original message source before channel routing
- senderId: platform-specific sender ID (Discord, Slack, etc.)
- senderName: sender's display name
- senderIsOwner: whether sender is a configured owner
- groupId: group/channel ID for group chats (null for DMs)
- spawnedBy: parent session key for sub-agent sessions

Also adds sessionKey to PluginHookMessageContext for message hooks.

Consolidates inline hook context objects in attempt.ts to use a
single hookCtx variable, ensuring all hook call sites receive the
full identity context consistently.

These fields are consumed by security plugins like openclaw-provenance
for taint classification and trust-level assignment. Follows the
pattern established in openclaw#28623 (trigger/channelId) and openclaw#26394
(sessionKey for session hooks).

[claude, human developer oversight]
AytuncYildizli pushed a commit to AytuncYildizli/openclaw that referenced this pull request Mar 4, 2026
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
zeroaltitude added a commit to zeroaltitude/openclaw that referenced this pull request Mar 6, 2026
…gent context

Enrich PluginHookAgentContext with security-relevant sender identity
fields that enable plugins to make trust decisions based on who
triggered an agent run:

- sourceProvider: original message source before channel routing
- senderId: platform-specific sender ID (Discord, Slack, etc.)
- senderName: sender's display name
- senderIsOwner: whether sender is a configured owner
- groupId: group/channel ID for group chats (null for DMs)
- spawnedBy: parent session key for sub-agent sessions

Also adds sessionKey to PluginHookMessageContext for message hooks.

Consolidates inline hook context objects in attempt.ts to use a
single hookCtx variable, ensuring all hook call sites receive the
full identity context consistently.

These fields are consumed by security plugins like openclaw-provenance
for taint classification and trust-level assignment. Follows the
pattern established in openclaw#28623 (trigger/channelId) and openclaw#26394
(sessionKey for session hooks).

[claude, human developer oversight]
zeroaltitude added a commit to zeroaltitude/openclaw that referenced this pull request Mar 6, 2026
…gent context

Enrich PluginHookAgentContext with security-relevant sender identity
fields that enable plugins to make trust decisions based on who
triggered an agent run:

- sourceProvider: original message source before channel routing
- senderId: platform-specific sender ID (Discord, Slack, etc.)
- senderName: sender's display name
- senderIsOwner: whether sender is a configured owner
- groupId: group/channel ID for group chats (null for DMs)
- spawnedBy: parent session key for sub-agent sessions

Also adds sessionKey to PluginHookMessageContext for message hooks.

Consolidates inline hook context objects in attempt.ts to use a
single hookCtx variable, ensuring all hook call sites receive the
full identity context consistently.

These fields are consumed by security plugins like openclaw-provenance
for taint classification and trust-level assignment. Follows the
pattern established in openclaw#28623 (trigger/channelId) and openclaw#26394
(sessionKey for session hooks).

[claude, human developer oversight]
zeroaltitude added a commit to zeroaltitude/openclaw that referenced this pull request Mar 7, 2026
…gent context

Enrich PluginHookAgentContext with security-relevant sender identity
fields that enable plugins to make trust decisions based on who
triggered an agent run:

- sourceProvider: original message source before channel routing
- senderId: platform-specific sender ID (Discord, Slack, etc.)
- senderName: sender's display name
- senderIsOwner: whether sender is a configured owner
- groupId: group/channel ID for group chats (null for DMs)
- spawnedBy: parent session key for sub-agent sessions

Also adds sessionKey to PluginHookMessageContext for message hooks.

Consolidates inline hook context objects in attempt.ts to use a
single hookCtx variable, ensuring all hook call sites receive the
full identity context consistently.

These fields are consumed by security plugins like openclaw-provenance
for taint classification and trust-level assignment. Follows the
pattern established in openclaw#28623 (trigger/channelId) and openclaw#26394
(sessionKey for session hooks).

[claude, human developer oversight]
zeroaltitude added a commit to zeroaltitude/openclaw that referenced this pull request Mar 7, 2026
…gent context

Enrich PluginHookAgentContext with security-relevant sender identity
fields that enable plugins to make trust decisions based on who
triggered an agent run:

- sourceProvider: original message source before channel routing
- senderId: platform-specific sender ID (Discord, Slack, etc.)
- senderName: sender's display name
- senderIsOwner: whether sender is a configured owner
- groupId: group/channel ID for group chats (null for DMs)
- spawnedBy: parent session key for sub-agent sessions

Also adds sessionKey to PluginHookMessageContext for message hooks.

Consolidates inline hook context objects in attempt.ts to use a
single hookCtx variable, ensuring all hook call sites receive the
full identity context consistently.

These fields are consumed by security plugins like openclaw-provenance
for taint classification and trust-level assignment. Follows the
pattern established in openclaw#28623 (trigger/channelId) and openclaw#26394
(sessionKey for session hooks).

[claude, human developer oversight]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants