eve-lark 0.4.6 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,15 +30,11 @@ A [Lark](https://www.larksuite.com) / [Feishu](https://www.feishu.cn) channel fo
30
30
  ### Out of scope (v1)
31
31
 
32
32
  These are intentionally **not** shipped — file an issue if you need them:
33
- - Audio / media / sticker / share_chat / share_user inbound (ack-and-skip only)
33
+ - Non-text inbound beyond image / file / audio: sticker / share_chat / share_user / interactive cards (ack-and-skip). Audio inbound is transcribed when an `asrProvider` is configured; without one it is also ack-and-skip.
34
34
  - Multi-account configuration
35
35
  - Per-user OAuth (`user_access_token` device flow)
36
36
  - Feishu API tools (docs / bitable / calendar / tasks / drive)
37
-
38
- > Card action buttons from ask_question ARE shipped (0.3.0+). The remaining
39
- > "card action buttons" gap in the list below refers to fully custom card
40
- > schemas authored by the agent itself.
41
- - Card action buttons (no interactive form handling)
37
+ - Fully custom agent-authored card schemas (interactive forms beyond `ask_question` buttons, which shipped in 0.3.0+)
42
38
 
43
39
  ## Quick start
44
40
 
@@ -75,7 +71,7 @@ In the [Feishu developer console](https://open.feishu.cn/app):
75
71
  4. Subscribe to `im.message.receive_v1`.
76
72
  5. Add the bot to a chat or DM it directly.
77
73
 
78
- For production, switch to HTTP-callback mode in the Feishu console and pass `mode: "webhook"` to `createLarkChannel`. See [Production](#production-deploy).
74
+ Both transport modes work in production; the right one depends on your deployment topology. See [Production deploy](#production-deploy).
79
75
 
80
76
  ## Configuration reference
81
77
 
@@ -92,7 +88,7 @@ All fields can be supplied as options or read from the matching env var (options
92
88
  | `mode` | `"long-connection" \| "webhook"` | no | `"long-connection"` | `LARK_MODE` |
93
89
  | `port` | `number` | no | `$PORT` or `2000` | `PORT` |
94
90
  | `webhookPath` | `string` | no | `/lark/webhook` | — |
95
- | `replyMode` | `"post" \| "streaming" \| "streaming-v2" \| "static"` | no | `"post"` | `LARK_REPLY_MODE` |
91
+ | `replyMode` | `"post" \| "streaming" \| "streaming-v2" \| "static"` | no | `"streaming-v2"` | `LARK_REPLY_MODE` |
96
92
  | `streamPatchIntervalMs` | `number` | no | `1000` | — |
97
93
  | `streamCreateThresholdMs` | `number` | no | `400` | — |
98
94
  | `dedupTtlMs` | `number` | no | `1_800_000` (30 min) | — |
@@ -119,15 +115,15 @@ Or via env: `LARK_BASE_URL=https://open.larksuite.com`.
119
115
 
120
116
  ## Reply modes
121
117
 
122
- - **`post`** (default): the channel waits for `message.completed` and delivers the reply as a `msg_type: "post"` rich-text message. **Renders at native chat-message size** with full markdown support (bold, links, code, `<font>` color tags). Tradeoff: no live streaming the user sees the reply only when the turn completes.
123
- - **`streaming`**: the channel creates an interactive card on the first delta, throttles live patches (~1s), and finalizes when the turn completes. **Live UX**, but card text renders smaller than native chat messages (Feishu treats cards as "structured content").
124
- - **`streaming-v2`**: same as `streaming` but uses Feishu's CardKit v2 schema (`schema: 2.0` + `streaming_mode`). Renders at slightly larger font size than v1 streaming cards and uses the newer live-patch API. Set `LARK_REPLY_MODE=streaming-v2` to opt in.
118
+ - **`streaming-v2`** (default): the channel creates an interactive card on the first delta and live-patches it through Feishu's CardKit v2 (`schema: "2.0"` + `streaming_mode`). Best live UX this channel ships. Card text renders smaller than native chat messages Feishu treats cards as "structured content".
119
+ - **`streaming`**: same live-patch UX as `streaming-v2` but on the older v1 card schema. Slightly smaller font than v2. Opt in only if you have a specific reason to avoid CardKit v2.
120
+ - **`post`**: the channel waits for `message.completed` and delivers the reply as a `msg_type: "post"` rich-text message. **Renders at native chat-message size** with full markdown support (bold, links, code, `<font>` color tags). No live streaming the user sees the reply only when the turn completes.
125
121
  - **`static`**: same wait-for-completion delivery as `post`, but uses an interactive card instead of a post. Useful if you need card features (buttons, multi-column layout) and don't mind the smaller text.
126
122
 
127
123
  Tune the streaming throttle with `streamPatchIntervalMs` (lower = smoother, more API calls).
128
124
 
129
125
  ```bash
130
- LARK_REPLY_MODE=streaming # opt into live patches
126
+ LARK_REPLY_MODE=post # opt into native-size markdown replies (no streaming)
131
127
  ```
132
128
 
133
129
  ## Continuation tokens & threading
@@ -209,24 +205,29 @@ See [`examples/README.md`](./examples/README.md) for a complete walkthrough. The
209
205
 
210
206
  ## Production deploy
211
207
 
212
- For production, switch to HTTP-callback mode:
208
+ Both transport modes are supported in production pick the one that fits your deployment topology.
209
+
210
+ - **`long-connection`** (default, WebSocket): the channel opens an outbound WS to Feishu, so **no public URL is needed**. Best fit for single-instance deployments (one container, one process). The WSClient singleton guard only dedupes within a single process — running N replicas means N independent WebSockets with every event delivered to all of them, so this mode does **not** work behind a multi-replica load balancer.
211
+ - **`webhook`** (HTTP callback): Feishu POSTs events to your public `/lark/webhook`. The load balancer routes each event to one replica, so it **scales horizontally**. Requires a publicly reachable URL.
212
+
213
+ To opt into webhook:
213
214
 
214
215
  ```ts
215
216
  // agent/channels/lark.ts
216
217
  export default createLarkChannel({
217
218
  // ... credentials ...
218
- mode: "webhook", // disables the WSClient side effect
219
+ mode: "webhook",
219
220
  });
220
221
  ```
221
222
 
222
- In the Feishu console, switch **Event Subscription** from 「长连接」back to **HTTP callback**, and set the URL to your deployed agent's `/lark/webhook`. Then:
223
+ In the Feishu console, set **Event Subscription** to **HTTP callback**, URL = your deployed agent's `/lark/webhook`. Then:
223
224
 
224
225
  ```bash
225
226
  eve build
226
227
  eve deploy # or: eve start on a server with a public URL
227
228
  ```
228
229
 
229
- Everything else (signing, AES, dedup, streaming) works unchanged.
230
+ Everything else (signing, AES, streaming, ask_question) works the same in both modes. Dedup is in-process either way — see the [Serverless caveat](#security-model) for the multi-instance implication.
230
231
 
231
232
  Test layout:
232
233
 
package/README.zh-CN.md CHANGED
@@ -31,13 +31,11 @@
31
31
  ### 不在 v1 范围内
32
32
 
33
33
  以下功能**未实现**——需要的话请提 issue:
34
- - 音频 / 媒体 / sticker / share_chat / share_user 入站(仅 ack-and-skip
34
+ - 图片 / 文件 / 音频以外的非文本入站:sticker / share_chat / share_user / 交互卡片(ack-and-skip)。音频入站在配置了 `asrProvider` 时会转写;没配置则同样是 ack-and-skip。
35
35
  - 多账号配置
36
36
  - 用户级 OAuth(`user_access_token` device flow)
37
37
  - 飞书 API 工具(docs / bitable / calendar / tasks / drive)
38
-
39
- > `ask_question` 的卡片按钮**已实现**(0.3.0+)。下面列表中剩下的「Card action buttons」指的是 agent 自己生成的完全自定义的卡片 schema。
40
- - Card action buttons(agent 自定义的交互表单)
38
+ - agent 自渲染的完全自定义卡片 schema(交互表单——`ask_question` 的卡片按钮已在 0.3.0+ 发出,这里指的是超出这个范围的)
41
39
 
42
40
  ## 快速开始
43
41
 
@@ -74,7 +72,7 @@ eve dev
74
72
  4. 订阅 `im.message.receive_v1`。
75
73
  5. 把 bot 拉进群或直接私聊。
76
74
 
77
- 生产部署时,在飞书后台切回 HTTP 回调模式,并给 `createLarkChannel` 传 `mode: "webhook"`。详见[生产部署](#生产部署)。
75
+ 两种传输模式在生产环境都可以用,选哪个取决于你的部署拓扑。详见[生产部署](#生产部署)。
78
76
 
79
77
  ## 配置参考
80
78
 
@@ -91,7 +89,7 @@ eve dev
91
89
  | `mode` | `"long-connection" \| "webhook"` | 否 | `"long-connection"` | `LARK_MODE` |
92
90
  | `port` | `number` | 否 | `$PORT` 或 `2000` | `PORT` |
93
91
  | `webhookPath` | `string` | 否 | `/lark/webhook` | — |
94
- | `replyMode` | `"post" \| "streaming" \| "static"` | 否 | `"post"` | `LARK_REPLY_MODE` |
92
+ | `replyMode` | `"post" \| "streaming" \| "streaming-v2" \| "static"` | 否 | `"streaming-v2"` | `LARK_REPLY_MODE` |
95
93
  | `streamPatchIntervalMs` | `number` | 否 | `1000` | — |
96
94
  | `streamCreateThresholdMs` | `number` | 否 | `400` | — |
97
95
  | `dedupTtlMs` | `number` | 否 | `1_800_000`(30 分钟) | — |
@@ -118,14 +116,15 @@ createLarkChannel({
118
116
 
119
117
  ## 回复模式
120
118
 
121
- - **`post`**(默认):channel `message.completed`,把回复作为 `msg_type: "post"` 富文本消息发出。**渲染为原生聊天消息大小**,完整支持 markdown(粗体、链接、代码、`<font>` 颜色 tag)。代价:不能流式——用户在 turn 完成时才看到回复。
122
- - **`streaming`**:channel 在第一个 delta 时创建交互卡片,节流地实时 patch(约 1 秒一次),turn 完成时收尾。**实时 UX 好**,但卡片文字比原生消息字号小(飞书把卡片当作「结构化内容」)。
119
+ - **`streaming-v2`**(默认):channel 在第一个 delta 时创建交互卡片,通过飞书 CardKit v2(`schema: "2.0"` + `streaming_mode`)实时 patch。**是这个 channel 能提供的最好的实时 UX**。卡片文字比原生消息字号小(飞书把卡片当作「结构化内容」)。
120
+ - **`streaming`**:和 `streaming-v2` 一样的实时 patch UX,但走老的 v1 卡片 schema,字号比 v2 略小。仅在你有特定原因想避开 CardKit v2 时才选。
121
+ - **`post`**:channel 等 `message.completed`,把回复作为 `msg_type: "post"` 富文本消息发出。**渲染为原生聊天消息大小**,完整支持 markdown(粗体、链接、代码、`<font>` 颜色 tag)。代价:不能流式——用户在 turn 完成时才看到回复。
123
122
  - **`static`**:和 `post` 一样等完成再发,但用交互卡片而非 post。适合需要卡片特性(按钮、多列布局)且不在乎字号小的场景。
124
123
 
125
124
  流式节流通过 `streamPatchIntervalMs` 调整(值越小越平滑,但 API 调用越多)。
126
125
 
127
126
  ```bash
128
- LARK_REPLY_MODE=streaming # 切到实时 patch
127
+ LARK_REPLY_MODE=post # 切到原生字号 + markdown(无流式)
129
128
  ```
130
129
 
131
130
  ## Continuation token 与线程
@@ -208,24 +207,29 @@ pnpm build # tsup build → dist/
208
207
 
209
208
  ## 生产部署
210
209
 
211
- 生产环境切到 HTTP 回调模式:
210
+ 两种传输模式在生产环境都支持——根据你的部署拓扑选择。
211
+
212
+ - **`long-connection`**(默认,WebSocket):channel 主动向飞书发起出站 WS 连接,**不需要公网 URL**。适合单实例部署(一个容器、一个进程)。WSClient 单例守卫只在单个进程内去重——跑 N 个副本就会有 N 条独立的 WebSocket,每个事件被投递到所有副本,所以这个模式**不**适用于多副本负载均衡场景。
213
+ - **`webhook`**(HTTP 回调):飞书把事件 POST 到你部署的 agent 的公网 `/lark/webhook`。负载均衡把每个事件路由到一个副本,**可以水平扩展**。需要有公网可达的 URL。
214
+
215
+ 切到 webhook:
212
216
 
213
217
  ```ts
214
218
  // agent/channels/lark.ts
215
219
  export default createLarkChannel({
216
220
  // ... 凭据 ...
217
- mode: "webhook", // 关闭 WSClient 副作用
221
+ mode: "webhook",
218
222
  });
219
223
  ```
220
224
 
221
- 在飞书后台,把**事件订阅**从「长连接」切回**HTTP 回调**,URL 设为你部署的 agent 的 `/lark/webhook`。然后:
225
+ 在飞书后台,把**事件订阅**设为 **HTTP 回调**,URL 设为你部署的 agent 的 `/lark/webhook`。然后:
222
226
 
223
227
  ```bash
224
228
  eve build
225
229
  eve deploy # 或:在有公网 URL 的服务器上跑 eve start
226
230
  ```
227
231
 
228
- 其他逻辑(签名、AES、去重、流式)不变。
232
+ 其他逻辑(签名、AES、流式、ask_question)在两种模式下都一样。去重在两种模式下都是进程内的——多实例场景的影响见[安全模型](#安全模型)里的 serverless 说明。
229
233
 
230
234
  测试目录:
231
235