- In group chats, only process slash commands when the message is
directed at this bot (via @mention or reply-to-bot), preventing
all bots from responding to the same command.
- Use raw_text metadata (before quote/forward context prepending)
for command detection so quoted content like "/fs" doesn't
accidentally match a command.
- Fix isTelegramBotMentioned text_mention entity check to verify
the mentioned bot matches the current bot, not just any bot.
User messages from channel inbound (Telegram, Discord, Feishu, etc.)
were previously persisted before the agent runs. Now they are written
together with assistant/tool messages at the end of a conversation turn
(or on abort), matching the behavior of WebSocket and sync chat paths.
Wire SetCommandHandler into ChannelInboundProcessor so slash commands
are intercepted before reaching the LLM. Also apply lint fixes across
command package (strconv.Itoa, comment formatting, unused code removal)
and remove obsolete tool-call-browser.vue component.
Refactor the attachment tag extraction into a generic TagResolver/StreamTagExtractor
system that supports multiple custom tags. Implement <reactions> tag allowing the
agent to embed emoji reactions directly in text responses, dispatched as side-effects
through the channel reactor interface.
- Add TagResolver interface and StreamTagExtractor streaming state machine
- Refactor AttachmentsStreamExtractor as backward-compatible wrapper
- Add reactionsResolver and ReactionDeltaAction stream event
- Wire reaction dispatch in Go channel inbound processor
- Fix .gitignore to scope compiled binary patterns to repo root
- Derive attachment name from path basename when not explicitly set in
parseAttachmentDelta, fixing the "file.bin" fallback on Telegram.
- Infer correct AttachmentType (image/audio/video) from MIME in
applyAssetToAttachment instead of keeping the generic "file" type.
- Remove outboundAttachments re-attachment to final messages since
attachments are already delivered during streaming via
StreamEventAttachment, preventing duplicate file sends on platforms.
Pass replyTarget through the full pipeline (ChatRequest → gateway
identity → agent headers → MCP session) so the send tool can detect
when the destination matches the current conversation and return an
error guiding the agent to reply directly instead.
Prepend replied-to message text and attachments into the user query so
the LLM can see what is being replied to, matching the existing Telegram
behavior. Also set is_reply_to_bot metadata for Feishu reply-to-bot
detection in group chats.
Migrate the imported WeCom adapter to current channel interfaces and stabilize stream delivery by preventing heartbeat/reply ACK timeout regressions and post-final overwrite updates.
- Extract ContainsMarkdown to shared channel package
- Auto-detect markdown in normalizeOutboundMessage and MCP send tool
- Apply markdown-to-HTML conversion during streaming deltas, not just
on the final message
- Remove resolveTelegramParseMode which incorrectly returned Telegram's
native "Markdown" mode instead of converting to HTML
- Fix all 14 Telegram send/edit paths for consistent parse mode handling
- Reset parseMode for plain-text error messages to avoid HTML corruption
Use rune-aware truncation for user-facing text and log previews so multibyte content is not corrupted in memory context, Telegram messages, or diagnostics.
- Extract parseTelegramTarget helper to consolidate duplicated @username
vs numeric chat ID parsing from 6+ locations (builder functions,
sendTelegramTextReturnMessage, sendTelegramAttachmentImpl)
- Extract Config.baseURL() to eliminate duplicate base URL resolution
between apiEndpoint() and fileEndpoint()
- Refactor stream.go Push method: extract resetStreamState(),
deliverFinalText(), and per-event-type sub-methods (pushDelta,
pushFinal, pushToolCallStart, pushAttachment, pushPhaseEnd,
pushError), reducing the 200-line switch-case to a clean dispatcher
- Use pushFinal's existing getBot() instead of duplicating parseConfig +
getOrCreateBot
- Replace sort.SliceStable with slices.SortStableFunc + cmp.Compare
- Replace strings.Index + manual slicing with strings.Cut in
decodeDataURLBytes, ResolveAttachment, and parseTelegramUserInput
* feat(channel): add qq adapter and outbound delivery
* feat(channel): ingest inbound qq messages
* feat(web): expose qq channel in management ui
* feat(channel): support qq attachment ingestion
* fix(mcp): fail read raw immediately for missing files
* fix(agent): parse inline image data into native image parts
* test(agent): align read_media tool tests with SDK options
* fix(channel): harden qq image delivery and reconnect loop
Avoid data URLs for qq channel images, reset reconnect backoff after healthy sessions, and fall back gracefully for malformed public image URLs.
* fix(channel): restore qq media delivery and target resolution
* fix(qq,mcp,agent): fix message/qq regressions and pass go lint
* fix(qq,agent): validate inline base64 and sync heartbeat seq
* fix(qq): validate remote voice mime for upload checks
* fix(qq): fall back intents and restore adapter wiring
* fix(qq): prevent final text leakage and dedupe persisted inbound query
Split long AI responses into multiple platform messages during streaming
instead of truncating them. The manager counts accumulated delta runes
and opens a new stream when approaching the platform's TextChunkLimit.
Uses a soft/hard limit strategy that prefers splitting at sentence ends
or line breaks over cutting mid-sentence.
- Add pushDelta with soft (75%) / hard (100%) limit and natural break
point detection
- Add splitStream, pushFinalAfterSplit, pushFinalWithChunking helpers
- Fix Discord adapter to use RuneCount Message Length
- Add tests for delta splitting, natural breaks, and final handling
* feat(telegram): use sendMessageDraft for streaming in private chats
Use Telegram Bot API 9.3's sendMessageDraft to stream partial messages
with smooth animation in private chats, replacing the sendMessage +
editMessageText approach. Group/channel chats keep the existing
edit-based streaming.
- Add sendTelegramDraft() for the sendMessageDraft API
- Detect private chats via conversation_type metadata in OpenStream
- Use 300ms throttle for drafts (vs 5s for edits)
- Send permanent messages at tool call boundaries and on final event
- Reset buffer atomically in StreamEventFinal to prevent duplicate
messages when multiple final events fire (one per assistant output)
* test(telegram): improve draft mode test assertions
Add sendTextForTest hook for sendTelegramTextReturnMessage to enable
direct assertion of send calls. Clean up residual unused variables
and replace indirect assertions with explicit mock-based verification.
Allow configuring a custom Telegram Bot API base URL (`apiBaseURL`) per
channel, enabling users behind restricted networks to route requests
through a reverse proxy (e.g. Nginx, Cloudflare Workers).
Both API calls and file downloads respect the configured endpoint.
When omitted, the official https://api.telegram.org is used.
Closes#159