QQ Bridge Adapter (new):
- qq-api.ts: AppAccessToken, WebSocket Gateway, heartbeat, passive reply,
msg_seq auto-increment — pure QQ protocol layer
- qq-adapter.ts: BaseChannelAdapter impl with WebSocket lifecycle,
C2C_MESSAGE_CREATE handling, image download (base64), dedup, reconnect
- Settings UI (QqBridgeSection.tsx) + API routes (/api/settings/qq)
- i18n keys for en/zh, PLATFORM_LIMITS qq: 2000
replyToMessageId threading:
- bridge-manager.ts: thread msg.messageId through all deliver/command/
permission paths — required for QQ passive reply (msg_id mandatory)
- permission-broker.ts: accept and forward replyToMessageId
SDK crash resilience (bug fix):
- bridge-manager.ts: check hasError BEFORE saving sdkSessionId — the SDK
may emit a session_id before crashing, and saving that broken ID caused
ALL subsequent messages to fail by repeatedly resuming a corrupted session
- claude-client.ts: always clear sdk_session_id on crash, even for fresh
sessions (SDK can persist a new ID via status event before dying)
- conversation-engine.ts: attach filePath to file objects after disk write
so streamClaude() reuses on-disk copies (prevents duplicate writes,
matches desktop route behavior)
QQ permission handling (P1 fix):
- permission-broker.ts: for channels without inline button support (QQ),
render text-based /perm allow|deny|allow_session commands instead of
invisible inlineButtons that would deadlock the stream
QQ passive reply budget (P2 fix):
- bridge-manager.ts: QQ-specific delivery limits chunks to 3 max,
truncates with notice to avoid exhausting passive reply quota
- delivery-layer.ts: export chunkText for reuse
Error message improvement:
- claude-client.ts: when exit code 1 with image files, hint "Provider may
not support image/vision input" instead of generic auth/network message
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>