Commit Graph

406 Commits

Author SHA1 Message Date
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
Peter Steinberger
d898ad6807 fix(telegram): cast fetch for grammY ApiClientOptions 2026-02-06 15:45:34 -08:00
nicolasstanley
4a5e9f0a4f fix(telegram): accept messages from group members in allowlisted groups (#9775)
* fix(telegram): accept messages from group members in allowlisted groups

Issue #4559: Telegram bot was silently dropping messages from non-paired users
in allowlisted group chats due to overly strict sender filtering.

The fix adds a check to distinguish between:
1. Group itself is allowlisted → accept messages from any member
2. Group is NOT allowlisted → only accept from allowlisted senders

Changes:
- Check if group ID is in the allowlist (or allowlist is wildcard)
- Only reject sender if they're not in allowlist AND group is not allowlisted
- Improved logging to indicate the actual reason for rejection

This preserves security controls while fixing the UX issue where group members
couldn't participate unless individually allowlisted.

Backwards compatible: existing allowlists continue to work as before.

* style: format telegram fix for oxfmt compliance

* refactor(telegram): clarify group allowlist semantics in fix for #4559

Changes:
- Rename 'isGroupInAllowlist' to 'isGroupChatIdInAllowlist' for clarity
- Expand comments to explain the semantic distinction:
  * Group chat ID in allowlist -> accept any group member (fixes #4559)
  * Group chat ID NOT in allowlist -> enforce sender allowlist (preserves security)
- This addresses concerns about config semantics raised in code review

The fix maintains backward compatibility:
- 'groupAllowFrom' with group chat IDs now correctly acts as group enablement
- 'groupAllowFrom' with sender IDs continues to work as sender allowlist
- Operators should use group chat IDs for group enablement, sender IDs for sender control

Note: If operators were using 'groupAllowFrom' with group IDs expecting sender-level
filtering, they should migrate to a separate sender allowlist config. This is the
intended behavior per issue #4559.

* Telegram: allow per-group groupPolicy overrides

* Telegram: support per-group groupPolicy overrides (#9775) (thanks @nicolasstanley)

---------

Co-authored-by: George Pickett <gpickett00@gmail.com>
2026-02-05 14:45:45 -08:00
Michael Lee
eb80b9acb3 feat: add Claude Opus 4.6 to built-in model catalog (#9853)
* feat: add Claude Opus 4.6 to built-in model catalog

- Update default model from claude-opus-4-5 to claude-opus-4-6
- Add opus-4.6 model ID normalization
- Add claude-opus-4-6 to live model filter prefixes
- Update image tool to prefer claude-opus-4-6 for vision
- Add CLI backend alias for opus-4.6
- Update onboard auth default selections to include opus-4.6
- Update model picker placeholder

Closes #9811

* test: update tests for claude-opus-4-6 default

- Fix model-alias-defaults test to use claude-opus-4-6
- Fix image-tool test to expect claude-opus-4-6 in fallbacks

* feat: support claude-opus-4-6

* docs: update changelog for opus 4.6 (#9853) (thanks @TinyTb)

* chore: bump pi to 0.52.0

---------

Co-authored-by: Slurpy <slurpy@openclaw.ai>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-05 12:09:23 -08:00
Christian Klotz
ddedb56c01 fix(telegram): pass parentPeer for forum topic binding inheritance (#9789)
Fixes #9545 and #9351.

When a message comes from a Telegram forum topic, the peer ID includes
the topic suffix (e.g., `-1001234567890:topic:99`). Users configure
bindings with the base group ID, which previously did not match.

This adds `parentPeer` to `resolveAgentRoute()` calls for forum groups,
enabling binding inheritance from the parent group to all topics.

- Extract `buildTelegramParentPeer()` helper in bot/helpers.ts
- Pass parentPeer in bot-message-context.ts, bot-handlers.ts,
  bot-native-commands.ts, and bot.ts (reaction handler)
- Add tests for forum topic routing and topic precedence
2026-02-05 18:25:03 +00:00
damaozi
c0b267a03a test(telegram): add DM topic threadId deliveryContext test for #8891
Verifies that threadId is passed to updateLastRoute for DM topics.
Test fails on main branch, passes with the fix.
2026-02-05 15:33:30 +05:30
damaozi
8860d2ed7f fix(telegram): preserve DM topic threadId in deliveryContext
When receiving messages in Telegram DM topics (Topics in Private Chats),
the threadId was not saved in the session's deliveryContext, causing
replies to go to General chat instead of the topic.

Now we pass threadId to updateLastRoute for DM topics.

Fixes #8891
2026-02-05 15:33:30 +05:30
Christian Klotz
21f8c3db18 Telegram: remove last @ts-nocheck from bot-handlers.ts (#9206)
* Telegram: remove @ts-nocheck from bot-handlers.ts, use Grammy types directly, deduplicate StickerMetadata

* Telegram: remove last @ts-nocheck from bot-handlers.ts (#9206)
2026-02-05 00:58:49 +00:00
Christian Klotz
90b4e54354 Telegram: remove @ts-nocheck from bot-message.ts (#9180)
* Telegram: remove @ts-nocheck from bot-message.ts, type deps via Omit<BuildTelegramMessageContextParams>

* Telegram: widen allMedia to TelegramMediaRef[] so stickerMetadata flows through

* Telegram: remove @ts-nocheck from bot-message.ts (#9180)
2026-02-05 00:20:44 +00:00
Christian Klotz
96abc1c864 Telegram: remove @ts-nocheck from bot.ts, fix duplicate error handler, harden sticker caching (#9077)
* Telegram: remove @ts-nocheck from bot.ts and bot-message-dispatch.ts

- bot/types.ts: TelegramContext.me uses UserFromGetMe (Grammy) instead of manual inline type
- bot.ts: remove 6 unsafe casts (as any, as unknown, as object), use Grammy types directly
- bot.ts: remove dead message_thread_id access on reactions (not in Telegram Bot API)
- bot.ts: remove resolveThreadSessionKeys import (no longer needed for reactions)
- bot-message-dispatch.ts: replace ': any' with DispatchTelegramMessageParams type
- bot-message-dispatch.ts: add sticker.fileId guard before cache access
- bot.test.ts: update reaction tests, remove dead DM thread-reaction test

* Telegram: remove duplicate bot.catch handler (only the last one runs in Grammy)

* Telegram: remove @ts-nocheck from bot.ts, fix duplicate error handler, harden sticker caching (#9077)
2026-02-04 22:35:51 +00:00
mudrii
5d82c82313 feat: per-channel responsePrefix override (#9001)
* feat: per-channel responsePrefix override

Add responsePrefix field to all channel config types and Zod schemas,
enabling per-channel and per-account outbound response prefix overrides.

Resolution cascade (most specific wins):
  L1: channels.<ch>.accounts.<id>.responsePrefix
  L2: channels.<ch>.responsePrefix
  L3: (reserved for channels.defaults)
  L4: messages.responsePrefix (existing global)

Semantics:
  - undefined -> inherit from parent level
  - empty string -> explicitly no prefix (stops cascade)
  - "auto" -> derive [identity.name] from routed agent

Changes:
  - Core logic: resolveResponsePrefix() in identity.ts accepts
    optional channel/accountId and walks the cascade
  - resolveEffectiveMessagesConfig() passes channel context through
  - Types: responsePrefix added to WhatsApp, Telegram, Discord, Slack,
    Signal, iMessage, Google Chat, MS Teams, Feishu, BlueBubbles configs
  - Zod schemas: responsePrefix added for config validation
  - All channel handlers wired: telegram, discord, slack, signal,
    imessage, line, heartbeat runner, route-reply, native commands
  - 23 new tests covering backward compat, channel/account levels,
    full cascade, auto keyword, empty string stops, unknown fallthrough

Fully backward compatible - no existing config is affected.
Fixes #8857

* fix: address CI lint + review feedback

- Replace Record<string, any> with proper typed helpers (no-explicit-any)
- Add curly braces to single-line if returns (eslint curly)
- Fix JSDoc: 'Per-channel' → 'channel/account' on shared config types
- Extract getChannelConfig() helper for type-safe dynamic key access

* fix: finish responsePrefix overrides (#9001) (thanks @mudrii)

* fix: normalize prefix wiring and types (#9001) (thanks @mudrii)

---------

Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-04 16:16:34 -05:00
Peter Steinberger
19ecdce275 fix: align proxy fetch typing 2026-02-04 04:09:53 -08:00
Ayaan Zaidi
78fd194722 fix: telegram forward metadata + cron delivery guard (#8392) (thanks @Glucksberg) 2026-02-04 16:43:20 +05:30
Glucksberg
b2361292e7 fix: trim legacy signature fallback, type fromChatType as union 2026-02-04 16:43:20 +05:30
Glucksberg
57566c5e4d fix(telegram): include forward_from_chat metadata in forwarded message context (#8133)
Extract missing metadata from forwarded Telegram messages:

- Add fromChatType to TelegramForwardedContext, capturing the original
  chat type (channel/supergroup/group) from forward_from_chat.type
  and forward_origin.chat/sender_chat.type
- Add fromMessageId to capture the original message ID from channel forwards
- Read author_signature from forward_origin objects (modern API),
  preferring it over the deprecated forward_signature field
- Pass ForwardedFromChatType and ForwardedFromMessageId through to
  the inbound context payload
- Add test coverage for forward_origin channel/chat types, including
  author_signature extraction and fromChatType propagation
2026-02-04 16:43:20 +05:30
Christian Klotz
da6de49815 Telegram: use Grammy types directly, add typed Probe/Audit to plugin interface (#8403)
* Telegram: replace duplicated types with Grammy imports, add Probe/Audit generics to plugin interface

* Telegram: remove legacy forward metadata (deprecated in Bot API 7.0), simplify required-field checks

* Telegram: clean up remaining legacy references and unnecessary casts

* Telegram: keep RequestInit parameter type in proxy fetch (addresses review feedback)

* Telegram: add exhaustiveness guard to resolveForwardOrigin switch
2026-02-04 10:09:28 +00:00
Ayaan Zaidi
41a4f1200b fix: honor telegram model overrides in buttons (#8193) (thanks @gildo) 2026-02-04 09:23:17 +05:30
Ermenegildo Fiorito
202c554d09 Telegram: fix model button review issues
- Add currentModel to callback handler for checkmark display
- Add 64-byte callback_data limit protection (skip long model IDs)
- Add tests for large model lists and callback_data limits
2026-02-04 09:23:17 +05:30
Ermenegildo Fiorito
16349b6e93 Telegram: add inline button model selection for /models and /model commands 2026-02-04 09:23:17 +05:30
Josh Palmer
0223416c61 Channels: finish Feishu/Lark integration 2026-02-03 14:27:39 -08:00
cpojer
425003417d fix: Remove tsconfig.oxlint.json AGAIN. 2026-02-03 21:53:48 +09:00
mac mimi
c6b4de520a fix(telegram): recover from grammY "timed out" long-poll errors (#7239)
grammY getUpdates returns "Request to getUpdates timed out after 500 seconds"
but RECOVERABLE_MESSAGE_SNIPPETS only had "timeout". Since
"timed out".includes("timeout") === false, the error was not classified as
recoverable, causing the polling loop to exit permanently.

Add "timed out" to RECOVERABLE_MESSAGE_SNIPPETS so the polling loop retries
instead of dying silently.

Fixes #7239
Fixes #7255
2026-02-02 22:37:22 +00:00
Christian Klotz
99b4f2a24e fix(telegram): handle Grammy HttpError network failures (#3815) (#7195)
* fix(telegram): handle Grammy HttpError network failures (#3815)

Grammy wraps fetch errors in an .error property (not .cause). Added .error
traversal to collectErrorCandidates in network-errors.ts.

Registered scoped unhandled rejection handler in monitorTelegramProvider
to catch network errors that escape the polling loop (e.g., from setMyCommands
during bot setup). Handler is unregistered when the provider stops.

* fix(telegram): address review feedback for Grammy HttpError handling

- Gate .error traversal on HttpError name to avoid widening search graph
- Use runtime logger instead of console.warn for consistency
- Add isGrammyHttpError check to scope unhandled rejection handler
- Consolidate isNetworkRelatedError into isRecoverableTelegramNetworkError
- Add 'timeout' to recoverable message snippets for full coverage
2026-02-02 15:25:41 +00:00
Peter Steinberger
9bd64c8a1f fix: expand SSRF guard coverage 2026-02-02 04:58:32 -08:00
cpojer
c429ccb64f chore: fix broken test. 2026-02-02 21:51:37 +09:00
chenglun.hu
d46b489e21 fix(telegram): add timeout to file download to prevent DoS (CWE-400)
Add AbortSignal.timeout() to both fetch calls in download.ts to prevent
indefinite hangs when Telegram API is slow or unresponsive.

- getTelegramFile(): 30s timeout for metadata API call
- downloadTelegramFile(): 60s timeout for file download

Both functions now accept optional timeoutMs parameter for configurability.

Fixes #6849

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:39:39 +05:30
cpojer
935a0e5708 chore: Enable typescript/no-explicit-any rule. 2026-02-02 16:18:09 +09:00
cpojer
baa1e95b9d chore: Enable no-unnecessary-template-expression lint rule. 2026-02-02 15:37:05 +09:00
Ayaan Zaidi
0bc8a592a6 fix: inline telegram thread scope type 2026-02-02 09:26:59 +05:30
Ayaan Zaidi
1d7dd5f261 fix: require thread specs for telegram sends 2026-02-02 09:26:59 +05:30
Ayaan Zaidi
19b8416a81 fix: unify telegram thread handling 2026-02-02 09:26:59 +05:30
Peter Steinberger
9d2784cdb9 test: speed up telegram suites 2026-02-01 22:23:16 +00:00
Ayaan Zaidi
633f848481 fix: use telegram user id for pairing request 2026-02-01 15:22:37 +05:30
Ayaan Zaidi
24fbafa9a7 refactor: use shared pairing store for telegram 2026-02-01 15:22:37 +05:30
cpojer
147eba11fd chore: Manually fix TypeScript errors uncovered by sorting imports.
Some TypeScript checks are order dependent, and the fixed types were `any`/`unknown`, TypeScript just didn't report it before for some reason.
2026-02-01 10:24:09 +09:00
cpojer
f06dd8df06 chore: Enable "experimentalSortImports" in Oxfmt and reformat all imorts. 2026-02-01 10:03:47 +09:00
Ayaan Zaidi
a64d8d2d66 fix: harden telegram streaming state 2026-01-31 22:46:19 +05:30
Ayaan Zaidi
37721ebd7c fix: restore telegram draft streaming partials 2026-01-31 22:46:19 +05:30
cpojer
5ceff756e1 chore: Enable "curly" rule to avoid single-statement if confusion/errors. 2026-01-31 16:19:20 +09:00
cpojer
15792b153f chore: Enable more lint rules, disable some that trigger a lot. Will clean up later. 2026-01-31 16:04:04 +09:00
cpojer
7a9ddcd590 chore: Enable some "perf" lint rules. 2026-01-31 15:58:24 +09:00
Ayaan Zaidi
e849df64dc fix: normalize telegram account token lookup (#5055) (thanks @jasonsschin) 2026-01-31 08:58:04 +05:30
jasonsschin
e913de0720 Fix Telegram token resolution for non-normalized accountId keys 2026-01-31 08:58:04 +05:30
Ayaan Zaidi
da71eaebd2 fix: correct telegram html nesting (#4578) (thanks @ThanhNguyxn) 2026-01-30 16:53:39 +05:30
ThanhNguyxn
8e5a684445 style: format test file 2026-01-30 16:53:39 +05:30
ThanhNguyxn
b05d57964b fix(telegram): properly nest overlapping HTML tags (#4071)
Unify style and link closing in render.ts to use LIFO order across
both element types, fixing cases where bold/italic spans containing
autolinks produced invalid HTML like <b><a></b></a>.
2026-01-30 16:53:39 +05:30
Ayaan Zaidi
3a85cb1833 fix: honor Telegram proxy dispatcher (#4456) (thanks @spiceoogway) 2026-01-30 14:38:39 +05:30
spiceoogway
7150268f84 fix(telegram): use undici fetch for proxy to fix dispatcher option
Fixes #4038

The global fetch in Node.js doesn't support undici's dispatcher option,
which is required for ProxyAgent to work. This fix imports fetch from
undici directly to enable proper proxy support for Telegram API calls.

Root cause: makeProxyFetch() was using global fetch with { dispatcher: agent },
but Node.js's global fetch ignores the dispatcher option. Using undici.fetch
ensures the ProxyAgent dispatcher is properly respected.

Tested: Build passes, TypeScript compilation successful.
2026-01-30 14:37:47 +05:30
Ayaan Zaidi
9025da2296 fix: scope telegram skill commands per bot (#4360) (thanks @robhparker) 2026-01-30 12:00:29 +05:30
robhparker
c6ddc95fc0 fix(telegram): scope skill commands to bound agent per bot
registerTelegramNativeCommands() calls listSkillCommandsForAgents()
without passing agentIds, causing ALL agents' skill commands to be
registered on EVERY Telegram bot. When multiple agents share skill
names (e.g. two agents both have a "butler" skill), the shared `used`
Set in listSkillCommandsForAgents causes de-duplication suffixes
(_2, _3) and all commands appear on every bot regardless of agent
binding.

This fix uses the existing resolveAgentRoute() (already imported) to
find the bound agent for the current Telegram accountId, then passes
that agentId to listSkillCommandsForAgents(). The function already
accepts an optional agentIds parameter — it just wasn't wired from
the Telegram registration path.

Before: All agents' skill commands registered on every Telegram bot,
causing /butler_2, /housekeeper_2 dedup suffixes and potential
BOT_COMMANDS_TOO_MUCH errors when total exceeds 100.

After: Each Telegram bot only registers skill commands for its own
bound agent. No cross-agent dedup, no command limit overflow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 11:58:23 +05:30