Skip to content

feat: Feishu document comment intelligent reply with 3-tier access control#11023

Closed
liujinkun2025 wants to merge 1 commit into
NousResearch:mainfrom
liujinkun2025:feat/feishu-document-comment
Closed

feat: Feishu document comment intelligent reply with 3-tier access control#11023
liujinkun2025 wants to merge 1 commit into
NousResearch:mainfrom
liujinkun2025:feat/feishu-document-comment

Conversation

@liujinkun2025

@liujinkun2025 liujinkun2025 commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Add support for responding to Feishu document comments (drive.notice.comment_add_v1 events) with LLM-powered intelligent replies, including a configurable 3-tier access control system.

Comment Handler

  • Full event orchestration: parse comment event → filter → fetch doc meta + comment details → build timeline → run agent → deliver reply
  • Smart timeline selection: local comments (max 20 entries), whole-document comments (max 12 entries)
  • Long text chunking (4000 chars) for reply delivery with automatic fallback
  • Cross-card session sharing per document (in-memory cache, 50 msg cap, 1h TTL)
  • Semantic text extraction (strip @mention noise)
  • Document link resolution via /wiki/v2/spaces/get_node
  • OK reaction indicator while agent is processing

Tools (5 new)

  • feishu_doc_read — read document raw content
  • feishu_drive_list_comments — list document comments
  • feishu_drive_list_comment_replies — list comment thread replies
  • feishu_drive_reply_comment — reply to a comment
  • feishu_drive_add_comment — add a whole-document comment

3-Tier Access Control (feishu_comment_rules.py)

  • Priority: exact document rule > wildcard "*" rule > top-level config > code defaults
  • Per-field fallback: enabled, policy, allow_from each resolve independently through the tiers
  • Policies: allowlist (static list only) / pairing (static list ∪ runtime-approved store)
  • Explicit-grant only: every user who triggers a reply must be listed in allow_from or in the pairing-approved store — there is no implicit allow-all mode
  • Config: ~/.hermes/feishu_comment_rules.json, mtime-cached hot-reload (no restart needed)
  • CLI: status / check <doc> <user> / pairing add|remove|list

Event Filtering

  • Self-reply filter using generalized self_open_id (supports both tenant and future user-identity modes)
  • Receiver check: only process events where the bot is the @mentioned target
  • Notice type filter: only add_comment and add_reply

Files Changed

File Description
gateway/platforms/feishu_comment.py Main comment handler + prompt builders + API layer
gateway/platforms/feishu_comment_rules.py 3-tier access control engine + pairing store + CLI
gateway/platforms/feishu.py Register comment event handler in adapter
tools/feishu_doc_tool.py Document read tool
tools/feishu_drive_tool.py 4 drive comment tools
toolsets.py Register feishu_doc and feishu_drive toolsets

Config Example

{
  "enabled": true,
  "policy": "pairing",
  "allow_from": ["ou_global_reviewer"],
  "documents": {
    "*": { "policy": "pairing", "allow_from": [] },
    "docx:dxxExxxxxMxxmxxxxxxxx": { "policy": "allowlist", "allow_from": ["ou_doc_owner", "ou_teammate"] }
  }
}

Test Plan

  • Allowlist policy: empty allow_from denies all users silently
  • Allowlist policy: user in allow_from gets normal reply
  • Exact document rule overrides top-level (doc allowlist + narrower allow_from, top pairing)
  • Exact document rule overrides wildcard (doc allowlist, wildcard pairing)
  • Wildcard rule applies when no exact match
  • Pairing policy: unapproved user silently denied
  • Pairing policy: CLI pairing add grants access immediately (hot-reload)
  • Pairing policy: user listed in allow_from granted without CLI approval (union semantics)
  • Self-reply filter: bot's own comments are skipped
  • Receiver filter: comments not @mentioning the bot are skipped

@liujinkun2025 liujinkun2025 force-pushed the feat/feishu-document-comment branch 4 times, most recently from 55a04c1 to 05f06f2 Compare April 16, 2026 14:35
@liujinkun2025 liujinkun2025 force-pushed the feat/feishu-document-comment branch from 05f06f2 to e899175 Compare April 17, 2026 07:30
…s control

