Commit Graph

395 Commits

Author SHA1 Message Date
Claw
e65b649993 fix(discord): ensure autoThread replies route to existing threads
Fixes #8278

When autoThread is enabled and a thread already exists (user continues
conversation in thread), replies were sometimes routing to the root
channel instead of the thread. This happened because the reply delivery
plan only explicitly set the thread target when a NEW thread was created
(createdThreadId), but not when the message was in an existing thread.

The fix adds a fallback case: when threadChannel is set (we're in an
existing thread) but no new thread was created, explicitly route to
the thread's channel ID. This ensures all thread replies go to the
correct destination.
2026-02-13 13:04:55 -06:00
Ramin Shirali Hossein Zade
1af0edf7ff fix: ensure exec approval is registered before returning (#2402) (#3357)
* feat(gateway): add register and awaitDecision methods to ExecApprovalManager

Separates registration (synchronous) from waiting (async) to allow callers
to confirm registration before the decision is made. Adds grace period for
resolved entries to prevent race conditions.

* feat(gateway): add two-phase response and waitDecision handler for exec approvals

Send immediate 'accepted' response after registration so callers can confirm
the approval ID is valid. Add exec.approval.waitDecision endpoint to wait for
decision on already-registered approvals.

* fix(exec): await approval registration before returning approval-pending

Ensures the approval ID is registered in the gateway before the tool returns.
Uses exec.approval.request with expectFinal:false for registration, then
fire-and-forget exec.approval.waitDecision for the decision phase.

Fixes #2402

* test(gateway): update exec-approval test for two-phase response

Add assertion for immediate 'accepted' response before final decision.

* test(exec): update approval-id test mocks for new two-phase flow

Mock both exec.approval.request (registration) and exec.approval.waitDecision
(decision) calls to match the new internal implementation.

* fix(lint): add cause to errors, use generics instead of type assertions

* fix(exec-approval): guard register() against duplicate IDs

* fix: remove unused timeoutMs param, guard register() against duplicates

* fix(exec-approval): throw on duplicate ID, capture entry in closure

* fix: return error on timeout, remove stale test mock branch

* fix: wrap register() in try/catch, make timeout handling consistent

* fix: update snapshot on timeout, make two-phase response opt-in

* fix: extend grace period to 15s, return 'expired' status

* fix: prevent double-resolve after timeout

* fix: make register() idempotent, capture snapshot before await

* fix(gateway): complete two-phase exec approval wiring

* fix: finalize exec approval race fix (openclaw#3357) thanks @ramin-shirali

* fix(protocol): regenerate exec approval request models (openclaw#3357) thanks @ramin-shirali

* fix(test): remove unused callCount in discord threading test

---------

Co-authored-by: rshirali <rshirali@rshirali-haga.local>
Co-authored-by: rshirali <rshirali@rshirali-haga-1.home>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-13 19:57:02 +01:00
Shadow
c87e481ec9 Discord: fix voice duration error handling 2026-02-13 12:44:14 -06:00
Shadow
1c9c01ff49 Discord: refine voice message handling 2026-02-13 12:44:14 -06:00
nyanjou
76ab377a19 style: use bit shift operators for Discord message flags 2026-02-13 12:44:14 -06:00
nyanjou
b4359c84f7 feat(discord): add silent support for voice messages
- Add silent flag to sendDiscordVoiceMessage
- Combines VOICE_MESSAGE (8192) + SUPPRESS_NOTIFICATIONS (4096) flags
- Pass silent through VoiceMessageOpts and action handlers
2026-02-13 12:44:14 -06:00
nyanjou
77df8b1104 feat(discord): add silent message support (SUPPRESS_NOTIFICATIONS flag)
- Add silent option to message tool for Discord
- Passes SUPPRESS_NOTIFICATIONS flag (4096) to Discord API
- Threads silent param through entire outbound chain:
  - message-action-runner.ts
  - outbound-send-service.ts
  - message.ts
  - deliver.ts
  - discord outbound adapter
  - send.outbound.ts
  - send.shared.ts

Usage: message tool with silent=true suppresses push/desktop notifications
2026-02-13 12:44:14 -06:00
nyanjou
b9da2c4679 fix: address code review feedback
- Remove unused ffmpeg astats command from generateWaveform()
- Use crypto.randomUUID() for temp file names to prevent collision
- Wrap upload URL request in retry runner for consistency
- Add validation: reject content with asVoice, require local file path
- Add clarifying comments for CDN upload behavior
2026-02-13 12:44:14 -06:00
nyanjou
36525a974e fix(discord): use fetch with proper headers for voice message upload
The @buape/carbon RequestClient wasn't setting Content-Type: application/json
for the attachments endpoint request. Use native fetch with explicit headers
for the upload URL request.

Also pass token through to sendDiscordVoiceMessage for authorization.
2026-02-13 12:44:14 -06:00
nyanjou
a09e4fac3f feat(discord): add voice message support
Adds support for sending Discord voice messages via the message tool
with asVoice: true parameter.

Voice messages require:
- OGG/Opus format (auto-converted if needed via ffmpeg)
- Waveform data (generated from audio samples)
- Duration in seconds
- Message flag 8192 (IS_VOICE_MESSAGE)

Implementation:
- New voice-message.ts with audio processing utilities
- getAudioDuration() using ffprobe
- generateWaveform() samples audio and creates base64 waveform
- ensureOggOpus() converts audio to required format
- sendDiscordVoiceMessage() handles 3-step Discord upload process

Usage:
message(action='send', channel='discord', target='...',
        path='/path/to/audio.mp3', asVoice=true)

Note: Voice messages cannot include text content (Discord limitation)
2026-02-13 12:44:14 -06:00
Hunter
f7e2b8ff5f fix(discord): autoThread race condition when multiple agents mentioned
When multiple agents with autoThread:true are @mentioned in the same
message, only the first agent successfully creates a thread. Subsequent
agents fail because Discord only allows one thread per message.

Previously, the failure was silently caught and the agent would fall
back to replying in the parent channel.

Now, when thread creation fails, the code re-fetches the message and
checks for an existing thread (created by another agent). If found,
the agent replies in that thread instead of falling back.

Fixes #7508
2026-02-13 12:39:11 -06:00
Clawdbot
42bfcd9c30 fix(discord): handle missing guild/channel data in link resolution
Add null checks for guild.id and guild.name when resolving Discord
entities. This prevents TypeError when processing invite links for
servers/channels the bot doesn't have cached.

Fixes #6606
2026-02-13 12:35:18 -06:00
headswim
f4e295a63b Discord: fix bare guild ID misrouted as channel ID in parser
The channel allowlist parser matches bare numeric strings as channel IDs
before checking for guild IDs, causing guild snowflakes to hit Discord's
/channels/ endpoint (404). Prefix guild-only entries with 'guild:' so the
parser routes them to the correct guild resolution path.

Fixes both the monitor provider and onboarding wizard call sites.
Adds regression tests.
2026-02-13 11:03:10 -06:00
Shadow
5325d2ca51 Discord: gate guild prefix to numeric keys 2026-02-13 10:57:29 -06:00
Peter Steinberger
9131b22a28 test: migrate suites to e2e coverage layout 2026-02-13 14:28:22 +00:00
Shadow
34c304727b Discord: honor Administrator in permission checks 2026-02-12 19:53:22 -06:00
Shadow
22fe30c1df fix: add discord role allowlists (#10650) (thanks @Minidoracat) 2026-02-12 19:52:24 -06:00
Minidoracat
e084f07420 fix: add missing role-based type definitions for RBAC routing 2026-02-12 19:52:24 -06:00
Minidoracat
ad508c8c89 fix: use member.roles as string[] per Discord API types 2026-02-12 19:52:24 -06:00
Minidoracat
334a291fb7 Discord: pass member role IDs to agent route resolution 2026-02-12 19:52:24 -06:00
Minidoracat
4c0ce46ac3 Discord: implement role allowlist with OR logic in preflight 2026-02-12 19:52:24 -06:00
CHISEN Kaoru
e25ae55879 fix(discord): replyToMode first behaviour 2026-02-12 18:50:36 -06:00
CHISEN Kaoru
4b3c9c9c5a fix(discord): respect replyToMode in thread channel 2026-02-12 18:50:36 -06:00
Shadow
fb8e6156ec fix: handle discord dm reaction allowlist 2026-02-12 16:47:39 -06:00
Marcus Castro
f8c7ae9b5e fix: use canonical 'direct' instead of 'dm' for DM peer kind (fixes TS2322) 2026-02-12 16:47:39 -06:00
Marcus Castro
ea3fb9570c fix: use proper LoadedConfig type in test mock 2026-02-12 16:47:39 -06:00
Marcus Castro
888f7dbbd8 fix: process Discord DM reactions instead of silently dropping them 2026-02-12 16:47:39 -06:00
Shadow
61d57be4c2 Discord: preserve media caption whitespace 2026-02-12 16:40:08 -06:00
Web Vijayi
4d0443391c fix: use iterator.done check for LRU eviction
Fixes edge case where empty string key would stop eviction early
2026-02-12 16:31:36 -06:00
Web Vijayi
5882cf2f5d fix(discord): add TTL and LRU eviction to thread starter cache
Fixes #5260

The DISCORD_THREAD_STARTER_CACHE Map was growing unbounded during
long-running gateway sessions, causing memory exhaustion.

This fix adds:
- 5-minute TTL expiry (thread starters rarely change)
- Max 500 entries with LRU eviction
- Same caching pattern used by Slack's thread resolver

The implementation mirrors src/slack/monitor/thread-resolution.ts
which already handles this correctly.
2026-02-12 16:31:36 -06:00
Shadow
149db5b2c2 Discord: handle thread edit params 2026-02-12 16:31:06 -06:00
Jake
a2ddcdadeb fix: fix: transcribe audio before mention check in groups with requireMention (openclaw#9973) thanks @mcinteerj
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: mcinteerj <3613653+mcinteerj@users.noreply.github.com>
2026-02-12 09:58:01 -06:00
Claude Code
a67752e6be fix(discord): use partial mock for @buape/carbon in slash test
Replace the full manual mock with importOriginal spread so new SDK
exports are available automatically. Only ChannelType, MessageType,
and Client are overridden — the rest come from the real module.

Prevents CI breakage when @buape/carbon adds exports (e.g. the
recent StringSelectMenu failure that blocked unrelated PRs).

Closes #13244
2026-02-11 13:10:37 -06:00
Shadow
e95f41b5df Discord: honor explicit thread type 2026-02-11 11:23:19 -06:00
Rain
9e92fc8fa1 fix(discord): default standalone threads to public type (#14147)
When creating a Discord thread without a messageId (standalone thread),
the Discord API defaults to type 12 (private). Most users expect public.

- Default standalone non-forum threads to ChannelType.PublicThread (11)
- Add optional type field to DiscordThreadCreate for explicit control

Closes #14147
2026-02-11 11:23:19 -06:00
Rain
42f7538320 fix(discord): default standalone threads to public type (#14147)
When creating a Discord thread without a messageId, the API defaults
to type 12 (private thread) which is unexpected. This adds an explicit
type: PublicThread (11) for standalone thread creation in non-forum
channels, matching user expectations.

Closes #14147
2026-02-11 11:23:19 -06:00
Vignesh
ef4a0e92b7 fix(memory/qmd): scope query to managed collections (#11645) 2026-02-09 23:35:27 -08:00
quotentiroler
40919b1fc8 fix(test): add StringSelectMenu to @buape/carbon mock 2026-02-09 23:11:53 -08:00
Peter Steinberger
53273b490b fix(auto-reply): prevent sender spoofing in group prompts 2026-02-10 00:44:38 -06:00
Shadow
8ff1618bfc Discord: add exec approval cleanup option (#13205) 2026-02-10 00:39:42 -06:00
Shadow
4537ebc43a fix: enforce Discord agent component DM auth (#11254) (thanks @thedudeabidesai) 2026-02-10 00:26:59 -06:00
quotentiroler
a26670a2fb refactor: consolidate fetchWithTimeout into shared utility 2026-02-09 20:34:56 -08:00
Yida-Dev
d3c71875e4 fix: cap Discord gateway reconnect at 50 attempts to prevent infinite loop (#12230)
* fix: cap Discord gateway reconnect attempts to prevent infinite loop

The Discord GatewayPlugin was configured with maxAttempts: Infinity,
which causes an unbounded reconnection loop when the Discord gateway
enters a persistent failure state (e.g. code 1005 with stalled HELLO).

In production, this manifested as 2,483+ reconnection attempts in a
single log file, starving the Node.js event loop and preventing cron,
heartbeat, and other subsystems from functioning.

Cap maxAttempts at 50, which provides ~25 minutes of retry time
(with 30s HELLO timeout between attempts) before cleanly exiting
via the existing "Max reconnect attempts" error handler.

Closes #11836

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

* Changelog: note Discord gateway reconnect cap (#12230) (thanks @Yida-Dev)

---------

Co-authored-by: Yida-Dev <reyifeijun@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Shadow <shadow@clawd.bot>
2026-02-09 20:36:43 -06:00
magendary
ead3bb645f discord: auto-create thread when sending to Forum/Media channels (#12380)
* discord: auto-create thread when sending to Forum/Media channels

* Discord: harden forum thread sends (#12380) (thanks @magendary)

* fix: clean up discord send exports (#12380) (thanks @magendary)

---------

Co-authored-by: Shadow <shadow@clawd.bot>
2026-02-09 20:26:42 -06:00
max
8d75a496bf refactor: centralize isPlainObject, isRecord, isErrno, isLoopbackHost utilities (#12926) 2026-02-09 17:02:55 -08:00
juanpablodlc
8d96955e19 fix(routing): make bindings dynamic by calling loadConfig() per-message (#11372) 2026-02-09 00:34:55 -06:00
max
223eee0a20 refactor: unify peer kind to ChatType, rename dm to direct (#11881)
* fix: use .js extension for ESM imports of RoutePeerKind

The imports incorrectly used .ts extension which doesn't resolve
with moduleResolution: NodeNext. Changed to .js and added 'type'
import modifier.

* fix tsconfig

* refactor: unify peer kind to ChatType, rename dm to direct

- Replace RoutePeerKind with ChatType throughout codebase
- Change 'dm' literal values to 'direct' in routing/session keys
- Keep backward compat: normalizeChatType accepts 'dm' -> 'direct'
- Add ChatType export to plugin-sdk, deprecate RoutePeerKind
- Update session key parsing to accept both 'dm' and 'direct' markers
- Update all channel monitors and extensions to use ChatType

BREAKING CHANGE: Session keys now use 'direct' instead of 'dm'.
Existing 'dm' keys still work via backward compat layer.

* fix tests

* test: update session key expectations for dmdirect migration

- Fix test expectations to expect :direct: in generated output
- Add explicit backward compat test for normalizeChatType('dm')
- Keep input test data with :dm: keys to verify backward compat

* fix: accept legacy 'dm' in session key parsing for backward compat

getDmHistoryLimitFromSessionKey now accepts both :dm: and :direct:
to ensure old session keys continue to work correctly.

* test: add explicit backward compat tests for dmdirect migration

- session-key.test.ts: verify both :dm: and :direct: keys are valid
- getDmHistoryLimitFromSessionKey: verify both formats work

* feat: backward compat for resetByType.dm config key

* test: skip unix-path Nix tests on Windows
2026-02-09 09:20:52 +09:00
max
a1123dd9be Centralize date/time formatting utilities (#11831) 2026-02-08 04:53:31 -08:00
jarvis89757
9949f82590 fix(discord): support forum channel thread-create (#10062)
* fix(discord): support forum channel thread-create

* fix: harden discord forum thread-create (#10062) (thanks @jarvis89757)

---------

Co-authored-by: Shakker <shakkerdroid@gmail.com>
2026-02-08 05:51:10 +00:00
Tyler Yust
1007d71f0c fix: comprehensive BlueBubbles and channel cleanup (#11093)
* feat(bluebubbles): auto-strip markdown from outbound messages (#7402)

* fix(security): add timeout to webhook body reading (#6762)

Adds 30-second timeout to readBody() in voice-call, bluebubbles, and nostr
webhook handlers. Prevents Slow-Loris DoS (CWE-400, CVSS 7.5).
Merged with existing maxBytes protection in voice-call.

* fix(security): unify Error objects and lint fixes in webhook timeouts (#6762)

* fix: prevent plugins from auto-enabling without user consent (#3961)

Changes default plugin enabled state from true to false in enablePluginEntry().
Preserves existing enabled:true values. Fixes #3932.

* fix: apply hierarchical mediaMaxMb config to all channels (#8749)

Generalizes resolveAttachmentMaxBytes() to use account → channel → global
config resolution for all channels, not just BlueBubbles. Fixes #7847.

* fix(bluebubbles): sanitize attachment filenames against header injection (#10333)

Strip ", \r, \n, and \\ from filenames after path.basename() to prevent
multipart Content-Disposition header injection (CWE-93, CVSS 5.4).
Also adds sanitization to setGroupIconBlueBubbles which had zero filename
sanitization.

* fix(lint): exclude extensions/ from Oxlint preflight check (#9313)

Extensions use PluginRuntime|null patterns that trigger
no-redundant-type-constituents because PluginRuntime resolves to any.
Excluding extensions/ from Oxlint unblocks user upgrades.
Re-applies the approach from closed PR #10087.

* fix(bluebubbles): add tempGuid to createNewChatWithMessage payload (#7745)

Non-Private-API mode (AppleScript) requires tempGuid in send payloads.
The main sendMessageBlueBubbles already had it, but createNewChatWithMessage
was missing it, causing 400 errors for new chat creation without Private API.

* fix: send stop-typing signal when run ends with NO_REPLY (#8785)

Adds onCleanup callback to the typing controller that fires when the
controller is cleaned up while typing was active (e.g., after NO_REPLY).
Channels using createTypingCallbacks automatically get stop-typing on
cleanup. This prevents the typing indicator from lingering in group chats
when the agent decides not to reply.

* fix(telegram): deduplicate skill commands in multi-agent setup (#5717)

Two fixes:
1. Skip duplicate workspace dirs when listing skill commands across agents.
   Multiple agents sharing the same workspace would produce duplicate commands
   with _2, _3 suffixes.
2. Clear stale commands via deleteMyCommands before registering new ones.
   Commands from deleted skills now get cleaned up on restart.

* fix: add size limits to unbounded in-memory caches (#4948)

Adds max-size caps with oldest-entry eviction to prevent OOM in
long-running deployments:
- BlueBubbles serverInfoCache: 64 entries (already has TTL)
- Google Chat authCache: 32 entries
- Matrix directRoomCache: 1024 entries
- Discord presenceCache: 5000 entries per account

* fix: address review concerns (#11093)

- Chain deleteMyCommands → setMyCommands to prevent race condition (#5717)
- Rename enablePluginEntry to registerPluginEntry (now sets enabled: false)
- Add Slow-Loris timeout test for readJsonBody (#6023)
2026-02-07 05:00:55 -08:00