feat(gateway): feishu rich text, streaming, slash commands, and bot-to-bot readiness#706
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…ness - /reset and /cancel interception in OAB core gateway adapter (src/gateway.rs) - Rich text (post) messaging: markdown_to_post() converter + send_post_message() - Bot-to-bot: AllowBots enum, trusted_bot_ids, max_bot_turns, bot detection heuristic - Updated docs/feishu.md with new sections and env vars
- OAB core: send_message request-response for Feishu (real message_id) - OAB core: edit_message sends command via WebSocket - OAB core: use_streaming returns true for Feishu only - GatewayResponse: add message_id field (backward compatible) - Gateway service: edit_feishu_message via PUT API - Gateway service: handle_reply returns message_id in response
…nd nits - Remove dead variables in markdown_to_post() (blocker openabdev#2) - Fix allowlist bypass: check allowlist before bot classification (suggested openabdev#3) - Fix thread key mismatch: use thread_id.unwrap_or(channel_id) (suggested openabdev#4) - Add /models and /agents slash commands - NITs: let mut→let, cancel format, DM comment, bot_turns TODO, timeout 5s, dead_code allow, warn on parse error
934ec24 to
ecf6cbc
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…dcode with supports_streaming() - Remove /models and /agents slash command interception (will be a separate PR with exact matching, ambiguity handling, get_or_create, category aliasing) - Replace channel.platform == "feishu" with supports_streaming() method - supports_streaming() uses matches!(platform_name, "feishu" | "lark")
This comment has been minimized.
This comment has been minimized.
- send_message: request-response only when self.streaming is true, fire-and-forget otherwise (no Telegram regression) - parse_inline: paired backtick parsing preserves literal content inside code spans, no longer strips markers inside inline code
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
chaodu-agent
left a comment
There was a problem hiding this comment.
Changes Requested
Three issues identified by the review team (超渡 + 擺渡 + 覺渡) that should be addressed before merge:
1. Bot-to-bot feature is non-functional (High)
gateway/src/adapters/feishu.rs:365 correctly sets is_bot: true for trusted bots, but src/gateway.rs:408-410 unconditionally skips all is_bot events before they reach router.handle_message(). The AllowBots / trusted_bot_ids / max_bot_turns feature chain is severed at the OAB core level.
Action: Either add bot-allow gating logic to src/gateway.rs (matching Discord/Slack adapters), or set is_bot: false in the gateway event and handle bot filtering entirely gateway-side.
2. Rich text parser corrupts literal text (Medium)
parse_inline() unconditionally strips all *, ~ characters and consumes unmatched backticks. This breaks ~/.ssh, *.rs, 3 * 4, and any prose with unmatched backticks. Since all Feishu replies now go through markdown_to_post(), this is a broad user-visible regression.
Action: Only strip paired markdown markers (**...**, *...*, ~~...~~). Preserve literal *, ~, and unmatched backticks.
3. Streaming send failure → silent degradation (Medium)
handle_reply() only sends GatewayResponse on the success path. When send_post_message() returns None, no response is sent. OAB core times out after 5s, falls back to "gw_sent", then sends edit_message targeting a non-existent message ID.
Action: Send a failure GatewayResponse (success: false) when send_post_message() returns None.
Everything else looks solid — architecture is clean, schema change is backward compatible, slash commands are well-placed, and the PR description is exemplary. Looking forward to the next revision!
…t-response - Extract SharedWsTx type alias (Arc<Mutex<SplitSink>>) so ws_tx can be shared between GatewayAdapter and the event loop - Add send_fire_and_forget() helper for slash command responses that don't need message_id back (no 5s timeout penalty) - send_message() still uses request-response when streaming=true (needed for placeholder message_id in streaming edit flow) Addresses review feedback: slash command responses (/reset, /cancel) no longer go through the request-response path, avoiding unnecessary latency when gateway.streaming is enabled.
This comment has been minimized.
This comment has been minimized.
Gateway core unconditionally drops is_bot events, but feishu adapter's AllowBots filtering happens before events reach core. When Feishu lifts the bot-to-bot delivery restriction, this guard needs to become adapter-aware. Telegram adapter does not filter bots, so we cannot simply remove the guard without introducing regression.
Per 擺渡法師 review: PR claimed 'bot-to-bot readiness' but OAB core unconditionally drops is_bot events (src/gateway.rs:430). The gateway adapter's AllowBots filtering works, but events never reach the router. Updated docs/feishu.md to explicitly state both blockers: 1. Feishu platform doesn't deliver bot messages to other bots 2. OAB core drops is_bot events before router Claim narrowed from 'readiness' to 'gateway-side scaffolding only'.
This comment has been minimized.
This comment has been minimized.
…法師 findings)
1. parse_inline(): only strip *paired* markdown markers (**, *, ~~).
Unpaired markers kept as literal text. Fixes: ~/.ssh → /.ssh,
*.rs → .rs, 3 * 4 → 3 4
2. handle_reply(): send GatewayResponse { success: false } when
send_post_message() fails. Prevents core from waiting 5s timeout
then sending edit_message to a non-existent message_id.
This comment has been minimized.
This comment has been minimized.
Four-Monk Collaborative Review — FinalVerdict: LGTM ✅ — All findings addressed. Ready for maintainer approval. What this PR doesFollow-up to #704. Adds four features to the Feishu/Lark gateway adapter:
OAB core changes (
Review fix commits (4 commits from 四法師 triage)
Known limitations (non-blocking)
Reviewers
|
chaodu-agent
left a comment
There was a problem hiding this comment.
LGTM ✅ — Four-monk collaborative review complete. All findings addressed (4 fix commits). Ready to merge.
Summary
Follow-up to #704. Adds streaming, rich text, slash commands (
/reset,/cancel), and gateway-side bot-to-bot scaffolding for the Feishu/Lark gateway adapter.Changes
gateway/src/adapters/feishu.rsmarkdown_to_post()+send_post_message(). Streaming:edit_feishu_message()via PUT API,handle_replyreturnsmessage_idin response. Bot-to-bot:AllowBotsenum,FEISHU_TRUSTED_BOT_IDS,FEISHU_MAX_BOT_TURNS, bot detection, turn trackingsrc/gateway.rs/reset,/cancel) via fire-and-forget. Streaming:send_messageuses request-response with 5s timeout only whenstreaming=true,edit_messageoverride,use_streamingreads config.SharedWsTxtype alias for shared WebSocket writer.GatewayResponseaddsmessage_idfieldsrc/config.rsGatewayConfigaddsstreaming: bool(default false)src/main.rsstreamingtoGatewayParamsgateway/src/main.rsevent_txto feishuhandle_replygateway/src/schema.rsGatewayResponseaddsmessage_id: Option<String>gateway/src/adapters/telegram.rsmessage_id: Nonein existing constructionsdocs/feishu.mdgateway/README.mdCargo.lockFeatures
1. Streaming (typewriter)
Agent replies stream incrementally via placeholder → edit loop → final edit. Controlled by
gateway.streaming = truein config (default false). Core has zero platform-specific logic — streaming capability is declared in config, not inferred from platform name.2. Rich text (post) messaging
Agent replies sent as Feishu
postformat.markdown_to_post()converts code blocks →code_blocktag, links →atag, inline formatting stripped (Feishu post doesn't support inline styles).3. Slash commands (
/reset,/cancel)Gateway-level interception before
router.handle_message(). Benefits all gateway platforms. 22 lines, pure additive.4. Bot-to-bot scaffolding (gateway-side only)
AllowBotsenum (Off/Mentions/All),FEISHU_TRUSTED_BOT_IDS,FEISHU_MAX_BOT_TURNSwith human-reset safety valve. User allowlist checked before bot classification — no bypass possible.Testing
/resetwith no active session/resetwith active session/cancelcargo test— 66 passed, 0 failedBreaking Changes
GatewayResponseschema adds optionalmessage_idfield — backward compatible.send_messageuses request-response only whengateway.streaming = true(to obtainmessage_idfor edit). Default (streaming = false) remains fire-and-forget with no behavioral change.Prior Art & Industry Research
OpenClaw (Feishu channel)
OpenClaw's Feishu integration is the most mature reference:
postmessage. Falls back to plain text if the API rejects the payload. Ourmarkdown_to_post()takes a similar approach but converts explicitly to post JSON structure (code_block, a, text tags) rather than relying on Feishu's markdown rendering.streaming: true,blockStreaming: true, both default true). Our approach uses the simplerPUT /messages/{id}edit API on post messages, which avoids the complexity of card templates but doesn't support block-level streaming./status,/reset,/modelas plain text messages — same approach we use for/resetand/cancel.allow_bots/trusted_bot_ids/max_bot_turns.thread_id(omt_*) for topic groups and reply root message ID (om_*) for normal group replies. This informs our upcoming thread support design.Hermes Agent (Feishu adapter)
Hermes Agent's Feishu adapter is comprehensive (531 lines of docs):
mdtag. Falls back to plain text on API rejection. Similar to OpenClaw's approach./set-homeand/cardcommands but these appear to be Hermes-specific, not general session control.FEISHU_ALLOW_BOTSwithnone/mentions/all— directly matches ourAllowBotsenum design. Hermes notes that peer bots don't need to be inFEISHU_ALLOWED_USERS(human-only allowlist). Our implementation now checks allowlist before bot classification, achieving the same separation.Key takeaways
Discord Discussion URL
https://discord.com/channels/1491295327620169908/1500160821567684660