#11084: feat(feishu): implement CardKit streaming card output with typewriter effect
channel: feishu
stale
Cluster:
Feishu Streaming Enhancements
## Problem
Feishu `im.message.patch` API has an edit count limit (~20-30 edits), which breaks block streaming for long responses — updates silently stop.
## Solution
Use Feishu **CardKit API** for streaming card updates:
1. Send an interactive card with `streaming_mode: true` and a named `element_id`
2. Convert `message_id` → `card_id` via `cardkit.v1.card.idConvert`
3. Stream-update element content via `cardkit.v1.cardElement.content` (typewriter effect, unlimited updates)
### Changes
**`send.ts`**
- `sendStreamingCardFeishu()` — sends streaming card + idConvert
- `streamUpdateCardElementFeishu()` — stream updates card element with sequence
**`reply-dispatcher.ts`**
- CardKit streaming state (cardId, sequence, accumulatedText)
- Accumulated text sending (full replacement per update)
- Auto-creates new card after 3000 chars to avoid display issues
- Graceful fallback to regular card send on CardKit failure
## Requirements
- Feishu app permission: `cardkit:card:write`
- Config: `channels.feishu.streaming: true`, `blockStreamingDefault: "on"`
- Recommended: `blockStreamingChunk.minChars: 100, maxChars: 300` for smooth streaming
## Testing
Tested with long streaming responses on Feishu mobile — typewriter effect works smoothly. Auto card splitting at 3000 chars works correctly. Fallback triggers properly when CardKit API is unavailable.
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adds Feishu CardKit-based “streaming card” support to avoid the `im.message.patch` edit-count limit during long block streaming. It introduces new helpers in `extensions/feishu/src/send.ts` to send an interactive card with `streaming_mode: true`, convert `message_id → card_id`, and then stream-update a specific markdown element via `cardkit.v1.cardElement.content`. The reply dispatcher is updated to maintain CardKit streaming state (cardId/sequence/accumulated text) and to split into new cards after a character threshold, with a fallback back to regular card/text sending on errors.
Key things to verify before merge are correctness of the CardKit ID conversion inputs and that the dispatcher’s streaming state is properly scoped/reset so non-streaming replies don’t accidentally append into a prior streamed card, and that the fallback path doesn’t resend stale accumulated content after an error.
<h3>Confidence Score: 3/5</h3>
- This PR is mergeable after fixing a few state/flow bugs in the new CardKit streaming path.
- The overall approach is coherent, but there are concrete correctness issues: `idConvert` can be called with a placeholder message id, and the dispatcher’s streaming state is not clearly gated/reset, which can lead to duplicated/incorrect message content after failures or across non-streaming sends.
- extensions/feishu/src/send.ts; extensions/feishu/src/reply-dispatcher.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#9505: fix: Refactor Feishu streaming to prevent truncation and simplify s...
by zhangyi-extra · 2026-02-05
82.1%
#13917: fix(feishu): card rendering for tables, blockquotes, images, and ou...
by yaoting · 2026-02-11
79.2%
#22826: feat(feishu): support optional header in streaming cards
by nszhsl · 2026-02-21
76.4%
#22598: feat(feishu): support card action callback handler
by yingyixu · 2026-02-21
75.9%
#16123: Feishu card default
by QiuYi111 · 2026-02-14
75.2%
#19027: fix(feishu): keep chunked messages in topic/thread context
by qiangu · 2026-02-17
74.5%
#22584: docs(feishu): add cardkit:card:write permission for streaming card ...
by Amateur0x1 · 2026-02-21
73.8%
#10675: feat(feishu): add audio message support and fix file upload
by YumoeZhung · 2026-02-06
73.5%
#23382: docs(feishu): add cardkit:card:write permission for streaming card ...
by SidQin-cyber · 2026-02-22
72.7%
#17863: Fix Feishu card button callback parameters dropped (missing handler)
by Clawborn · 2026-02-16
72.3%