feat(feishu): add list_group_members tool + native @mention support (#280)

* feat(feishu): add list_group_members tool + native @mention support

Add agent tool to list all members in a Lark group chat via
GET /open-apis/im/v1/chats/{chat_id}/members with auto-pagination.

- New tool `list_group_members`: returns member IDs + display names
- Native @mention conversion: @ou_xxx in outbound messages becomes
  real Lark mentions (post: "at" element, card: <at> markdown tag)
- Auto-sync discovered members into ContactStore
- GroupMemberProvider interface on Channel for extensibility
- Add im:chat.members:read scope to channel setup UI

Requires Lark app scope: im:chat.members:read

* fix(feishu): URL-encode API params + channel-aware tool filtering

- URL-encode chatID (PathEscape) and pageToken (QueryEscape) in
  ListChatMembers to prevent path traversal and query corruption
- Add slog.Warn when ListGroupMembers fails for observability
- Add ChannelAware interface so tools can declare required channel types
- ListGroupMembersTool now declares RequiredChannelTypes=["feishu"]
- loop.go filters out channel-specific tools when channel type mismatches,
  so list_group_members only appears for Feishu conversations

---------

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Co-authored-by: viettranx <viettranx@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-03-19 17:21:38 +07:00
1 parent ec24b982f3
commit bb783b952c
10 files changed
+288 -12

No files matched your search

+8
View File
@@ -184,6 +184,8 @@ func runGateway() {
// Message tool (send to channels)
toolsReg.Register(tools.NewMessageTool(workspace, agentCfg.RestrictToWorkspace))
// Group members tool (list members in group chats)
toolsReg.Register(tools.NewListGroupMembersTool())
slog.Info("session + message tools registered")
// Register legacy tool aliases (backward-compat names from policy.go).
@@ -426,6 +428,12 @@ func runGateway() {
cs.SetChannelSender(channelMgr.SendToChannel)
}
}
// Wire group member lister on list_group_members tool
if t, ok := toolsReg.Get("list_group_members"); ok {
if gl, ok := t.(tools.GroupMemberListerAware); ok {
gl.SetGroupMemberLister(channelMgr.ListGroupMembers)
}
}
// Load channel instances from DB.
var instanceLoader *channels.InstanceLoader