- Full comment handler: parse drive.notice.comment_add_v1 events, build
  timeline, run agent, deliver reply with chunking support.
- 5 tools: feishu_doc_read, feishu_drive_list_comments,
  feishu_drive_list_comment_replies, feishu_drive_reply_comment,
  feishu_drive_add_comment.
- 3-tier access control rules (exact doc > wildcard "*" > top-level >
  defaults) with per-field fallback. Config via
  ~/.hermes/feishu_comment_rules.json, mtime-cached hot-reload.
- Self-reply filter using generalized self_open_id (supports future
  user-identity subscriptions). Receiver check: only process events
  where the bot is the @mentioned target.
- Smart timeline selection, long text chunking, semantic text extraction,
  session sharing per document, wiki link resolution.

Change-Id: I31e82fd6355173dbcc400b8934b6d9799e3137b9
@liujinkun2025 liujinkun2025 force-pushed the feat/feishu-document-comment branch from e899175 to 82b3c46 Compare April 17, 2026 09:54
teknium1 added a commit that referenced this pull request Apr 18, 2026
…; AUTHOR_MAP

Follow-up polish on top of the cherry-picked #11023 commit.

- feishu_comment_rules.py: replace import-time "~/.hermes" expanduser fallback
  with get_hermes_home() from hermes_constants (canonical, profile-safe).
- tools/feishu_doc_tool.py, tools/feishu_drive_tool.py: drop the
  asyncio.get_event_loop().run_until_complete(asyncio.to_thread(...)) dance.
  Tool handlers run synchronously in a worker thread with no running loop, so
  the RuntimeError branch was always the one that executed. Calls client.request
  directly now. Unused asyncio import removed.
- tests/gateway/test_feishu.py: add register_p2_customized_event to the mock
  EventDispatcher builder so the existing adapter test matches the new handler
  registration for drive.notice.comment_add_v1.
- scripts/release.py: map liujinkun@bytedance.com -> liujinkun2025 for
  contributor attribution on release notes.
teknium1 added a commit that referenced this pull request Apr 18, 2026
…; AUTHOR_MAP

Follow-up polish on top of the cherry-picked #11023 commit.

- feishu_comment_rules.py: replace import-time "~/.hermes" expanduser fallback
  with get_hermes_home() from hermes_constants (canonical, profile-safe).
- tools/feishu_doc_tool.py, tools/feishu_drive_tool.py: drop the
  asyncio.get_event_loop().run_until_complete(asyncio.to_thread(...)) dance.
  Tool handlers run synchronously in a worker thread with no running loop, so
  the RuntimeError branch was always the one that executed. Calls client.request
  directly now. Unused asyncio import removed.
- tests/gateway/test_feishu.py: add register_p2_customized_event to the mock
  EventDispatcher builder so the existing adapter test matches the new handler
  registration for drive.notice.comment_add_v1.
- scripts/release.py: map liujinkun@bytedance.com -> liujinkun2025 for
  contributor attribution on release notes.
@teknium1

Copy link
Copy Markdown
Contributor

Merged via #11898 onto current main.

Your commit was cherry-picked with authorship preserved — git log on main shows commit 85cdb04 authored by you. Thanks for the thoughtful design (3-tier access model, mtime hot-reload, per-doc session cache, no implicit allow-all) and the solid test coverage.

Small polish we added on top:

  • switched ~/.hermes path resolution in feishu_comment_rules.py to get_hermes_home() from hermes_constants (canonical helper, profile-safe)
  • dropped the asyncio.get_event_loop().run_until_complete(...) dance in the tool handlers — tool handlers run in a worker thread with no running loop, so the RuntimeError fallback was always the branch that executed. Replaced with a direct client.request(req) call
  • updated the existing test_build_event_handler_registers_reaction_and_card_processors mock builder to include register_p2_customized_event for the new handler

Closes #11465.

@teknium1 teknium1 closed this Apr 18, 2026
ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
…; AUTHOR_MAP

Follow-up polish on top of the cherry-picked NousResearch#11023 commit.

- feishu_comment_rules.py: replace import-time "~/.hermes" expanduser fallback
  with get_hermes_home() from hermes_constants (canonical, profile-safe).
