Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Upgrade all package versions from 0.14.5 to 0.15.0 across the monorepo, including package-lock.json and sandbox image references.
PNG's magic bytes are 89 50 4E 47, but detectImageMime only checked
the first three. The WebP branch in the same function correctly checks
all four bytes of its signature — the PNG path was clearly an oversight.
Extend the PNG check to include 0x47 ('G') for consistency and to
eliminate the (admittedly rare) false-positive window.
DingTalk provides two user identifiers: `senderId` (an internal platform ID
like `$:LWCP_v1:xxx`) and `senderStaffId` (the human-readable staff/employee
ID). The current code uses `senderId` first when constructing the Envelope,
which means `allowedUsers` configuration cannot match against the staff ID
that users actually know and configure.
Swap the priority so `senderStaffId` is preferred when available, falling back
to `senderId` only when `senderStaffId` is absent.
Made-with: Cursor
Add handler for message:voice in Telegram channel adapter.
Voice messages are downloaded via Telegram Bot API and passed as
audio attachments in the Envelope, matching the existing pattern
for document handling.
This enables users to send voice messages through Telegram which
can then be processed by the model or transcription skills.
Co-authored-by: Teafey <Teafey@users.noreply.github.com>
`qwen channel start` never calls `loadCliConfig`, so the proxy
configured via `--proxy` or `HTTPS_PROXY`/`HTTP_PROXY` env vars
was not applied. This caused Telegram's `getMe` (and all other
channel HTTP traffic) to bypass the proxy entirely.
The fix has two parts:
1. Resolve proxy in `start.ts` bootstrap and call
`setGlobalDispatcher(new ProxyAgent(...))` for native fetch()
calls (file downloads, other channels). This mirrors the same
pattern used by Config constructor in the main CLI path.
2. Thread the proxy URL through `ChannelBaseOptions` so adapters
can configure their own HTTP clients. TelegramAdapter passes
an `HttpsProxyAgent` to grammy's `baseFetchConfig.agent` since
grammy uses node-fetch which ignores undici's global dispatcher.
Fixes#3122
PR #2943 fixed headers in buildHeaders() but the login flow in
waitForLogin() still used a hardcoded incomplete header object.
Reuse the shared buildHeaders() so all endpoints send consistent
iLink-App-Id and iLink-App-ClientVersion headers.
Also wrap channel.connect() in startSingle() with a try/catch so
configuration errors print a clean message instead of dumping the
yargs help text and a stack trace.
The previous version bump commit (bb4376c) only updated the root
package.json but did not run `npm run release:version` to propagate
the version and sandboxImageUri to all workspace packages.
This caused Docker sandbox integration tests to fail in CI with
"manifest unknown" because build_sandbox.js built image 0.14.1
(from packages/cli/package.json) while sandboxConfig.ts expected
image 0.14.2 (from root package.json).
Fixes: https://github.com/QwenLM/qwen-code/actions/runs/24135197272/job/70424966323
The iLink Bot API requires these headers for session authentication.
Without them, getupdates returns errcode -14 (session timeout).
The protocol version (2.0.0) is tracked independently from our channel
version, matching the current official plugin's API compatibility level.
Closes#2908
The HTML send fallback was sending the entire original text instead of
just the failed chunk, and returning early which skipped remaining
chunks. Now strips HTML from only the failed chunk and continues to
send the remaining chunks.
- Mark @qwen-code/channel-plugin-example as private in package.json
- Remove publish step from release workflow
- Remove file: to semver rewrite logic in version script
- Use file: reference for @qwen-code/channel-base dependency
This change prevents the example plugin from being published to npm, as it's only intended for internal/development use.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Update all packages from 0.13.x to 0.14.0
- Update sandbox image URI to 0.14.0
This prepares the 0.14.0 release with updated version numbers
across all workspace packages.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Replace Telegraf with Grammy as the Telegram Bot framework.
- Replace @telegraf/types with @grammyjs/types in package-lock.json
- Swap telegraf dependency for grammy ^1.41.1 in package.json
- Update TelegramAdapter.ts: Bot instead of Telegraf, .api.* instead
of .telegram.* calls, .start() instead of .launch(), adjusted event
subscription syntax (message:text, message:photo, message:document)
Grammy is a more modern and actively maintained Telegram bot framework
for Node.js, improving reliability and reduce legacy dependencies.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add plugin-example to build order in scripts/build.js
- Add prepublishOnly script to auto-build before npm publish
This ensures the plugin-example package is built during the main build process and automatically compiled before publishing to npm.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- fix: sanitize remote filenames with basename() and isolate uploads
in UUID subdirs to prevent path traversal and collision (#2-4, #27)
- fix: use crypto.randomInt() for pairing codes instead of Math.random() (#5)
- fix: pass config.sessionScope instead of hardcoded 'user' (#6);
add per-channel scope overrides via setChannelScope() for startAll (#7)
- fix: removeSession now returns removed session IDs and persists
when chatId is provided (#8)
- fix: /clear only removes the cleared session from instructedSessions,
not all sessions (#9)
- fix: DingTalk @mention stripping now removes only the first mention
instead of all mentions (#10)
- fix: remove dead TELEGRAF_COMMANDS Set and its guard (#13)
- fix: WeChat cursor saved after message processing, not before (#14)
- fix: crash recovery uses time-window counting instead of resettable
counter to prevent infinite restart loops (#17)
- fix: call channel.disconnect() before exit on crash exhaustion (#18)
Add three dispatch modes for handling concurrent messages:
- steer (default): cancel current prompt and start new one
- collect: buffer messages and coalesce into follow-up prompt
- followup: queue messages for sequential processing
Introduce onPromptStart/onPromptEnd lifecycle hooks for working
indicators. These fire only when a prompt actually begins processing,
not for buffered (collect mode) or gated/blocked messages.
Refactor Telegram, WeChat, and DingTalk adapters to use the new hooks
instead of overriding handleInbound, simplifying the working indicator
pattern and ensuring correct behavior with dispatch modes.
This enables better UX for async workflows and prevents indicator
leaks when messages are buffered or cancelled.
- Add Attachments interface docs with handling examples
- Document block streaming configuration and behavior
- Update architecture diagrams to show attachment resolution
- Add Attachment type to exported types reference
- Update plugin-example README
Covers new structured attachment support and block streaming
that delivers responses as multiple progressive messages.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add Attachment interface with type, data, filePath, mimeType, fileName
- Resolve attachments in ChannelBase before prompting bridge
- Update DingTalk, Telegram, Weixin adapters to use structured attachments
- Clean up placeholder text when files are received
- Export Attachment type from base package index
This enables proper handling of images and files across all channels,
separating attachment metadata from message text.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add BlockStreamer class to split streaming responses into multiple messages
- Configure block streaming with min/max chars and idle coalescing
- Integrate into ChannelBase when blockStreaming: 'on'
- Add comprehensive test coverage (16 tests)
This improves UX by delivering completed paragraphs as separate messages
instead of waiting for the full response.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add onResponseChunk hook for progressive text display during streaming
- Add onResponseComplete hook for customizing response delivery
- Update mock plugin channel to support streaming chunks
This enables channels to display AI responses progressively as they stream,
improving user experience with real-time feedback.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This provides documentation for the base infrastructure used to build
Qwen Code channel adapters, including architecture overview, quick start
guide, and API reference.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Update package.json exports to point to dist directory
- Add TypeScript build scripts to each channel adapter
- Include channel adapters in build order
This enables proper TypeScript compilation and distribution of channel
adapter packages.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Bump channel package versions to 0.13.0
- Add publish steps for @qwen-code/channel-base and @qwen-code/channel-plugin-example
- Update version script to convert file: references to semver for published packages
This enables proper npm publishing of channel packages during the release process.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Update channel-base to use built dist/ output with proper exports
- Add README with quick start guide and usage instructions
- Add qwen-extension.json manifest for extension discovery
- Add start-server CLI for running the mock WebSocket server
- Update dependencies from local file: to npm version
This enables the plugin-example package to be published and installed
as a standalone extension for testing the channel plugin system.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add comprehensive developer guide for building channel plugins
- Add user-facing docs for installing/configuring custom channel plugins
- Replace custom-channels.md with new plugins.md
- Rename @qwen-code/channel-mock to @qwen-code/channel-plugin-example
- Add messageId field to Envelope type for response correlation
This provides clear documentation for developers building custom channel
adapters and renames the mock package to better reflect its purpose as
a reference implementation example.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add @qwen-code/channel-mock package with MockPluginChannel
- Add createMockServer for programmatic test control via WebSocket
- Refactor integration test to use real WebSocket E2E flow
This enables testing the full channel pipeline (WebSocket → ChannelBase → AcpBridge → agent)
instead of the previous in-process loopback approach.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Refactor channel system to use a formal plugin interface:
- Add ChannelPlugin interface to @qwen-code/channel-base (channelType,
displayName, requiredConfigFields, createChannel factory)
- Each built-in adapter (Telegram, WeChat, DingTalk) exports a plugin object
- Replace hardcoded if/else factory with a Map-based channel registry
- Config validation now uses plugin's requiredConfigFields dynamically
- ChannelType changed from fixed union to string for extensibility
- Multi-channel mode now picks model from first channel config
This is the foundation for external plugin support — built-in channels
go through the exact same code path that third-party plugins will use.
Two bugs caused cross-talk between DM and group conversations:
1. Session routing key only used senderId, so the same user in DM and
group shared one ACP session (and conversation context). Now includes
chatId: `channelName:senderId:chatId`.
2. Concurrent messages on the same session caused textChunk listener
pollution in AcpBridge.prompt(), leaking response text across chats.
Added per-session promise queue in ChannelBase to serialize prompts.
- Add interfaces for DingTalkRepliedMsg and DingTalkRichTextPart types
- Support both newer text.repliedMsg and legacy quoteMessage formats
- Extract quoted message context with isReplyToBot detection
- Summarize replied content (text, richText, media placeholders)
This enables the bot to understand when users are replying to specific
messages and provides context about what message is being referenced.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Handle richText, picture, file, audio, video message types
- Download media via DingTalk API two-step flow
- Attach images as base64, save other files to temp dir
- Add DingTalkMessageData interface for richer payloads
This enables the DingTalk channel to process media attachments
in incoming messages.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Convert tables to pipe-separated plain text (DingTalk doesn't render tables)
- Split long messages into chunks respecting ~3800 char limit
- Handle code fences across chunk boundaries
- Extract title from first markdown line for webhook payload
This ensures markdown content renders correctly in DingTalk's limited renderer.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Cache sessionWebhook by conversationId for reliable message routing
- Show 👀 reaction while processing messages, then recall it
- Use conversationId as chatId instead of webhook URL
- Fix rawData parsing for already-parsed message data
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add @qwen-code/channel-dingtalk package with stream-based bot integration
- Support clientId/clientSecret authentication for DingTalk
- Add message deduplication and group chat mention handling
- Update ChannelConfig type to include dingtalk channel type
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add referencedText field to Envelope for quoted/replied-to messages
- TelegramAdapter extracts text from reply_to_message
- WeixinAdapter extracts text from ref_msg field
- ChannelBase prepends referenced text to agent prompt
This allows the agent to see what message a user is replying to,
providing better context for conversations in both Telegram and WeChat.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add /help, /clear (aliases: /reset, /new), /status commands to ChannelBase
- Commands are handled locally without agent round-trip
- TelegramAdapter skips "Working..." indicator for local commands
- Update docs to reflect new command structure
This provides a consistent command interface across all channel types
(Telegram, WeChat, etc.) with platform-specific extensibility.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add session persistence to SessionRouter for crash recovery
- Add loadSession method to AcpBridge for restoring sessions
- Add ChannelBaseOptions to support external router injection
- Refactor start.ts to support both standalone and gateway modes
- Extract config utilities into separate module
This enables channels to recover sessions after bridge crashes and
supports running multiple channels under a gateway process.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add photo message handling in Telegram adapter with base64 encoding
- Add document/file message handling with temp directory storage
- Extend WeChat adapter to support file downloads from CDN
- Refactor envelope building into reusable buildEnvelope method
- Rename ImageCdnRef to CdnRef for generic media handling
This enables users to send images and files through both Telegram and WeChat channels, with files saved to a temp directory for agent access.
- Add model configuration option for channel-specific model selection
- Support base64-encoded images in prompts via AcpBridge
- Add media utilities for WeChat/Weixin channel
- Update settings schema for model configuration
Enables channels to process images and use custom models.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add GroupGate class for group access control with three policies:
disabled (default), allowlist, and open
- Implement mention gating: bot only responds when @mentioned or replied to
in groups (configurable per-group)
- Extend Envelope type with isGroup, isMentioned, isReplyToBot fields
- Update TelegramAdapter to detect group context and mentions
- Add comprehensive documentation for group chat setup and troubleshooting
This enables using Qwen Code bots in Telegram groups with fine-grained
access control and mention-based activation to prevent noise.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Add PairingStore for managing pending requests and approved users
- Update SenderGate to support pairing policy with code generation
- Add CLI commands: `qwen channel pairing list/approve`
- Document pairing flow with rules and usage examples
This allows unknown senders to request access via a pairing code
that the bot operator approves through the CLI.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>