The sticker code path called ctx.getFile() directly without retry,
unlike the non-sticker media path which uses resolveTelegramFileWithRetry
(3 attempts with jitter). This made sticker downloads vulnerable to
transient Telegram API failures, particularly in group topics where
file availability can be delayed.
Refs #32326
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Stickers, voice notes, and captionless photos from the bot also lack
text and caption fields, so the previous check incorrectly classified
them as system messages and suppressed implicitMention.
Switch to checking for Telegram's forum_topic_* / general_forum_topic_*
service-message fields which only appear on actual service messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address Greptile review feedback: bot media messages (photo/video) use
caption instead of text, so they would be incorrectly classified as
system messages. Add !caption guard to the system message check.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a Telegram Forum topic is created by the bot, Telegram generates a
system message with from.id=botId and empty text. Every subsequent user
message in that topic has reply_to_message pointing to this system
message, causing the implicitMention check to fire and bypassing
requireMention for every single message.
Add a guard that recognises system messages (is_bot=true with no text)
and excludes them from implicit mention detection, so that only genuine
replies to bot messages trigger the bypass.
Closes#32256
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(skills): use explicit skill-command scope APIs
* test(skills): cover scoped listing and telegram allowlist
* fix(skills): add mergeSkillFilters edge-case tests and simplify dead code
Cover unrestricted-co-tenant and empty-allowlist merge paths in
skill-commands tests. Remove dead ternary in bot-handlers pagination.
Add clarifying comments on undefined vs [] filter semantics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(skills): collapse scope functions into single listSkillCommandsForAgents
Replace listSkillCommandsForAgentIds, listSkillCommandsForAllAgents, and
the deprecated listSkillCommandsForAgents with a single function that
accepts optional agentIds and falls back to all agents when omitted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(skills): harden realpathSync race and add missing test coverage
- Wrap fs.realpathSync in try-catch to gracefully skip workspaces that
disappear between existsSync and realpathSync (TOCTOU race).
- Log verbose diagnostics for missing/unresolvable workspace paths.
- Add test for overlapping allowlists deduplication on shared workspaces.
- Add test for graceful skip of missing workspaces.
- Add test for pagination callback without agent suffix (default agent).
- Clean up temp directories in skill-commands tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(telegram): warn when nativeSkillsEnabled but no agent route is bound
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use runtime.log instead of nonexistent runtime.warn
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Move makeProxyFetch to src/infra/net/proxy-fetch.ts and add
resolveProxyFetchFromEnv which reads standard proxy env vars
(HTTPS_PROXY, HTTP_PROXY, and lowercase variants) and returns a
proxy-aware fetch via undici's EnvHttpProxyAgent. Telegram re-exports
from the shared location to avoid duplication.
The downloadAndSaveTelegramFile inner function only used the server-side
file path (e.g. "documents/file_42.pdf") or the Content-Disposition
header (which Telegram doesn't send) to derive the saved filename.
The original filename provided by Telegram via msg.document.file_name,
msg.audio.file_name, msg.video.file_name, and msg.animation.file_name
was never passed through, causing all inbound files to lose their
user-provided names.
Now downloadAndSaveTelegramFile accepts an optional telegramFileName
parameter that takes priority over the fetched/server-side name.
The resolveMedia call site extracts the original name from the message
and passes it through.
Closes#31768
Made-with: Cursor
Hash the command list and cache it to disk per account. On restart,
compare the current hash against the cached one and skip the
deleteMyCommands + setMyCommands round-trip when nothing changed.
This prevents 429 rate-limit errors when the gateway restarts
several times in quick succession.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>