@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,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* FIFO-based message deduplication.
|
|
6
|
+
*
|
|
7
|
+
* Feishu WebSocket connections may redeliver messages on reconnect.
|
|
8
|
+
* This module tracks recently-seen message IDs and filters duplicates.
|
|
9
|
+
*
|
|
10
|
+
* Design choices:
|
|
11
|
+
* - FIFO eviction (not LRU) — message IDs are write-once/check-once,
|
|
12
|
+
* no hot/cold access pattern. FIFO naturally expires the oldest entry
|
|
13
|
+
* first, which matches the dedup semantics.
|
|
14
|
+
* - ES2015 `Map` preserves insertion order, giving us FIFO for free.
|
|
15
|
+
* - Periodic sweep leverages FIFO ordering: iterate from oldest and
|
|
16
|
+
* `break` at the first non-expired entry → O(expired), not O(n).
|
|
17
|
+
*/
|
|
18
|
+
export interface MessageDedupOpts {
|
|
19
|
+
/** Time-to-live for each entry in milliseconds (default: 5 min). */
|
|
20
|
+
ttlMs?: number;
|
|
21
|
+
/** Maximum number of tracked entries (default: 10 000). */
|
|
22
|
+
maxEntries?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check whether a message is too old to process.
|
|
26
|
+
*
|
|
27
|
+
* Feishu message `create_time` is a millisecond Unix timestamp encoded
|
|
28
|
+
* as a string. When a WebSocket reconnects after a long outage, stale
|
|
29
|
+
* messages may be redelivered — this function lets callers discard them
|
|
30
|
+
* before entering the full handling pipeline.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isMessageExpired(createTimeStr: string | undefined, expiryMs?: number): boolean;
|
|
33
|
+
export declare class MessageDedup {
|
|
34
|
+
private readonly store;
|
|
35
|
+
private readonly ttlMs;
|
|
36
|
+
private readonly maxEntries;
|
|
37
|
+
private readonly sweepTimer;
|
|
38
|
+
constructor(opts?: MessageDedupOpts);
|
|
39
|
+
/**
|
|
40
|
+
* Try to record a message ID.
|
|
41
|
+
*
|
|
42
|
+
* @param id Unique message identifier (e.g. Feishu `message_id`).
|
|
43
|
+
* @param scope Optional scope prefix (e.g. accountId) to namespace IDs.
|
|
44
|
+
* @returns `true` if the message is **new**; `false` if it is a duplicate.
|
|
45
|
+
*/
|
|
46
|
+
tryRecord(id: string, scope?: string): boolean;
|
|
47
|
+
/** Current number of tracked entries (for diagnostics). */
|
|
48
|
+
get size(): number;
|
|
49
|
+
/** Remove all entries and stop the periodic sweep. */
|
|
50
|
+
clear(): void;
|
|
51
|
+
/** Stop the periodic sweep timer and clear all tracked entries. */
|
|
52
|
+
dispose(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Sweep expired entries from the front of the map.
|
|
55
|
+
* Because entries are in insertion order (FIFO), we can stop as soon as
|
|
56
|
+
* we hit one that hasn't expired yet.
|
|
57
|
+
*/
|
|
58
|
+
private sweep;
|
|
59
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* FIFO-based message deduplication.
|
|
7
|
+
*
|
|
8
|
+
* Feishu WebSocket connections may redeliver messages on reconnect.
|
|
9
|
+
* This module tracks recently-seen message IDs and filters duplicates.
|
|
10
|
+
*
|
|
11
|
+
* Design choices:
|
|
12
|
+
* - FIFO eviction (not LRU) — message IDs are write-once/check-once,
|
|
13
|
+
* no hot/cold access pattern. FIFO naturally expires the oldest entry
|
|
14
|
+
* first, which matches the dedup semantics.
|
|
15
|
+
* - ES2015 `Map` preserves insertion order, giving us FIFO for free.
|
|
16
|
+
* - Periodic sweep leverages FIFO ordering: iterate from oldest and
|
|
17
|
+
* `break` at the first non-expired entry → O(expired), not O(n).
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.MessageDedup = void 0;
|
|
21
|
+
exports.isMessageExpired = isMessageExpired;
|
|
22
|
+
const DEFAULT_TTL_MS = 12 * 60 * 60 * 1000; // 12 hours
|
|
23
|
+
const DEFAULT_MAX_ENTRIES = 5_000;
|
|
24
|
+
const SWEEP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Message expiry check
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
const DEFAULT_EXPIRY_MS = 30 * 60 * 1000; // 30 minutes
|
|
29
|
+
/**
|
|
30
|
+
* Check whether a message is too old to process.
|
|
31
|
+
*
|
|
32
|
+
* Feishu message `create_time` is a millisecond Unix timestamp encoded
|
|
33
|
+
* as a string. When a WebSocket reconnects after a long outage, stale
|
|
34
|
+
* messages may be redelivered — this function lets callers discard them
|
|
35
|
+
* before entering the full handling pipeline.
|
|
36
|
+
*/
|
|
37
|
+
function isMessageExpired(createTimeStr, expiryMs = DEFAULT_EXPIRY_MS) {
|
|
38
|
+
if (!createTimeStr)
|
|
39
|
+
return false;
|
|
40
|
+
const createTime = parseInt(createTimeStr, 10);
|
|
41
|
+
if (Number.isNaN(createTime))
|
|
42
|
+
return false;
|
|
43
|
+
return Date.now() - createTime > expiryMs;
|
|
44
|
+
}
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Message deduplication
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
class MessageDedup {
|
|
49
|
+
store = new Map();
|
|
50
|
+
ttlMs;
|
|
51
|
+
maxEntries;
|
|
52
|
+
sweepTimer;
|
|
53
|
+
constructor(opts = {}) {
|
|
54
|
+
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
55
|
+
this.maxEntries = opts.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
56
|
+
// Periodic sweep — relies on FIFO ordering so we can break early.
|
|
57
|
+
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
58
|
+
this.sweepTimer.unref();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Try to record a message ID.
|
|
62
|
+
*
|
|
63
|
+
* @param id Unique message identifier (e.g. Feishu `message_id`).
|
|
64
|
+
* @param scope Optional scope prefix (e.g. accountId) to namespace IDs.
|
|
65
|
+
* @returns `true` if the message is **new**; `false` if it is a duplicate.
|
|
66
|
+
*/
|
|
67
|
+
tryRecord(id, scope) {
|
|
68
|
+
const key = scope ? `${scope}:${id}` : id;
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
const existing = this.store.get(key);
|
|
71
|
+
if (existing !== undefined) {
|
|
72
|
+
// Entry exists — check TTL.
|
|
73
|
+
if (now - existing < this.ttlMs) {
|
|
74
|
+
// Still within TTL → duplicate.
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// Expired — remove so we can re-insert at the tail (refresh position).
|
|
78
|
+
this.store.delete(key);
|
|
79
|
+
}
|
|
80
|
+
// Enforce capacity via FIFO: drop the oldest entry.
|
|
81
|
+
if (this.store.size >= this.maxEntries) {
|
|
82
|
+
const oldest = this.store.keys().next().value;
|
|
83
|
+
if (oldest !== undefined) {
|
|
84
|
+
this.store.delete(oldest);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this.store.set(key, now);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
/** Current number of tracked entries (for diagnostics). */
|
|
91
|
+
get size() {
|
|
92
|
+
return this.store.size;
|
|
93
|
+
}
|
|
94
|
+
/** Remove all entries and stop the periodic sweep. */
|
|
95
|
+
clear() {
|
|
96
|
+
clearInterval(this.sweepTimer);
|
|
97
|
+
this.store.clear();
|
|
98
|
+
}
|
|
99
|
+
/** Stop the periodic sweep timer and clear all tracked entries. */
|
|
100
|
+
dispose() {
|
|
101
|
+
clearInterval(this.sweepTimer);
|
|
102
|
+
this.store.clear();
|
|
103
|
+
}
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Internal
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
/**
|
|
108
|
+
* Sweep expired entries from the front of the map.
|
|
109
|
+
* Because entries are in insertion order (FIFO), we can stop as soon as
|
|
110
|
+
* we hit one that hasn't expired yet.
|
|
111
|
+
*/
|
|
112
|
+
sweep() {
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
for (const [key, ts] of this.store) {
|
|
115
|
+
if (now - ts < this.ttlMs)
|
|
116
|
+
break;
|
|
117
|
+
this.store.delete(key);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.MessageDedup = MessageDedup;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Pure construction functions for the agent dispatch pipeline.
|
|
6
|
+
*
|
|
7
|
+
* All functions in this module are side-effect-free: they build data
|
|
8
|
+
* structures (message bodies, envelope payloads, inbound context) but
|
|
9
|
+
* never perform I/O, send messages, or mutate external state.
|
|
10
|
+
*/
|
|
11
|
+
import type { HistoryEntry } from 'openclaw/plugin-sdk/reply-history';
|
|
12
|
+
import type { MessageContext } from '../types';
|
|
13
|
+
import type { LarkClient } from '../../core/lark-client';
|
|
14
|
+
import type { DispatchContext } from './dispatch-context';
|
|
15
|
+
import type { SentinelEntry } from './sentinel-store';
|
|
16
|
+
/**
|
|
17
|
+
* Build a `[System: ...]` mention annotation when the message @-mentions
|
|
18
|
+
* non-self-bot users or when the previous reply had unresolved mentions.
|
|
19
|
+
* Returns `undefined` when there is nothing to report.
|
|
20
|
+
*
|
|
21
|
+
* Sender identity / chat metadata are handled by the SDK's own
|
|
22
|
+
* `buildInboundUserContextPrefix` (via SenderId, SenderName, ReplyToBody,
|
|
23
|
+
* InboundHistory, etc.), so we only inject the mention data that the SDK
|
|
24
|
+
* does not natively support.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildMentionAnnotation(ctx: MessageContext, sentinels?: SentinelEntry[]): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Pure function: build the annotated message body with optional quote,
|
|
29
|
+
* speaker prefix, and mention annotation (for the envelope Body).
|
|
30
|
+
*
|
|
31
|
+
* Note: message_id and reply_to are now conveyed via system-event tags
|
|
32
|
+
* (msg:om_xxx, reply_to:om_yyy) instead of inline annotations, keeping
|
|
33
|
+
* the body cleaner and avoiding misleading heuristics for non-text
|
|
34
|
+
* message types (merge_forward, interactive cards, etc.).
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildMessageBody(ctx: MessageContext, quotedContent?: string, sentinels?: SentinelEntry[]): string;
|
|
37
|
+
/**
|
|
38
|
+
* Build the BodyForAgent value: the clean message content plus an
|
|
39
|
+
* optional mention annotation.
|
|
40
|
+
*
|
|
41
|
+
* SDK >= 2026.2.10 changed the BodyForAgent fallback chain from
|
|
42
|
+
* `BodyForAgent ?? Body` to `BodyForAgent ?? CommandBody ?? RawBody ?? Body`,
|
|
43
|
+
* so annotations embedded only in Body never reach the AI. Setting
|
|
44
|
+
* BodyForAgent explicitly ensures the mention annotation survives.
|
|
45
|
+
*
|
|
46
|
+
* Sender identity, reply context, and chat history are NOT duplicated
|
|
47
|
+
* here — they are injected by the SDK's `buildInboundUserContextPrefix`
|
|
48
|
+
* via the standard fields (SenderId, SenderName, ReplyToBody,
|
|
49
|
+
* InboundHistory) that we pass in buildInboundPayload.
|
|
50
|
+
*
|
|
51
|
+
* Note: media file paths are substituted into `ctx.content` upstream
|
|
52
|
+
* (handler.ts -> substituteMediaPaths) before this function is called.
|
|
53
|
+
* The SDK's `detectAndLoadPromptImages` will discover image paths from
|
|
54
|
+
* the text and inject them as multimodal content blocks.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildBodyForAgent(ctx: MessageContext, sentinels?: SentinelEntry[]): string;
|
|
57
|
+
/**
|
|
58
|
+
* Unified call to `finalizeInboundContext`, eliminating the duplicated
|
|
59
|
+
* field-mapping between permission notification and main message paths.
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildInboundPayload(dc: DispatchContext, opts: {
|
|
62
|
+
body: string;
|
|
63
|
+
bodyForAgent: string;
|
|
64
|
+
rawBody: string;
|
|
65
|
+
commandBody: string;
|
|
66
|
+
originatingTo?: string;
|
|
67
|
+
senderName: string;
|
|
68
|
+
senderId: string;
|
|
69
|
+
messageSid: string;
|
|
70
|
+
wasMentioned: boolean;
|
|
71
|
+
replyToBody?: string;
|
|
72
|
+
inboundHistory?: {
|
|
73
|
+
sender: string;
|
|
74
|
+
body: string;
|
|
75
|
+
timestamp: number;
|
|
76
|
+
}[];
|
|
77
|
+
extraFields?: Record<string, unknown>;
|
|
78
|
+
}): ReturnType<typeof LarkClient.runtime.channel.reply.finalizeInboundContext>;
|
|
79
|
+
/**
|
|
80
|
+
* Structured identity signals injected into the agent envelope so the LLM
|
|
81
|
+
* can tell "who is talking to me" apart — in particular whether the sender
|
|
82
|
+
* is a bot, and what the bot's own open_id is.
|
|
83
|
+
*
|
|
84
|
+
* BotOpenId is omitted when unknown (e.g. startup race before the bot info
|
|
85
|
+
* probe completes) to avoid surfacing an empty identity to the agent.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildFeishuIdentityFields(ctx: MessageContext, botOpenId?: string): Record<string, unknown>;
|
|
88
|
+
/**
|
|
89
|
+
* Build the effective group system prompt for a Feishu group chat.
|
|
90
|
+
*
|
|
91
|
+
* Always prepends bot-at-bot guidance (self-identity + @ semantics + loop
|
|
92
|
+
* hygiene) so the agent knows which open_id is itself, how Feishu @-delivery
|
|
93
|
+
* works, and when to stop; then appends any operator-configured group
|
|
94
|
+
* systemPrompt. Returns `undefined` only when there is nothing to inject.
|
|
95
|
+
*/
|
|
96
|
+
export declare function buildFeishuGroupSystemPrompt(configured: string | undefined, botOpenId?: string): string | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Format the agent envelope and prepend group chat history if applicable.
|
|
99
|
+
* Returns the combined body and the history key (undefined for DMs).
|
|
100
|
+
*/
|
|
101
|
+
export declare function buildEnvelopeWithHistory(dc: DispatchContext, messageBody: string, chatHistories: Map<string, HistoryEntry[]> | undefined, historyLimit: number): {
|
|
102
|
+
combinedBody: string;
|
|
103
|
+
historyKey: string | undefined;
|
|
104
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* Pure construction functions for the agent dispatch pipeline.
|
|
7
|
+
*
|
|
8
|
+
* All functions in this module are side-effect-free: they build data
|
|
9
|
+
* structures (message bodies, envelope payloads, inbound context) but
|
|
10
|
+
* never perform I/O, send messages, or mutate external state.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.buildMentionAnnotation = buildMentionAnnotation;
|
|
14
|
+
exports.buildMessageBody = buildMessageBody;
|
|
15
|
+
exports.buildBodyForAgent = buildBodyForAgent;
|
|
16
|
+
exports.buildInboundPayload = buildInboundPayload;
|
|
17
|
+
exports.buildFeishuIdentityFields = buildFeishuIdentityFields;
|
|
18
|
+
exports.buildFeishuGroupSystemPrompt = buildFeishuGroupSystemPrompt;
|
|
19
|
+
exports.buildEnvelopeWithHistory = buildEnvelopeWithHistory;
|
|
20
|
+
const reply_history_1 = require("openclaw/plugin-sdk/reply-history");
|
|
21
|
+
const chat_queue_1 = require("../../channel/chat-queue.js");
|
|
22
|
+
const mention_1 = require("./mention.js");
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Mention annotation
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
const MENTION_USAGE_HINT = 'To @mention in a reply, use `<at user_id="ou_xxx">Name</at>`; plain "@Name" won\'t notify.';
|
|
27
|
+
/**
|
|
28
|
+
* Build a `[System: ...]` mention annotation when the message @-mentions
|
|
29
|
+
* non-self-bot users or when the previous reply had unresolved mentions.
|
|
30
|
+
* Returns `undefined` when there is nothing to report.
|
|
31
|
+
*
|
|
32
|
+
* Sender identity / chat metadata are handled by the SDK's own
|
|
33
|
+
* `buildInboundUserContextPrefix` (via SenderId, SenderName, ReplyToBody,
|
|
34
|
+
* InboundHistory, etc.), so we only inject the mention data that the SDK
|
|
35
|
+
* does not natively support.
|
|
36
|
+
*/
|
|
37
|
+
function buildMentionAnnotation(ctx, sentinels) {
|
|
38
|
+
// When this bot itself was @-mentioned, tell the agent explicitly. The
|
|
39
|
+
// leading self-mention is stripped from the body, so without this the
|
|
40
|
+
// agent has no signal that it was the addressee and may mis-attribute
|
|
41
|
+
// instructions to another mentioned party.
|
|
42
|
+
const selfMention = ctx.mentions.find((m) => m.isBot);
|
|
43
|
+
const sections = [
|
|
44
|
+
selfMention
|
|
45
|
+
? `You (${selfMention.name}, open_id: ${selfMention.openId}) were directly @mentioned in this message; ` +
|
|
46
|
+
`the message body is addressed to you.`
|
|
47
|
+
: undefined,
|
|
48
|
+
formatMentionList((0, mention_1.nonBotMentions)(ctx)),
|
|
49
|
+
formatSentinelFeedback(sentinels),
|
|
50
|
+
].filter((s) => !!s);
|
|
51
|
+
if (sections.length === 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
sections.push(MENTION_USAGE_HINT);
|
|
54
|
+
return `[System: ${sections.join(' ')}]`;
|
|
55
|
+
}
|
|
56
|
+
function formatMentionList(mentions) {
|
|
57
|
+
if (mentions.length === 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
const details = mentions.map((t) => `${t.name} (open_id: ${t.openId})`).join(', ');
|
|
60
|
+
return (`This message @mentions the following users: ${details}. ` +
|
|
61
|
+
`Use these open_ids when performing actions involving these users.`);
|
|
62
|
+
}
|
|
63
|
+
function formatSentinelFeedback(sentinels) {
|
|
64
|
+
if (!sentinels || sentinels.length === 0)
|
|
65
|
+
return undefined;
|
|
66
|
+
const lines = sentinels.map((s) => {
|
|
67
|
+
if (s.reason === 'not_found') {
|
|
68
|
+
return `"@${s.name}" was not recognized in the chat`;
|
|
69
|
+
}
|
|
70
|
+
if (s.reason === 'ambiguous' && s.candidates && s.candidates.length > 0) {
|
|
71
|
+
const ids = s.candidates.map((c) => c.openId).join(' / ');
|
|
72
|
+
return `"@${s.name}" matched multiple users (${ids}); use explicit <at user_id="...">`;
|
|
73
|
+
}
|
|
74
|
+
return `"@${s.name}" failed to resolve`;
|
|
75
|
+
});
|
|
76
|
+
return `Previous reply had unresolved mentions: ${lines.join('; ')}.`;
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Message body builders
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
/**
|
|
82
|
+
* Pure function: build the annotated message body with optional quote,
|
|
83
|
+
* speaker prefix, and mention annotation (for the envelope Body).
|
|
84
|
+
*
|
|
85
|
+
* Note: message_id and reply_to are now conveyed via system-event tags
|
|
86
|
+
* (msg:om_xxx, reply_to:om_yyy) instead of inline annotations, keeping
|
|
87
|
+
* the body cleaner and avoiding misleading heuristics for non-text
|
|
88
|
+
* message types (merge_forward, interactive cards, etc.).
|
|
89
|
+
*/
|
|
90
|
+
function buildMessageBody(ctx, quotedContent, sentinels) {
|
|
91
|
+
let messageBody = ctx.content;
|
|
92
|
+
if (quotedContent) {
|
|
93
|
+
messageBody = `[Replying to: "${quotedContent}"]\n\n${ctx.content}`;
|
|
94
|
+
}
|
|
95
|
+
const speaker = ctx.senderName ?? ctx.senderId;
|
|
96
|
+
messageBody = `${speaker}: ${messageBody}`;
|
|
97
|
+
const mentionAnnotation = buildMentionAnnotation(ctx, sentinels);
|
|
98
|
+
if (mentionAnnotation) {
|
|
99
|
+
messageBody += `\n\n${mentionAnnotation}`;
|
|
100
|
+
}
|
|
101
|
+
return messageBody;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build the BodyForAgent value: the clean message content plus an
|
|
105
|
+
* optional mention annotation.
|
|
106
|
+
*
|
|
107
|
+
* SDK >= 2026.2.10 changed the BodyForAgent fallback chain from
|
|
108
|
+
* `BodyForAgent ?? Body` to `BodyForAgent ?? CommandBody ?? RawBody ?? Body`,
|
|
109
|
+
* so annotations embedded only in Body never reach the AI. Setting
|
|
110
|
+
* BodyForAgent explicitly ensures the mention annotation survives.
|
|
111
|
+
*
|
|
112
|
+
* Sender identity, reply context, and chat history are NOT duplicated
|
|
113
|
+
* here — they are injected by the SDK's `buildInboundUserContextPrefix`
|
|
114
|
+
* via the standard fields (SenderId, SenderName, ReplyToBody,
|
|
115
|
+
* InboundHistory) that we pass in buildInboundPayload.
|
|
116
|
+
*
|
|
117
|
+
* Note: media file paths are substituted into `ctx.content` upstream
|
|
118
|
+
* (handler.ts -> substituteMediaPaths) before this function is called.
|
|
119
|
+
* The SDK's `detectAndLoadPromptImages` will discover image paths from
|
|
120
|
+
* the text and inject them as multimodal content blocks.
|
|
121
|
+
*/
|
|
122
|
+
function buildBodyForAgent(ctx, sentinels) {
|
|
123
|
+
const mentionAnnotation = buildMentionAnnotation(ctx, sentinels);
|
|
124
|
+
if (mentionAnnotation) {
|
|
125
|
+
return `${ctx.content}\n\n${mentionAnnotation}`;
|
|
126
|
+
}
|
|
127
|
+
return ctx.content;
|
|
128
|
+
}
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Inbound payload builder
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
/**
|
|
133
|
+
* Unified call to `finalizeInboundContext`, eliminating the duplicated
|
|
134
|
+
* field-mapping between permission notification and main message paths.
|
|
135
|
+
*/
|
|
136
|
+
function buildInboundPayload(dc, opts) {
|
|
137
|
+
return dc.core.channel.reply.finalizeInboundContext({
|
|
138
|
+
// extraFields first — fixed fields below always take precedence
|
|
139
|
+
...opts.extraFields,
|
|
140
|
+
Body: opts.body,
|
|
141
|
+
BodyForAgent: opts.bodyForAgent,
|
|
142
|
+
RawBody: opts.rawBody,
|
|
143
|
+
CommandBody: opts.commandBody,
|
|
144
|
+
From: dc.feishuFrom,
|
|
145
|
+
To: dc.feishuTo,
|
|
146
|
+
SessionKey: dc.threadSessionKey ?? dc.route.sessionKey,
|
|
147
|
+
AccountId: dc.route.accountId,
|
|
148
|
+
ChatType: dc.isGroup ? 'group' : 'direct',
|
|
149
|
+
GroupSubject: dc.isGroup ? dc.ctx.chatId : undefined,
|
|
150
|
+
SenderName: opts.senderName,
|
|
151
|
+
SenderId: opts.senderId,
|
|
152
|
+
Provider: 'feishu',
|
|
153
|
+
Surface: 'feishu',
|
|
154
|
+
MessageSid: opts.messageSid,
|
|
155
|
+
ReplyToBody: opts.replyToBody,
|
|
156
|
+
InboundHistory: opts.inboundHistory,
|
|
157
|
+
Timestamp: dc.ctx.createTime ?? Date.now(),
|
|
158
|
+
WasMentioned: opts.wasMentioned,
|
|
159
|
+
CommandAuthorized: dc.commandAuthorized,
|
|
160
|
+
OriginatingChannel: 'feishu',
|
|
161
|
+
OriginatingTo: opts.originatingTo ?? dc.feishuTo,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Bot-at-Bot identity & guidance
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
/**
|
|
168
|
+
* Structured identity signals injected into the agent envelope so the LLM
|
|
169
|
+
* can tell "who is talking to me" apart — in particular whether the sender
|
|
170
|
+
* is a bot, and what the bot's own open_id is.
|
|
171
|
+
*
|
|
172
|
+
* BotOpenId is omitted when unknown (e.g. startup race before the bot info
|
|
173
|
+
* probe completes) to avoid surfacing an empty identity to the agent.
|
|
174
|
+
*/
|
|
175
|
+
function buildFeishuIdentityFields(ctx, botOpenId) {
|
|
176
|
+
return {
|
|
177
|
+
SenderIsBot: ctx.senderIsBot ?? false,
|
|
178
|
+
...(botOpenId ? { BotOpenId: botOpenId } : {}),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Static guidance about Feishu's bot-at-bot @ semantics and loop hygiene. */
|
|
182
|
+
const FEISHU_BOT_AT_BOT_GUIDANCE = 'On Feishu, another bot only receives a message when you explicitly @-mention it; ' +
|
|
183
|
+
'a plain message or a reply without an @ will NOT reach another bot. ' +
|
|
184
|
+
'When you need another bot to continue the work, @-mention it. ' +
|
|
185
|
+
'When no further action is needed, or you are asked to stop, do not reply — ' +
|
|
186
|
+
'this avoids endless bot-to-bot loops.';
|
|
187
|
+
/**
|
|
188
|
+
* Build the effective group system prompt for a Feishu group chat.
|
|
189
|
+
*
|
|
190
|
+
* Always prepends bot-at-bot guidance (self-identity + @ semantics + loop
|
|
191
|
+
* hygiene) so the agent knows which open_id is itself, how Feishu @-delivery
|
|
192
|
+
* works, and when to stop; then appends any operator-configured group
|
|
193
|
+
* systemPrompt. Returns `undefined` only when there is nothing to inject.
|
|
194
|
+
*/
|
|
195
|
+
function buildFeishuGroupSystemPrompt(configured, botOpenId) {
|
|
196
|
+
const parts = [];
|
|
197
|
+
if (botOpenId) {
|
|
198
|
+
parts.push(`Your own Feishu open_id is "${botOpenId}"; any @-mention of this open_id refers to you.`);
|
|
199
|
+
}
|
|
200
|
+
parts.push(FEISHU_BOT_AT_BOT_GUIDANCE);
|
|
201
|
+
const trimmedConfigured = configured?.trim();
|
|
202
|
+
if (trimmedConfigured) {
|
|
203
|
+
parts.push(trimmedConfigured);
|
|
204
|
+
}
|
|
205
|
+
const merged = parts.join('\n\n').trim();
|
|
206
|
+
return merged || undefined;
|
|
207
|
+
}
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Envelope + history builder
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
/**
|
|
212
|
+
* Format the agent envelope and prepend group chat history if applicable.
|
|
213
|
+
* Returns the combined body and the history key (undefined for DMs).
|
|
214
|
+
*/
|
|
215
|
+
function buildEnvelopeWithHistory(dc, messageBody, chatHistories, historyLimit) {
|
|
216
|
+
const body = dc.core.channel.reply.formatAgentEnvelope({
|
|
217
|
+
channel: 'Feishu',
|
|
218
|
+
from: dc.envelopeFrom,
|
|
219
|
+
timestamp: new Date(),
|
|
220
|
+
envelope: dc.envelopeOptions,
|
|
221
|
+
body: messageBody,
|
|
222
|
+
});
|
|
223
|
+
let combinedBody = body;
|
|
224
|
+
const historyKey = dc.isGroup ? (0, chat_queue_1.threadScopedKey)(dc.ctx.chatId, dc.isThread ? dc.ctx.threadId : undefined) : undefined;
|
|
225
|
+
if (dc.isGroup && historyKey && chatHistories) {
|
|
226
|
+
combinedBody = (0, reply_history_1.buildPendingHistoryContextFromMap)({
|
|
227
|
+
historyMap: chatHistories,
|
|
228
|
+
historyKey,
|
|
229
|
+
limit: historyLimit,
|
|
230
|
+
currentMessage: combinedBody,
|
|
231
|
+
formatEntry: (entry) => dc.core.channel.reply.formatAgentEnvelope({
|
|
232
|
+
channel: 'Feishu',
|
|
233
|
+
from: `${dc.ctx.chatId}:${entry.sender}`,
|
|
234
|
+
timestamp: entry.timestamp,
|
|
235
|
+
body: entry.body,
|
|
236
|
+
envelope: dc.envelopeOptions,
|
|
237
|
+
}),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return { combinedBody, historyKey };
|
|
241
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* System command and permission notification dispatch for inbound messages.
|
|
6
|
+
*
|
|
7
|
+
* Handles control commands (/help, /reset, etc.) via plain-text delivery
|
|
8
|
+
* and permission-error notifications via the streaming card flow.
|
|
9
|
+
*/
|
|
10
|
+
import type { LarkClient } from '../../core/lark-client';
|
|
11
|
+
import type { PermissionError } from './permission';
|
|
12
|
+
import type { DispatchContext } from './dispatch-context';
|
|
13
|
+
/**
|
|
14
|
+
* Dispatch a permission-error notification to the agent so it can
|
|
15
|
+
* inform the user about the missing Feishu API scope.
|
|
16
|
+
*/
|
|
17
|
+
export declare function dispatchPermissionNotification(dc: DispatchContext, permissionError: PermissionError, replyToMessageId?: string): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Dispatch a system command (/help, /reset, etc.) via plain-text delivery.
|
|
20
|
+
* No streaming card, no "Processing..." state.
|
|
21
|
+
*/
|
|
22
|
+
export declare function dispatchSystemCommand(dc: DispatchContext, ctxPayload: ReturnType<typeof LarkClient.runtime.channel.reply.finalizeInboundContext>, replyToMessageId?: string): Promise<void>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* System command and permission notification dispatch for inbound messages.
|
|
7
|
+
*
|
|
8
|
+
* Handles control commands (/help, /reset, etc.) via plain-text delivery
|
|
9
|
+
* and permission-error notifications via the streaming card flow.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.dispatchPermissionNotification = dispatchPermissionNotification;
|
|
13
|
+
exports.dispatchSystemCommand = dispatchSystemCommand;
|
|
14
|
+
const lark_logger_1 = require("../../core/lark-logger.js");
|
|
15
|
+
const lark_ticket_1 = require("../../core/lark-ticket.js");
|
|
16
|
+
const reply_dispatcher_1 = require("../../card/reply-dispatcher.js");
|
|
17
|
+
const tool_use_trace_store_1 = require("../../card/tool-use-trace-store.js");
|
|
18
|
+
const send_1 = require("../outbound/send.js");
|
|
19
|
+
const dispatch_builders_1 = require("./dispatch-builders.js");
|
|
20
|
+
const log = (0, lark_logger_1.larkLogger)('inbound/dispatch-commands');
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Permission error notification
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* Dispatch a permission-error notification to the agent so it can
|
|
26
|
+
* inform the user about the missing Feishu API scope.
|
|
27
|
+
*/
|
|
28
|
+
async function dispatchPermissionNotification(dc, permissionError, replyToMessageId) {
|
|
29
|
+
const grantUrl = permissionError.grantUrl ?? '';
|
|
30
|
+
const permissionNotifyBody = `[System: The bot encountered a Feishu API permission error. Please inform the user about this issue and provide the permission grant URL for the admin to authorize. Permission grant URL: ${grantUrl}]`;
|
|
31
|
+
const permBody = dc.core.channel.reply.formatAgentEnvelope({
|
|
32
|
+
channel: 'Feishu',
|
|
33
|
+
from: dc.envelopeFrom,
|
|
34
|
+
timestamp: new Date(),
|
|
35
|
+
envelope: dc.envelopeOptions,
|
|
36
|
+
body: permissionNotifyBody,
|
|
37
|
+
});
|
|
38
|
+
const permCtx = (0, dispatch_builders_1.buildInboundPayload)(dc, {
|
|
39
|
+
body: permBody,
|
|
40
|
+
bodyForAgent: permissionNotifyBody,
|
|
41
|
+
rawBody: permissionNotifyBody,
|
|
42
|
+
commandBody: permissionNotifyBody,
|
|
43
|
+
senderName: 'system',
|
|
44
|
+
senderId: 'system',
|
|
45
|
+
messageSid: `${dc.ctx.messageId}:permission-error`,
|
|
46
|
+
wasMentioned: false,
|
|
47
|
+
});
|
|
48
|
+
(0, tool_use_trace_store_1.startToolUseTraceRun)(dc.threadSessionKey ?? dc.route.sessionKey);
|
|
49
|
+
const { dispatcher: permDispatcher, replyOptions: permReplyOptions, markDispatchIdle: markPermIdle, markFullyComplete: markPermComplete, } = (0, reply_dispatcher_1.createFeishuReplyDispatcher)({
|
|
50
|
+
cfg: dc.accountScopedCfg,
|
|
51
|
+
agentId: dc.route.agentId,
|
|
52
|
+
chatId: dc.ctx.chatId,
|
|
53
|
+
sessionKey: dc.threadSessionKey ?? dc.route.sessionKey,
|
|
54
|
+
replyToMessageId: replyToMessageId ?? dc.ctx.messageId,
|
|
55
|
+
accountId: dc.account.accountId,
|
|
56
|
+
chatType: dc.ctx.chatType,
|
|
57
|
+
replyInThread: dc.isThread,
|
|
58
|
+
toolUseDisplay: {
|
|
59
|
+
mode: 'off',
|
|
60
|
+
showToolUse: false,
|
|
61
|
+
showToolResultDetails: false,
|
|
62
|
+
showFullPaths: false,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
dc.log(`feishu[${dc.account.accountId}]: dispatching permission error notification to agent`);
|
|
66
|
+
await dc.core.channel.reply.dispatchReplyFromConfig({
|
|
67
|
+
ctx: permCtx,
|
|
68
|
+
cfg: dc.accountScopedCfg,
|
|
69
|
+
dispatcher: permDispatcher,
|
|
70
|
+
replyOptions: permReplyOptions,
|
|
71
|
+
});
|
|
72
|
+
await permDispatcher.waitForIdle();
|
|
73
|
+
markPermComplete();
|
|
74
|
+
markPermIdle();
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// System command dispatch
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
/**
|
|
80
|
+
* Dispatch a system command (/help, /reset, etc.) via plain-text delivery.
|
|
81
|
+
* No streaming card, no "Processing..." state.
|
|
82
|
+
*/
|
|
83
|
+
async function dispatchSystemCommand(dc, ctxPayload, replyToMessageId) {
|
|
84
|
+
let delivered = false;
|
|
85
|
+
const suppressToolDetails = isLifecycleSessionCommand(dc.ctx.content);
|
|
86
|
+
dc.log(`feishu[${dc.account.accountId}]: detected system command, using plain-text dispatch`);
|
|
87
|
+
log.info('system command detected, plain-text dispatch');
|
|
88
|
+
await dc.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
89
|
+
ctx: ctxPayload,
|
|
90
|
+
cfg: dc.accountScopedCfg,
|
|
91
|
+
dispatcherOptions: {
|
|
92
|
+
deliver: async (payload, info) => {
|
|
93
|
+
if (suppressToolDetails && info.kind === 'tool') {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const text = payload.text?.trim() ?? '';
|
|
97
|
+
if (!text)
|
|
98
|
+
return;
|
|
99
|
+
await (0, send_1.sendMessageFeishu)({
|
|
100
|
+
cfg: dc.accountScopedCfg,
|
|
101
|
+
to: dc.ctx.chatId,
|
|
102
|
+
text,
|
|
103
|
+
replyToMessageId: replyToMessageId ?? dc.ctx.messageId,
|
|
104
|
+
accountId: dc.account.accountId,
|
|
105
|
+
replyInThread: dc.isThread,
|
|
106
|
+
});
|
|
107
|
+
delivered = true;
|
|
108
|
+
},
|
|
109
|
+
onSkip: (_payload, info) => {
|
|
110
|
+
if (info.reason !== 'silent') {
|
|
111
|
+
dc.log(`feishu[${dc.account.accountId}]: command reply skipped (reason=${info.reason})`);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
onError: (err, info) => {
|
|
115
|
+
dc.error(`feishu[${dc.account.accountId}]: command ${info.kind} reply failed: ${String(err)}`);
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
replyOptions: {},
|
|
119
|
+
});
|
|
120
|
+
dc.log(`feishu[${dc.account.accountId}]: system command dispatched (delivered=${delivered})`);
|
|
121
|
+
log.info(`system command dispatched (delivered=${delivered}, elapsed=${(0, lark_ticket_1.ticketElapsed)()}ms)`);
|
|
122
|
+
}
|
|
123
|
+
function isLifecycleSessionCommand(text) {
|
|
124
|
+
if (!text)
|
|
125
|
+
return false;
|
|
126
|
+
const match = text.trim().match(/^\/([^\s@]+)/);
|
|
127
|
+
if (!match)
|
|
128
|
+
return false;
|
|
129
|
+
const command = match[1]?.toLowerCase();
|
|
130
|
+
return command === 'new' || command === 'reset';
|
|
131
|
+
}
|