Skip to content

feat: add Feishu/Lark connector with webhook event ingestion#2828

Open
joezhoujinjing wants to merge 7 commits intodevelopfrom
feat/feishu-connector
Open

feat: add Feishu/Lark connector with webhook event ingestion#2828
joezhoujinjing wants to merge 7 commits intodevelopfrom
feat/feishu-connector

Conversation

@joezhoujinjing
Copy link
Contributor

Summary

  • Implements Feishu/Lark connector backend (FeishuConnectorBackend) with dual auth model (bot tenant_access_token + user OAuth via OAuthConnectorMixin)
  • Adds webhook router at POST /api/v2/webhooks/feishu for inbound event ingestion with challenge verification and EventBus publishing
  • Registers connector in backends registry, service map, OAuth config, and pyproject.toml (lark-oapi dependency)

Details

Connector (Task #82 + #84):

  • VFS mount structure: /mnt/feishu/groups/{name}.yaml and /mnt/feishu/p2p/{name}.yaml
  • list_dir fetches chats from Feishu API, read_content returns messages as YAML, write_content sends messages via API
  • Supports YAML/JSON write format: msg_type: text + content: {text: "hello"}
  • Cache support via CacheConnectorMixin

Webhook (Task #83):

  • URL verification challenge handling (Feishu setup handshake)
  • Event token + signature verification
  • Event mapping: im.message.receive_v1FILE_WRITE, im.chat.member.bot.added_v1DIR_CREATE, im.chat.member.bot.deleted_v1DIR_DELETE
  • Publishes FileEvent to EventBus for downstream processing

Test plan

  • E2E tested list_chats — 11 chats returned from real Feishu workspace
  • E2E tested list_messages_from_chat — messages fetched from real chat
  • E2E tested send_message — message delivered to Feishu group
  • E2E tested full connector list_dir, read_content, write_content flow
  • Tested webhook challenge response
  • Tested event mapping (3 event types + unknown)
  • Tested EventBus publishing with mock bus
  • Integration test with VFS mount (manual)

🤖 Generated with Claude Code

Implement Nexus Feishu connector (Tasks #82-84):
- FeishuConnectorBackend with dual auth (bot + user OAuth)
- VFS mount at /mnt/feishu/ with groups/ and p2p/ structure
- Read (list_dir, read_content as YAML) and write (send_message) support
- Webhook router at POST /api/v2/webhooks/feishu for event ingestion
- Event mapping: message.receive -> FILE_WRITE, bot.added/deleted -> DIR_CREATE/DELETE
- lark-oapi SDK integration with retry/rate-limiting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor Author

@joezhoujinjing joezhoujinjing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technical Lead Review: Real-Time Cache Invalidation Strategy

The implementation correctly follows the 'Proxy-with-Cache' philosophy, but there is a consistency gap between the webhook ingestion and the VFS read path.

Problem:
The router publishes events, but the is currently unaware of these events. This means an agent reading a chat file might see stale data from the even after a new message has arrived via webhook.

Requested Solution:
Please implement a Cache Invalidation Loop:

  1. Subscribe: The should subscribe to events matching its mount prefix.
  2. Invalidate: When an event is received for a chat path (e.g., ), the backend should call .
  3. Consistency: This ensures the next call bypasses the stale cache and fetches the latest state from the Feishu SSOT.

This closes the loop between 'Real-Time Events' and 'Filesystem Reads,' ensuring our AI agents always operate on the most current reality.

Copy link
Contributor Author

@joezhoujinjing joezhoujinjing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technical Lead Review: Real-Time Cache Invalidation Strategy

The implementation correctly follows the 'Proxy-with-Cache' philosophy, but there is a consistency gap between the webhook ingestion and the VFS read path.

Problem:
The feishu_webhook router publishes FILE_WRITE events, but the FeishuConnectorBackend is currently unaware of these events. This means an agent reading a chat file might see stale data from the CacheConnectorMixin even after a new message has arrived via webhook.

Requested Solution:
Please implement a Cache Invalidation Loop:

  1. Subscribe: The FeishuConnectorBackend should subscribe to FILE_WRITE events matching its mount prefix.
  2. Invalidate: When an event is received for a chat path (e.g., /mnt/feishu/groups/{id}.yaml), the backend should call self._invalidate_cache(path).
  3. Consistency: This ensures the next read_content call bypasses the stale cache and fetches the latest state from the Feishu SSOT.

This closes the loop between 'Real-Time Events' and 'Filesystem Reads,' ensuring our AI agents always operate on the most current reality.

When a Feishu webhook event arrives (e.g. new message), the connector's
cached YAML for that chat is invalidated so the next read_content()
fetches fresh data from the Feishu API instead of serving stale cache.

Implementation:
- Webhook router: register_cache_invalidator() callback registry
- Webhook handler: calls all registered invalidators after publishing FileEvent
- Connector: handle_event() strips mount prefix and calls _invalidate_cache()
- Connector registers itself with webhook on init

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor Author

@joezhoujinjing joezhoujinjing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technical Lead Refactor: Semantic Prefix & Hybrid Naming Strategy

After architectural review, we are shifting the Feishu VFS structure to a more robust and semantically clear model. Please refactor the implementation with the following changes:

  1. New Prefix: Change the mount prefix from /mnt/feishu/ to /chat/feishu/. This aligns with our 'Unified Messaging OS' philosophy.
  2. Hybrid Naming Scheme: Filenames must use the pattern {Name} [{ID}].yaml (e.g., General [oc_123].yaml).
    • Requirement: list_dir must append the ID in brackets.
    • Requirement: read_content and write_content must parse the ID from the brackets to ensure persistent resolution even if the chat is renamed.
  3. Webhook Alignment: Update the feishu_webhook.py router to map inbound events to the new /chat/feishu/ paths.

This change ensures that AI agent 'links' to these files remain stable over time and that the filesystem remains human-readable.

joezhoujinjing and others added 5 commits March 8, 2026 15:02
Per tech lead review:
1. Mount prefix: /mnt/feishu/ -> /chat/feishu/ (Unified Messaging OS)
2. Hybrid filenames: "{Name} [{chat_id}].yaml" (e.g. "General [oc_123].yaml")
   - list_dir appends ID in brackets for human-readable + stable links
   - read_content/write_content parse chat_id from brackets for API resolution
   - Chat renames don't break agent file references
3. Webhook paths updated to /chat/feishu/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P2P bot conversations are not returned by Feishu's List Chats API.
This adds a P2P registry populated from webhook events:
- _discover_p2p_chat() resolves peer name via get_chat_members()
- list_dir("p2p") returns discovered P2P chats with hybrid naming
- New utils: send_p2p_message(), get_chat_members(), chat_mode in get_chat_info()
- Fix import paths for current branch module structure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add FeishuEventTranslator (events.py): single source of truth for
  Feishu event -> FileEvent mapping, used by both webhook and WS worker
- Add FeishuWebSocketWorker (ws_worker.py): persistent long connection
  via lark_oapi.ws.Client, zero-config (no public URL needed)
- Refactor webhook router to use shared translate_feishu_event()
- Auto-start WS worker in server lifespan if credentials found
  (UserSecretsService -> env var fallback)
- Graceful shutdown on server stop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When debug_echo=True, the WS worker replies to incoming messages with
the VFS path they were mapped to, e.g. "[nexus-echo] /chat/feishu/p2p/oc_xxx.yaml".
Useful for E2E debugging without running the full Nexus server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gestion

Delete feishu_webhook.py and its registration in fastapi_server.py.
Update connector cache invalidation to use ws_worker directly.
Clean up all webhook references in comments and docstrings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant