@larksuite/openclaw-lark 2026.5.13 → 2026.5.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larksuite/openclaw-lark",
3
- "version": "2026.5.13",
3
+ "version": "2026.5.20",
4
4
  "description": "OpenClaw Lark/Feishu channel plugin",
5
5
  "exports": {
6
6
  ".": {
@@ -23,8 +23,8 @@
23
23
  "node": ">=22"
24
24
  },
25
25
  "dependencies": {
26
- "@larksuiteoapi/node-sdk": "^1.60.0",
27
- "@sinclair/typebox": "0.34.48",
26
+ "@larksuiteoapi/node-sdk": "^1.64.0",
27
+ "@sinclair/typebox": "0.34.49",
28
28
  "image-size": "^2.0.2",
29
29
  "undici-types": "^8.1.0",
30
30
  "zod": "^4.3.6"
@@ -196,12 +196,13 @@ function createFeishuReplyDispatcher(params) {
196
196
  }
197
197
  // 提取文本和媒体 URL
198
198
  const text = getVisiblePayloadText(payload);
199
+ const reasoningText = payload.isReasoning === true ? (payload.text ?? '') : '';
199
200
  const payloadMediaUrls = payload.mediaUrls?.length
200
201
  ? payload.mediaUrls
201
202
  : payload.mediaUrl
202
203
  ? [payload.mediaUrl]
203
204
  : [];
204
- if (!text.trim() && payloadMediaUrls.length === 0) {
205
+ if (!text.trim() && !reasoningText.trim() && payloadMediaUrls.length === 0) {
205
206
  log.debug('deliver: empty text and no media, skipping');
206
207
  return;
207
208
  }
@@ -211,12 +212,17 @@ function createFeishuReplyDispatcher(params) {
211
212
  await controller.onToolPayload(payload);
212
213
  return;
213
214
  }
214
- if (text.trim()) {
215
+ const controllerText = reasoningText.trim() ? reasoningText : text;
216
+ if (controllerText.trim()) {
215
217
  await controller.ensureCardCreated();
216
218
  if (controller.isTerminated)
217
219
  return;
218
220
  if (controller.cardMessageId) {
219
- await controller.onDeliver({ ...payload, text });
221
+ if (payload.isReasoning === true) {
222
+ await controller.onReasoningStream({ ...payload, text: controllerText });
223
+ return;
224
+ }
225
+ await controller.onDeliver({ ...payload, text: controllerText });
220
226
  return;
221
227
  }
222
228
  // Card creation failed — fall through to static delivery
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.dispatchFeishuPluginInteractiveHandler = dispatchFeishuPluginInteractiveHandler;
17
17
  // NOTE: This is the SDK-standard interactive pipeline.
18
18
  const plugin_runtime_1 = require("openclaw/plugin-sdk/plugin-runtime");
19
+ const card_action_operator_1 = require("../core/card-action-operator.js");
19
20
  const lark_logger_1 = require("../core/lark-logger.js");
20
21
  const send_1 = require("../messaging/outbound/send.js");
21
22
  const log = (0, lark_logger_1.larkLogger)('channel/interactive-dispatch');
@@ -29,7 +30,7 @@ function extractBasics(data) {
29
30
  const openMessageId = ev.open_message_id ?? ev.context?.open_message_id;
30
31
  return {
31
32
  action: action.trim(),
32
- senderOpenId: ev.operator?.open_id,
33
+ senderOpenId: (0, card_action_operator_1.resolveCardCallbackOperatorId)(ev.operator),
33
34
  openChatId,
34
35
  openMessageId,
35
36
  };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Card callback operator identity extraction.
6
+ *
7
+ * Feishu Schema 2 card callbacks may carry the operator identity under
8
+ * either `operator.open_id` (Schema 1 / default) or `operator.user_id`
9
+ * (Schema 2 when the user has no open_id in the app's tenant).
10
+ *
11
+ * This helper provides a single, consistent extraction point so that
12
+ * every card callback handler resolves the operator identity the same
13
+ * way. See openclaw/openclaw#71670 for the upstream Schema 2 change.
14
+ */
15
+ /**
16
+ * Minimal shape of the `operator` object in a Feishu card callback event.
17
+ * Both fields are optional because Schema 2 may omit `open_id` entirely.
18
+ */
19
+ export interface CardCallbackOperator {
20
+ open_id?: string;
21
+ user_id?: string;
22
+ }
23
+ /**
24
+ * Extract the operator's identity from a Feishu card callback event.
25
+ *
26
+ * Prefers `open_id` (the stable per-app user identifier) and falls back
27
+ * to `user_id` when `open_id` is absent or empty — this is the Schema 2 path.
28
+ *
29
+ * @param operator - The `operator` field from the card callback payload.
30
+ * @returns The resolved operator identifier, or `undefined` when neither
31
+ * field is present.
32
+ */
33
+ export declare function resolveCardCallbackOperatorId(operator: CardCallbackOperator | undefined): string | undefined;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Card callback operator identity extraction.
7
+ *
8
+ * Feishu Schema 2 card callbacks may carry the operator identity under
9
+ * either `operator.open_id` (Schema 1 / default) or `operator.user_id`
10
+ * (Schema 2 when the user has no open_id in the app's tenant).
11
+ *
12
+ * This helper provides a single, consistent extraction point so that
13
+ * every card callback handler resolves the operator identity the same
14
+ * way. See openclaw/openclaw#71670 for the upstream Schema 2 change.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.resolveCardCallbackOperatorId = resolveCardCallbackOperatorId;
18
+ /**
19
+ * Extract the operator's identity from a Feishu card callback event.
20
+ *
21
+ * Prefers `open_id` (the stable per-app user identifier) and falls back
22
+ * to `user_id` when `open_id` is absent or empty — this is the Schema 2 path.
23
+ *
24
+ * @param operator - The `operator` field from the card callback payload.
25
+ * @returns The resolved operator identifier, or `undefined` when neither
26
+ * field is present.
27
+ */
28
+ function resolveCardCallbackOperatorId(operator) {
29
+ return operator?.open_id || operator?.user_id;
30
+ }
@@ -59,16 +59,19 @@ const runtime_store_1 = require("./runtime-store.js");
59
59
  const version_1 = require("./version.js");
60
60
  const log = (0, lark_logger_1.larkLogger)('core/lark-client');
61
61
  // ---------------------------------------------------------------------------
62
- // 注入 User-Agent 到所有飞书 SDK 请求
62
+ // Inject User-Agent into all Feishu SDK requests
63
63
  // ---------------------------------------------------------------------------
64
64
  const GLOBAL_LARK_USER_AGENT_KEY = 'LARK_USER_AGENT';
65
65
  function installGlobalUserAgent() {
66
- // node-sdk 内置拦截器最终会读取 global.LARK_USER_AGENT 并覆盖 User-Agent
66
+ // node-sdk's built-in interceptor reads global.LARK_USER_AGENT to override User-Agent
67
67
  globalThis[GLOBAL_LARK_USER_AGENT_KEY] = (0, version_1.getUserAgent)();
68
68
  }
69
69
  installGlobalUserAgent();
70
+ // Disable axios auto-proxy to prevent HTTP_PROXY env vars from corrupting request URLs.
71
+ // Proxy routing is managed centrally by OpenClaw core's global-agent.
72
+ Lark.defaultHttpInstance.defaults.proxy = false;
70
73
  Lark.defaultHttpInstance.interceptors.request.handlers = [];
71
- // 使用 interceptors 在所有 HTTP 请求中注入 User-Agent header
74
+ // Inject User-Agent header into all HTTP requests via interceptor
72
75
  Lark.defaultHttpInstance.interceptors.request.use((req) => {
73
76
  if (req.headers) {
74
77
  req.headers['User-Agent'] = (0, version_1.getUserAgent)();
@@ -11,12 +11,16 @@ const utils_1 = require("./utils.js");
11
11
  const convertVideoChat = (raw) => {
12
12
  const parsed = (0, utils_1.safeParse)(raw);
13
13
  const topic = parsed?.topic ?? '';
14
+ const meetingNo = parsed?.meet_number?.trim() ?? '';
14
15
  const parts = [];
15
16
  if (topic) {
16
- parts.push(`📹 ${topic}`);
17
+ parts.push(`Topic: ${topic}`);
17
18
  }
18
19
  if (parsed?.start_time) {
19
- parts.push(`🕙 ${(0, utils_1.millisToDatetime)(parsed.start_time)}`);
20
+ parts.push(`Start time: ${(0, utils_1.millisToDatetime)(parsed.start_time)}`);
21
+ }
22
+ if (meetingNo) {
23
+ parts.push(`Meeting number: ${meetingNo}`);
20
24
  }
21
25
  const inner = parts.join('\n') || '[video chat]';
22
26
  return {
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.feishuMessageActions = void 0;
18
18
  const tool_send_1 = require("openclaw/plugin-sdk/tool-send");
19
19
  const param_readers_1 = require("openclaw/plugin-sdk/param-readers");
20
+ const typebox_1 = require("@sinclair/typebox");
20
21
  const sdk_compat_1 = require("../../core/sdk-compat.js");
21
22
  const lark_client_1 = require("../../core/lark-client.js");
22
23
  const accounts_1 = require("../../core/accounts.js");
@@ -25,6 +26,14 @@ const reactions_1 = require("./reactions.js");
25
26
  const deliver_1 = require("./deliver.js");
26
27
  const media_1 = require("./media.js");
27
28
  const log = (0, lark_logger_1.larkLogger)('outbound/actions');
29
+ const FEISHU_SEND_TEXT_DESCRIPTION = 'Text to send as a separate Feishu message. During a normal Feishu streaming-card reply, do not call send just to repeat or finalize the same answer; return the final answer normally so the active card can be completed by the reply dispatcher. Use send only when the user explicitly needs an additional separate message.';
30
+ const FEISHU_MESSAGE_TOOL_SCHEMA = {
31
+ properties: {
32
+ message: typebox_1.Type.Optional(typebox_1.Type.String({ description: FEISHU_SEND_TEXT_DESCRIPTION })),
33
+ text: typebox_1.Type.Optional(typebox_1.Type.String({ description: FEISHU_SEND_TEXT_DESCRIPTION })),
34
+ },
35
+ visibility: 'current-channel',
36
+ };
28
37
  // ---------------------------------------------------------------------------
29
38
  // Helpers
30
39
  // ---------------------------------------------------------------------------
@@ -131,7 +140,7 @@ exports.feishuMessageActions = {
131
140
  return {
132
141
  actions: Array.from(SUPPORTED_ACTIONS),
133
142
  capabilities: ['cards'],
134
- schema: null,
143
+ schema: FEISHU_MESSAGE_TOOL_SCHEMA,
135
144
  };
136
145
  },
137
146
  supportsAction: ({ action }) => SUPPORTED_ACTIONS.has(action),
@@ -26,6 +26,7 @@ exports.registerAskUserQuestionTool = registerAskUserQuestionTool;
26
26
  const node_crypto_1 = require("node:crypto");
27
27
  const typebox_1 = require("@sinclair/typebox");
28
28
  const lark_ticket_1 = require("../core/lark-ticket.js");
29
+ const card_action_operator_1 = require("../core/card-action-operator.js");
29
30
  const lark_logger_1 = require("../core/lark-logger.js");
30
31
  const cardkit_1 = require("../card/cardkit.js");
31
32
  const chat_queue_1 = require("../channel/chat-queue.js");
@@ -164,7 +165,7 @@ function handleAskUserAction(data, _cfg, accountId) {
164
165
  let openChatId;
165
166
  try {
166
167
  const event = data;
167
- senderOpenId = event.operator?.open_id;
168
+ senderOpenId = (0, card_action_operator_1.resolveCardCallbackOperatorId)(event.operator);
168
169
  // open_chat_id may be at top level or inside context (form submit callbacks use context)
169
170
  openChatId = event.open_chat_id ?? event.context?.open_chat_id;
170
171
  const actionTag = event.action?.tag;
@@ -33,6 +33,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.handleCardAction = handleCardAction;
34
34
  exports.handleInvokeErrorWithAutoAuth = handleInvokeErrorWithAutoAuth;
35
35
  const lark_ticket_1 = require("../core/lark-ticket.js");
36
+ const card_action_operator_1 = require("../core/card-action-operator.js");
36
37
  const lark_logger_1 = require("../core/lark-logger.js");
37
38
  const log = (0, lark_logger_1.larkLogger)('tools/auto-auth');
38
39
  const api_error_1 = require("../core/api-error.js");
@@ -591,7 +592,7 @@ async function handleCardAction(data, cfg, accountId) {
591
592
  const event = data;
592
593
  action = event.action?.value?.action;
593
594
  operationId = event.action?.value?.operation_id;
594
- senderOpenId = event.operator?.open_id;
595
+ senderOpenId = (0, card_action_operator_1.resolveCardCallbackOperatorId)(event.operator);
595
596
  }
596
597
  catch {
597
598
  return;