- Flush inbound debouncers BEFORE markGatewayDraining() so flushed
messages can still enqueue into the command queue (CWE-672)
- Reorder restart drain: flush debouncers -> active tasks -> followup
queues (followups need active turns to finish before they can drain)
- Always drain followup queues regardless of flushed debouncer count
- Only deregister debouncer handles after all buffers confirmed drained;
keep partially-flushed handles for subsequent sweeps
- Wrap flushAll with deadline-based timeout (Promise.race) to prevent
hung provider calls from blocking restart indefinitely
- Unregister MSTeams debouncer on startup failure (EADDRINUSE etc)
- Update test expectations for new drain ordering
* feat(telegram): support custom apiRoot for alternative API endpoints
Add `apiRoot` config option to allow users to specify custom Telegram Bot
API endpoints (e.g., self-hosted Bot API servers). Threads the configured
base URL through all Telegram API call sites: bot creation, send, probe,
audit, media download, and api-fetch. Extends SSRF policy to dynamically
trust custom apiRoot hostname for media downloads.
Closes#28535
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(telegram): thread apiRoot through allowFrom lookups
* fix(telegram): honor lookup transport and local file paths
* refactor(telegram): unify username lookup plumbing
* fix(telegram): restore doctor lookup imports
* fix: document Telegram apiRoot support (#48842) (thanks @Cypherm)
---------
Co-authored-by: Cypherm <28184436+Cypherm@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
- Added a test to ensure no warnings for legacy Brave config when bundled web search allowlist compatibility is applied.
- Updated validation logic to incorporate compatibility configuration for bundled web search plugins.
- Refactored the ensureRegistry function to utilize the new compatibility handling.
Telegram, Feishu, and MSTeams channel monitors created inbound debouncers
without calling unregister() during teardown. On reconnect a new debouncer
was registered while the old one stayed in the global INBOUND_DEBOUNCERS
map, accumulating stale entries that increased restart latency and memory.
- Telegram: registerTelegramHandlers now returns unregisterDebouncer;
called in bot.stop override
- Feishu: registerEventHandlers now returns unregisterDebouncer;
monitorSingleAccount wraps transport in try/finally
- MSTeams: createMSTeamsMessageHandler returns { handleTeamsMessage,
unregisterDebouncer }; threaded through registerMSTeamsHandlers and
called in monitor shutdown
Safety net: flushAllInboundDebouncers auto-evicts debouncers idle >5 min
so orphaned entries from channels that forget unregister() are cleaned up.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
flushBuffer/flushKey now return whether messages were actually flushed,
so flushAll only increments flushedBufferCount for non-empty buffers.
Prevents idle registered debouncers from triggering unnecessary followup
queue drain waits during SIGUSR1 restart. Also wraps per-key flush in
try/catch so one onError throw cannot strand later buffered messages.
When config.patch triggers a SIGUSR1 restart, two in-memory message
buffers were silently wiped:
1. Per-channel inbound debounce buffers (closure-local Map + setTimeout)
2. Followup queues (global Map of pending session messages)
This caused inbound messages received during the debounce window to be
permanently lost on config-triggered gateway restarts.
Fix:
- Add a global registry of inbound debouncers so they can be flushed
collectively during restart. Each createInboundDebouncer() call now
auto-registers in a shared Symbol.for() map, with a new flushAll()
method that immediately processes all buffered items.
- Add flushAllInboundDebouncers() which iterates the global registry
and forces all debounce timers to fire immediately.
- Add waitForFollowupQueueDrain() which polls the FOLLOWUP_QUEUES map
until all queues finish processing (or timeout).
- Hook both into the SIGUSR1 restart flow in run-loop.ts: before
markGatewayDraining(), flush all debouncers first (pushing buffered
messages into the followup queues), then wait up to 5s for the
followup drain loops to process them.
The ordering is critical: flush debouncers → wait for followup drain →
then mark draining. This ensures messages that were mid-debounce get
delivered to sessions before the gateway reinitializes.
Tests:
- flushAllInboundDebouncers: flushes multiple registered debouncers,
returns count, deregisters after flush
- createInboundDebouncer.flushAll: flushes all keys in a single debouncer
- waitForFollowupQueueDrain: immediate return when empty, waits for
drain, returns not-drained on timeout, counts draining queues
- run-loop: SIGUSR1 calls flush before markGatewayDraining, skips
followup wait when no debouncers had buffered messages, logs warning
on followup drain timeout
* test: align extension runtime mocks with plugin-sdk
Update stale extension tests to mock the plugin-sdk runtime barrels that production code now imports, and harden the Signal tool-result harness around system-event assertions so the channels lane matches current extension boundaries.
Regeneration-Prompt: |
Verify the failing channels-lane tests against current origin/main in an isolated worktree before changing anything. If the failures reproduce on main, keep the fix test-only unless production behavior is clearly wrong. Recent extension refactors moved Telegram, WhatsApp, and Signal code onto plugin-sdk runtime barrels, so update stale tests that still mock old core module paths to intercept the seams production code now uses. For Signal reaction notifications, avoid brittle assertions that depend on shared queued system-event state when a direct harness spy on enqueue behavior is sufficient. Preserve scope: only touch the failing tests and their local harness, then rerun the reproduced targeted tests plus the full channels lane and repo check gate.
* test: fix extension test drift on main
* fix: lazy-load bundled web search plugin registry
* test: make matrix sweeper failure injection portable
* fix: split heavy matrix runtime-api seams
* fix: simplify bundled web search id lookup
* test: tolerate windows env key casing
Reuse pi-ai's Anthropic client injection seam for streaming, and add
the OpenClaw-side provider discovery, auth, model catalog, and tests
needed to expose anthropic-vertex cleanly.
Signed-off-by: sallyom <somalley@redhat.com>