@larksuite/openclaw-lark 2025.7.15-beta.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/LICENSE +21 -0
- package/README.md +76 -0
- package/bin/openclaw-lark.js +39 -0
- package/index.d.ts +36 -0
- package/index.js +188 -0
- package/openclaw.plugin.json +64 -0
- package/package.json +63 -0
- package/secret-contract-api.d.ts +15 -0
- package/secret-contract-api.js +78 -0
- package/skills/feishu-bitable/SKILL.md +248 -0
- package/skills/feishu-bitable/references/examples.md +813 -0
- package/skills/feishu-bitable/references/field-properties.md +763 -0
- package/skills/feishu-bitable/references/record-values.md +911 -0
- package/skills/feishu-calendar/SKILL.md +242 -0
- package/skills/feishu-channel-rules/SKILL.md +18 -0
- package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
- package/skills/feishu-create-doc/SKILL.md +719 -0
- package/skills/feishu-fetch-doc/SKILL.md +93 -0
- package/skills/feishu-im-read/SKILL.md +163 -0
- package/skills/feishu-task/SKILL.md +340 -0
- package/skills/feishu-troubleshoot/SKILL.md +70 -0
- package/skills/feishu-update-doc/SKILL.md +285 -0
- package/src/card/builder.d.ts +152 -0
- package/src/card/builder.js +794 -0
- package/src/card/card-error.d.ts +91 -0
- package/src/card/card-error.js +206 -0
- package/src/card/cardkit.d.ts +90 -0
- package/src/card/cardkit.js +203 -0
- package/src/card/flush-controller.d.ts +45 -0
- package/src/card/flush-controller.js +138 -0
- package/src/card/image-resolver.d.ts +45 -0
- package/src/card/image-resolver.js +116 -0
- package/src/card/markdown-style.d.ts +16 -0
- package/src/card/markdown-style.js +106 -0
- package/src/card/reasoning-utils.d.ts +14 -0
- package/src/card/reasoning-utils.js +64 -0
- package/src/card/reply-dispatcher-types.d.ts +132 -0
- package/src/card/reply-dispatcher-types.js +61 -0
- package/src/card/reply-dispatcher.d.ts +15 -0
- package/src/card/reply-dispatcher.js +447 -0
- package/src/card/reply-mode.d.ts +41 -0
- package/src/card/reply-mode.js +70 -0
- package/src/card/streaming-card-controller.d.ts +118 -0
- package/src/card/streaming-card-controller.js +1045 -0
- package/src/card/tool-use-config.d.ts +26 -0
- package/src/card/tool-use-config.js +76 -0
- package/src/card/tool-use-display.d.ts +37 -0
- package/src/card/tool-use-display.js +476 -0
- package/src/card/tool-use-trace-store.d.ts +51 -0
- package/src/card/tool-use-trace-store.js +271 -0
- package/src/card/unavailable-guard.d.ts +35 -0
- package/src/card/unavailable-guard.js +87 -0
- package/src/channel/abort-detect.d.ts +47 -0
- package/src/channel/abort-detect.js +219 -0
- package/src/channel/chat-queue.d.ts +41 -0
- package/src/channel/chat-queue.js +68 -0
- package/src/channel/config-adapter.d.ts +23 -0
- package/src/channel/config-adapter.js +107 -0
- package/src/channel/directory.d.ts +57 -0
- package/src/channel/directory.js +197 -0
- package/src/channel/event-handlers.d.ts +17 -0
- package/src/channel/event-handlers.js +380 -0
- package/src/channel/interactive-dispatch.d.ts +59 -0
- package/src/channel/interactive-dispatch.js +195 -0
- package/src/channel/monitor.d.ts +17 -0
- package/src/channel/monitor.js +140 -0
- package/src/channel/onboarding-config.d.ts +18 -0
- package/src/channel/onboarding-config.js +96 -0
- package/src/channel/onboarding-migrate.d.ts +25 -0
- package/src/channel/onboarding-migrate.js +70 -0
- package/src/channel/onboarding.d.ts +12 -0
- package/src/channel/onboarding.js +300 -0
- package/src/channel/plugin.d.ts +13 -0
- package/src/channel/plugin.js +310 -0
- package/src/channel/probe.d.ts +14 -0
- package/src/channel/probe.js +24 -0
- package/src/channel/types.d.ts +37 -0
- package/src/channel/types.js +8 -0
- package/src/commands/auth.d.ts +21 -0
- package/src/commands/auth.js +165 -0
- package/src/commands/diagnose.d.ts +69 -0
- package/src/commands/diagnose.js +848 -0
- package/src/commands/doctor.d.ts +27 -0
- package/src/commands/doctor.js +588 -0
- package/src/commands/index.d.ts +25 -0
- package/src/commands/index.js +219 -0
- package/src/commands/locale.d.ts +7 -0
- package/src/commands/locale.js +8 -0
- package/src/core/accounts.d.ts +51 -0
- package/src/core/accounts.js +219 -0
- package/src/core/agent-config.d.ts +100 -0
- package/src/core/agent-config.js +148 -0
- package/src/core/api-error.d.ts +48 -0
- package/src/core/api-error.js +117 -0
- package/src/core/app-owner-fallback.d.ts +22 -0
- package/src/core/app-owner-fallback.js +39 -0
- package/src/core/app-scope-checker.d.ts +87 -0
- package/src/core/app-scope-checker.js +198 -0
- package/src/core/auth-errors.d.ts +144 -0
- package/src/core/auth-errors.js +160 -0
- package/src/core/card-action-operator.d.ts +33 -0
- package/src/core/card-action-operator.js +30 -0
- package/src/core/chat-info-cache.d.ts +67 -0
- package/src/core/chat-info-cache.js +165 -0
- package/src/core/comment-target.d.ts +65 -0
- package/src/core/comment-target.js +100 -0
- package/src/core/config-schema.d.ts +490 -0
- package/src/core/config-schema.js +223 -0
- package/src/core/device-flow.d.ts +77 -0
- package/src/core/device-flow.js +217 -0
- package/src/core/domains.d.ts +18 -0
- package/src/core/domains.js +34 -0
- package/src/core/feishu-fetch.d.ts +18 -0
- package/src/core/feishu-fetch.js +28 -0
- package/src/core/footer-config.d.ts +24 -0
- package/src/core/footer-config.js +51 -0
- package/src/core/lark-client.d.ts +125 -0
- package/src/core/lark-client.js +468 -0
- package/src/core/lark-logger.d.ts +23 -0
- package/src/core/lark-logger.js +160 -0
- package/src/core/lark-ticket.d.ts +29 -0
- package/src/core/lark-ticket.js +40 -0
- package/src/core/message-unavailable.d.ts +53 -0
- package/src/core/message-unavailable.js +142 -0
- package/src/core/owner-policy.d.ts +32 -0
- package/src/core/owner-policy.js +55 -0
- package/src/core/permission-url.d.ts +22 -0
- package/src/core/permission-url.js +76 -0
- package/src/core/raw-request.d.ts +27 -0
- package/src/core/raw-request.js +90 -0
- package/src/core/runtime-store.d.ts +13 -0
- package/src/core/runtime-store.js +29 -0
- package/src/core/scope-manager.d.ts +168 -0
- package/src/core/scope-manager.js +224 -0
- package/src/core/sdk-compat.d.ts +20 -0
- package/src/core/sdk-compat.js +28 -0
- package/src/core/security-check.d.ts +72 -0
- package/src/core/security-check.js +184 -0
- package/src/core/shutdown-hooks.d.ts +22 -0
- package/src/core/shutdown-hooks.js +61 -0
- package/src/core/synthetic-target.d.ts +33 -0
- package/src/core/synthetic-target.js +40 -0
- package/src/core/targets.d.ts +60 -0
- package/src/core/targets.js +173 -0
- package/src/core/token-store.d.ts +54 -0
- package/src/core/token-store.js +326 -0
- package/src/core/tool-client.d.ts +176 -0
- package/src/core/tool-client.js +426 -0
- package/src/core/tool-scopes.d.ts +156 -0
- package/src/core/tool-scopes.js +344 -0
- package/src/core/tools-config.d.ts +55 -0
- package/src/core/tools-config.js +143 -0
- package/src/core/types.d.ts +91 -0
- package/src/core/types.js +12 -0
- package/src/core/uat-client.d.ts +46 -0
- package/src/core/uat-client.js +192 -0
- package/src/core/version.d.ts +31 -0
- package/src/core/version.js +69 -0
- package/src/messaging/converters/audio.d.ts +8 -0
- package/src/messaging/converters/audio.js +24 -0
- package/src/messaging/converters/calendar.d.ts +13 -0
- package/src/messaging/converters/calendar.js +56 -0
- package/src/messaging/converters/content-converter-helpers.d.ts +30 -0
- package/src/messaging/converters/content-converter-helpers.js +82 -0
- package/src/messaging/converters/content-converter.d.ts +24 -0
- package/src/messaging/converters/content-converter.js +40 -0
- package/src/messaging/converters/file.d.ts +8 -0
- package/src/messaging/converters/file.js +24 -0
- package/src/messaging/converters/folder.d.ts +8 -0
- package/src/messaging/converters/folder.js +24 -0
- package/src/messaging/converters/hongbao.d.ts +8 -0
- package/src/messaging/converters/hongbao.js +20 -0
- package/src/messaging/converters/image.d.ts +8 -0
- package/src/messaging/converters/image.js +22 -0
- package/src/messaging/converters/index.d.ts +8 -0
- package/src/messaging/converters/index.js +53 -0
- package/src/messaging/converters/interactive/card-converter.d.ts +76 -0
- package/src/messaging/converters/interactive/card-converter.js +1177 -0
- package/src/messaging/converters/interactive/card-utils.d.ts +9 -0
- package/src/messaging/converters/interactive/card-utils.js +47 -0
- package/src/messaging/converters/interactive/index.d.ts +8 -0
- package/src/messaging/converters/interactive/index.js +25 -0
- package/src/messaging/converters/interactive/legacy.d.ts +11 -0
- package/src/messaging/converters/interactive/legacy.js +60 -0
- package/src/messaging/converters/interactive/types.d.ts +23 -0
- package/src/messaging/converters/interactive/types.js +27 -0
- package/src/messaging/converters/location.d.ts +8 -0
- package/src/messaging/converters/location.js +23 -0
- package/src/messaging/converters/merge-forward.d.ts +32 -0
- package/src/messaging/converters/merge-forward.js +235 -0
- package/src/messaging/converters/post.d.ts +11 -0
- package/src/messaging/converters/post.js +235 -0
- package/src/messaging/converters/share.d.ts +9 -0
- package/src/messaging/converters/share.js +28 -0
- package/src/messaging/converters/sticker.d.ts +8 -0
- package/src/messaging/converters/sticker.js +22 -0
- package/src/messaging/converters/system.d.ts +12 -0
- package/src/messaging/converters/system.js +36 -0
- package/src/messaging/converters/text.d.ts +8 -0
- package/src/messaging/converters/text.js +18 -0
- package/src/messaging/converters/todo.d.ts +8 -0
- package/src/messaging/converters/todo.js +45 -0
- package/src/messaging/converters/types.d.ts +114 -0
- package/src/messaging/converters/types.js +8 -0
- package/src/messaging/converters/unknown.d.ts +8 -0
- package/src/messaging/converters/unknown.js +20 -0
- package/src/messaging/converters/utils.d.ts +22 -0
- package/src/messaging/converters/utils.js +57 -0
- package/src/messaging/converters/video-chat.d.ts +8 -0
- package/src/messaging/converters/video-chat.js +31 -0
- package/src/messaging/converters/video.d.ts +8 -0
- package/src/messaging/converters/video.js +35 -0
- package/src/messaging/converters/vote.d.ts +8 -0
- package/src/messaging/converters/vote.js +28 -0
- package/src/messaging/inbound/bot-content.d.ts +84 -0
- package/src/messaging/inbound/bot-content.js +117 -0
- package/src/messaging/inbound/bot-loop-guard.d.ts +48 -0
- package/src/messaging/inbound/bot-loop-guard.js +89 -0
- package/src/messaging/inbound/comment-context.d.ts +82 -0
- package/src/messaging/inbound/comment-context.js +353 -0
- package/src/messaging/inbound/comment-handler.d.ts +30 -0
- package/src/messaging/inbound/comment-handler.js +269 -0
- package/src/messaging/inbound/dedup.d.ts +59 -0
- package/src/messaging/inbound/dedup.js +121 -0
- package/src/messaging/inbound/dispatch-builders.d.ts +104 -0
- package/src/messaging/inbound/dispatch-builders.js +241 -0
- package/src/messaging/inbound/dispatch-commands.d.ts +22 -0
- package/src/messaging/inbound/dispatch-commands.js +131 -0
- package/src/messaging/inbound/dispatch-context.d.ts +67 -0
- package/src/messaging/inbound/dispatch-context.js +153 -0
- package/src/messaging/inbound/dispatch.d.ts +50 -0
- package/src/messaging/inbound/dispatch.js +477 -0
- package/src/messaging/inbound/enrich.d.ts +102 -0
- package/src/messaging/inbound/enrich.js +236 -0
- package/src/messaging/inbound/gate-effects.d.ts +23 -0
- package/src/messaging/inbound/gate-effects.js +46 -0
- package/src/messaging/inbound/gate.d.ts +91 -0
- package/src/messaging/inbound/gate.js +342 -0
- package/src/messaging/inbound/handler-registry.d.ts +25 -0
- package/src/messaging/inbound/handler-registry.js +23 -0
- package/src/messaging/inbound/handler.d.ts +37 -0
- package/src/messaging/inbound/handler.js +286 -0
- package/src/messaging/inbound/media-resolver.d.ts +32 -0
- package/src/messaging/inbound/media-resolver.js +91 -0
- package/src/messaging/inbound/mention-registry.d.ts +59 -0
- package/src/messaging/inbound/mention-registry.js +115 -0
- package/src/messaging/inbound/mention.d.ts +48 -0
- package/src/messaging/inbound/mention.js +102 -0
- package/src/messaging/inbound/parse-io.d.ts +50 -0
- package/src/messaging/inbound/parse-io.js +86 -0
- package/src/messaging/inbound/parse.d.ts +28 -0
- package/src/messaging/inbound/parse.js +128 -0
- package/src/messaging/inbound/permission.d.ts +17 -0
- package/src/messaging/inbound/permission.js +44 -0
- package/src/messaging/inbound/policy.d.ts +95 -0
- package/src/messaging/inbound/policy.js +168 -0
- package/src/messaging/inbound/reaction-handler.d.ts +62 -0
- package/src/messaging/inbound/reaction-handler.js +259 -0
- package/src/messaging/inbound/sentinel-store.d.ts +37 -0
- package/src/messaging/inbound/sentinel-store.js +98 -0
- package/src/messaging/inbound/synthetic-message.d.ts +26 -0
- package/src/messaging/inbound/synthetic-message.js +59 -0
- package/src/messaging/inbound/user-name-cache-store.d.ts +62 -0
- package/src/messaging/inbound/user-name-cache-store.js +236 -0
- package/src/messaging/inbound/user-name-cache.d.ts +78 -0
- package/src/messaging/inbound/user-name-cache.js +306 -0
- package/src/messaging/inbound/vc-meeting-invited-handler.d.ts +20 -0
- package/src/messaging/inbound/vc-meeting-invited-handler.js +236 -0
- package/src/messaging/inbound/vc-sender.d.ts +41 -0
- package/src/messaging/inbound/vc-sender.js +53 -0
- package/src/messaging/outbound/actions.d.ts +16 -0
- package/src/messaging/outbound/actions.js +329 -0
- package/src/messaging/outbound/bot-peer-context.d.ts +42 -0
- package/src/messaging/outbound/bot-peer-context.js +40 -0
- package/src/messaging/outbound/chat-manage.d.ts +64 -0
- package/src/messaging/outbound/chat-manage.js +117 -0
- package/src/messaging/outbound/deliver.d.ts +186 -0
- package/src/messaging/outbound/deliver.js +410 -0
- package/src/messaging/outbound/fetch.d.ts +12 -0
- package/src/messaging/outbound/fetch.js +17 -0
- package/src/messaging/outbound/forward.d.ts +26 -0
- package/src/messaging/outbound/forward.js +51 -0
- package/src/messaging/outbound/media-url-utils.d.ts +29 -0
- package/src/messaging/outbound/media-url-utils.js +172 -0
- package/src/messaging/outbound/media.d.ts +260 -0
- package/src/messaging/outbound/media.js +865 -0
- package/src/messaging/outbound/normalize-mentions.d.ts +50 -0
- package/src/messaging/outbound/normalize-mentions.js +166 -0
- package/src/messaging/outbound/outbound-mention.d.ts +41 -0
- package/src/messaging/outbound/outbound-mention.js +112 -0
- package/src/messaging/outbound/outbound.d.ts +79 -0
- package/src/messaging/outbound/outbound.js +195 -0
- package/src/messaging/outbound/reactions.d.ts +124 -0
- package/src/messaging/outbound/reactions.js +384 -0
- package/src/messaging/outbound/send.d.ts +153 -0
- package/src/messaging/outbound/send.js +417 -0
- package/src/messaging/outbound/typing.d.ts +60 -0
- package/src/messaging/outbound/typing.js +139 -0
- package/src/messaging/shared/message-lookup.d.ts +54 -0
- package/src/messaging/shared/message-lookup.js +120 -0
- package/src/messaging/types.d.ts +316 -0
- package/src/messaging/types.js +11 -0
- package/src/tools/ask-user-question.d.ts +32 -0
- package/src/tools/ask-user-question.js +922 -0
- package/src/tools/auto-auth.d.ts +57 -0
- package/src/tools/auto-auth.js +904 -0
- package/src/tools/helpers.d.ts +264 -0
- package/src/tools/helpers.js +389 -0
- package/src/tools/mcp/doc/create.d.ts +12 -0
- package/src/tools/mcp/doc/create.js +47 -0
- package/src/tools/mcp/doc/fetch.d.ts +12 -0
- package/src/tools/mcp/doc/fetch.js +39 -0
- package/src/tools/mcp/doc/index.d.ts +12 -0
- package/src/tools/mcp/doc/index.js +50 -0
- package/src/tools/mcp/doc/update.d.ts +12 -0
- package/src/tools/mcp/doc/update.js +64 -0
- package/src/tools/mcp/shared.d.ts +59 -0
- package/src/tools/mcp/shared.js +239 -0
- package/src/tools/oapi/bitable/app-table-field.d.ts +16 -0
- package/src/tools/oapi/bitable/app-table-field.js +225 -0
- package/src/tools/oapi/bitable/app-table-record.d.ts +20 -0
- package/src/tools/oapi/bitable/app-table-record.js +439 -0
- package/src/tools/oapi/bitable/app-table-view.d.ts +16 -0
- package/src/tools/oapi/bitable/app-table-view.js +172 -0
- package/src/tools/oapi/bitable/app-table.d.ts +17 -0
- package/src/tools/oapi/bitable/app-table.js +195 -0
- package/src/tools/oapi/bitable/app.d.ts +18 -0
- package/src/tools/oapi/bitable/app.js +189 -0
- package/src/tools/oapi/bitable/index.d.ts +9 -0
- package/src/tools/oapi/bitable/index.js +17 -0
- package/src/tools/oapi/calendar/calendar.d.ts +15 -0
- package/src/tools/oapi/calendar/calendar.js +125 -0
- package/src/tools/oapi/calendar/event-attendee.d.ts +14 -0
- package/src/tools/oapi/calendar/event-attendee.js +161 -0
- package/src/tools/oapi/calendar/event.d.ts +16 -0
- package/src/tools/oapi/calendar/event.js +712 -0
- package/src/tools/oapi/calendar/freebusy.d.ts +13 -0
- package/src/tools/oapi/calendar/freebusy.js +114 -0
- package/src/tools/oapi/calendar/index.d.ts +8 -0
- package/src/tools/oapi/calendar/index.js +15 -0
- package/src/tools/oapi/chat/chat.d.ts +16 -0
- package/src/tools/oapi/chat/chat.js +127 -0
- package/src/tools/oapi/chat/index.d.ts +10 -0
- package/src/tools/oapi/chat/index.js +23 -0
- package/src/tools/oapi/chat/members.d.ts +11 -0
- package/src/tools/oapi/chat/members.js +84 -0
- package/src/tools/oapi/common/get-user.d.ts +12 -0
- package/src/tools/oapi/common/get-user.js +109 -0
- package/src/tools/oapi/common/index.d.ts +6 -0
- package/src/tools/oapi/common/index.js +11 -0
- package/src/tools/oapi/common/search-user.d.ts +11 -0
- package/src/tools/oapi/common/search-user.js +76 -0
- package/src/tools/oapi/drive/doc-comments.d.ts +15 -0
- package/src/tools/oapi/drive/doc-comments.js +351 -0
- package/src/tools/oapi/drive/doc-media.d.ts +19 -0
- package/src/tools/oapi/drive/doc-media.js +371 -0
- package/src/tools/oapi/drive/file.d.ts +19 -0
- package/src/tools/oapi/drive/file.js +519 -0
- package/src/tools/oapi/drive/index.d.ts +12 -0
- package/src/tools/oapi/drive/index.js +45 -0
- package/src/tools/oapi/helpers.d.ts +183 -0
- package/src/tools/oapi/helpers.js +385 -0
- package/src/tools/oapi/im/format-messages.d.ts +50 -0
- package/src/tools/oapi/im/format-messages.js +169 -0
- package/src/tools/oapi/im/index.d.ts +10 -0
- package/src/tools/oapi/im/index.js +25 -0
- package/src/tools/oapi/im/message-read.d.ts +13 -0
- package/src/tools/oapi/im/message-read.js +419 -0
- package/src/tools/oapi/im/message.d.ts +16 -0
- package/src/tools/oapi/im/message.js +152 -0
- package/src/tools/oapi/im/resource.d.ts +13 -0
- package/src/tools/oapi/im/resource.js +186 -0
- package/src/tools/oapi/im/time-utils.d.ts +46 -0
- package/src/tools/oapi/im/time-utils.js +212 -0
- package/src/tools/oapi/im/user-name-uat.d.ts +26 -0
- package/src/tools/oapi/im/user-name-uat.js +143 -0
- package/src/tools/oapi/index.d.ts +11 -0
- package/src/tools/oapi/index.js +64 -0
- package/src/tools/oapi/sdk-types.d.ts +96 -0
- package/src/tools/oapi/sdk-types.js +13 -0
- package/src/tools/oapi/search/doc-search.d.ts +13 -0
- package/src/tools/oapi/search/doc-search.js +194 -0
- package/src/tools/oapi/search/index.d.ts +12 -0
- package/src/tools/oapi/search/index.js +37 -0
- package/src/tools/oapi/sheets/index.d.ts +12 -0
- package/src/tools/oapi/sheets/index.js +35 -0
- package/src/tools/oapi/sheets/sheet.d.ts +16 -0
- package/src/tools/oapi/sheets/sheet.js +688 -0
- package/src/tools/oapi/task/attachment.d.ts +18 -0
- package/src/tools/oapi/task/attachment.js +107 -0
- package/src/tools/oapi/task/comment.d.ts +15 -0
- package/src/tools/oapi/task/comment.js +149 -0
- package/src/tools/oapi/task/index.d.ts +11 -0
- package/src/tools/oapi/task/index.js +21 -0
- package/src/tools/oapi/task/section.d.ts +17 -0
- package/src/tools/oapi/task/section.js +293 -0
- package/src/tools/oapi/task/subtask.d.ts +14 -0
- package/src/tools/oapi/task/subtask.js +171 -0
- package/src/tools/oapi/task/task.d.ts +19 -0
- package/src/tools/oapi/task/task.js +512 -0
- package/src/tools/oapi/task/task_agent.d.ts +14 -0
- package/src/tools/oapi/task/task_agent.js +108 -0
- package/src/tools/oapi/task/tasklist.d.ts +19 -0
- package/src/tools/oapi/task/tasklist.js +276 -0
- package/src/tools/oapi/wiki/index.d.ts +12 -0
- package/src/tools/oapi/wiki/index.js +42 -0
- package/src/tools/oapi/wiki/space-node.d.ts +17 -0
- package/src/tools/oapi/wiki/space-node.js +233 -0
- package/src/tools/oapi/wiki/space.d.ts +15 -0
- package/src/tools/oapi/wiki/space.js +133 -0
- package/src/tools/oauth-batch-auth.d.ts +11 -0
- package/src/tools/oauth-batch-auth.js +150 -0
- package/src/tools/oauth-cards.d.ts +39 -0
- package/src/tools/oauth-cards.js +324 -0
- package/src/tools/oauth.d.ts +47 -0
- package/src/tools/oauth.js +592 -0
- package/src/tools/onboarding-auth.d.ts +27 -0
- package/src/tools/onboarding-auth.js +133 -0
- package/src/tools/tat/im/index.d.ts +15 -0
- package/src/tools/tat/im/index.js +22 -0
- package/src/tools/tat/im/resource.d.ts +15 -0
- package/src/tools/tat/im/resource.js +192 -0
- package/tsdown.config.d.ts +2 -0
- package/tsdown.config.js +25 -0
- package/vitest.config.d.ts +2 -0
- package/vitest.config.js +12 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* Agent dispatch for inbound Feishu messages.
|
|
7
|
+
*
|
|
8
|
+
* Builds the agent envelope, prepends chat history context, and
|
|
9
|
+
* dispatches through the appropriate reply path (system command
|
|
10
|
+
* vs. normal streaming/static flow).
|
|
11
|
+
*
|
|
12
|
+
* Implementation details are split across focused modules:
|
|
13
|
+
* - dispatch-context.ts — DispatchContext type, route/session/event
|
|
14
|
+
* - dispatch-builders.ts — pure payload/body/envelope construction
|
|
15
|
+
* - dispatch-commands.ts — system command & permission notification
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.dispatchToAgent = dispatchToAgent;
|
|
19
|
+
const reply_history_1 = require("openclaw/plugin-sdk/reply-history");
|
|
20
|
+
const lark_logger_1 = require("../../core/lark-logger.js");
|
|
21
|
+
const lark_ticket_1 = require("../../core/lark-ticket.js");
|
|
22
|
+
const reply_dispatcher_1 = require("../../card/reply-dispatcher.js");
|
|
23
|
+
const chat_queue_1 = require("../../channel/chat-queue.js");
|
|
24
|
+
const tool_use_config_1 = require("../../card/tool-use-config.js");
|
|
25
|
+
const tool_use_trace_store_1 = require("../../card/tool-use-trace-store.js");
|
|
26
|
+
const abort_detect_1 = require("../../channel/abort-detect.js");
|
|
27
|
+
const bot_peer_context_1 = require("../outbound/bot-peer-context.js");
|
|
28
|
+
const comment_target_1 = require("../../core/comment-target.js");
|
|
29
|
+
const synthetic_target_1 = require("../../core/synthetic-target.js");
|
|
30
|
+
const targets_1 = require("../../core/targets.js");
|
|
31
|
+
const deliver_1 = require("../outbound/deliver.js");
|
|
32
|
+
const doctor_1 = require("../../commands/doctor.js");
|
|
33
|
+
const auth_1 = require("../../commands/auth.js");
|
|
34
|
+
const index_1 = require("../../commands/index.js");
|
|
35
|
+
const send_1 = require("../outbound/send.js");
|
|
36
|
+
const bot_content_1 = require("./bot-content.js");
|
|
37
|
+
const dispatch_commands_1 = require("./dispatch-commands.js");
|
|
38
|
+
const dispatch_builders_1 = require("./dispatch-builders.js");
|
|
39
|
+
const sentinel_store_1 = require("./sentinel-store.js");
|
|
40
|
+
const dispatch_context_1 = require("./dispatch-context.js");
|
|
41
|
+
const mention_1 = require("./mention.js");
|
|
42
|
+
const gate_1 = require("./gate.js");
|
|
43
|
+
const log = (0, lark_logger_1.larkLogger)('inbound/dispatch');
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Internal: normal message dispatch
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/**
|
|
48
|
+
* Dispatch a normal (non-command) message via the streaming card flow.
|
|
49
|
+
* Cleans up consumed history entries after dispatch completes.
|
|
50
|
+
*
|
|
51
|
+
* Note: history cleanup is intentionally placed here and NOT in the
|
|
52
|
+
* system-command path — command handlers don't consume history context,
|
|
53
|
+
* so the entries should be preserved for the next normal message.
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* Dispatch a comment-target message via the buffered block dispatcher.
|
|
57
|
+
*
|
|
58
|
+
* Comment targets cannot use the streaming card flow (IM APIs don't
|
|
59
|
+
* understand comment:... targets). Instead we use the SDK's buffered
|
|
60
|
+
* block dispatcher with a deliver callback that sends via the Drive
|
|
61
|
+
* comment reply API.
|
|
62
|
+
*/
|
|
63
|
+
async function dispatchCommentMessage(dc, ctxPayload, skillFilter) {
|
|
64
|
+
const effectiveSessionKey = dc.threadSessionKey ?? dc.route.sessionKey;
|
|
65
|
+
dc.log(`feishu[${dc.account.accountId}]: dispatching comment reply (session=${effectiveSessionKey})`);
|
|
66
|
+
log.info(`dispatching comment reply (session=${effectiveSessionKey})`);
|
|
67
|
+
let delivered = false;
|
|
68
|
+
await dc.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
69
|
+
ctx: ctxPayload,
|
|
70
|
+
cfg: dc.accountScopedCfg,
|
|
71
|
+
dispatcherOptions: {
|
|
72
|
+
deliver: async (payload) => {
|
|
73
|
+
const text = payload.text?.trim() ?? '';
|
|
74
|
+
if (!text || text === 'NO_REPLY')
|
|
75
|
+
return;
|
|
76
|
+
await (0, deliver_1.sendCommentReplyLark)({
|
|
77
|
+
cfg: dc.accountScopedCfg,
|
|
78
|
+
to: dc.ctx.chatId,
|
|
79
|
+
text,
|
|
80
|
+
accountId: dc.account.accountId,
|
|
81
|
+
});
|
|
82
|
+
delivered = true;
|
|
83
|
+
},
|
|
84
|
+
onSkip: (_payload, info) => {
|
|
85
|
+
if (info.reason !== 'silent') {
|
|
86
|
+
dc.log(`feishu[${dc.account.accountId}]: comment reply skipped (reason=${info.reason})`);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
onError: (err, info) => {
|
|
90
|
+
dc.error(`feishu[${dc.account.accountId}]: comment ${info.kind} reply failed: ${String(err)}`);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
replyOptions: {
|
|
94
|
+
...(skillFilter ? { skillFilter } : {}),
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
dc.log(`feishu[${dc.account.accountId}]: comment dispatch complete (delivered=${delivered})`);
|
|
98
|
+
log.info(`comment dispatch complete (delivered=${delivered}, elapsed=${(0, lark_ticket_1.ticketElapsed)()}ms)`);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Dispatch a synthetic-target message via the buffered block dispatcher
|
|
102
|
+
* while discarding every delivered payload.
|
|
103
|
+
*
|
|
104
|
+
* Synthetic contexts (e.g. VC meeting-invited) trigger the agent for its
|
|
105
|
+
* side-effects (tool calls) — they do not correspond to a real IM chat,
|
|
106
|
+
* so any text / card the agent emits must be dropped instead of being
|
|
107
|
+
* sent as a DM to whatever open_id happens to be in ctx.chatId.
|
|
108
|
+
*/
|
|
109
|
+
async function dispatchSyntheticMessage(dc, ctxPayload, skillFilter) {
|
|
110
|
+
const effectiveSessionKey = dc.threadSessionKey ?? dc.route.sessionKey;
|
|
111
|
+
const isVcSynthetic = dc.ctx.chatId === synthetic_target_1.SYNTHETIC_VC_CHAT_ID;
|
|
112
|
+
let deliveredFinalToSender = false;
|
|
113
|
+
dc.log(`feishu[${dc.account.accountId}]: dispatching synthetic reply (session=${effectiveSessionKey}, target=${dc.ctx.chatId})`);
|
|
114
|
+
log.info(`dispatching synthetic reply (session=${effectiveSessionKey})`);
|
|
115
|
+
await dc.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
116
|
+
ctx: ctxPayload,
|
|
117
|
+
cfg: dc.accountScopedCfg,
|
|
118
|
+
dispatcherOptions: {
|
|
119
|
+
deliver: async (payload, info) => {
|
|
120
|
+
const text = payload.text?.trim() ?? '';
|
|
121
|
+
const preview = text.slice(0, 120);
|
|
122
|
+
// VC invited flows intentionally keep the synthetic target to avoid
|
|
123
|
+
// leaking intermediate tool output to IM, but the final business
|
|
124
|
+
// result should be explicitly notified to the inviter.
|
|
125
|
+
//
|
|
126
|
+
// Important: this DM is only a transport bridge for the final text.
|
|
127
|
+
// It does not rebind the inviter's DM conversation to the synthetic
|
|
128
|
+
// meeting-scoped session; later DM replies will still be routed by the
|
|
129
|
+
// normal OpenClaw DM session rules.
|
|
130
|
+
if (isVcSynthetic && info.kind === 'final' && text && text !== 'NO_REPLY' && !deliveredFinalToSender) {
|
|
131
|
+
deliveredFinalToSender = true;
|
|
132
|
+
try {
|
|
133
|
+
await (0, send_1.sendMessageFeishu)({
|
|
134
|
+
cfg: dc.accountScopedCfg,
|
|
135
|
+
to: dc.ctx.senderId,
|
|
136
|
+
text,
|
|
137
|
+
accountId: dc.account.accountId,
|
|
138
|
+
});
|
|
139
|
+
dc.log(`feishu[${dc.account.accountId}]: synthetic VC final delivered explicitly to sender=${dc.ctx.senderId}, preview="${preview}"`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
deliveredFinalToSender = false;
|
|
144
|
+
dc.error(`feishu[${dc.account.accountId}]: synthetic VC final delivery failed to sender=${dc.ctx.senderId}: ${String(err)}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (info.kind === 'final') {
|
|
148
|
+
dc.log(`feishu[${dc.account.accountId}]: synthetic final payload dropped (target=${dc.ctx.chatId})`);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
onSkip: (_payload, info) => {
|
|
152
|
+
if (info.reason !== 'silent') {
|
|
153
|
+
dc.log(`feishu[${dc.account.accountId}]: synthetic reply skipped (reason=${info.reason})`);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
onError: (err, info) => {
|
|
157
|
+
dc.error(`feishu[${dc.account.accountId}]: synthetic ${info.kind} reply failed: ${String(err)}`);
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
replyOptions: {
|
|
161
|
+
...(skillFilter ? { skillFilter } : {}),
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
dc.log(`feishu[${dc.account.accountId}]: synthetic dispatch complete (elapsed=${(0, lark_ticket_1.ticketElapsed)()}ms)`);
|
|
165
|
+
}
|
|
166
|
+
async function dispatchNormalMessage(dc, ctxPayload, routing, chatHistories, historyKey, historyLimit, replyToMessageId, skillFilter, skipTyping, botPeer) {
|
|
167
|
+
// Synthetic targets (e.g. VC meeting-invited) have no real IM peer to
|
|
168
|
+
// deliver replies to. Route them through the buffered block dispatcher
|
|
169
|
+
// with a deliver() that drops every payload — the agent still runs
|
|
170
|
+
// (tool calls, side-effects) but produces no outbound IM traffic.
|
|
171
|
+
if ((0, synthetic_target_1.isSyntheticTarget)(dc.ctx.chatId)) {
|
|
172
|
+
await dispatchSyntheticMessage(dc, ctxPayload, skillFilter);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Comment targets bypass the streaming card / IM flow entirely —
|
|
176
|
+
// route through the Drive comment reply API.
|
|
177
|
+
if ((0, comment_target_1.isCommentTarget)(dc.ctx.chatId)) {
|
|
178
|
+
await dispatchCommentMessage(dc, ctxPayload, skillFilter);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Abort messages should never create streaming cards — dispatch via the
|
|
182
|
+
// plain-text system-command path so the SDK's abort handler can reply
|
|
183
|
+
// without touching CardKit.
|
|
184
|
+
if ((0, abort_detect_1.isLikelyAbortText)(dc.ctx.content?.trim() ?? '')) {
|
|
185
|
+
dc.log(`feishu[${dc.account.accountId}]: abort message detected, using plain-text dispatch`);
|
|
186
|
+
log.info('abort message detected, using plain-text dispatch');
|
|
187
|
+
await (0, dispatch_commands_1.dispatchSystemCommand)(dc, ctxPayload, replyToMessageId);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const effectiveSessionKey = dc.threadSessionKey ?? dc.route.sessionKey;
|
|
191
|
+
const toolUseDisplay = (0, tool_use_config_1.resolveToolUseDisplayConfig)({
|
|
192
|
+
cfg: dc.accountScopedCfg,
|
|
193
|
+
feishuCfg: dc.account.config,
|
|
194
|
+
agentId: dc.route.agentId,
|
|
195
|
+
sessionKey: effectiveSessionKey,
|
|
196
|
+
body: dc.ctx.content,
|
|
197
|
+
});
|
|
198
|
+
if (toolUseDisplay.showToolUse) {
|
|
199
|
+
(0, tool_use_trace_store_1.startToolUseTraceRun)(effectiveSessionKey);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
(0, tool_use_trace_store_1.clearToolUseTraceRun)(effectiveSessionKey);
|
|
203
|
+
}
|
|
204
|
+
const { dispatcher, replyOptions, markDispatchIdle, markFullyComplete, abortCard } = (0, reply_dispatcher_1.createFeishuReplyDispatcher)({
|
|
205
|
+
cfg: dc.accountScopedCfg,
|
|
206
|
+
agentId: dc.route.agentId,
|
|
207
|
+
chatId: dc.ctx.chatId,
|
|
208
|
+
sessionKey: effectiveSessionKey,
|
|
209
|
+
replyToMessageId: replyToMessageId ?? dc.ctx.messageId,
|
|
210
|
+
accountId: dc.account.accountId,
|
|
211
|
+
chatType: dc.ctx.chatType,
|
|
212
|
+
skipTyping,
|
|
213
|
+
replyInThread: routing.replyInThread,
|
|
214
|
+
threadId: routing.threadId,
|
|
215
|
+
toolUseDisplay,
|
|
216
|
+
});
|
|
217
|
+
// Create an AbortController so the abort fast-path can cancel the
|
|
218
|
+
// underlying LLM request (not just the streaming card UI).
|
|
219
|
+
const abortController = new AbortController();
|
|
220
|
+
// Register the active dispatcher so the monitor abort fast-path can
|
|
221
|
+
// terminate the streaming card before this task completes.
|
|
222
|
+
const queueKey = (0, chat_queue_1.buildQueueKey)(dc.account.accountId, dc.ctx.chatId, dc.ctx.threadId);
|
|
223
|
+
(0, chat_queue_1.registerActiveDispatcher)(queueKey, { abortCard, abortController });
|
|
224
|
+
dc.log(`feishu[${dc.account.accountId}]: dispatching to agent (session=${effectiveSessionKey})`);
|
|
225
|
+
log.info(`dispatching to agent (session=${effectiveSessionKey})`);
|
|
226
|
+
// Attach the resolved bot-peer (if any) so the outbound `ensureMention`
|
|
227
|
+
// backstop can guarantee an @ even when the LLM forgets. Resolved by the
|
|
228
|
+
// caller (dispatchToAgent) and decoupled from thread routing. Undefined →
|
|
229
|
+
// pure no-op.
|
|
230
|
+
const withBotPeer = botPeer
|
|
231
|
+
? (fn) => (0, bot_peer_context_1.runWithBotPeerContext)(botPeer, fn)
|
|
232
|
+
: (fn) => fn();
|
|
233
|
+
try {
|
|
234
|
+
const { queuedFinal, counts } = await withBotPeer(() => dc.core.channel.reply.dispatchReplyFromConfig({
|
|
235
|
+
ctx: ctxPayload,
|
|
236
|
+
cfg: dc.accountScopedCfg,
|
|
237
|
+
dispatcher,
|
|
238
|
+
replyOptions: {
|
|
239
|
+
...replyOptions,
|
|
240
|
+
abortSignal: abortController.signal,
|
|
241
|
+
...(skillFilter ? { skillFilter } : {}),
|
|
242
|
+
},
|
|
243
|
+
}));
|
|
244
|
+
// Wait for all enqueued deliver() calls in the SDK's sendChain to
|
|
245
|
+
// complete before marking the dispatch as done. Without this,
|
|
246
|
+
// dispatchReplyFromConfig() may return while the final deliver() is
|
|
247
|
+
// still pending in the Promise chain, causing markFullyComplete() to
|
|
248
|
+
// block it and leaving completedText incomplete — which in turn makes
|
|
249
|
+
// the streaming card's final update show truncated content.
|
|
250
|
+
//
|
|
251
|
+
// Run under withBotPeer too so any deliveries flushed during waitForIdle
|
|
252
|
+
// still see the peer context.
|
|
253
|
+
await withBotPeer(() => dispatcher.waitForIdle());
|
|
254
|
+
markFullyComplete();
|
|
255
|
+
markDispatchIdle();
|
|
256
|
+
// Clean up consumed history entries
|
|
257
|
+
if (dc.isGroup && historyKey && chatHistories) {
|
|
258
|
+
(0, reply_history_1.clearHistoryEntriesIfEnabled)({
|
|
259
|
+
historyMap: chatHistories,
|
|
260
|
+
historyKey,
|
|
261
|
+
limit: historyLimit,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
dc.log(`feishu[${dc.account.accountId}]: dispatch complete (queuedFinal=${queuedFinal}, replies=${counts.final})`);
|
|
265
|
+
log.info(`dispatch complete (replies=${counts.final}, elapsed=${(0, lark_ticket_1.ticketElapsed)()}ms)`);
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
(0, chat_queue_1.unregisterActiveDispatcher)(queueKey);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
// Public API
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
async function dispatchToAgent(params) {
|
|
275
|
+
// 1. Derive shared context (including route resolution + system event)
|
|
276
|
+
const dc = (0, dispatch_context_1.buildDispatchContext)(params);
|
|
277
|
+
// 1a. Reply routing: handles topic-group thread inference (may mutate dc)
|
|
278
|
+
// and bot-peer suppression for bot→bot group scenarios (#32980).
|
|
279
|
+
// See src/messaging/inbound/bot-content.ts for the full rationale.
|
|
280
|
+
const replyInThreadConfig = params.groupConfig?.replyInThread ??
|
|
281
|
+
params.defaultGroupConfig?.replyInThread ??
|
|
282
|
+
dc.account.config?.replyInThread;
|
|
283
|
+
const routing = await (0, bot_content_1.resolveFeishuReplyRouting)(dc, { replyInThreadConfig });
|
|
284
|
+
// 1b. Resolve thread session isolation (async: may query group info API)
|
|
285
|
+
if (dc.isThread && dc.ctx.threadId) {
|
|
286
|
+
dc.threadSessionKey = await (0, dispatch_context_1.resolveThreadSessionKey)({
|
|
287
|
+
accountScopedCfg: dc.accountScopedCfg,
|
|
288
|
+
account: dc.account,
|
|
289
|
+
chatId: dc.ctx.chatId,
|
|
290
|
+
threadId: dc.ctx.threadId,
|
|
291
|
+
baseSessionKey: dc.route.sessionKey,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
// Consume any pending mention sentinels for this thread. Take and
|
|
295
|
+
// delete is one shot per inbound — capture once, hand to both body
|
|
296
|
+
// builders below.
|
|
297
|
+
const sentinelKey = (0, chat_queue_1.threadScopedKey)(dc.ctx.chatId, dc.isThread ? dc.ctx.threadId : undefined);
|
|
298
|
+
const sentinels = (0, sentinel_store_1.getSentinelStore)(dc.account.accountId).consumeSentinels(sentinelKey);
|
|
299
|
+
// 3. Build annotated message body
|
|
300
|
+
const messageBody = (0, dispatch_builders_1.buildMessageBody)(params.ctx, params.quotedContent, sentinels);
|
|
301
|
+
// 4. Permission-error notification (optional side-effect).
|
|
302
|
+
// Isolated so a failure here does not block the main message dispatch.
|
|
303
|
+
// Skipped for comment targets: the streaming card dispatcher inside
|
|
304
|
+
// dispatchPermissionNotification sends via IM APIs which don't
|
|
305
|
+
// understand comment:... targets.
|
|
306
|
+
if (params.permissionError && !(0, comment_target_1.isCommentTarget)(dc.ctx.chatId)) {
|
|
307
|
+
try {
|
|
308
|
+
await (0, dispatch_commands_1.dispatchPermissionNotification)(dc, params.permissionError, params.replyToMessageId);
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
dc.error(`feishu[${dc.account.accountId}]: permission notification failed, continuing: ${String(err)}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// 5. Build main envelope (with group chat history)
|
|
315
|
+
const { combinedBody, historyKey } = (0, dispatch_builders_1.buildEnvelopeWithHistory)(dc, messageBody, params.chatHistories, params.historyLimit);
|
|
316
|
+
// 6. Build BodyForAgent with mention annotation (if any).
|
|
317
|
+
// SDK >= 2026.2.10 no longer falls back to Body for BodyForAgent,
|
|
318
|
+
// so we must set it explicitly to preserve the annotation.
|
|
319
|
+
const bodyForAgent = (0, dispatch_builders_1.buildBodyForAgent)(params.ctx, sentinels);
|
|
320
|
+
// 7. Build InboundHistory for SDK metadata injection (>= 2026.2.10).
|
|
321
|
+
// The SDK's buildInboundUserContextPrefix renders these as structured
|
|
322
|
+
// JSON blocks; earlier SDK versions simply ignore unknown fields.
|
|
323
|
+
const threadHistoryKey = (0, chat_queue_1.threadScopedKey)(dc.ctx.chatId, dc.isThread ? dc.ctx.threadId : undefined);
|
|
324
|
+
const inboundHistory = dc.isGroup && params.chatHistories && params.historyLimit > 0
|
|
325
|
+
? (params.chatHistories.get(threadHistoryKey) ?? []).map((entry) => ({
|
|
326
|
+
sender: entry.sender,
|
|
327
|
+
body: entry.body,
|
|
328
|
+
timestamp: entry.timestamp ?? Date.now(),
|
|
329
|
+
}))
|
|
330
|
+
: undefined;
|
|
331
|
+
// 8. Build inbound context payload
|
|
332
|
+
const isBareNewOrReset = /^\/(?:new|reset)\s*$/i.test((params.ctx.content ?? '').trim());
|
|
333
|
+
const configuredGroupPrompt = dc.isGroup
|
|
334
|
+
? params.groupConfig?.systemPrompt?.trim() || params.defaultGroupConfig?.systemPrompt?.trim() || undefined
|
|
335
|
+
: undefined;
|
|
336
|
+
// In group chats, always inject bot-at-bot guidance (self open_id + @
|
|
337
|
+
// delivery rules + loop hygiene), merged with any operator-configured
|
|
338
|
+
// group prompt. Complements the deterministic ensureMention safety net.
|
|
339
|
+
const groupSystemPrompt = dc.isGroup
|
|
340
|
+
? (0, dispatch_builders_1.buildFeishuGroupSystemPrompt)(configuredGroupPrompt, params.botOpenId)
|
|
341
|
+
: undefined;
|
|
342
|
+
const originatingTo = isBareNewOrReset && dc.isThread
|
|
343
|
+
? (0, targets_1.encodeFeishuRouteTarget)({
|
|
344
|
+
target: dc.feishuTo,
|
|
345
|
+
replyToMessageId: params.replyToMessageId ?? params.ctx.messageId,
|
|
346
|
+
threadId: dc.ctx.threadId,
|
|
347
|
+
})
|
|
348
|
+
: undefined;
|
|
349
|
+
const ctxPayload = (0, dispatch_builders_1.buildInboundPayload)(dc, {
|
|
350
|
+
body: combinedBody,
|
|
351
|
+
bodyForAgent,
|
|
352
|
+
rawBody: params.ctx.content,
|
|
353
|
+
commandBody: params.ctx.content,
|
|
354
|
+
originatingTo,
|
|
355
|
+
senderName: params.ctx.senderName ?? params.ctx.senderId,
|
|
356
|
+
senderId: params.ctx.senderId,
|
|
357
|
+
messageSid: params.ctx.messageId,
|
|
358
|
+
wasMentioned: (0, mention_1.mentionedBot)(params.ctx) ||
|
|
359
|
+
(params.ctx.mentionAll &&
|
|
360
|
+
(0, gate_1.resolveRespondToMentionAll)({
|
|
361
|
+
groupConfig: params.groupConfig,
|
|
362
|
+
defaultConfig: params.defaultGroupConfig,
|
|
363
|
+
accountFeishuCfg: params.account.config,
|
|
364
|
+
})),
|
|
365
|
+
replyToBody: params.quotedContent,
|
|
366
|
+
inboundHistory,
|
|
367
|
+
extraFields: {
|
|
368
|
+
...params.mediaPayload,
|
|
369
|
+
...(params.extraInboundFields ?? {}),
|
|
370
|
+
...(0, dispatch_builders_1.buildFeishuIdentityFields)(params.ctx, params.botOpenId),
|
|
371
|
+
...(groupSystemPrompt ? { GroupSystemPrompt: groupSystemPrompt } : {}),
|
|
372
|
+
...(dc.ctx.threadId ? { MessageThreadId: dc.ctx.threadId } : {}),
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
// 9a. Intercept /feishu commands for i18n multi-locale card dispatch
|
|
376
|
+
// Must run BEFORE the SDK command check — the SDK does not recognise
|
|
377
|
+
// plugin-registered commands via isControlCommandMessage, so
|
|
378
|
+
// /feishu_* falls through to the AI agent otherwise.
|
|
379
|
+
// Skipped for comment targets: comment text won't match /feishu_*
|
|
380
|
+
// patterns in practice, and sendCardFeishu/sendMessageFeishu can't
|
|
381
|
+
// deliver to comment:... targets.
|
|
382
|
+
const contentTrimmed = (params.ctx.content ?? '').trim();
|
|
383
|
+
const isCommentFlow = (0, comment_target_1.isCommentTarget)(dc.ctx.chatId);
|
|
384
|
+
const isDoctorCommand = !isCommentFlow && /^\/feishu[_ ]doctor\s*$/i.test(contentTrimmed);
|
|
385
|
+
const isAuthCommand = !isCommentFlow && /^\/feishu[_ ](?:auth|onboarding)\s*$/i.test(contentTrimmed);
|
|
386
|
+
const isStartCommand = !isCommentFlow && /^\/feishu[_ ]start\s*$/i.test(contentTrimmed);
|
|
387
|
+
const isHelpCommand = !isCommentFlow && /^\/feishu(?:[_ ]help)?\s*$/i.test(contentTrimmed);
|
|
388
|
+
const i18nCommandName = isDoctorCommand
|
|
389
|
+
? 'doctor'
|
|
390
|
+
: isAuthCommand
|
|
391
|
+
? 'auth'
|
|
392
|
+
: isStartCommand
|
|
393
|
+
? 'start'
|
|
394
|
+
: isHelpCommand
|
|
395
|
+
? 'help'
|
|
396
|
+
: null;
|
|
397
|
+
if (i18nCommandName) {
|
|
398
|
+
dc.log(`feishu[${dc.account.accountId}]: ${i18nCommandName} command detected, using i18n dispatch`);
|
|
399
|
+
log.info(`${i18nCommandName} command detected, using i18n dispatch`);
|
|
400
|
+
try {
|
|
401
|
+
let i18nTexts;
|
|
402
|
+
if (isDoctorCommand) {
|
|
403
|
+
i18nTexts = await (0, doctor_1.runFeishuDoctorI18n)(dc.accountScopedCfg, dc.account.accountId);
|
|
404
|
+
}
|
|
405
|
+
else if (isAuthCommand) {
|
|
406
|
+
i18nTexts = await (0, auth_1.runFeishuAuthI18n)(dc.accountScopedCfg);
|
|
407
|
+
}
|
|
408
|
+
else if (isStartCommand) {
|
|
409
|
+
i18nTexts = (0, index_1.runFeishuStartI18n)(dc.accountScopedCfg);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
i18nTexts = (0, index_1.getFeishuHelpI18n)();
|
|
413
|
+
}
|
|
414
|
+
const card = (0, send_1.buildI18nMarkdownCard)(i18nTexts);
|
|
415
|
+
await (0, send_1.sendCardFeishu)({
|
|
416
|
+
cfg: dc.accountScopedCfg,
|
|
417
|
+
to: dc.ctx.chatId,
|
|
418
|
+
card,
|
|
419
|
+
replyToMessageId: params.replyToMessageId ?? dc.ctx.messageId,
|
|
420
|
+
accountId: dc.account.accountId,
|
|
421
|
+
replyInThread: routing.replyInThread,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
catch (err) {
|
|
425
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
426
|
+
dc.error(`feishu[${dc.account.accountId}]: ${i18nCommandName} i18n dispatch failed: ${errMsg}`);
|
|
427
|
+
await (0, send_1.sendMessageFeishu)({
|
|
428
|
+
cfg: dc.accountScopedCfg,
|
|
429
|
+
to: dc.ctx.chatId,
|
|
430
|
+
text: `${i18nCommandName} failed: ${errMsg}`,
|
|
431
|
+
replyToMessageId: params.replyToMessageId ?? dc.ctx.messageId,
|
|
432
|
+
accountId: dc.account.accountId,
|
|
433
|
+
replyInThread: routing.replyInThread,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// 8. Dispatch: system command vs. normal message
|
|
439
|
+
// Comment targets always go to normal dispatch — system command
|
|
440
|
+
// delivery uses sendMessageFeishu which can't reach comment threads.
|
|
441
|
+
const isCommand = !isCommentFlow &&
|
|
442
|
+
dc.core.channel.commands.isControlCommandMessage(params.ctx.content, params.accountScopedCfg);
|
|
443
|
+
// Resolve per-group skill filter (per-group > default "*")
|
|
444
|
+
const skillFilter = dc.isGroup ? (params.groupConfig?.skills ?? params.defaultGroupConfig?.skills) : undefined;
|
|
445
|
+
if (isCommand) {
|
|
446
|
+
await (0, dispatch_commands_1.dispatchSystemCommand)(dc, ctxPayload, params.replyToMessageId);
|
|
447
|
+
// /new and /reset explicitly start a new session — clear pending history
|
|
448
|
+
if (isBareNewOrReset && dc.isGroup && historyKey && params.chatHistories) {
|
|
449
|
+
(0, reply_history_1.clearHistoryEntriesIfEnabled)({
|
|
450
|
+
historyMap: params.chatHistories,
|
|
451
|
+
historyKey,
|
|
452
|
+
limit: params.historyLimit,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
// Normal message dispatch; history cleanup happens inside.
|
|
458
|
+
// System commands intentionally skip history cleanup — command handlers
|
|
459
|
+
// don't consume history context, so entries are preserved for the next
|
|
460
|
+
// normal message.
|
|
461
|
+
// A human asking the bots to stop ("中断对话", "stop talking", …) must NOT
|
|
462
|
+
// get a forced peer-@: the deterministic ensureMention backstop would
|
|
463
|
+
// re-wake the peer bot and defeat the interruption. Skip peer resolution
|
|
464
|
+
// on stop-intent; the kickoff/continue path ("你们辩论") is unaffected.
|
|
465
|
+
const botPeer = (0, abort_detect_1.isConversationStopIntent)(dc.ctx.content ?? '')
|
|
466
|
+
? undefined
|
|
467
|
+
: (0, bot_content_1.resolveBotPeerForMention)({
|
|
468
|
+
isGroup: dc.isGroup,
|
|
469
|
+
senderIsBot: dc.ctx.senderIsBot,
|
|
470
|
+
senderId: dc.ctx.senderId,
|
|
471
|
+
senderName: dc.ctx.senderName ?? undefined,
|
|
472
|
+
mentions: dc.ctx.mentions,
|
|
473
|
+
botOpenId: params.botOpenId,
|
|
474
|
+
});
|
|
475
|
+
await dispatchNormalMessage(dc, ctxPayload, routing, params.chatHistories, historyKey, params.historyLimit, params.replyToMessageId, skillFilter, params.skipTyping, botPeer);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Context enrichment for inbound Feishu messages.
|
|
6
|
+
*
|
|
7
|
+
* Enrichment phases:
|
|
8
|
+
*
|
|
9
|
+
* - **resolveSenderInfo** (lightweight, before gate) — resolves sender
|
|
10
|
+
* display name and tracks permission errors.
|
|
11
|
+
* - **prefetchUserNames** (after gate, before content resolution) — batch
|
|
12
|
+
* pre-warm the account-scoped user-name cache for the sender and all
|
|
13
|
+
* non-bot mentions so that downstream merge_forward expansion and
|
|
14
|
+
* quoted-message formatting can read names synchronously.
|
|
15
|
+
* - **resolveMedia** (after gate) — downloads binary media attachments
|
|
16
|
+
* using ResourceDescriptors from the converter phase.
|
|
17
|
+
* - **resolveQuotedContent** (after gate) — fetches the replied-to
|
|
18
|
+
* message text for context.
|
|
19
|
+
*
|
|
20
|
+
* Note: merge_forward expansion for the primary message is now handled
|
|
21
|
+
* at parse time in {@link parseMessageEvent}. Quoted merge_forward
|
|
22
|
+
* messages are still expanded here via {@link resolveQuotedContent}.
|
|
23
|
+
*/
|
|
24
|
+
import type { ClawdbotConfig } from 'openclaw/plugin-sdk';
|
|
25
|
+
import type { FeishuMediaInfo, MessageContext } from '../types';
|
|
26
|
+
import type { LarkAccount } from '../../core/types';
|
|
27
|
+
import type { PermissionError } from './permission';
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the sender display name and track permission errors.
|
|
30
|
+
*
|
|
31
|
+
* This must run before the gate check because per-group sender
|
|
32
|
+
* allowlists may match on senderName.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveSenderInfo(params: {
|
|
35
|
+
ctx: MessageContext;
|
|
36
|
+
account: LarkAccount;
|
|
37
|
+
log: (...args: unknown[]) => void;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
ctx: MessageContext;
|
|
40
|
+
permissionError?: PermissionError;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Batch-prefetch user display names for the sender and all non-bot
|
|
44
|
+
* mentions. Mention names that are already known from the event payload
|
|
45
|
+
* are written into the cache for free.
|
|
46
|
+
*/
|
|
47
|
+
export declare function prefetchUserNames(params: {
|
|
48
|
+
ctx: MessageContext;
|
|
49
|
+
account: LarkAccount;
|
|
50
|
+
log: (...args: unknown[]) => void;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
/** Result of media resolution: envelope payload + per-file mapping. */
|
|
53
|
+
export interface ResolveMediaResult {
|
|
54
|
+
payload: Record<string, unknown>;
|
|
55
|
+
mediaList: FeishuMediaInfo[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Download and save binary media attachments (images, files, audio,
|
|
59
|
+
* video, stickers) from the inbound message.
|
|
60
|
+
*
|
|
61
|
+
* Uses ResourceDescriptors extracted by content converters during the
|
|
62
|
+
* parse phase — no re-parsing of rawMessage.content needed.
|
|
63
|
+
*
|
|
64
|
+
* Returns a payload object whose keys (`MediaPath`, `MediaType`, …)
|
|
65
|
+
* are spread directly into the agent envelope, plus the raw mediaList
|
|
66
|
+
* for content substitution.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveMedia(params: {
|
|
69
|
+
ctx: MessageContext;
|
|
70
|
+
/** account 级别的 ClawdbotConfig(channels.feishu 已替换为 per-account 合并后的配置) */
|
|
71
|
+
accountScopedCfg: ClawdbotConfig;
|
|
72
|
+
account: LarkAccount;
|
|
73
|
+
log: (...args: unknown[]) => void;
|
|
74
|
+
}): Promise<ResolveMediaResult>;
|
|
75
|
+
/**
|
|
76
|
+
* Replace Feishu file-key references in message content with actual
|
|
77
|
+
* local file paths after download.
|
|
78
|
+
*
|
|
79
|
+
* This is critical for:
|
|
80
|
+
* - **Images / stickers**: The SDK's `detectAndLoadPromptImages` scans
|
|
81
|
+
* the prompt text for local file paths with image extensions.
|
|
82
|
+
* - **Audio / video / files**: Gives the AI meaningful context about
|
|
83
|
+
* what was received (the SDK reads these via `MediaPath` directly,
|
|
84
|
+
* but the text body should still reflect the actual attachments).
|
|
85
|
+
*/
|
|
86
|
+
export declare function substituteMediaPaths(content: string, mediaList: FeishuMediaInfo[]): string;
|
|
87
|
+
/**
|
|
88
|
+
* Fetch the text content of the message that the user replied to.
|
|
89
|
+
*
|
|
90
|
+
* If the quoted message is itself a merge_forward, its sub-messages are
|
|
91
|
+
* fetched and formatted as a single text block.
|
|
92
|
+
*
|
|
93
|
+
* Returns `"senderName: content"` when the sender name is available so
|
|
94
|
+
* the AI knows who originally wrote the quoted message.
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveQuotedContent(params: {
|
|
97
|
+
ctx: MessageContext;
|
|
98
|
+
/** account 级别的 ClawdbotConfig(channels.feishu 已替换为 per-account 合并后的配置) */
|
|
99
|
+
accountScopedCfg: ClawdbotConfig;
|
|
100
|
+
account: LarkAccount;
|
|
101
|
+
log: (...args: unknown[]) => void;
|
|
102
|
+
}): Promise<string | undefined>;
|