@larksuite/openclaw-lark 2026.4.9 → 2026.4.10-beta.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/package.json +1 -2
- package/skills/feishu-task/SKILL.md +1 -29
- package/src/channel/event-handlers.d.ts +0 -1
- package/src/channel/event-handlers.js +0 -73
- package/src/channel/monitor.js +0 -1
- package/src/core/raw-request.js +2 -26
- package/src/core/targets.js +1 -0
- package/src/core/tool-scopes.d.ts +2 -2
- package/src/core/tool-scopes.js +1 -5
- package/src/messaging/inbound/dispatch.d.ts +0 -2
- package/src/messaging/inbound/dispatch.js +0 -76
- package/src/messaging/inbound/handler.js +2 -2
- package/src/messaging/outbound/outbound.js +0 -18
- package/src/messaging/types.d.ts +0 -63
- package/src/tools/ask-user-question.d.ts +4 -0
- package/src/tools/ask-user-question.js +13 -2
- package/src/tools/oapi/index.js +0 -2
- package/src/tools/oapi/task/index.d.ts +0 -2
- package/src/tools/oapi/task/index.js +1 -5
- package/src/tools/oapi/task/task.d.ts +1 -2
- package/src/tools/oapi/task/task.js +2 -89
- package/src/core/synthetic-target.d.ts +0 -33
- package/src/core/synthetic-target.js +0 -40
- package/src/messaging/inbound/vc-meeting-invited-handler.d.ts +0 -20
- package/src/messaging/inbound/vc-meeting-invited-handler.js +0 -226
- package/src/messaging/inbound/vc-sender.d.ts +0 -41
- package/src/messaging/inbound/vc-sender.js +0 -53
- package/src/tools/oapi/task/attachment.d.ts +0 -18
- package/src/tools/oapi/task/attachment.js +0 -107
- package/src/tools/oapi/task/task_agent.d.ts +0 -14
- package/src/tools/oapi/task/task_agent.js +0 -108
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larksuite/openclaw-lark",
|
|
3
|
-
"version": "2026.4.
|
|
3
|
+
"version": "2026.4.10-beta.1",
|
|
4
4
|
"description": "OpenClaw Lark/Feishu channel plugin",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
"@larksuiteoapi/node-sdk": "^1.60.0",
|
|
27
27
|
"@sinclair/typebox": "0.34.48",
|
|
28
28
|
"image-size": "^2.0.2",
|
|
29
|
-
"undici-types": "^8.1.0",
|
|
30
29
|
"zod": "^4.3.6"
|
|
31
30
|
},
|
|
32
31
|
"peerDependencies": {
|
|
@@ -9,9 +9,6 @@ description: |
|
|
|
9
9
|
(3) 需要查看任务列表或清单内的任务
|
|
10
10
|
(4) 用户提到"任务"、"待办"、"to-do"、"清单"、"task"
|
|
11
11
|
(5) 需要设置任务负责人、关注人、截止时间、添加成员
|
|
12
|
-
(6) 需要追加任务步骤记录(Task 的 steps)
|
|
13
|
-
(7) 需要上传任务附件(支持 task / task_delivery)
|
|
14
|
-
(8) 需要注册 Agent / 更新 Agent 信息(register / update_profile)
|
|
15
12
|
---
|
|
16
13
|
|
|
17
14
|
# 飞书任务管理
|
|
@@ -20,13 +17,11 @@ description: |
|
|
|
20
17
|
|
|
21
18
|
- ✅ **时间格式**:ISO 8601 / RFC 3339(带时区),例如 `2026-02-28T17:00:00+08:00`
|
|
22
19
|
- ✅ **身份授权**:工具支持 `auth_type` 为 `user`(默认,用户身份)或 `tenant`(应用身份)。
|
|
23
|
-
- ✅ **任务 Agent(feishu_task_agent)**:仅支持应用身份(tenant),不支持 user 身份
|
|
24
20
|
- ✅ **current_user_id 强烈建议**:从消息上下文的 SenderId 获取(ou_...),工具会自动添加为 follower(如不在 members 中),确保创建者可以编辑任务
|
|
25
21
|
- ✅ **patch/get 必须**:task_guid
|
|
26
22
|
- ✅ **tasklist.tasks 必须**:tasklist_guid
|
|
27
23
|
- ✅ **完成任务**:completed_at = "2026-02-26 15:00:00"
|
|
28
24
|
- ✅ **反完成(恢复未完成)**:completed_at = "0"
|
|
29
|
-
- ✅ **append_steps 的 task_steps[].timestamp**:秒级 Unix 时间戳(10 位),不要用毫秒(13 位)
|
|
30
25
|
|
|
31
26
|
---
|
|
32
27
|
|
|
@@ -35,19 +30,15 @@ description: |
|
|
|
35
30
|
| 用户意图 | 工具 | action | 必填参数 | 强烈建议 | 常用可选 |
|
|
36
31
|
|---------|------|--------|---------|---------|---------|
|
|
37
32
|
| 新建待办 | feishu_task_task | create | summary | current_user_id(SenderId) | members, due, description, auth_type |
|
|
38
|
-
| 查未完成任务 | feishu_task_task | list | - | completed=false | page_size, auth_type
|
|
33
|
+
| 查未完成任务 | feishu_task_task | list | - | completed=false | page_size, auth_type |
|
|
39
34
|
| 获取任务详情 | feishu_task_task | get | task_guid | - | auth_type |
|
|
40
35
|
| 完成任务 | feishu_task_task | patch | task_guid, completed_at | - | auth_type |
|
|
41
36
|
| 反完成任务 | feishu_task_task | patch | task_guid, completed_at="0" | - | auth_type |
|
|
42
37
|
| 改截止时间 | feishu_task_task | patch | task_guid, due | - | auth_type |
|
|
43
38
|
| 添加任务成员 | feishu_task_task | add_members | task_guid, members[] | - | auth_type |
|
|
44
|
-
| 追加任务步骤记录 | feishu_task_task | append_steps | task_guid, idempotent_key, task_steps[] | task_steps[].timestamp 用秒级(10 位) | - |
|
|
45
39
|
| 创建清单 | feishu_task_tasklist | create | name | - | members |
|
|
46
40
|
| 查看清单任务 | feishu_task_tasklist | tasks | tasklist_guid | - | completed |
|
|
47
41
|
| 添加清单成员 | feishu_task_tasklist | add_members | tasklist_guid, members[] | - | - |
|
|
48
|
-
| 上传任务附件 | feishu_task_attachment | upload | resource_id, file(base64) | name | resource_type |
|
|
49
|
-
| 注册任务 Agent | feishu_task_agent | register | - | 仅支持 tenant(应用身份) | - |
|
|
50
|
-
| 更新任务 Agent Profile | feishu_task_agent | update_profile | profile_content | 仅支持 tenant(应用身份) | - |
|
|
51
42
|
|
|
52
43
|
---
|
|
53
44
|
|
|
@@ -242,25 +233,6 @@ description: |
|
|
|
242
233
|
}
|
|
243
234
|
```
|
|
244
235
|
|
|
245
|
-
### 场景 9: 注册任务 Agent(仅应用身份)
|
|
246
|
-
|
|
247
|
-
```json
|
|
248
|
-
{
|
|
249
|
-
"action": "register",
|
|
250
|
-
"auth_type": "tenant"
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### 场景 10: 更新任务 Agent Profile(仅应用身份)
|
|
255
|
-
|
|
256
|
-
```json
|
|
257
|
-
{
|
|
258
|
-
"action": "update_profile",
|
|
259
|
-
"auth_type": "tenant",
|
|
260
|
-
"profile_content": "some profile content"
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
236
|
---
|
|
265
237
|
|
|
266
238
|
## 🔍 常见错误与排查
|
|
@@ -12,6 +12,5 @@ import type { MonitorContext } from './types';
|
|
|
12
12
|
export declare function handleMessageEvent(ctx: MonitorContext, data: unknown): Promise<void>;
|
|
13
13
|
export declare function handleReactionEvent(ctx: MonitorContext, data: unknown): Promise<void>;
|
|
14
14
|
export declare function handleBotMembershipEvent(ctx: MonitorContext, data: unknown, action: 'added' | 'removed'): Promise<void>;
|
|
15
|
-
export declare function handleVcMeetingInvitedEvent(ctx: MonitorContext, data: unknown): Promise<void>;
|
|
16
15
|
export declare function handleCommentEvent(ctx: MonitorContext, data: unknown): Promise<void>;
|
|
17
16
|
export declare function handleCardActionEvent(ctx: MonitorContext, data: unknown): Promise<unknown>;
|
|
@@ -13,14 +13,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.handleMessageEvent = handleMessageEvent;
|
|
14
14
|
exports.handleReactionEvent = handleReactionEvent;
|
|
15
15
|
exports.handleBotMembershipEvent = handleBotMembershipEvent;
|
|
16
|
-
exports.handleVcMeetingInvitedEvent = handleVcMeetingInvitedEvent;
|
|
17
16
|
exports.handleCommentEvent = handleCommentEvent;
|
|
18
17
|
exports.handleCardActionEvent = handleCardActionEvent;
|
|
19
18
|
const handler_1 = require("../messaging/inbound/handler.js");
|
|
20
19
|
const reaction_handler_1 = require("../messaging/inbound/reaction-handler.js");
|
|
21
20
|
const comment_handler_1 = require("../messaging/inbound/comment-handler.js");
|
|
22
|
-
const vc_meeting_invited_handler_1 = require("../messaging/inbound/vc-meeting-invited-handler.js");
|
|
23
|
-
const vc_sender_1 = require("../messaging/inbound/vc-sender.js");
|
|
24
21
|
const comment_context_1 = require("../messaging/inbound/comment-context.js");
|
|
25
22
|
const dedup_1 = require("../messaging/inbound/dedup.js");
|
|
26
23
|
const lark_ticket_1 = require("../core/lark-ticket.js");
|
|
@@ -226,76 +223,6 @@ async function handleBotMembershipEvent(ctx, data, action) {
|
|
|
226
223
|
}
|
|
227
224
|
}
|
|
228
225
|
// ---------------------------------------------------------------------------
|
|
229
|
-
// VC meeting invited handler
|
|
230
|
-
// ---------------------------------------------------------------------------
|
|
231
|
-
async function handleVcMeetingInvitedEvent(ctx, data) {
|
|
232
|
-
if (!isEventOwnershipValid(ctx, data))
|
|
233
|
-
return;
|
|
234
|
-
const { accountId, log, error } = ctx;
|
|
235
|
-
try {
|
|
236
|
-
const event = data;
|
|
237
|
-
const meetingNo = event.meeting?.meeting_no?.trim() ?? '';
|
|
238
|
-
const eventId = event.event_id?.trim() ?? '';
|
|
239
|
-
// Resolve the inviter identity through the shared helper so the
|
|
240
|
-
// diagnostics log and the dispatch handler always agree on the
|
|
241
|
-
// same sender semantics.
|
|
242
|
-
const sender = (0, vc_sender_1.resolveVcSender)(event);
|
|
243
|
-
const senderId = sender.senderId;
|
|
244
|
-
const invitedBotOpenId = event.bot?.id?.open_id?.trim() ?? '';
|
|
245
|
-
// VC invited origin/ownership diagnostics:
|
|
246
|
-
// - This handler is only reachable from the WebSocket monitor path.
|
|
247
|
-
// - We still log app_id/bot_open_id so operators can confirm the event
|
|
248
|
-
// is delivered to the expected bot/account, and see which required
|
|
249
|
-
// fields are missing when we skip.
|
|
250
|
-
const expectedAppId = ctx.lark.account.appId ?? '';
|
|
251
|
-
const eventAppId = event.app_id?.trim() ?? '';
|
|
252
|
-
log(`feishu[${accountId}]: vc invited event received (ingress=websocket)` +
|
|
253
|
-
`${eventId ? ` event_id=${eventId}` : ''}` +
|
|
254
|
-
`${eventAppId ? ` app_id=${eventAppId}` : ' app_id=<missing>'}` +
|
|
255
|
-
`${expectedAppId ? ` expected_app_id=${expectedAppId}` : ''}` +
|
|
256
|
-
`${invitedBotOpenId ? ` bot_open_id=${invitedBotOpenId}` : ' bot_open_id=<missing>'}` +
|
|
257
|
-
`${ctx.lark.botOpenId ? ` expected_bot_open_id=${ctx.lark.botOpenId}` : ''}` +
|
|
258
|
-
`${event.invite_time ? ` invite_time=${event.invite_time}` : ''}` +
|
|
259
|
-
` meeting_no_present=${meetingNo ? 'true' : 'false'}` +
|
|
260
|
-
` sender_present=${senderId ? 'true' : 'false'}` +
|
|
261
|
-
` sender_from=${sender.fromFallback}`);
|
|
262
|
-
if (!meetingNo) {
|
|
263
|
-
log(`feishu[${accountId}]: vc invited event missing meeting_no, skipping`);
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (!senderId) {
|
|
267
|
-
log(`feishu[${accountId}]: vc invited event missing inviter identity, skipping`);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (ctx.lark.botOpenId && invitedBotOpenId && invitedBotOpenId !== ctx.lark.botOpenId) {
|
|
271
|
-
log(`feishu[${accountId}]: vc invited event for another bot, expected=${ctx.lark.botOpenId}, got=${invitedBotOpenId}, skipping`);
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
// Prefer event_id when the SDK exposes it: historical raw payload logs
|
|
275
|
-
// show WebSocket reconnect replays reuse the same event_id, while a real
|
|
276
|
-
// second invitation yields a new event_id even for the same meeting/bot.
|
|
277
|
-
// Fallback to (meeting_no, bot) only when event_id is absent so older
|
|
278
|
-
// payload shapes still remain deduplicated.
|
|
279
|
-
const dedupBotKey = ctx.lark.botOpenId ?? invitedBotOpenId ?? 'no-bot';
|
|
280
|
-
const dedupKey = eventId ? `vc-invited:by-event:${eventId}` : `vc-invited:by-meeting:${meetingNo}:${dedupBotKey}`;
|
|
281
|
-
if (!ctx.messageDedup.tryRecord(dedupKey, accountId)) {
|
|
282
|
-
log(`feishu[${accountId}]: duplicate vc invited event detected, skipping`);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
log(`feishu[${accountId}]: vc invited event accepted for synthetic dispatch`);
|
|
286
|
-
await (0, vc_meeting_invited_handler_1.handleFeishuVcMeetingInvited)({
|
|
287
|
-
cfg: ctx.cfg,
|
|
288
|
-
event,
|
|
289
|
-
runtime: ctx.runtime,
|
|
290
|
-
chatHistories: ctx.chatHistories,
|
|
291
|
-
accountId,
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
catch (err) {
|
|
295
|
-
error(`feishu[${accountId}]: error handling vc invited event: ${String(err)}`);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
// ---------------------------------------------------------------------------
|
|
299
226
|
// Drive comment handler
|
|
300
227
|
// ---------------------------------------------------------------------------
|
|
301
228
|
async function handleCommentEvent(ctx, data) {
|
package/src/channel/monitor.js
CHANGED
|
@@ -77,7 +77,6 @@ async function monitorSingleAccount(params) {
|
|
|
77
77
|
'im.chat.access_event.bot_p2p_chat_entered_v1': async () => { },
|
|
78
78
|
'im.chat.member.bot.added_v1': (data) => (0, event_handlers_1.handleBotMembershipEvent)(ctx, data, 'added'),
|
|
79
79
|
'im.chat.member.bot.deleted_v1': (data) => (0, event_handlers_1.handleBotMembershipEvent)(ctx, data, 'removed'),
|
|
80
|
-
'vc.bot.meeting_invited_v1': (data) => (0, event_handlers_1.handleVcMeetingInvitedEvent)(ctx, data),
|
|
81
80
|
// Drive comment event — fires when a user adds a comment or reply on a document.
|
|
82
81
|
'drive.notice.comment_add_v1': (data) => (0, event_handlers_1.handleCommentEvent)(ctx, data),
|
|
83
82
|
// 飞书 SDK EventDispatcher.register 不支持带返回值的处理器,此处 as any 是 SDK 类型限制的变通
|
package/src/core/raw-request.js
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.resolveDomainUrl = resolveDomainUrl;
|
|
13
13
|
exports.rawLarkRequest = rawLarkRequest;
|
|
14
|
-
const node_url_1 = require("node:url");
|
|
15
14
|
const feishu_fetch_1 = require("./feishu-fetch.js");
|
|
16
15
|
// ---------------------------------------------------------------------------
|
|
17
16
|
// Domain URL resolution
|
|
@@ -24,24 +23,6 @@ function resolveDomainUrl(brand) {
|
|
|
24
23
|
};
|
|
25
24
|
return map[brand] ?? `https://${brand}`;
|
|
26
25
|
}
|
|
27
|
-
function isFormDataBody(body) {
|
|
28
|
-
return (typeof body === 'object' &&
|
|
29
|
-
body !== null &&
|
|
30
|
-
typeof body.append === 'function' &&
|
|
31
|
-
typeof body.entries === 'function');
|
|
32
|
-
}
|
|
33
|
-
function isBinaryBody(body) {
|
|
34
|
-
return body instanceof ArrayBuffer || ArrayBuffer.isView(body);
|
|
35
|
-
}
|
|
36
|
-
function buildRequestBody(body) {
|
|
37
|
-
if (typeof body === 'string' || body instanceof node_url_1.URLSearchParams || isFormDataBody(body) || isBinaryBody(body)) {
|
|
38
|
-
return { body: body };
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
headers: { 'Content-Type': 'application/json' },
|
|
42
|
-
body: JSON.stringify(body),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
26
|
/**
|
|
46
27
|
* 发起 raw HTTP 请求到飞书 API,自动处理域名解析、header 注入和错误检测。
|
|
47
28
|
*
|
|
@@ -56,16 +37,11 @@ async function rawLarkRequest(options) {
|
|
|
56
37
|
}
|
|
57
38
|
}
|
|
58
39
|
const headers = {};
|
|
59
|
-
let requestBody;
|
|
60
40
|
if (options.accessToken) {
|
|
61
41
|
headers['Authorization'] = `Bearer ${options.accessToken}`;
|
|
62
42
|
}
|
|
63
43
|
if (options.body !== undefined) {
|
|
64
|
-
|
|
65
|
-
requestBody = prepared.body;
|
|
66
|
-
if (prepared.headers) {
|
|
67
|
-
Object.assign(headers, prepared.headers);
|
|
68
|
-
}
|
|
44
|
+
headers['Content-Type'] = 'application/json';
|
|
69
45
|
}
|
|
70
46
|
if (options.headers) {
|
|
71
47
|
Object.assign(headers, options.headers);
|
|
@@ -73,7 +49,7 @@ async function rawLarkRequest(options) {
|
|
|
73
49
|
const resp = await (0, feishu_fetch_1.feishuFetch)(url.toString(), {
|
|
74
50
|
method: options.method ?? 'GET',
|
|
75
51
|
headers,
|
|
76
|
-
...(
|
|
52
|
+
...(options.body !== undefined ? { body: JSON.stringify(options.body) } : {}),
|
|
77
53
|
});
|
|
78
54
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
55
|
const data = (await resp.json());
|
package/src/core/targets.js
CHANGED
|
@@ -145,6 +145,7 @@ function resolveReceiveIdType(id) {
|
|
|
145
145
|
return 'chat_id';
|
|
146
146
|
if (id.startsWith(OPEN_ID_PREFIX))
|
|
147
147
|
return 'open_id';
|
|
148
|
+
// Default to open_id for any other pattern (safer for outbound API calls).
|
|
148
149
|
return 'open_id';
|
|
149
150
|
}
|
|
150
151
|
function normalizeMessageId(messageId) {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
*
|
|
54
54
|
* 总计:96 个工具动作
|
|
55
55
|
*/
|
|
56
|
-
export type ToolActionKey = 'feishu_bitable_app.copy' | 'feishu_bitable_app.create' | 'feishu_bitable_app.get' | 'feishu_bitable_app.list' | 'feishu_bitable_app.patch' | 'feishu_bitable_app_table.batch_create' | 'feishu_bitable_app_table.create' | 'feishu_bitable_app_table.list' | 'feishu_bitable_app_table.patch' | 'feishu_bitable_app_table_field.create' | 'feishu_bitable_app_table_field.delete' | 'feishu_bitable_app_table_field.list' | 'feishu_bitable_app_table_field.update' | 'feishu_bitable_app_table_record.batch_create' | 'feishu_bitable_app_table_record.batch_delete' | 'feishu_bitable_app_table_record.batch_update' | 'feishu_bitable_app_table_record.create' | 'feishu_bitable_app_table_record.delete' | 'feishu_bitable_app_table_record.list' | 'feishu_bitable_app_table_record.update' | 'feishu_bitable_app_table_view.create' | 'feishu_bitable_app_table_view.get' | 'feishu_bitable_app_table_view.list' | 'feishu_bitable_app_table_view.patch' | 'feishu_calendar_calendar.get' | 'feishu_calendar_calendar.list' | 'feishu_calendar_calendar.primary' | 'feishu_calendar_event.create' | 'feishu_calendar_event.delete' | 'feishu_calendar_event.get' | 'feishu_calendar_event.instance_view' | 'feishu_calendar_event.instances' | 'feishu_calendar_event.list' | 'feishu_calendar_event.patch' | 'feishu_calendar_event.reply' | 'feishu_calendar_event.search' | 'feishu_calendar_event_attendee.create' | 'feishu_calendar_event_attendee.list' | 'feishu_calendar_freebusy.list' | 'feishu_chat.get' | 'feishu_chat.search' | 'feishu_chat_members.default' | 'feishu_create_doc.default' | 'feishu_doc_comments.create' | 'feishu_doc_comments.list' | 'feishu_doc_comments.list_replies' | 'feishu_doc_comments.patch' | 'feishu_doc_comments.reply' | 'feishu_doc_media.download' | 'feishu_doc_media.insert' | 'feishu_drive_file.copy' | 'feishu_drive_file.delete' | 'feishu_drive_file.download' | 'feishu_drive_file.get_meta' | 'feishu_drive_file.list' | 'feishu_drive_file.move' | 'feishu_drive_file.upload' | 'feishu_fetch_doc.default' | 'feishu_get_user.basic_batch' | 'feishu_get_user.default' | 'feishu_im_user_fetch_resource.default' | 'feishu_im_user_get_messages.default' | 'feishu_im_user_message.reply' | 'feishu_im_user_message.send' | 'feishu_im_user_search_messages.default' | 'feishu_search_doc_wiki.search' | 'feishu_search_user.default' | '
|
|
56
|
+
export type ToolActionKey = 'feishu_bitable_app.copy' | 'feishu_bitable_app.create' | 'feishu_bitable_app.get' | 'feishu_bitable_app.list' | 'feishu_bitable_app.patch' | 'feishu_bitable_app_table.batch_create' | 'feishu_bitable_app_table.create' | 'feishu_bitable_app_table.list' | 'feishu_bitable_app_table.patch' | 'feishu_bitable_app_table_field.create' | 'feishu_bitable_app_table_field.delete' | 'feishu_bitable_app_table_field.list' | 'feishu_bitable_app_table_field.update' | 'feishu_bitable_app_table_record.batch_create' | 'feishu_bitable_app_table_record.batch_delete' | 'feishu_bitable_app_table_record.batch_update' | 'feishu_bitable_app_table_record.create' | 'feishu_bitable_app_table_record.delete' | 'feishu_bitable_app_table_record.list' | 'feishu_bitable_app_table_record.update' | 'feishu_bitable_app_table_view.create' | 'feishu_bitable_app_table_view.get' | 'feishu_bitable_app_table_view.list' | 'feishu_bitable_app_table_view.patch' | 'feishu_calendar_calendar.get' | 'feishu_calendar_calendar.list' | 'feishu_calendar_calendar.primary' | 'feishu_calendar_event.create' | 'feishu_calendar_event.delete' | 'feishu_calendar_event.get' | 'feishu_calendar_event.instance_view' | 'feishu_calendar_event.instances' | 'feishu_calendar_event.list' | 'feishu_calendar_event.patch' | 'feishu_calendar_event.reply' | 'feishu_calendar_event.search' | 'feishu_calendar_event_attendee.create' | 'feishu_calendar_event_attendee.list' | 'feishu_calendar_freebusy.list' | 'feishu_chat.get' | 'feishu_chat.search' | 'feishu_chat_members.default' | 'feishu_create_doc.default' | 'feishu_doc_comments.create' | 'feishu_doc_comments.list' | 'feishu_doc_comments.list_replies' | 'feishu_doc_comments.patch' | 'feishu_doc_comments.reply' | 'feishu_doc_media.download' | 'feishu_doc_media.insert' | 'feishu_drive_file.copy' | 'feishu_drive_file.delete' | 'feishu_drive_file.download' | 'feishu_drive_file.get_meta' | 'feishu_drive_file.list' | 'feishu_drive_file.move' | 'feishu_drive_file.upload' | 'feishu_fetch_doc.default' | 'feishu_get_user.basic_batch' | 'feishu_get_user.default' | 'feishu_im_user_fetch_resource.default' | 'feishu_im_user_get_messages.default' | 'feishu_im_user_message.reply' | 'feishu_im_user_message.send' | 'feishu_im_user_search_messages.default' | 'feishu_search_doc_wiki.search' | 'feishu_search_user.default' | 'feishu_task_comment.create' | 'feishu_task_comment.get' | 'feishu_task_comment.list' | 'feishu_task_section.create' | 'feishu_task_section.get' | 'feishu_task_section.list' | 'feishu_task_section.patch' | 'feishu_task_section.tasks' | 'feishu_task_subtask.create' | 'feishu_task_subtask.list' | 'feishu_task_task.create' | 'feishu_task_task.get' | 'feishu_task_task.list' | 'feishu_task_task.patch' | 'feishu_task_task.add_members' | 'feishu_task_tasklist.add_members' | 'feishu_task_tasklist.create' | 'feishu_task_tasklist.get' | 'feishu_task_tasklist.list' | 'feishu_task_tasklist.patch' | 'feishu_task_tasklist.tasks' | 'feishu_update_doc.default' | 'feishu_wiki_space.create' | 'feishu_wiki_space.get' | 'feishu_wiki_space.list' | 'feishu_wiki_space_node.copy' | 'feishu_wiki_space_node.create' | 'feishu_wiki_space_node.get' | 'feishu_wiki_space_node.list' | 'feishu_wiki_space_node.move' | 'feishu_sheet.info' | 'feishu_sheet.read' | 'feishu_sheet.write' | 'feishu_sheet.append' | 'feishu_sheet.find' | 'feishu_sheet.create' | 'feishu_sheet.export';
|
|
57
57
|
/**
|
|
58
58
|
* Tool Scope 映射类型
|
|
59
59
|
*
|
|
@@ -153,4 +153,4 @@ export declare function filterSensitiveScopes(scopes: string[]): string[];
|
|
|
153
153
|
* 唯一 scope 总数: 74
|
|
154
154
|
* 必需应用权限总数: 20
|
|
155
155
|
* 高敏感权限总数: 4
|
|
156
|
-
*/
|
|
156
|
+
*/
|
package/src/core/tool-scopes.js
CHANGED
|
@@ -107,13 +107,11 @@ exports.TOOL_SCOPES = {
|
|
|
107
107
|
'feishu_calendar_event_attendee.create': ['calendar:calendar.event:update'],
|
|
108
108
|
'feishu_calendar_event_attendee.list': ['calendar:calendar.event:read'],
|
|
109
109
|
'feishu_calendar_freebusy.list': ['calendar:calendar.free_busy:read'],
|
|
110
|
-
'feishu_task_attachment.upload': ['task:attachment:write'],
|
|
111
110
|
'feishu_task_task.create': ['task:task:write', 'task:task:writeonly'],
|
|
112
111
|
'feishu_task_task.get': ['task:task:read', 'task:task:write'],
|
|
113
112
|
'feishu_task_task.list': ['task:task:read', 'task:task:write'],
|
|
114
113
|
'feishu_task_task.patch': ['task:task:write', 'task:task:writeonly'],
|
|
115
114
|
'feishu_task_task.add_members': ['task:task:write', 'task:task:writeonly'],
|
|
116
|
-
'feishu_task_task.append_steps': ['task:task:write', 'task:task:writeonly'],
|
|
117
115
|
'feishu_task_tasklist.create': ['task:tasklist:write'],
|
|
118
116
|
'feishu_task_tasklist.get': ['task:tasklist:read', 'task:tasklist:write'],
|
|
119
117
|
'feishu_task_tasklist.list': ['task:tasklist:read', 'task:tasklist:write'],
|
|
@@ -130,8 +128,6 @@ exports.TOOL_SCOPES = {
|
|
|
130
128
|
'feishu_task_section.tasks': ['task:task'],
|
|
131
129
|
'feishu_task_subtask.create': ['task:task:write'],
|
|
132
130
|
'feishu_task_subtask.list': ['task:task:read', 'task:task:write'],
|
|
133
|
-
'feishu_task_agent.register': ['task:task:write'],
|
|
134
|
-
'feishu_task_agent.update_profile': ['task:task:write', 'task:task:writeonly'],
|
|
135
131
|
'feishu_chat.search': ['im:chat:read'],
|
|
136
132
|
'feishu_chat.get': ['im:chat:read'],
|
|
137
133
|
'feishu_chat_members.default': ['im:chat.members:read'],
|
|
@@ -341,4 +337,4 @@ function filterSensitiveScopes(scopes) {
|
|
|
341
337
|
* 唯一 scope 总数: 74
|
|
342
338
|
* 必需应用权限总数: 20
|
|
343
339
|
* 高敏感权限总数: 4
|
|
344
|
-
*/
|
|
340
|
+
*/
|
|
@@ -22,8 +22,6 @@ export declare function dispatchToAgent(params: {
|
|
|
22
22
|
ctx: MessageContext;
|
|
23
23
|
permissionError?: PermissionError;
|
|
24
24
|
mediaPayload: Record<string, unknown>;
|
|
25
|
-
/** Additional structured metadata for synthetic or event-driven inbound flows. */
|
|
26
|
-
extraInboundFields?: Record<string, unknown>;
|
|
27
25
|
quotedContent?: string;
|
|
28
26
|
account: LarkAccount;
|
|
29
27
|
/** account 级别的 ClawdbotConfig(channels.feishu 已替换为 per-account 合并后的配置) */
|
|
@@ -26,7 +26,6 @@ const tool_use_trace_store_1 = require("../../card/tool-use-trace-store.js");
|
|
|
26
26
|
const abort_detect_1 = require("../../channel/abort-detect.js");
|
|
27
27
|
const chat_info_cache_1 = require("../../core/chat-info-cache.js");
|
|
28
28
|
const comment_target_1 = require("../../core/comment-target.js");
|
|
29
|
-
const synthetic_target_1 = require("../../core/synthetic-target.js");
|
|
30
29
|
const targets_1 = require("../../core/targets.js");
|
|
31
30
|
const deliver_1 = require("../outbound/deliver.js");
|
|
32
31
|
const doctor_1 = require("../../commands/doctor.js");
|
|
@@ -95,81 +94,7 @@ async function dispatchCommentMessage(dc, ctxPayload, skillFilter) {
|
|
|
95
94
|
dc.log(`feishu[${dc.account.accountId}]: comment dispatch complete (delivered=${delivered})`);
|
|
96
95
|
log.info(`comment dispatch complete (delivered=${delivered}, elapsed=${(0, lark_ticket_1.ticketElapsed)()}ms)`);
|
|
97
96
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Dispatch a synthetic-target message via the buffered block dispatcher
|
|
100
|
-
* while discarding every delivered payload.
|
|
101
|
-
*
|
|
102
|
-
* Synthetic contexts (e.g. VC meeting-invited) trigger the agent for its
|
|
103
|
-
* side-effects (tool calls) — they do not correspond to a real IM chat,
|
|
104
|
-
* so any text / card the agent emits must be dropped instead of being
|
|
105
|
-
* sent as a DM to whatever open_id happens to be in ctx.chatId.
|
|
106
|
-
*/
|
|
107
|
-
async function dispatchSyntheticMessage(dc, ctxPayload, skillFilter) {
|
|
108
|
-
const effectiveSessionKey = dc.threadSessionKey ?? dc.route.sessionKey;
|
|
109
|
-
const isVcSynthetic = dc.ctx.chatId === synthetic_target_1.SYNTHETIC_VC_CHAT_ID;
|
|
110
|
-
let deliveredFinalToSender = false;
|
|
111
|
-
dc.log(`feishu[${dc.account.accountId}]: dispatching synthetic reply (session=${effectiveSessionKey}, target=${dc.ctx.chatId})`);
|
|
112
|
-
log.info(`dispatching synthetic reply (session=${effectiveSessionKey})`);
|
|
113
|
-
await dc.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
114
|
-
ctx: ctxPayload,
|
|
115
|
-
cfg: dc.accountScopedCfg,
|
|
116
|
-
dispatcherOptions: {
|
|
117
|
-
deliver: async (payload, info) => {
|
|
118
|
-
const text = payload.text?.trim() ?? '';
|
|
119
|
-
const preview = text.slice(0, 120);
|
|
120
|
-
// VC invited flows intentionally keep the synthetic target to avoid
|
|
121
|
-
// leaking intermediate tool output to IM, but the final business
|
|
122
|
-
// result should be explicitly notified to the inviter.
|
|
123
|
-
//
|
|
124
|
-
// Important: this DM is only a transport bridge for the final text.
|
|
125
|
-
// It does not rebind the inviter's DM conversation to the synthetic
|
|
126
|
-
// meeting-scoped session; later DM replies will still be routed by the
|
|
127
|
-
// normal OpenClaw DM session rules.
|
|
128
|
-
if (isVcSynthetic && info.kind === 'final' && text && text !== 'NO_REPLY' && !deliveredFinalToSender) {
|
|
129
|
-
deliveredFinalToSender = true;
|
|
130
|
-
try {
|
|
131
|
-
await (0, send_1.sendMessageFeishu)({
|
|
132
|
-
cfg: dc.accountScopedCfg,
|
|
133
|
-
to: dc.ctx.senderId,
|
|
134
|
-
text,
|
|
135
|
-
accountId: dc.account.accountId,
|
|
136
|
-
});
|
|
137
|
-
dc.log(`feishu[${dc.account.accountId}]: synthetic VC final delivered explicitly to sender=${dc.ctx.senderId}, preview="${preview}"`);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
catch (err) {
|
|
141
|
-
deliveredFinalToSender = false;
|
|
142
|
-
dc.error(`feishu[${dc.account.accountId}]: synthetic VC final delivery failed to sender=${dc.ctx.senderId}: ${String(err)}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (info.kind === 'final') {
|
|
146
|
-
dc.log(`feishu[${dc.account.accountId}]: synthetic final payload dropped (target=${dc.ctx.chatId})`);
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
onSkip: (_payload, info) => {
|
|
150
|
-
if (info.reason !== 'silent') {
|
|
151
|
-
dc.log(`feishu[${dc.account.accountId}]: synthetic reply skipped (reason=${info.reason})`);
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
onError: (err, info) => {
|
|
155
|
-
dc.error(`feishu[${dc.account.accountId}]: synthetic ${info.kind} reply failed: ${String(err)}`);
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
replyOptions: {
|
|
159
|
-
...(skillFilter ? { skillFilter } : {}),
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
dc.log(`feishu[${dc.account.accountId}]: synthetic dispatch complete (elapsed=${(0, lark_ticket_1.ticketElapsed)()}ms)`);
|
|
163
|
-
}
|
|
164
97
|
async function dispatchNormalMessage(dc, ctxPayload, chatHistories, historyKey, historyLimit, replyToMessageId, skillFilter, skipTyping) {
|
|
165
|
-
// Synthetic targets (e.g. VC meeting-invited) have no real IM peer to
|
|
166
|
-
// deliver replies to. Route them through the buffered block dispatcher
|
|
167
|
-
// with a deliver() that drops every payload — the agent still runs
|
|
168
|
-
// (tool calls, side-effects) but produces no outbound IM traffic.
|
|
169
|
-
if ((0, synthetic_target_1.isSyntheticTarget)(dc.ctx.chatId)) {
|
|
170
|
-
await dispatchSyntheticMessage(dc, ctxPayload, skillFilter);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
98
|
// Comment targets bypass the streaming card / IM flow entirely —
|
|
174
99
|
// route through the Drive comment reply API.
|
|
175
100
|
if ((0, comment_target_1.isCommentTarget)(dc.ctx.chatId)) {
|
|
@@ -352,7 +277,6 @@ async function dispatchToAgent(params) {
|
|
|
352
277
|
inboundHistory,
|
|
353
278
|
extraFields: {
|
|
354
279
|
...params.mediaPayload,
|
|
355
|
-
...(params.extraInboundFields ?? {}),
|
|
356
280
|
...(groupSystemPrompt ? { GroupSystemPrompt: groupSystemPrompt } : {}),
|
|
357
281
|
...(dc.ctx.threadId ? { MessageThreadId: dc.ctx.threadId } : {}),
|
|
358
282
|
},
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.handleFeishuMessage = handleFeishuMessage;
|
|
19
19
|
const reply_history_1 = require("openclaw/plugin-sdk/reply-history");
|
|
20
|
-
const
|
|
20
|
+
const command_auth_1 = require("openclaw/plugin-sdk/command-auth");
|
|
21
21
|
const allow_from_1 = require("openclaw/plugin-sdk/allow-from");
|
|
22
22
|
const accounts_1 = require("../../core/accounts.js");
|
|
23
23
|
const lark_client_1 = require("../../core/lark-client.js");
|
|
@@ -139,7 +139,7 @@ async function handleFeishuMessage(params) {
|
|
|
139
139
|
const explicitSenderPolicy = groupConfig?.groupPolicy ?? defaultGroupConfig?.groupPolicy ?? accountFeishuCfg?.groupPolicy;
|
|
140
140
|
return explicitSenderPolicy === 'open' ? ['*'] : [];
|
|
141
141
|
})();
|
|
142
|
-
const { commandAuthorized } = await (0,
|
|
142
|
+
const { commandAuthorized } = await (0, command_auth_1.resolveSenderCommandAuthorization)({
|
|
143
143
|
rawBody: ctx.content,
|
|
144
144
|
cfg: accountScopedCfg,
|
|
145
145
|
isGroup,
|
|
@@ -6,7 +6,6 @@ const lark_client_1 = require("../../core/lark-client.js");
|
|
|
6
6
|
const lark_logger_1 = require("../../core/lark-logger.js");
|
|
7
7
|
const targets_1 = require("../../core/targets.js");
|
|
8
8
|
const comment_target_1 = require("../../core/comment-target.js");
|
|
9
|
-
const synthetic_target_1 = require("../../core/synthetic-target.js");
|
|
10
9
|
const deliver_1 = require("./deliver.js");
|
|
11
10
|
const log = (0, lark_logger_1.larkLogger)('outbound/outbound');
|
|
12
11
|
/**
|
|
@@ -42,13 +41,6 @@ exports.feishuOutbound = {
|
|
|
42
41
|
textChunkLimit: 15000,
|
|
43
42
|
sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
|
|
44
43
|
log.info(`sendText: target=${to}, textLength=${text.length}`);
|
|
45
|
-
// Synthetic targets (e.g. VC meeting-invited) have no real IM peer —
|
|
46
|
-
// drop the send silently so the agent pipeline stays uniform without
|
|
47
|
-
// producing unsolicited DMs. See core/synthetic-target.ts.
|
|
48
|
-
if ((0, synthetic_target_1.isSyntheticTarget)(to)) {
|
|
49
|
-
log.debug(`sendText: synthetic target ${to}, dropping outbound IM send`);
|
|
50
|
-
return { channel: 'feishu', messageId: '', chatId: to };
|
|
51
|
-
}
|
|
52
44
|
// Comment thread routing — route replies through Drive comment API
|
|
53
45
|
if ((0, comment_target_1.isCommentTarget)(to)) {
|
|
54
46
|
log.info(`sendText: detected comment target, routing through Drive comment API`);
|
|
@@ -61,11 +53,6 @@ exports.feishuOutbound = {
|
|
|
61
53
|
},
|
|
62
54
|
sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId, replyToId, threadId }) => {
|
|
63
55
|
log.info(`sendMedia: target=${to}, ` + `hasText=${Boolean(text?.trim())}, mediaUrl=${mediaUrl ?? '(none)'}`);
|
|
64
|
-
// Synthetic targets — drop silently (see sendText for rationale).
|
|
65
|
-
if ((0, synthetic_target_1.isSyntheticTarget)(to)) {
|
|
66
|
-
log.debug(`sendMedia: synthetic target ${to}, dropping outbound IM send`);
|
|
67
|
-
return { channel: 'feishu', messageId: '', chatId: to };
|
|
68
|
-
}
|
|
69
56
|
// Comment thread routing — send text (with media URL appended) via Drive comment API
|
|
70
57
|
if ((0, comment_target_1.isCommentTarget)(to)) {
|
|
71
58
|
log.info(`sendMedia: detected comment target, routing through Drive comment API`);
|
|
@@ -98,11 +85,6 @@ exports.feishuOutbound = {
|
|
|
98
85
|
};
|
|
99
86
|
},
|
|
100
87
|
sendPayload: async ({ cfg, to, payload, mediaLocalRoots, accountId, replyToId, threadId }) => {
|
|
101
|
-
// Synthetic targets — drop silently (see sendText for rationale).
|
|
102
|
-
if ((0, synthetic_target_1.isSyntheticTarget)(to)) {
|
|
103
|
-
log.debug(`sendPayload: synthetic target ${to}, dropping outbound IM send`);
|
|
104
|
-
return { channel: 'feishu', messageId: '', chatId: to };
|
|
105
|
-
}
|
|
106
88
|
const ctx = resolveFeishuSendContext({ cfg, to, accountId, replyToId, threadId });
|
|
107
89
|
// --- channelData.feishu: card message support ---
|
|
108
90
|
const feishuData = payload.channelData?.feishu;
|
package/src/messaging/types.d.ts
CHANGED
|
@@ -135,69 +135,6 @@ export interface FeishuBotAddedEvent {
|
|
|
135
135
|
ja_jp?: string;
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Raw event shape for `vc.bot.meeting_invited_v1`.
|
|
140
|
-
*
|
|
141
|
-
* Fired when a user invites the bot to join a meeting.
|
|
142
|
-
*
|
|
143
|
-
* Identity fields (for bot / inviter / host_user) are nested under an
|
|
144
|
-
* `id` object; individual id variants (open_id / user_id / union_id)
|
|
145
|
-
* may be null depending on the tenant configuration.
|
|
146
|
-
*/
|
|
147
|
-
export interface FeishuVcMeetingInvitedEvent {
|
|
148
|
-
app_id?: string;
|
|
149
|
-
event_id?: string;
|
|
150
|
-
meeting?: {
|
|
151
|
-
id?: string;
|
|
152
|
-
meeting_no?: string;
|
|
153
|
-
topic?: string;
|
|
154
|
-
host_user?: {
|
|
155
|
-
id?: {
|
|
156
|
-
open_id?: string | null;
|
|
157
|
-
user_id?: string | null;
|
|
158
|
-
union_id?: string | null;
|
|
159
|
-
};
|
|
160
|
-
user_name?: string;
|
|
161
|
-
};
|
|
162
|
-
};
|
|
163
|
-
bot?: {
|
|
164
|
-
id?: {
|
|
165
|
-
open_id?: string | null;
|
|
166
|
-
user_id?: string | null;
|
|
167
|
-
union_id?: string | null;
|
|
168
|
-
};
|
|
169
|
-
user_name?: string;
|
|
170
|
-
};
|
|
171
|
-
inviter?: {
|
|
172
|
-
id?: {
|
|
173
|
-
open_id?: string | null;
|
|
174
|
-
user_id?: string | null;
|
|
175
|
-
union_id?: string | null;
|
|
176
|
-
};
|
|
177
|
-
user_name?: string;
|
|
178
|
-
};
|
|
179
|
-
invite_time?: string;
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Internal synthetic event model for VC meeting-invited flows.
|
|
183
|
-
*
|
|
184
|
-
* This preserves the business semantics before the event is converted into a
|
|
185
|
-
* synthetic MessageContext for dispatchToAgent().
|
|
186
|
-
*/
|
|
187
|
-
export interface VcMeetingInvitedSyntheticEvent {
|
|
188
|
-
eventType: 'vc.bot.meeting_invited_v1';
|
|
189
|
-
source: 'feishu-vc-event';
|
|
190
|
-
eventId?: string;
|
|
191
|
-
meetingId?: string;
|
|
192
|
-
meetingNo: string;
|
|
193
|
-
topic?: string;
|
|
194
|
-
senderId: string;
|
|
195
|
-
senderOpenId?: string;
|
|
196
|
-
senderUserId?: string;
|
|
197
|
-
senderUnionId?: string;
|
|
198
|
-
senderName?: string;
|
|
199
|
-
inviteTime?: string;
|
|
200
|
-
}
|
|
201
138
|
/** Metadata describing a media resource in a message (no binary data). */
|
|
202
139
|
export interface ResourceDescriptor {
|
|
203
140
|
type: 'image' | 'file' | 'audio' | 'video' | 'sticker';
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
*
|
|
15
15
|
* 所有卡片统一使用 form 容器,交互组件在本地缓存值,
|
|
16
16
|
* 提交时通过 form_value 一次性回调,避免独立回调导致的 loading 闪烁。
|
|
17
|
+
*
|
|
18
|
+
* 注意:提交按钮必须设置 value 字段。飞书客户端会校验交互组件是否携带 value,
|
|
19
|
+
* 缺失时直接报 200340 错误,回调不会发送到服务端。虽然 form_submit 场景下
|
|
20
|
+
* form_value 是主要数据载体,但 value 作为按钮的合法性标记仍需存在。
|
|
17
21
|
*/
|
|
18
22
|
import type { ClawdbotConfig, OpenClawPluginApi } from 'openclaw/plugin-sdk';
|
|
19
23
|
/**
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
*
|
|
16
16
|
* 所有卡片统一使用 form 容器,交互组件在本地缓存值,
|
|
17
17
|
* 提交时通过 form_value 一次性回调,避免独立回调导致的 loading 闪烁。
|
|
18
|
+
*
|
|
19
|
+
* 注意:提交按钮必须设置 value 字段。飞书客户端会校验交互组件是否携带 value,
|
|
20
|
+
* 缺失时直接报 200340 错误,回调不会发送到服务端。虽然 form_submit 场景下
|
|
21
|
+
* form_value 是主要数据载体,但 value 作为按钮的合法性标记仍需存在。
|
|
18
22
|
*/
|
|
19
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
24
|
exports.handleAskUserAction = handleAskUserAction;
|
|
@@ -537,12 +541,19 @@ function buildAskUserCard(questions, questionId) {
|
|
|
537
541
|
}
|
|
538
542
|
formElements.push(...buildQuestionFormElements(questions[i], i));
|
|
539
543
|
}
|
|
540
|
-
// Submit button
|
|
544
|
+
// Submit button.
|
|
545
|
+
// questionId is encoded in both button name and value for resilience:
|
|
546
|
+
// - `value` is the primary carrier and is included in card.action.trigger
|
|
547
|
+
// callbacks per Feishu's documented event structure.
|
|
548
|
+
// - `name` acts as a fallback in case the SDK version strips value
|
|
549
|
+
// from form_submit events.
|
|
550
|
+
// Both are needed because a button without a `value` field fails
|
|
551
|
+
// Feishu's client-side interactive-component validation with code 200340.
|
|
541
552
|
formElements.push({ tag: 'hr' });
|
|
542
553
|
formElements.push({
|
|
543
554
|
tag: 'button',
|
|
544
|
-
// Encode questionId in button name — value does NOT propagate for form submit buttons
|
|
545
555
|
name: `${SUBMIT_BUTTON_PREFIX}${questionId}`,
|
|
556
|
+
value: { action: 'ask_user_submit', operation_id: questionId },
|
|
546
557
|
text: {
|
|
547
558
|
tag: 'plain_text',
|
|
548
559
|
content: '📮 提交',
|
package/src/tools/oapi/index.js
CHANGED
|
@@ -39,11 +39,9 @@ function registerOapiTools(api) {
|
|
|
39
39
|
// Task tools
|
|
40
40
|
(0, index_3.registerFeishuTaskTaskTool)(api);
|
|
41
41
|
(0, index_3.registerFeishuTaskTasklistTool)(api);
|
|
42
|
-
(0, index_3.registerFeishuTaskAttachmentTool)(api);
|
|
43
42
|
(0, index_3.registerFeishuTaskSectionTool)(api);
|
|
44
43
|
(0, index_3.registerFeishuTaskCommentTool)(api);
|
|
45
44
|
(0, index_3.registerFeishuTaskSubtaskTool)(api);
|
|
46
|
-
(0, index_3.registerFeishuTaskAgentTool)(api);
|
|
47
45
|
// Bitable tools
|
|
48
46
|
(0, index_4.registerFeishuBitableAppTool)(api);
|
|
49
47
|
(0, index_4.registerFeishuBitableAppTableTool)(api);
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { registerFeishuTaskTaskTool } from './task';
|
|
6
6
|
export { registerFeishuTaskTasklistTool } from './tasklist';
|
|
7
|
-
export { registerFeishuTaskAttachmentTool } from './attachment';
|
|
8
7
|
export { registerFeishuTaskCommentTool } from './comment';
|
|
9
8
|
export { registerFeishuTaskSubtaskTool } from './subtask';
|
|
10
9
|
export { registerFeishuTaskSectionTool } from './section';
|
|
11
|
-
export { registerFeishuTaskAgentTool } from './task_agent';
|