- tools/feishu_doc_tool.py, tools/feishu_drive_tool.py: drop the
  asyncio.get_event_loop().run_until_complete(asyncio.to_thread(...)) dance.
  Tool handlers run synchronously in a worker thread with no running loop, so
  the RuntimeError branch was always the one that executed. Calls client.request
  directly now. Unused asyncio import removed.
- tests/gateway/test_feishu.py: add register_p2_customized_event to the mock
  EventDispatcher builder so the existing adapter test matches the new handler
  registration for drive.notice.comment_add_v1.
- scripts/release.py: map liujinkun@bytedance.com -> liujinkun2025 for
  contributor attribution on release notes.
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…; AUTHOR_MAP

Follow-up polish on top of the cherry-picked NousResearch#11023 commit.

- feishu_comment_rules.py: replace import-time "~/.hermes" expanduser fallback
  with get_hermes_home() from hermes_constants (canonical, profile-safe).
- tools/feishu_doc_tool.py, tools/feishu_drive_tool.py: drop the
  asyncio.get_event_loop().run_until_complete(asyncio.to_thread(...)) dance.
  Tool handlers run synchronously in a worker thread with no running loop, so
  the RuntimeError branch was always the one that executed. Calls client.request
  directly now. Unused asyncio import removed.
- tests/gateway/test_feishu.py: add register_p2_customized_event to the mock
  EventDispatcher builder so the existing adapter test matches the new handler
  registration for drive.notice.comment_add_v1.
- scripts/release.py: map liujinkun@bytedance.com -> liujinkun2025 for
  contributor attribution on release notes.
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…; AUTHOR_MAP

Follow-up polish on top of the cherry-picked NousResearch#11023 commit.

- feishu_comment_rules.py: replace import-time "~/.hermes" expanduser fallback
  with get_hermes_home() from hermes_constants (canonical, profile-safe).
- tools/feishu_doc_tool.py, tools/feishu_drive_tool.py: drop the
  asyncio.get_event_loop().run_until_complete(asyncio.to_thread(...)) dance.
  Tool handlers run synchronously in a worker thread with no running loop, so
  the RuntimeError branch was always the one that executed. Calls client.request
  directly now. Unused asyncio import removed.
- tests/gateway/test_feishu.py: add register_p2_customized_event to the mock
  EventDispatcher builder so the existing adapter test matches the new handler
  registration for drive.notice.comment_add_v1.
- scripts/release.py: map liujinkun@bytedance.com -> liujinkun2025 for
  contributor attribution on release notes.
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…; AUTHOR_MAP

Follow-up polish on top of the cherry-picked NousResearch#11023 commit.

- feishu_comment_rules.py: replace import-time "~/.hermes" expanduser fallback
  with get_hermes_home() from hermes_constants (canonical, profile-safe).
- tools/feishu_doc_tool.py, tools/feishu_drive_tool.py: drop the
  asyncio.get_event_loop().run_until_complete(asyncio.to_thread(...)) dance.
  Tool handlers run synchronously in a worker thread with no running loop, so
  the RuntimeError branch was always the one that executed. Calls client.request
  directly now. Unused asyncio import removed.
- tests/gateway/test_feishu.py: add register_p2_customized_event to the mock
  EventDispatcher builder so the existing adapter test matches the new handler
  registration for drive.notice.comment_add_v1.
- scripts/release.py: map liujinkun@bytedance.com -> liujinkun2025 for
  contributor attribution on release notes.
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…; AUTHOR_MAP

Follow-up polish on top of the cherry-picked NousResearch#11023 commit.

- feishu_comment_rules.py: replace import-time "~/.hermes" expanduser fallback
  with get_hermes_home() from hermes_constants (canonical, profile-safe).
- tools/feishu_doc_tool.py, tools/feishu_drive_tool.py: drop the
  asyncio.get_event_loop().run_until_complete(asyncio.to_thread(...)) dance.
  Tool handlers run synchronously in a worker thread with no running loop, so
  the RuntimeError branch was always the one that executed. Calls client.request
  directly now. Unused asyncio import removed.
- tests/gateway/test_feishu.py: add register_p2_customized_event to the mock
  EventDispatcher builder so the existing adapter test matches the new handler
  registration for drive.notice.comment_add_v1.
- scripts/release.py: map liujinkun@bytedance.com -> liujinkun2025 for
  contributor attribution on release notes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants