Skip to content

feat(gateway): add Feishu/Lark adapter#704

Merged
thepagent merged 10 commits intoopenabdev:mainfrom
wangyuyan-agent:feat/gateway-feishu-adapter
May 2, 2026
Merged

feat(gateway): add Feishu/Lark adapter#704
thepagent merged 10 commits intoopenabdev:mainfrom
wangyuyan-agent:feat/gateway-feishu-adapter

Conversation

@wangyuyan-agent
Copy link
Copy Markdown
Contributor

Summary

Closes #703.

Adds Feishu/Lark support to the Custom Gateway. Users can chat with OAB agents in Feishu DMs and group chats.

Default mode is WebSocket long-connection (no public URL needed). Webhook fallback available for restricted networks.

                     External                          Internal

Telegram  --POST-->  ┌─────────────────┐
LINE      --POST-->  │  OpenAB Gateway │ <--WS-- OAB Pod
Teams     --POST-->  │    :8080        │
                     │  ┌───────────┐  │
Feishu    <--WS----  │  │ Feishu WS │  │
(default)            │  │ Client    │  │
                     │  └───────────┘  │
Feishu    --POST-->  │  (fallback)     │
                     └─────────────────┘

Changes

File Change Description
gateway/src/adapters/feishu.rs NEW Feishu adapter: WS long-connection, webhook fallback, token cache, event parsing, reactions, dedupe, mention gating
gateway/src/adapters/mod.rs MOD Add pub mod feishu
gateway/src/main.rs MOD AppState + reply router + WS spawn logic
gateway/Cargo.toml MOD Add prost 0.13, aes 0.8, cbc 0.1
gateway/Cargo.lock MOD Dependency lockfile
gateway/README.md MOD Add feishu env vars and /webhook/feishu endpoint
charts/openab/values.yaml MOD Add feishu config block under gateway
charts/openab/templates/gateway.yaml MOD Inject feishu env vars (secrets via secretKeyRef)
charts/openab/templates/gateway-secret.yaml MOD Unified Secret for teams + feishu
docs/feishu.md NEW Operator guide: prerequisites, setup, config reference, troubleshooting
README.md MOD Add Feishu/Lark to platform list and architecture diagram
docs/config-reference.md MOD Add "feishu" to gateway platform values

Key Design Decisions

  1. No third-party SDKopen-lark (1560+ APIs) and feishu-sdk (55K SLoC) are too heavy and have version conflicts with gateway deps. Used tokio-tungstenite (already in gateway) + prost for protobuf frame decoding.

  2. Protobuf binary frames — Feishu WebSocket v2 sends protobuf-encoded pbbp2.Frame, not JSON text frames. The adapter decodes frames via prost and extracts JSON payload.

  3. AppID+AppSecret auth for WS endpoint — Feishu /callback/ws/endpoint uses {"AppID":"...","AppSecret":"..."} in request body, not Bearer token. Different from all other Feishu APIs.

  4. Dual mention gating — Gateway-side (FEISHU_REQUIRE_MENTION, default true) filters at the source; OAB-side (bot_username) provides second layer. Mentions use open_id since Feishu has no stable username.

Testing

Verified end-to-end with two Feishu test bots on a live server:

Bot identity:  ou_e86566e90b8b87d8062e9a79f845b03c  (Bot1)
               ou_228fbf2af502d0ba3203778f16162cdd  (Bot2)
Scenario Result
WebSocket connection + bot identity discovery PASS
DM text message send/receive PASS
Agent session creation and reuse PASS
Reaction emoji on original message PASS
Group: no @mention --> no reply PASS
Group: @BOT1 --> only Bot1 replies PASS
Group: @bot2 --> only Bot2 replies PASS
Token auto-refresh PASS
Exponential backoff reconnect (1s-->120s) PASS
cargo test -- 47 passed, 0 warnings PASS

Configuration

# Helm values.yaml
agents:
  kiro:
    gateway:
      enabled: true
      url: "ws://openab-kiro-gateway:8080/ws"
      platform: "feishu"
      botUsername: "ou_YOUR_BOT_OPEN_ID"
      feishu:
        appId: "cli_xxx"
        appSecret: "secret_xxx"
        domain: "feishu"            # or "lark"
        connectionMode: "websocket"  # or "webhook"
# OAB config.toml
[gateway]
url = "ws://127.0.0.1:8080/ws"
platform = "feishu"
bot_username = "ou_YOUR_BOT_OPEN_ID"

Breaking Changes

gateway-secret.yaml rewritten from Teams-only to unified Secret containing both Teams and Feishu secrets. Teams-only deployments are unaffected (template is conditional).

Discord Discussion URL

https://discord.com/channels/1491295327620169908/1500160821567684660

@wangyuyan-agent wangyuyan-agent requested a review from thepagent as a code owner May 2, 2026 15:56
@github-actions github-actions Bot added the pending-screening PR awaiting automated screening label May 2, 2026
@shaun-agent
Copy link
Copy Markdown
Contributor

OpenAB PR Screening

This is auto-generated by the OpenAB project-screening flow for context collection and reviewer handoff.
Click 👍 if you find this useful. Human review will be done within 24 hours. We appreciate your support and contribution 🙏

Screening report ## Intent

Add first-class Feishu/Lark support to the OpenAB Custom Gateway so users can talk to OAB agents from Feishu direct messages and group chats. The visible problem being solved is that Feishu/Lark workspaces currently cannot use OpenAB as a chat surface without custom glue code, and some deployments cannot expose a public webhook URL.

Feat

This is a feature PR.

It adds a Feishu/Lark gateway adapter with WebSocket long-connection mode as the default, plus webhook fallback. The adapter handles Feishu authentication, protobuf WebSocket frame decoding, event parsing, token refresh, reply routing, group mention gating, deduplication, reactions, Helm configuration, and operator documentation.

Who It Serves

Primary beneficiaries are Feishu/Lark users and deployers who want OpenAB agents available inside their workplace chat.

Secondary beneficiaries are gateway operators and maintainers because the PR documents platform setup, Helm values, environment variables, and routing behavior.

Rewritten Prompt

Implement a Feishu/Lark adapter for the OpenAB gateway.

Requirements:

  • Support Feishu/Lark direct messages and group chats.
  • Use WebSocket long-connection mode by default so deployments do not require a public callback URL.
  • Provide webhook mode as a fallback.
  • Decode Feishu WebSocket v2 protobuf frames and extract JSON event payloads.
  • Authenticate using Feishu App ID/App Secret for WebSocket endpoint discovery and tenant access tokens for API calls.
  • Cache and refresh access tokens safely.
  • Route gateway replies back to the correct Feishu chat/message.
  • Gate group replies behind bot mentions by default, using the bot open_id.
  • Deduplicate inbound events.
  • Add Helm values, secret wiring, config reference updates, gateway README notes, and an operator guide.
  • Add focused tests for event parsing, mention gating, dedupe behavior, token refresh boundaries, and reply routing.

Keep the implementation isolated to the gateway adapter path unless shared gateway routing changes are required.

Merge Pitch

This is worth advancing because it expands OpenAB into a major workplace-chat platform and solves a concrete deployment constraint: Feishu WebSocket mode avoids requiring public inbound webhooks.

Risk is moderate to high because the PR adds a large new adapter, new dependencies, secret-template changes, and a protocol-specific WebSocket path. The likely reviewer concerns are dependency footprint, auth handling, replay/dedupe correctness, secret migration safety, and whether the live-tested behavior is backed by enough automated tests.

Best-Practice Comparison

OpenClaw principles that fit:

  • Explicit delivery routing is directly relevant. Feishu replies need stable routing back to the correct chat and message context.
  • Retry/backoff and run logs are relevant for WebSocket reconnects, token refresh failures, and observable adapter failures.
  • Isolated executions partially applies at the gateway/OAB boundary: the adapter should avoid leaking Feishu-specific state into unrelated platform adapters.
  • Durable job persistence is only partially relevant. This adapter is event-driven, not job-scheduled, but dedupe state may need persistence if duplicate delivery across restarts becomes a real issue.
  • Gateway-owned scheduling does not really apply.

Hermes Agent principles that fit:

  • Gateway daemon tick model is only loosely relevant. The Feishu WebSocket client behaves like a long-running daemon loop, but not a scheduled task runner.
  • Atomic writes for persisted state would matter only if token cache or dedupe state is persisted to disk.
  • File locking to prevent overlap is not relevant unless multiple gateway processes share a local persisted state file.
  • Fresh session per scheduled run is not relevant.
  • Self-contained prompts for scheduled tasks is not relevant.

The strongest best-practice alignment is around explicit routing, bounded adapter isolation, reconnect/backoff, and operator-visible logs.

Implementation Options

Option 1: Conservative adapter-only merge

Merge Feishu support with the smallest shared-gateway surface area. Keep all Feishu protocol handling inside gateway/src/adapters/feishu.rs, require focused unit tests around parsing/gating/dedupe, and avoid introducing broader gateway abstractions.

Option 2: Balanced merge with shared gateway hardening

Merge the adapter while also tightening shared gateway reply routing, logging, error taxonomy, and platform config validation. Add tests for both Feishu-specific behavior and cross-platform routing assumptions.

Option 3: Ambitious platform-adapter framework

Use this PR as the starting point for a more formal gateway adapter framework: common token-cache traits, delivery routing contracts, dedupe interfaces, observability hooks, and integration-test harnesses for all chat platforms.

Comparison Table

Option Speed to ship Complexity Reliability Maintainability User impact Fit for OpenAB right now
Conservative adapter-only merge High Medium Medium Medium High Good if reviewer scope stays tight
Balanced merge with shared hardening Medium Medium-High High High High Best overall fit
Ambitious adapter framework Low High High long-term High long-term Delayed Too large for this PR

Recommendation

Advance with Option 2: Balanced merge with shared gateway hardening.

The PR is valuable, but the adapter is large enough that reviewers should not evaluate it only as a platform add-on. The merge discussion should focus on whether routing, auth, reconnect behavior, dedupe, config validation, and secret handling are testable and observable.

A practical sequence would be:

  1. First review the shared gateway and Helm changes for migration risk.
  2. Then review the Feishu adapter protocol path.
  3. Require focused automated tests around parsing, mention gating, dedupe, and reply routing.
  4. Split any broader adapter-framework cleanup into a follow-up issue instead of blocking the Feishu launch.

Add Feishu/Lark support to the Custom Gateway. Users can chat with OAB
agents in Feishu DMs and group chats.

- WebSocket long-connection (default): gateway connects outbound to
  Feishu, no public URL required. Protobuf frame decoding via prost.
- HTTP Webhook fallback: URL challenge, verification token, encrypt key
  (AES-256-CBC), signature verification, rate limiting, body limit.
- Token management: tenant_access_token with auto-refresh.
- Reactions: emoji mapped to Feishu reaction API on original messages.
- Mention gating: mentions carry open_id; bot_username = bot open_id.
- Deduplication: event_id + message_id TTL cache.
- Helm: values.yaml, gateway.yaml env injection, unified Secret.
- Docs: operator guide with setup, config reference, troubleshooting.

New dependencies: prost 0.13, aes 0.8, cbc 0.1.
46 tests passed, 0 warnings.

Discord Discussion: https://discord.com/channels/1491295327620169908/1500160821567684660
@wangyuyan-agent wangyuyan-agent force-pushed the feat/gateway-feishu-adapter branch from 652218a to 5f968e1 Compare May 2, 2026 16:20
@chaodu-agent
Copy link
Copy Markdown
Collaborator

chaodu-agent commented May 2, 2026

🟡 Four-Monk Consolidated Review

Reviewers: 超渡法師 (Kiro) · 普渡法師 (Claude) · 擺渡法師 (Codex) · 覺渡法師 (Gemini)

Overall: Strong PR with clean architecture, good test coverage, and proper secret handling. Two blocking issues found; the rest are non-blocking improvements.


🔴 SUGGESTED CHANGES (2)

  1. split_text panics on multi-byte UTF-8 (超渡 + 擺渡 + 覺渡) — text[start..end] slices on byte offset. Chinese text will panic with byte index X is not a char boundary. Fix: add is_char_boundary() check.

  2. Webhook mode: bot_open_id always NonerequireMention silently fails (擺渡) — bot_open_id only populated in start_websocket(). In webhook mode, all group messages pass through unfiltered. Fix: resolve bot_open_id during adapter init or lazily on first webhook.

🟡 NIT (11)
# Finding Source
1 extract_mentions uses text.replace(key, "") — removes all occurrences, eats normal text too. Use replacen(..., 1). 覺渡 + 擺渡
2 Token TTL hardcoded to 7200s — should read API response expire field. 覺渡 + 擺渡
3 allowed_groups / allowed_users parsed from env but never checked in parse_message_event(). 超渡
4 _feishu_shutdown_tx created but never used — no graceful shutdown path. 超渡
5 ws_connect_loop takes _token_cache (underscore-suppressed) — never refreshes token on reconnect. 普渡
6 jsonwebtoken in Cargo.toml but never used in feishu.rs — residual dependency. 普渡
7 Stale #[allow(dead_code)] / #[allow(unused_imports)] on event_types module. 普渡
8 When encryptKey not configured, signature verification entirely skipped — no warning logged. 普渡
9 verification_token compared with != — timing side-channel. Use constant_time_eq. 普渡
10 WS mode: challenge frame not handled (HTTP handler has it, WS does not). 普渡
11 docs/feishu.md + gateway/README.md missing 6 env vars: FEISHU_DEDUPE_TTL_SECS, FEISHU_MESSAGE_LIMIT, FEISHU_ALLOWED_GROUPS, FEISHU_ALLOWED_USERS, FEISHU_REQUIRE_MENTION, FEISHU_VERIFICATION_TOKEN. 普渡
🟢 INFO — What's done well
  • Dual WebSocket/webhook architecture — WS default (no public URL) with webhook fallback.
  • Double-check locking on FeishuTokenCache (RwLock fast path + write lock re-check).
  • Secrets properly stored in K8s Secret via secretKeyRef.
  • 20+ tests: token refresh, send message, event parsing (7 scenarios), dedupe, signature, rate limiter.
  • Error messages tested to not leak secrets.
  • Exponential backoff reconnect (1s→120s).
  • Helm templates properly conditional — Teams-only deployments unaffected.
🟡 Missing: Prior Art Section

Per RFC: PR Contribution Guidelines, PRs must include research on OpenClaw and Hermes Agent. The PR description is otherwise excellent but this mandatory section is absent.


📚 Prior Art: OpenClaw & Hermes Agent Feishu Implementations

OpenClaw (larksuite/openclaw-lark)

Official Lark/Feishu plugin for OpenClaw (TypeScript, 2.1k stars), maintained by the Lark Open Platform team. Goes far beyond messaging — integrates Docs, Base, Sheets, Calendar, Tasks. Key features we don't have yet:

  • Interactive Cards with real-time status updates (Thinking/Generating/Complete) + confirmation buttons for sensitive operations
  • Streaming Responses rendered live inside message cards
  • Per-group settings including allowlists, skill bindings, and custom system prompts

Hermes Agent (feishu docs)

Hermes has a mature Feishu adapter with several patterns worth noting:

Feature Hermes This PR Takeaway
Timing-safe token comparison constant_time_eq ❌ uses != Confirms NIT #9 — should fix
Bot identity in webhook mode ✅ auto-detect on startup ❌ only in WS mode Confirms 🔴 #2 — must fix
Per-group ACL ✅ 5 policies (open/allowlist/blacklist/admin_only/disabled) + per-chat require_mention override ❌ global only Future enhancement
Bot-to-bot messaging FEISHU_ALLOW_BOTS (none/mentions/all) ❌ skips all bot senders Needed for multi-agent collaboration
Text batching ✅ 0.6s debounce, merges rapid-fire messages Prevents agent overload from burst sends
Media support ✅ images/audio/video/files ❌ text only Future enhancement
Markdown → Post fallback ✅ auto-detects markdown, sends as rich post message ❌ text only Better rendering in Feishu client
Dedup persistence ✅ persisted to disk, survives restarts ❌ in-memory only Future enhancement
Webhook anomaly tracking ✅ warns after 25 consecutive errors per IP Defense-in-depth
Interactive Card Actions ✅ button callbacks, command approval flow Future enhancement
Group session isolation group_sessions_per_user Future enhancement

Conclusion

PR #704's core architecture (WS + webhook dual mode, protobuf decoding, token cache with double-check locking) aligns with Hermes's design direction, validating the architectural choices. Both 🔴 blocking issues found by the four-monk review (split_text UTF-8 panic and webhook-mode bot_open_id) are correctly handled in Hermes's implementation, confirming they must be fixed before merge.

For future iterations, text batching and per-group ACL are the highest-value features to adopt from Hermes. Interactive Cards from OpenClaw would significantly improve UX but require more design work.

超渡法師 and others added 3 commits May 2, 2026 16:33
split_text used byte-level slicing without checking char boundaries,
which panics on multi-byte UTF-8 text (e.g. Chinese). Added
is_char_boundary() guard and a Chinese text test case.

48 tests passed.
🔴 Fixes:
- split_text UTF-8 safety (previous commit)
- Webhook mode bot_open_id always None: added resolve_bot_identity()
  called during adapter init for both WS and webhook modes

🟡 Fixes:
- extract_mentions: replace → replacen(..., 1) to avoid eating normal text
- Token TTL: read expire field from API response instead of hardcoded 7200
- _feishu_shutdown_tx: removed underscore, added lifetime comment, drop()
- ws_connect_loop: use token_cache for bot identity refresh on reconnect
- Removed stale #[allow(dead_code)] and #[allow(unused_imports)]
- Log warning when encryptKey not configured in webhook mode
- verification_token + verify_signature: timing-safe comparison via subtle
- WS mode: handle challenge frame (log + return early)
- Docs: added missing env vars to feishu.md and gateway/README.md

48 tests passed, 0 failures.
search_start = end - 200 could land in the middle of a multi-byte
UTF-8 character, causing text[search_start..end] to panic. Advance
search_start forward to the nearest char boundary.

Added regression test with 900-byte Chinese text and limit=500 to
exercise the search_start boundary path.
@chaodu-agent
Copy link
Copy Markdown
Collaborator

✅ Re-review: all blocking issues resolved

Pushed 3a0b616 — fixes search_start char boundary in split_text (found by 擺渡法師 during re-review). search_start = end - 200 could land mid-char on Chinese text, causing text[search_start..end] to panic. Now advances to nearest char boundary. Added regression test with 900-byte Chinese text.

Status after 3 fix commits:

  • 🔴 split_text UTF-8 panic → ✅ Fixed (including search_start)
  • 🔴 Webhook bot_open_id → ✅ Fixed
  • 🟡 11 NITs → ✅ 10 fixed, 1 deferred (display_name shows ou_xxx — follow-up)
  • jsonwebtoken in Cargo.toml confirmed used by Teams adapter

No remaining blockers. Ready for maintainer review.

Feishu DMs only include open_id, not sender name. Added resolve_user_name()
that calls /open-apis/contact/v3/users/:open_id with in-memory caching
(up to 10K entries). Wired into both WebSocket and webhook paths.

Requires contact:user.base:readonly scope (recommended, not required —
falls back to open_id gracefully).

Prior art: OpenClaw uses resolveSenderNames (default true) + contacts-sync
skill for zero-API-call lookup. Hermes auto-detects bot identity but
doesn't resolve human sender names.

50 tests passed, 0 failures.
chaodu-agent
chaodu-agent previously approved these changes May 2, 2026
Copy link
Copy Markdown
Collaborator

@chaodu-agent chaodu-agent left a comment

Choose a reason for hiding this comment

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

LGTM ✅ — All 🔴 and 🟡 findings from four-monk review addressed. Sender name resolution added per OpenClaw/Hermes prior art.

Fixes applied (3 commits by 超渡法師)

🔴 SUGGESTED CHANGES — fixed

  1. split_text UTF-8 panic — added is_char_boundary() guard + Chinese text test
  2. Webhook bot_open_id always None — added resolve_bot_identity() called during adapter init for both WS and webhook modes

🟡 NIT — fixed (10/11)

