Fedora and some other distros install Node.js with a version suffix
(e.g., /usr/bin/node-22) and create a symlink from /usr/bin/node.
When Node resolves process.execPath, it returns the real binary path,
not the symlink, causing buildParseArgv to fail the looksLikeNode check.
This adds executable.startsWith('node-') to handle versioned binaries.
Fixes#2442
When a secondary agent's OAuth token expires and refresh fails, the agent
would error out even if the main agent had fresh, valid credentials for
the same profile.
This fix adds a fallback mechanism that:
1. Detects when OAuth refresh fails for a secondary agent (agentDir is set)
2. Checks if the main agent has fresh credentials for the same profileId
3. If so, copies those credentials to the secondary agent and uses them
4. Logs the inheritance for debugging
This prevents the situation where users have to manually copy auth-profiles.json
between agent directories when tokens expire at different times.
Fixes: Secondary agents failing with 'OAuth token refresh failed' while main
agent continues to work fine.
- Add bot.catch() to prevent unhandled rejections from middleware
- Add isRecoverableNetworkError() to retry on transient failures
- Add maxRetryTime and exponential backoff to grammY runner
- Global unhandled rejection handler now logs recoverable errors
instead of crashing (fetch failures, timeouts, connection resets)
Fixes crash loop when Telegram API is temporarily unreachable.
* fix(security): properly test Windows ACL audit for config includes
The test expected fs.config_include.perms_writable on Windows but
chmod 0o644 has no effect on Windows ACLs. Use icacls to grant
Everyone write access, which properly triggers the security check.
Also stubs execIcacls to return proper ACL output so the audit
can parse permissions without running actual icacls on the system.
Adds cleanup via try/finally to remove temp directory containing
world-writable test file.
Fixes checks-windows CI failure.
* test: isolate heartbeat runner tests from user workspace
* docs: update changelog for #2403
---------
Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
Add evictOldHistoryKeys() function that removes oldest keys when the
history map exceeds MAX_HISTORY_KEYS (1000). Called automatically in
appendHistoryEntry() to bound memory growth.
The map previously grew unbounded as users interacted with more groups
over time. Growth is O(unique groups) not O(messages), but still causes
slow memory accumulation on long-running instances.
Fixes#2384
The async setTimeout callback re-threw errors without a .catch() handler,
causing unhandled promise rejections that crashed the gateway. The error
is already logged by the heartbeat runner and a retry is scheduled, so
the re-throw served no purpose.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add isHeartbeat to AgentRunContext to track heartbeat runs
- Pass isHeartbeat flag through agent runner execution
- Suppress webchat broadcast (deltas + final) for heartbeat runs when showOk is false
- Webchat uses channels.defaults.heartbeat settings (no per-channel config)
- Default behavior: hide HEARTBEAT_OK from webchat (matches other channels)
This allows users to control whether heartbeat responses appear in
the webchat UI via channels.defaults.heartbeat.showOk (defaults to false).
This commit removes the URL balloon message handling logic from the monitor, simplifying the message processing flow. Additionally, it enhances error logging by including the account ID in the error messages for better traceability.
When users send iMessages containing URLs, BlueBubbles sends separate
webhook events for the text message and the URL balloon/link preview.
This caused Clawdbot to receive them as separate queued messages.
This fix adds inbound debouncing (following the pattern from WhatsApp/MS Teams):
- Uses the existing createInboundDebouncer utility from plugin-sdk
- Adds debounceMs config option to BlueBubblesAccountConfig (default: 500ms)
- Routes inbound messages through debouncer before processing
- Combines messages from same sender/chat within the debounce window
- Handles URLBalloonProvider messages by coalescing with preceding text
- Skips debouncing for messages with attachments or control commands
Config example:
channels.bluebubbles.debounceMs: 500 # milliseconds (0 to disable)
Fixes inbound URL message splitting issue.
When the gateway restarts, the WebSocket disconnects and any in-flight
chat.final events are lost. On reconnect, chatRunId/chatStream were
still set from the orphaned run, making the UI think a run was still
in progress and not updating properly.
Fix: Reset chatRunId, chatStream, chatStreamStartedAt, and tool stream
state in the onHello callback when the WebSocket reconnects.
Fixes issue where users had to refresh the page after gateway restart
to see completed messages.
* feat(telegram): add silent message option (disable_notification)
Add support for sending Telegram messages silently without notification
sound via the `silent` parameter on the message tool.
Changes:
- Add `silent` boolean to message tool schema
- Extract and pass `silent` through telegram plugin
- Add `disable_notification: true` to Telegram API calls
- Add `--silent` flag to CLI `message send` command
- Add unit test for silent flag
Closes#2249
AI-assisted (Claude) - fully tested with unit tests + manual Telegram testing
* feat(telegram): add silent send option (#2382) (thanks @Suksham-sharma)
---------
Co-authored-by: Pocket Clawd <pocket@Pockets-Mac-mini.local>