@zhin.js/adapter-lark 6.0.13 → 7.0.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # @zhin.js/adapter-lark
2
2
 
3
+ ## 7.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - f2c532f: Expose exact per-Endpoint message operations through one validated Adapter capability model, route Core control calls through declared active capabilities, and connect existing recall, edit, reaction, and typing implementations across platform adapters.
8
+ - Updated dependencies [b10d058]
9
+ - Updated dependencies [f2c532f]
10
+ - Updated dependencies [3dbf990]
11
+ - @zhin.js/host-http@1.0.12
12
+ - @zhin.js/adapter@1.2.0
13
+ - @zhin.js/core@1.5.13
14
+ - @zhin.js/agent@1.1.15
15
+ - zhin.js@6.0.13
16
+
17
+ ## 6.0.14
18
+
19
+ ### Patch Changes
20
+
21
+ - 974772e: Replace the user-facing `Prompt` vocabulary with the `UserInteraction` authoring surface for input, confirmation, and selection. Commands and handlers now expose `interaction`; IM Runtime exposes `createInteraction`; schema-driven endpoint collection is named `SchemaInteraction`. The old prompt-named interaction types and properties are removed rather than aliased. User interactions render through one canonical Markdown and keyboard/list presentation module shared by commands and Agent `ask_user` turns.
22
+
23
+ Extract the transport-neutral interaction contract into `@zhin.js/interaction`. A discriminated `ask()` API supports text, number, confirmation, single-select, multi-select, and typed lists with structured `title`, `description`, and `tip` content. Typed `sequence()` interactions return one result object keyed by step id, render progress, and retry invalid replies without leaking invalid values to callers.
24
+
25
+ Preserve AI Markdown and card command actions through outbound publishing. QQ delivers Markdown with native command buttons; KOOK, Discord, Telegram, DingTalk, and Lark/Feishu now declare and encode their native Markdown dialects while retaining each adapter's interaction policy. Correct QQ callback button action encoding and button style mapping.
26
+
27
+ - 5969c5b: Add SideEventGateway so adapters forward notice/request/system into HandlerIndex. HandlerContext now exposes only generation-safe capabilities and prompt ports; live Endpoint escape hatches are removed.
28
+ - Updated dependencies [5969c5b]
29
+ - Updated dependencies [d336a3f]
30
+ - Updated dependencies [0c82a7e]
31
+ - Updated dependencies [b9217e4]
32
+ - Updated dependencies [5969c5b]
33
+ - Updated dependencies [5969c5b]
34
+ - Updated dependencies [974772e]
35
+ - Updated dependencies [5969c5b]
36
+ - Updated dependencies [5969c5b]
37
+ - Updated dependencies [2f786bd]
38
+ - Updated dependencies [63d89f9]
39
+ - Updated dependencies [71c7cdd]
40
+ - Updated dependencies [3cca0ea]
41
+ - Updated dependencies [1312ca0]
42
+ - Updated dependencies [985fa22]
43
+ - Updated dependencies [04b861d]
44
+ - Updated dependencies [a23d544]
45
+ - Updated dependencies [8cddabf]
46
+ - Updated dependencies [dbe5081]
47
+ - @zhin.js/im-contract@1.0.4
48
+ - @zhin.js/core@1.5.12
49
+ - @zhin.js/adapter@1.1.11
50
+ - @zhin.js/agent@1.1.14
51
+ - @zhin.js/host-http@1.0.11
52
+ - @zhin.js/command@1.0.15
53
+ - zhin.js@6.0.12
54
+ - @zhin.js/permission@1.0.3
55
+
3
56
  ## 6.0.13
4
57
 
5
58
  ### Patch Changes
package/README.md CHANGED
@@ -8,6 +8,7 @@ Zhin.js 飞书 / Lark 适配器(Plugin Runtime),通过 Runtime Host HTTP W
8
8
  - URL 验证挑战(`url_verification`)
9
9
  - Tenant Access Token 自动刷新
10
10
  - 支持飞书与 Lark 国际版 API 基址
11
+ - canonical `markdown` 段编码为 `interactive` 卡片中的 `lark_md`
11
12
  - 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
12
13
 
13
14
  ## 安装
@@ -70,6 +71,7 @@ plugins:
70
71
  | 飞书类型 | 入站 content(文本摘要) | 出站 wire |