# Fix
1 extract_mentions: replacereplacen(..., 1)
2 Token TTL: read API response expire field instead of hardcoded 7200
3 allowed_groups/allowed_users: verified already checked in parse_message_event() — finding was incorrect
4 _feishu_shutdown_tx: removed underscore, added lifetime comment, drop()
5 ws_connect_loop: use token_cache for bot identity refresh on reconnect
6 jsonwebtoken: pre-existing dep (Teams adapter), not introduced by this PR — no change needed
7 Removed stale #[allow(dead_code)] and #[allow(unused_imports)]
8 Added warning when FEISHU_ENCRYPT_KEY not configured in webhook mode
9 Timing-safe comparison via subtle::ConstantTimeEq for verification_token and verify_signature
10 WS mode: handle challenge frame (log + early return)
11 Docs: added missing env vars to feishu.md and gateway/README.md

Additional improvement

  • Sender display name resolution — lazy resolve_user_name() via Contact API with in-memory cache. Prior art: OpenClaw resolveSenderNames: true, Hermes auto-detects bot identity only.

Test results

51 tests passed, 0 failures.

Coverage for review fixes:
- extract_mentions replacen (only removes first occurrence)
- allowed_users filtering (blocks unlisted, permits listed)
- allowed_groups filtering (blocks unlisted, permits listed)
- Token TTL from API expire field (short expire triggers re-fetch)
- constant_time_eq (same, different, different length)
- Thread ID parsing (root_id, parent_id)
- Emoji reaction mapping (known, unknown)

64 tests passed, 0 failures.
超渡法師 and others added 4 commits May 2, 2026 17:00
Per RFC: PR Contribution Guidelines, document how OpenClaw and Hermes
Agent handle Feishu integration. Comparison tables show where OpenAB
aligns and what's deferred to future enhancements.
When Contact API returns an error or user not found, the fallback
open_id was being cached permanently. This meant that even after
granting contact:user.base:readonly permission, previously-seen
users would still show as ou_xxx until pod restart.

Only cache successful name resolutions so retries can succeed.
Feishu WebSocket v2 requires an ACK frame (echo the received frame
with {"code":200} payload) after processing each data event. Without
ACK, Feishu considers events undelivered and replays them on reconnect,
causing duplicate replies after gateway restart.

Learned from larksuite/node-sdk ws-client implementation.
Copy link
Copy Markdown
Collaborator

@chaodu-agent chaodu-agent left a comment

Choose a reason for hiding this comment

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

Re-reviewed the latest PR head after the follow-up fixes.

The issues I previously flagged are now addressed:

  • gating now works in webhook mode because bot identity is resolved during startup, not only in the WebSocket path.
  • no longer panics on multibyte UTF-8 boundaries; the follow-up boundary fix and regression test cover the remaining panic path.
  • Token cache now uses the API field instead of hardcoding .
  • Mention stripping was narrowed from global replacement to .
  • Sender display names are now resolved via Contact API with cache + fallback, and this is wired into both WebSocket and webhook event paths.

I did a final pass over the latest changes and I don't have new findings at this point.

@chaodu-agent
Copy link
Copy Markdown
Collaborator

✅ Four-Monk Final Re-review — LGTM

Reviewers: 超渡法師 (Kiro) · 普渡法師 (Claude) · 擺渡法師 (Codex) · 覺渡法師 (Gemini)

All previously identified issues have been resolved across 8 fix commits. No remaining blockers.


🔴 Blocking Issues — All Resolved

Issue Fix Verified By
split_text panics on multi-byte UTF-8 is_char_boundary() on both end and search_start 超渡 + 擺渡 + 覺渡 + 普渡
Webhook mode bot_open_id always None → requireMention fails resolve_bot_identity() called at startup for both modes 擺渡 + 普渡

🟡 NITs — All Resolved

# Issue Fix
1 extract_mentions global replace replacen(..., 1)
2 Token TTL hardcoded 7200s Reads API expire field with saturating_sub
3 allowed_groups/allowed_users not enforced Added checks in parse_message_event()
4 _feishu_shutdown_tx unused Wired to drop() on exit
5 _token_cache suppressed in WS reconnect Removed underscore, refreshes bot identity on reconnect
6 Stale #[allow(dead_code)] / #[allow(unused_imports)] Removed
7 No warning when encryptKey missing Added warn!()
8 Timing side-channel on token comparison subtle::ConstantTimeEq
9 WS challenge frame unhandled Debug log + return
10 Docs missing 6 env vars Added to README.md + feishu.md
11 display_name shows ou_xxx resolve_user_name() via Contact API with cache + fallback

Additional Fix (found during re-review)

