* fix(feishu): require exact bot_open_id match for mention detection
- Previously, when bot_open_id was empty, ALL mentions were treated as bot mentions
- This caused multiple agents in same group to all respond to any mention
- Now requires bot_open_id to be set AND match exactly for mentionedBot=true
- Fixes issue where CPPAI PM would respond even when other agent was mentioned
* fix(feishu): gate group pairing by target bot
---------
Co-authored-by: ntduc <ntduc@cpp.ai.vn>
Bot responses to messages inside Lark topic threads were dropped
outside the thread because outbound Send always used the new-message
endpoint. This change:
- Adds LarkClient.ReplyMessage() that POSTs to
/open-apis/im/v1/messages/{id}/reply with reply_in_thread=true
- Parses thread_id from im.message.receive_v1 events (distinct from
root_id which fires on any quote reply) and stamps
feishu_reply_target_id into the message metadata
- Propagates the key through cmd/gateway_consumer_normal.go and the
new package-level routingMetaKeys var in internal/channels/events.go
so block replies and retry notifications also land in thread
- Routes sendText, sendMarkdownCard, sendImage, sendFile, and
sendMediaAttachment via a new deliverMessage helper that falls back
to SendMessage with a warning log on reply endpoint errors (e.g.
thread root deleted)
- Adds 10 unit tests covering routing, fallback, content
double-encoding, and the thread_id gate that prevents plain quote
replies from being silently promoted to threads
Closes#818
* fix(vision): fix Lark inline image parsing and MCP bridge read_image
Two fixes for image reading through Lark channel with claude-cli provider:
1. Lark post messages use flat format {"content":[[...]]} but resolvePostElements
only handled language-wrapped format {"zh_cn":{"content":[...]}}. Now checks
flat format first, and skips non-map values in fallback iteration.
2. read_image tool failed via MCP bridge because bridge creates fresh HTTP context
losing WithMediaImages. Fix: enrich <media:image> tags with path attribute
(like documents), pass workspace through X-Workspace bridge header with HMAC,
so read_image can load images from disk via path parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(security): add HMAC backward compat and guard workspace injection
- VerifyBridgeContext falls back to old signature format (without workspace)
for sessions with pre-existing MCP configs, preventing 403 on server restart
- Workspace context injection now requires agent context (agentID/userID)
to prevent unauthenticated path injection bypassing HMAC
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: viettranx <viettranx@gmail.com>
Post messages with embedded img tags were only producing [image]
placeholder text — the actual image_key was discarded and never
downloaded. Now extractPostImageKeys() collects image keys from
post JSON, and resolvePostImages() downloads them via the existing
Lark resource API. Also handles reply-to-post with images.
Refactored: shared resolvePostElements() helper for language variant
lookup, mediaMaxBytes() helper, and dedup via seen map.
- Update go.mod and Dockerfile to Go 1.26
- Apply `go fix ./...` stdlib modernizations across 170+ files
- Add `go fix` to post-implementation checklist in CLAUDE.md
- Fix go fix misapplied rewrite in loop_history.go
If probeBotInfo fails (missing bot:read permission), botOpenID is empty
and mention detection always returns false — causing all group messages
to be silently recorded to history instead of processed.
Now treats any mention as bot mention when botOpenID is unknown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>