71
72
  |----------|--------------------------|-----------|
72
73
  | text | 原文 | text |
74
+ | markdown | 原文 | interactive(`lark_md`) |
73
75
  | image | `[image]` | image(需 `file_key`) |
74
76
  | file | `[file: name]` | file |
75
77
  | audio / video / sticker | `[audio]` / `[video]` / `[sticker]` | — |
package/adapters/lark.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Convention entry: discover `adapters/lark.ts` → defineAdapter.
4
4
  */
5
5
  import { defineAdapter } from 'zhin.js/adapter';
6
- import { messageGatewayToken } from '@zhin.js/core/runtime';
6
+ import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
7
7
  import { httpHostToken } from '@zhin.js/host-http';
8
8
  import { LarkEndpoint } from "../lib/endpoint.js";
9
9
  import { resolveLarkConfig, } from "../lib/protocol.js";
@@ -11,12 +11,15 @@ import { larkRuntimeStateToken } from "../lib/lark-runtime-state.js";
11
11
  export { LarkEndpoint } from "../lib/endpoint.js";
12
12
  export default defineAdapter({
13
13
  capabilities: ['inbound', 'outbound'],
14
+ operations: ['recall'],
14
15
  // image 段 canonical MediaRef 经 /im/v1/images 物化为 image_key
15
16
  // (base64 解码 / path 读盘 / url 下载后上传,kind=file 平台引用直通);
17
+ // canonical Markdown 由 endpoint codec 转为 lark_md 消息卡片;
16
18
  // 卡片交互未接入出站通道,交互段降级纯文本。
17
19
  segments: {
18
20
  outboundMedia: ['url', 'upload'],
19
21
  interactive: 'text',
22
+ markdown: 'native',
20
23
  },
21
24
  create(context) {
22
25
  const config = resolveLarkConfig(context.config);
@@ -28,6 +31,7 @@ export default defineAdapter({
28
31
  return new LarkEndpoint({
29
32
  id: context.id,
30
33
  gateway: context.use(messageGatewayToken),
34
+ sideEvents: context.use(sideEventGatewayToken),
31
35
  http: context.use(httpHostToken),
32
36
  config,
33
37
  });
package/adapters/lark.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Convention entry: discover `adapters/lark.ts` → defineAdapter.
3
3
  */
4
4
  import { defineAdapter } from 'zhin.js/adapter';
5
- import { messageGatewayToken } from '@zhin.js/core/runtime';
5
+ import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
6
6
  import { httpHostToken } from '@zhin.js/host-http';
7
7
  import { LarkEndpoint } from '../src/endpoint.js';
8
8
  import {
@@ -16,12 +16,15 @@ export type { LarkEndpointOptions, LarkFetch } from '../src/endpoint.js';
16
16
 
17
17
  export default defineAdapter<LarkAdapterConfig>({
18
18
  capabilities: ['inbound', 'outbound'],
19
+ operations: ['recall'],
19
20
  // image 段 canonical MediaRef 经 /im/v1/images 物化为 image_key
20
21
  // (base64 解码 / path 读盘 / url 下载后上传,kind=file 平台引用直通);
22
+ // canonical Markdown 由 endpoint codec 转为 lark_md 消息卡片;
21
23
  // 卡片交互未接入出站通道,交互段降级纯文本。
22
24
  segments: {
23
25
  outboundMedia: ['url', 'upload'],
24
26
  interactive: 'text',
27
+ markdown: 'native',
25
28
  },
26
29
  create(context) {
27
30
  const config = resolveLarkConfig(context.config);
@@ -33,6 +36,7 @@ export default defineAdapter<LarkAdapterConfig>({
33
36
  return new LarkEndpoint({
34
37
  id: context.id,
35
38
  gateway: context.use(messageGatewayToken),
39
+ sideEvents: context.use(sideEventGatewayToken),
36
40
  http: context.use(httpHostToken),
37
41
  config,
38
42
  });
package/lib/endpoint.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
2
- import type { MessageGateway } from '@zhin.js/core/runtime';
1
+ import { type EndpointControl, type EndpointInstance, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
2
+ import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
3
3
  import type { HttpHost } from '@zhin.js/host-http';
4
4
  import type { CapabilityId } from 'zhin.js';
5
5
  import { resolveMediaBinary } from './media-upload.js';
@@ -18,6 +18,7 @@ export type LarkFetch = (url: string, init?: {
18
18
  export interface LarkEndpointOptions {
19
19
  readonly id: CapabilityId;
20
20
  readonly gateway: MessageGateway;
21
+ readonly sideEvents?: SideEventGateway;
21
22
  readonly http: HttpHost;
22
23
  readonly config: ResolvedLarkConfig;
23
24
  readonly fetch?: LarkFetch;
@@ -32,6 +33,7 @@ export declare class LarkEndpoint implements EndpointInstance {
32
33
  * listChannels(无频道/guild 概念)、写操作(审批/踢人/禁言等本轮不在范围内)。
33
34
  */
34
35
  readonly management: EndpointManagement;
36
+ readonly control: EndpointControl;
35
37
  constructor(options: LarkEndpointOptions);
36
38
  /** Used by webhook handler. */
37
39
  get isOpen(): boolean;
package/lib/endpoint.js CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { readFile } from 'node:fs/promises';
5
5
  import { basename } from 'node:path';
6
+ import { createRecallEndpointControl, } from 'zhin.js/adapter';
6
7
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
7
8
  import { registerLarkAgentEndpoint } from './lark-agent-deps.js';
8
9
  import { buildImageUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
@@ -38,6 +39,7 @@ export class LarkEndpoint {
38
39
  },
39
40
  listGroupMembers: (groupId) => this.#listAllPages(`/im/v1/chats/${encodeURIComponent(groupId)}/members`, { member_id_type: 'open_id' }),
40
41
  });
42
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
41
43
  #routeReleases = [];
42
44
  #accessToken = { token: '', expires_in: 0, timestamp: 0 };
43
45
  #refreshPromise = null;
package/lib/protocol.js CHANGED
@@ -153,6 +153,7 @@ export function formatOutboundBody(payload) {
153
153
  : payload && typeof payload === 'object' && 'type' in payload
154
154
  ? [payload]
155
155
  : [];
156
+ const hasMarkdown = items.some((item) => typeof item !== 'string' && item.type === 'markdown');
156
157
  if (items.length === 0) {
157
158
  const text = payload == null
158
159
  ? ''
@@ -176,6 +177,9 @@ export function formatOutboundBody(payload) {
176
177
  case 'text':
177
178
  textParts.push(String(data.content ?? data.text ?? ''));
178
179
  break;
180
+ case 'markdown':
181
+ textParts.push(String(data.content ?? data.text ?? ''));
182
+ break;
179
183
  case 'at':
180
184
  textParts.push(`<at user_id="${String(data.id ?? '')}">${String(data.name || data.id || '')}</at>`);
181
185
  break;
@@ -230,6 +234,17 @@ export function formatOutboundBody(payload) {
230
234
  }
231
235
  if (media)
232
236
  return media;
237
+ if (hasMarkdown) {
238
+ return {
239
+ msg_type: 'interactive',
240
+ content: JSON.stringify({
241
+ elements: [{
242
+ tag: 'div',
243
+ text: { tag: 'lark_md', content: textParts.join('') },
244
+ }],
245
+ }),
246
+ };
247
+ }
233
248
  return {
234
249
  msg_type: 'text',
235
250
  content: JSON.stringify({ text: textParts.join('') }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-lark",
3
- "version": "6.0.13",
3
+ "version": "7.0.0",
4
4
  "description": "Zhin.js Lark/Feishu (飞书) adapter for Plugin Runtime (HTTP webhook)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,21 +34,21 @@
34
34
  "directory": "plugins/adapters/lark"
35
35
  },
36
36
  "dependencies": {
37
- "@zhin.js/adapter": "1.1.10",
38
- "@zhin.js/core": "1.5.11",
39
- "@zhin.js/host-http": "1.0.10",
40
- "@zhin.js/im-contract": "1.0.3",
37
+ "@zhin.js/adapter": "1.2.0",
38
+ "@zhin.js/core": "1.5.13",
39
+ "@zhin.js/host-http": "1.0.12",
40
+ "@zhin.js/im-contract": "1.0.4",
41
41
  "@zhin.js/logger": "1.0.76"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "zod": "^4.0.0",
45
- "@zhin.js/adapter": "1.1.10",
46
- "@zhin.js/agent": "1.1.13",
47
- "@zhin.js/command": "1.0.14",
48
- "@zhin.js/core": "1.5.11",
49
- "@zhin.js/host-http": "1.0.10",
50
- "@zhin.js/permission": "1.0.2",
51
- "zhin.js": "6.0.11"
45
+ "@zhin.js/adapter": "1.2.0",
46
+ "@zhin.js/agent": "1.1.15",
47
+ "@zhin.js/command": "1.0.15",
48
+ "@zhin.js/core": "1.5.13",
49
+ "@zhin.js/host-http": "1.0.12",
50
+ "@zhin.js/permission": "1.0.3",
51
+ "zhin.js": "6.0.13"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@zhin.js/agent": {
@@ -69,8 +69,8 @@
69
69
  "typescript": "^6.0.3",
70
70
  "vitest": "^4.1.10",
71
71
  "zod": "^4.4.3",
72
- "@zhin.js/agent": "1.1.13",
73
- "zhin.js": "6.0.11"
72
+ "@zhin.js/agent": "1.1.15",
73
+ "zhin.js": "6.0.13"
74
74
  },
75
75
  "files": [
76
76
  "adapters",
package/src/endpoint.ts CHANGED
@@ -3,8 +3,15 @@
3
3
  */
4
4
  import { readFile } from 'node:fs/promises';
5
5
  import { basename } from 'node:path';
6
- import type { EndpointGroup, EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
7
- import type { MessageGateway } from '@zhin.js/core/runtime';
6
+ import {
7
+ createRecallEndpointControl,
8
+ type EndpointControl,
9
+ type EndpointGroup,
10
+ type EndpointInstance,
11
+ type EndpointManagement,
12
+ type EndpointSendRequest,
13
+ } from 'zhin.js/adapter';
14
+ import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
8
15
  import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
9
16
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
10
17
  import type { CapabilityId } from 'zhin.js';
@@ -49,6 +56,7 @@ export type LarkFetch = (
49
56
  export interface LarkEndpointOptions {
50
57
  readonly id: CapabilityId;
51
58
  readonly gateway: MessageGateway;
59
+ readonly sideEvents?: SideEventGateway;
52
60
  readonly http: HttpHost;
53
61
  readonly config: ResolvedLarkConfig;
54
62
  readonly fetch?: LarkFetch;
@@ -85,6 +93,7 @@ export class LarkEndpoint implements EndpointInstance {
85
93
  { member_id_type: 'open_id' },
86
94
  ),
87
95
  });
96
+ readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
88
97
  #routeReleases: HttpRouteRegistration[] = [];
89
98
  #accessToken: AccessToken = { token: '', expires_in: 0, timestamp: 0 };
90
99
  #refreshPromise: Promise<string> | null = null;
package/src/protocol.ts CHANGED
@@ -272,6 +272,7 @@ export function formatOutboundBody(payload: unknown): LarkSendBody {
272
272
  : payload && typeof payload === 'object' && 'type' in (payload as object)
273
273
  ? [payload as LarkWireSegment]
274
274
  : [];
275
+ const hasMarkdown = items.some((item) => typeof item !== 'string' && item.type === 'markdown');
275
276
 
276
277
  if (items.length === 0) {
277
278
  const text = payload == null
@@ -298,6 +299,9 @@ export function formatOutboundBody(payload: unknown): LarkSendBody {
298
299
  case 'text':
299
300
  textParts.push(String(data.content ?? data.text ?? ''));
300
301
  break;
302
+ case 'markdown':
303
+ textParts.push(String(data.content ?? data.text ?? ''));
304
+ break;
301
305
  case 'at':
302
306
  textParts.push(
303
307
  `<at user_id="${String(data.id ?? '')}">${String(data.name || data.id || '')}</at>`,
@@ -352,6 +356,17 @@ export function formatOutboundBody(payload: unknown): LarkSendBody {
352
356
  }
353
357
 
354
358
  if (media) return media;
359
+ if (hasMarkdown) {
360
+ return {
361
+ msg_type: 'interactive',
362
+ content: JSON.stringify({
363
+ elements: [{
364
+ tag: 'div',
365
+ text: { tag: 'lark_md', content: textParts.join('') },
366
+ }],
367
+ }),
368
+ };
369
+ }
355
370
  return {
356
371
  msg_type: 'text',
357
372
  content: JSON.stringify({ text: textParts.join('') }),