Issue Fix Found By
Negative cache bug: failed name lookups cached permanently Only cache successful resolutions; failures retry on next message 普渡
search_start not on char boundary in split_text while !text.is_char_boundary(search_start) added 擺渡
Test Coverage

Tests increased from 20 → 38, covering:

  • split_text: short, exact limit, Chinese UTF-8, search_start boundary, newline break
  • Token: refresh success, API error, dynamic TTL from expire field
  • Send message: success + API failure
  • Event parsing: DM, group with/without mention, bot sender, empty text, non-text, self-message, allowed_users, allowed_groups, thread ID
  • Name resolution: success + cache hit, API error fallback
  • extract_mentions: replacen only removes first occurrence
  • constant_time_eq: same, different, different length
  • Emoji mapping: known + unknown
  • Dedupe, signature verification, rate limiter
📚 Prior Art: OpenClaw & Hermes Agent

Core architecture (WS + webhook dual mode, protobuf decoding, token cache) aligns with Hermes Agent's design. Both 🔴 issues found in review are correctly handled in Hermes's implementation. Key features for future iterations: text batching (Hermes), per-group ACL (Hermes), Interactive Cards (OpenClaw).

See OpenClaw Lark plugin and Hermes Feishu docs for reference.

@chaodu-agent
Copy link
Copy Markdown
Collaborator

chaodu-agent commented May 2, 2026

四法師聯合 Review — PR #704 feat(gateway): add Feishu/Lark adapter

Reviewers: 超渡法師、擺渡法師、覺渡法師、普渡法師
已合併


🔴 Blocking(已修)

# Finding 狀態
1 split_text byte-range slice 在 CJK 文字觸發 panic(end + search_start 均未校正至 char boundary) ✅ 已修(is_char_boundary() + search_start while loop,commit 3a0b616
2 Webhook mode 下 bot_open_id 永遠為 NonerequireMention 形同虛設,群組噪音直接灌進 OAB ✅ 已修(新增 resolve_bot_identity() 在啟動時呼叫)

🟡 Medium(已修)

# Finding 狀態
3 extract_mentionsreplace 全局替換,正文中相同 token 會被誤刪 ✅ 已修(replacen(..., 1)
4 tenant_access_token TTL 硬編碼 7200,API 回傳 expire 未使用 ✅ 已修(讀 API expiresaturating_sub 防 underflow)
5 Verification token 用 != 比較,有 timing side-channel ✅ 已修(subtle::ConstantTimeEq
6 allowed_groups / allowed_users 未 enforce ✅ 已修(p2p DM 只受 allowed_users,群組受 allowed_groups,語意正確)
7 display_name 顯示 ou_xxx ✅ 已修(新增 resolve_user_name() + Contact API + cache + fallback)
8 resolve_user_name 負向快取 bug:API 失敗時 open_id 被永久 cache ✅ 已修(commit 9e356c5f,只有成功解析才 cache)
9 Feishu WS v2 未回 ACK frame,導致事件重送 ✅ 已修(commit bb44119method == 1 gate 後回 {"code":200}

🔵 Low / Nit(已知,可後續跟進)

# Finding
10 name_cache 在 async code 中使用 std::sync::Mutex,建議改 tokio::sync::Mutex(不會 deadlock,但 antipattern)
11 ACK 在 malformed WS payload 也發送,malformed frame 靜默丟棄無 retry
12 X-Forwarded-For 整條 header 當 rate limit key,可 trivially spoof
13 encryptKey 缺席時 signature check 整段跳過,docs/feishu.md 可補警告
14 name_cache 超過 10K 靜默丟棄,建議 LRU 或 TTL sweep
15 jsonwebtoken 依賴確認為 Teams adapter 預留,可接受

總結

所有 blocking issues 與 medium issues 均已完整修復,測試品質良好(38 個測試,覆蓋主要路徑),CI 全綠。Low/Nit 項目可於後續 PR 跟進。

@thepagent thepagent merged commit 9638590 into openabdev:main May 2, 2026
4 checks passed
@chaodu-agent chaodu-agent removed the pending-screening PR awaiting automated screening label May 2, 2026
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.

feat(gateway): add Feishu/Lark adapter with WebSocket long-connection

4 participants