@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.
Files changed (426) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/bin/openclaw-lark.js +39 -0
  4. package/index.d.ts +36 -0
  5. package/index.js +188 -0
  6. package/openclaw.plugin.json +64 -0
  7. package/package.json +63 -0
  8. package/secret-contract-api.d.ts +15 -0
  9. package/secret-contract-api.js +78 -0
  10. package/skills/feishu-bitable/SKILL.md +248 -0
  11. package/skills/feishu-bitable/references/examples.md +813 -0
  12. package/skills/feishu-bitable/references/field-properties.md +763 -0
  13. package/skills/feishu-bitable/references/record-values.md +911 -0
  14. package/skills/feishu-calendar/SKILL.md +242 -0
  15. package/skills/feishu-channel-rules/SKILL.md +18 -0
  16. package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  17. package/skills/feishu-create-doc/SKILL.md +719 -0
  18. package/skills/feishu-fetch-doc/SKILL.md +93 -0
  19. package/skills/feishu-im-read/SKILL.md +163 -0
  20. package/skills/feishu-task/SKILL.md +340 -0
  21. package/skills/feishu-troubleshoot/SKILL.md +70 -0
  22. package/skills/feishu-update-doc/SKILL.md +285 -0
  23. package/src/card/builder.d.ts +152 -0
  24. package/src/card/builder.js +794 -0
  25. package/src/card/card-error.d.ts +91 -0
  26. package/src/card/card-error.js +206 -0
  27. package/src/card/cardkit.d.ts +90 -0
  28. package/src/card/cardkit.js +203 -0
  29. package/src/card/flush-controller.d.ts +45 -0
  30. package/src/card/flush-controller.js +138 -0
  31. package/src/card/image-resolver.d.ts +45 -0
  32. package/src/card/image-resolver.js +116 -0
  33. package/src/card/markdown-style.d.ts +16 -0
  34. package/src/card/markdown-style.js +106 -0
  35. package/src/card/reasoning-utils.d.ts +14 -0
  36. package/src/card/reasoning-utils.js +64 -0
  37. package/src/card/reply-dispatcher-types.d.ts +132 -0
  38. package/src/card/reply-dispatcher-types.js +61 -0
  39. package/src/card/reply-dispatcher.d.ts +15 -0
  40. package/src/card/reply-dispatcher.js +447 -0
  41. package/src/card/reply-mode.d.ts +41 -0
  42. package/src/card/reply-mode.js +70 -0
  43. package/src/card/streaming-card-controller.d.ts +118 -0
  44. package/src/card/streaming-card-controller.js +1045 -0
  45. package/src/card/tool-use-config.d.ts +26 -0
  46. package/src/card/tool-use-config.js +76 -0
  47. package/src/card/tool-use-display.d.ts +37 -0
  48. package/src/card/tool-use-display.js +476 -0
  49. package/src/card/tool-use-trace-store.d.ts +51 -0
  50. package/src/card/tool-use-trace-store.js +271 -0
  51. package/src/card/unavailable-guard.d.ts +35 -0
  52. package/src/card/unavailable-guard.js +87 -0
  53. package/src/channel/abort-detect.d.ts +47 -0
  54. package/src/channel/abort-detect.js +219 -0
  55. package/src/channel/chat-queue.d.ts +41 -0
  56. package/src/channel/chat-queue.js +68 -0
  57. package/src/channel/config-adapter.d.ts +23 -0
  58. package/src/channel/config-adapter.js +107 -0
  59. package/src/channel/directory.d.ts +57 -0
  60. package/src/channel/directory.js +197 -0
  61. package/src/channel/event-handlers.d.ts +17 -0
  62. package/src/channel/event-handlers.js +380 -0
  63. package/src/channel/interactive-dispatch.d.ts +59 -0
  64. package/src/channel/interactive-dispatch.js +195 -0
  65. package/src/channel/monitor.d.ts +17 -0
  66. package/src/channel/monitor.js +140 -0
  67. package/src/channel/onboarding-config.d.ts +18 -0
  68. package/src/channel/onboarding-config.js +96 -0
  69. package/src/channel/onboarding-migrate.d.ts +25 -0
  70. package/src/channel/onboarding-migrate.js +70 -0
  71. package/src/channel/onboarding.d.ts +12 -0
  72. package/src/channel/onboarding.js +300 -0
  73. package/src/channel/plugin.d.ts +13 -0
  74. package/src/channel/plugin.js +310 -0
  75. package/src/channel/probe.d.ts +14 -0
  76. package/src/channel/probe.js +24 -0
  77. package/src/channel/types.d.ts +37 -0
  78. package/src/channel/types.js +8 -0
  79. package/src/commands/auth.d.ts +21 -0
  80. package/src/commands/auth.js +165 -0
  81. package/src/commands/diagnose.d.ts +69 -0
  82. package/src/commands/diagnose.js +848 -0
  83. package/src/commands/doctor.d.ts +27 -0
  84. package/src/commands/doctor.js +588 -0
  85. package/src/commands/index.d.ts +25 -0
  86. package/src/commands/index.js +219 -0
  87. package/src/commands/locale.d.ts +7 -0
  88. package/src/commands/locale.js +8 -0
  89. package/src/core/accounts.d.ts +51 -0
  90. package/src/core/accounts.js +219 -0
  91. package/src/core/agent-config.d.ts +100 -0
  92. package/src/core/agent-config.js +148 -0
  93. package/src/core/api-error.d.ts +48 -0
  94. package/src/core/api-error.js +117 -0
  95. package/src/core/app-owner-fallback.d.ts +22 -0
  96. package/src/core/app-owner-fallback.js +39 -0
  97. package/src/core/app-scope-checker.d.ts +87 -0
  98. package/src/core/app-scope-checker.js +198 -0
  99. package/src/core/auth-errors.d.ts +144 -0
  100. package/src/core/auth-errors.js +160 -0
  101. package/src/core/card-action-operator.d.ts +33 -0
  102. package/src/core/card-action-operator.js +30 -0
  103. package/src/core/chat-info-cache.d.ts +67 -0
  104. package/src/core/chat-info-cache.js +165 -0
  105. package/src/core/comment-target.d.ts +65 -0
  106. package/src/core/comment-target.js +100 -0
  107. package/src/core/config-schema.d.ts +490 -0
  108. package/src/core/config-schema.js +223 -0
  109. package/src/core/device-flow.d.ts +77 -0
  110. package/src/core/device-flow.js +217 -0
  111. package/src/core/domains.d.ts +18 -0
  112. package/src/core/domains.js +34 -0
  113. package/src/core/feishu-fetch.d.ts +18 -0
  114. package/src/core/feishu-fetch.js +28 -0
  115. package/src/core/footer-config.d.ts +24 -0
  116. package/src/core/footer-config.js +51 -0
  117. package/src/core/lark-client.d.ts +125 -0
  118. package/src/core/lark-client.js +468 -0
  119. package/src/core/lark-logger.d.ts +23 -0
  120. package/src/core/lark-logger.js +160 -0
  121. package/src/core/lark-ticket.d.ts +29 -0
  122. package/src/core/lark-ticket.js +40 -0
  123. package/src/core/message-unavailable.d.ts +53 -0
  124. package/src/core/message-unavailable.js +142 -0
  125. package/src/core/owner-policy.d.ts +32 -0
  126. package/src/core/owner-policy.js +55 -0
  127. package/src/core/permission-url.d.ts +22 -0
  128. package/src/core/permission-url.js +76 -0
  129. package/src/core/raw-request.d.ts +27 -0
  130. package/src/core/raw-request.js +90 -0
  131. package/src/core/runtime-store.d.ts +13 -0
  132. package/src/core/runtime-store.js +29 -0
  133. package/src/core/scope-manager.d.ts +168 -0
  134. package/src/core/scope-manager.js +224 -0
  135. package/src/core/sdk-compat.d.ts +20 -0
  136. package/src/core/sdk-compat.js +28 -0
  137. package/src/core/security-check.d.ts +72 -0
  138. package/src/core/security-check.js +184 -0
  139. package/src/core/shutdown-hooks.d.ts +22 -0
  140. package/src/core/shutdown-hooks.js +61 -0
  141. package/src/core/synthetic-target.d.ts +33 -0
  142. package/src/core/synthetic-target.js +40 -0
  143. package/src/core/targets.d.ts +60 -0
  144. package/src/core/targets.js +173 -0
  145. package/src/core/token-store.d.ts +54 -0
  146. package/src/core/token-store.js +326 -0
  147. package/src/core/tool-client.d.ts +176 -0
  148. package/src/core/tool-client.js +426 -0
  149. package/src/core/tool-scopes.d.ts +156 -0
  150. package/src/core/tool-scopes.js +344 -0
  151. package/src/core/tools-config.d.ts +55 -0
  152. package/src/core/tools-config.js +143 -0
  153. package/src/core/types.d.ts +91 -0
  154. package/src/core/types.js +12 -0
  155. package/src/core/uat-client.d.ts +46 -0
  156. package/src/core/uat-client.js +192 -0
  157. package/src/core/version.d.ts +31 -0
  158. package/src/core/version.js +69 -0
  159. package/src/messaging/converters/audio.d.ts +8 -0
  160. package/src/messaging/converters/audio.js +24 -0
  161. package/src/messaging/converters/calendar.d.ts +13 -0
  162. package/src/messaging/converters/calendar.js +56 -0
  163. package/src/messaging/converters/content-converter-helpers.d.ts +30 -0
  164. package/src/messaging/converters/content-converter-helpers.js +82 -0
  165. package/src/messaging/converters/content-converter.d.ts +24 -0
  166. package/src/messaging/converters/content-converter.js +40 -0
  167. package/src/messaging/converters/file.d.ts +8 -0
  168. package/src/messaging/converters/file.js +24 -0
  169. package/src/messaging/converters/folder.d.ts +8 -0
  170. package/src/messaging/converters/folder.js +24 -0
  171. package/src/messaging/converters/hongbao.d.ts +8 -0
  172. package/src/messaging/converters/hongbao.js +20 -0
  173. package/src/messaging/converters/image.d.ts +8 -0
  174. package/src/messaging/converters/image.js +22 -0
  175. package/src/messaging/converters/index.d.ts +8 -0
  176. package/src/messaging/converters/index.js +53 -0
  177. package/src/messaging/converters/interactive/card-converter.d.ts +76 -0
  178. package/src/messaging/converters/interactive/card-converter.js +1177 -0
  179. package/src/messaging/converters/interactive/card-utils.d.ts +9 -0
  180. package/src/messaging/converters/interactive/card-utils.js +47 -0
  181. package/src/messaging/converters/interactive/index.d.ts +8 -0
  182. package/src/messaging/converters/interactive/index.js +25 -0
  183. package/src/messaging/converters/interactive/legacy.d.ts +11 -0
  184. package/src/messaging/converters/interactive/legacy.js +60 -0
  185. package/src/messaging/converters/interactive/types.d.ts +23 -0
  186. package/src/messaging/converters/interactive/types.js +27 -0
  187. package/src/messaging/converters/location.d.ts +8 -0
  188. package/src/messaging/converters/location.js +23 -0
  189. package/src/messaging/converters/merge-forward.d.ts +32 -0
  190. package/src/messaging/converters/merge-forward.js +235 -0
  191. package/src/messaging/converters/post.d.ts +11 -0
  192. package/src/messaging/converters/post.js +235 -0
  193. package/src/messaging/converters/share.d.ts +9 -0
  194. package/src/messaging/converters/share.js +28 -0
  195. package/src/messaging/converters/sticker.d.ts +8 -0
  196. package/src/messaging/converters/sticker.js +22 -0
  197. package/src/messaging/converters/system.d.ts +12 -0
  198. package/src/messaging/converters/system.js +36 -0
  199. package/src/messaging/converters/text.d.ts +8 -0
  200. package/src/messaging/converters/text.js +18 -0
  201. package/src/messaging/converters/todo.d.ts +8 -0
  202. package/src/messaging/converters/todo.js +45 -0
  203. package/src/messaging/converters/types.d.ts +114 -0
  204. package/src/messaging/converters/types.js +8 -0
  205. package/src/messaging/converters/unknown.d.ts +8 -0
  206. package/src/messaging/converters/unknown.js +20 -0
  207. package/src/messaging/converters/utils.d.ts +22 -0
  208. package/src/messaging/converters/utils.js +57 -0
  209. package/src/messaging/converters/video-chat.d.ts +8 -0
  210. package/src/messaging/converters/video-chat.js +31 -0
  211. package/src/messaging/converters/video.d.ts +8 -0
  212. package/src/messaging/converters/video.js +35 -0
  213. package/src/messaging/converters/vote.d.ts +8 -0
  214. package/src/messaging/converters/vote.js +28 -0
  215. package/src/messaging/inbound/bot-content.d.ts +84 -0
  216. package/src/messaging/inbound/bot-content.js +117 -0
  217. package/src/messaging/inbound/bot-loop-guard.d.ts +48 -0
  218. package/src/messaging/inbound/bot-loop-guard.js +89 -0
  219. package/src/messaging/inbound/comment-context.d.ts +82 -0
  220. package/src/messaging/inbound/comment-context.js +353 -0
  221. package/src/messaging/inbound/comment-handler.d.ts +30 -0
  222. package/src/messaging/inbound/comment-handler.js +269 -0
  223. package/src/messaging/inbound/dedup.d.ts +59 -0
  224. package/src/messaging/inbound/dedup.js +121 -0
  225. package/src/messaging/inbound/dispatch-builders.d.ts +104 -0
  226. package/src/messaging/inbound/dispatch-builders.js +241 -0
  227. package/src/messaging/inbound/dispatch-commands.d.ts +22 -0
  228. package/src/messaging/inbound/dispatch-commands.js +131 -0
  229. package/src/messaging/inbound/dispatch-context.d.ts +67 -0
  230. package/src/messaging/inbound/dispatch-context.js +153 -0
  231. package/src/messaging/inbound/dispatch.d.ts +50 -0
  232. package/src/messaging/inbound/dispatch.js +477 -0
  233. package/src/messaging/inbound/enrich.d.ts +102 -0
  234. package/src/messaging/inbound/enrich.js +236 -0
  235. package/src/messaging/inbound/gate-effects.d.ts +23 -0
  236. package/src/messaging/inbound/gate-effects.js +46 -0
  237. package/src/messaging/inbound/gate.d.ts +91 -0
  238. package/src/messaging/inbound/gate.js +342 -0
  239. package/src/messaging/inbound/handler-registry.d.ts +25 -0
  240. package/src/messaging/inbound/handler-registry.js +23 -0
  241. package/src/messaging/inbound/handler.d.ts +37 -0
  242. package/src/messaging/inbound/handler.js +286 -0
  243. package/src/messaging/inbound/media-resolver.d.ts +32 -0
  244. package/src/messaging/inbound/media-resolver.js +91 -0
  245. package/src/messaging/inbound/mention-registry.d.ts +59 -0
  246. package/src/messaging/inbound/mention-registry.js +115 -0
  247. package/src/messaging/inbound/mention.d.ts +48 -0
  248. package/src/messaging/inbound/mention.js +102 -0
  249. package/src/messaging/inbound/parse-io.d.ts +50 -0
  250. package/src/messaging/inbound/parse-io.js +86 -0
  251. package/src/messaging/inbound/parse.d.ts +28 -0
  252. package/src/messaging/inbound/parse.js +128 -0
  253. package/src/messaging/inbound/permission.d.ts +17 -0
  254. package/src/messaging/inbound/permission.js +44 -0
  255. package/src/messaging/inbound/policy.d.ts +95 -0
  256. package/src/messaging/inbound/policy.js +168 -0
  257. package/src/messaging/inbound/reaction-handler.d.ts +62 -0
  258. package/src/messaging/inbound/reaction-handler.js +259 -0
  259. package/src/messaging/inbound/sentinel-store.d.ts +37 -0
  260. package/src/messaging/inbound/sentinel-store.js +98 -0
  261. package/src/messaging/inbound/synthetic-message.d.ts +26 -0
  262. package/src/messaging/inbound/synthetic-message.js +59 -0
  263. package/src/messaging/inbound/user-name-cache-store.d.ts +62 -0
  264. package/src/messaging/inbound/user-name-cache-store.js +236 -0
  265. package/src/messaging/inbound/user-name-cache.d.ts +78 -0
  266. package/src/messaging/inbound/user-name-cache.js +306 -0
  267. package/src/messaging/inbound/vc-meeting-invited-handler.d.ts +20 -0
  268. package/src/messaging/inbound/vc-meeting-invited-handler.js +236 -0
  269. package/src/messaging/inbound/vc-sender.d.ts +41 -0
  270. package/src/messaging/inbound/vc-sender.js +53 -0
  271. package/src/messaging/outbound/actions.d.ts +16 -0
  272. package/src/messaging/outbound/actions.js +329 -0
  273. package/src/messaging/outbound/bot-peer-context.d.ts +42 -0
  274. package/src/messaging/outbound/bot-peer-context.js +40 -0
  275. package/src/messaging/outbound/chat-manage.d.ts +64 -0
  276. package/src/messaging/outbound/chat-manage.js +117 -0
  277. package/src/messaging/outbound/deliver.d.ts +186 -0
  278. package/src/messaging/outbound/deliver.js +410 -0
  279. package/src/messaging/outbound/fetch.d.ts +12 -0
  280. package/src/messaging/outbound/fetch.js +17 -0
  281. package/src/messaging/outbound/forward.d.ts +26 -0
  282. package/src/messaging/outbound/forward.js +51 -0
  283. package/src/messaging/outbound/media-url-utils.d.ts +29 -0
  284. package/src/messaging/outbound/media-url-utils.js +172 -0
  285. package/src/messaging/outbound/media.d.ts +260 -0
  286. package/src/messaging/outbound/media.js +865 -0
  287. package/src/messaging/outbound/normalize-mentions.d.ts +50 -0
  288. package/src/messaging/outbound/normalize-mentions.js +166 -0
  289. package/src/messaging/outbound/outbound-mention.d.ts +41 -0
  290. package/src/messaging/outbound/outbound-mention.js +112 -0
  291. package/src/messaging/outbound/outbound.d.ts +79 -0
  292. package/src/messaging/outbound/outbound.js +195 -0
  293. package/src/messaging/outbound/reactions.d.ts +124 -0
  294. package/src/messaging/outbound/reactions.js +384 -0
  295. package/src/messaging/outbound/send.d.ts +153 -0
  296. package/src/messaging/outbound/send.js +417 -0
  297. package/src/messaging/outbound/typing.d.ts +60 -0
  298. package/src/messaging/outbound/typing.js +139 -0
  299. package/src/messaging/shared/message-lookup.d.ts +54 -0
  300. package/src/messaging/shared/message-lookup.js +120 -0
  301. package/src/messaging/types.d.ts +316 -0
  302. package/src/messaging/types.js +11 -0
  303. package/src/tools/ask-user-question.d.ts +32 -0
  304. package/src/tools/ask-user-question.js +922 -0
  305. package/src/tools/auto-auth.d.ts +57 -0
  306. package/src/tools/auto-auth.js +904 -0
  307. package/src/tools/helpers.d.ts +264 -0
  308. package/src/tools/helpers.js +389 -0
  309. package/src/tools/mcp/doc/create.d.ts +12 -0
  310. package/src/tools/mcp/doc/create.js +47 -0
  311. package/src/tools/mcp/doc/fetch.d.ts +12 -0
  312. package/src/tools/mcp/doc/fetch.js +39 -0
  313. package/src/tools/mcp/doc/index.d.ts +12 -0
  314. package/src/tools/mcp/doc/index.js +50 -0
  315. package/src/tools/mcp/doc/update.d.ts +12 -0
  316. package/src/tools/mcp/doc/update.js +64 -0
  317. package/src/tools/mcp/shared.d.ts +59 -0
  318. package/src/tools/mcp/shared.js +239 -0
  319. package/src/tools/oapi/bitable/app-table-field.d.ts +16 -0
  320. package/src/tools/oapi/bitable/app-table-field.js +225 -0
  321. package/src/tools/oapi/bitable/app-table-record.d.ts +20 -0
  322. package/src/tools/oapi/bitable/app-table-record.js +439 -0
  323. package/src/tools/oapi/bitable/app-table-view.d.ts +16 -0
  324. package/src/tools/oapi/bitable/app-table-view.js +172 -0
  325. package/src/tools/oapi/bitable/app-table.d.ts +17 -0
  326. package/src/tools/oapi/bitable/app-table.js +195 -0
  327. package/src/tools/oapi/bitable/app.d.ts +18 -0
  328. package/src/tools/oapi/bitable/app.js +189 -0
  329. package/src/tools/oapi/bitable/index.d.ts +9 -0
  330. package/src/tools/oapi/bitable/index.js +17 -0
  331. package/src/tools/oapi/calendar/calendar.d.ts +15 -0
  332. package/src/tools/oapi/calendar/calendar.js +125 -0
  333. package/src/tools/oapi/calendar/event-attendee.d.ts +14 -0
  334. package/src/tools/oapi/calendar/event-attendee.js +161 -0
  335. package/src/tools/oapi/calendar/event.d.ts +16 -0
  336. package/src/tools/oapi/calendar/event.js +712 -0
  337. package/src/tools/oapi/calendar/freebusy.d.ts +13 -0
  338. package/src/tools/oapi/calendar/freebusy.js +114 -0
  339. package/src/tools/oapi/calendar/index.d.ts +8 -0
  340. package/src/tools/oapi/calendar/index.js +15 -0
  341. package/src/tools/oapi/chat/chat.d.ts +16 -0
  342. package/src/tools/oapi/chat/chat.js +127 -0
  343. package/src/tools/oapi/chat/index.d.ts +10 -0
  344. package/src/tools/oapi/chat/index.js +23 -0
  345. package/src/tools/oapi/chat/members.d.ts +11 -0
  346. package/src/tools/oapi/chat/members.js +84 -0
  347. package/src/tools/oapi/common/get-user.d.ts +12 -0
  348. package/src/tools/oapi/common/get-user.js +109 -0
  349. package/src/tools/oapi/common/index.d.ts +6 -0
  350. package/src/tools/oapi/common/index.js +11 -0
  351. package/src/tools/oapi/common/search-user.d.ts +11 -0
  352. package/src/tools/oapi/common/search-user.js +76 -0
  353. package/src/tools/oapi/drive/doc-comments.d.ts +15 -0
  354. package/src/tools/oapi/drive/doc-comments.js +351 -0
  355. package/src/tools/oapi/drive/doc-media.d.ts +19 -0
  356. package/src/tools/oapi/drive/doc-media.js +371 -0
  357. package/src/tools/oapi/drive/file.d.ts +19 -0
  358. package/src/tools/oapi/drive/file.js +519 -0
  359. package/src/tools/oapi/drive/index.d.ts +12 -0
  360. package/src/tools/oapi/drive/index.js +45 -0
  361. package/src/tools/oapi/helpers.d.ts +183 -0
  362. package/src/tools/oapi/helpers.js +385 -0
  363. package/src/tools/oapi/im/format-messages.d.ts +50 -0
  364. package/src/tools/oapi/im/format-messages.js +169 -0
  365. package/src/tools/oapi/im/index.d.ts +10 -0
  366. package/src/tools/oapi/im/index.js +25 -0
  367. package/src/tools/oapi/im/message-read.d.ts +13 -0
  368. package/src/tools/oapi/im/message-read.js +419 -0
  369. package/src/tools/oapi/im/message.d.ts +16 -0
  370. package/src/tools/oapi/im/message.js +152 -0
  371. package/src/tools/oapi/im/resource.d.ts +13 -0
  372. package/src/tools/oapi/im/resource.js +186 -0
  373. package/src/tools/oapi/im/time-utils.d.ts +46 -0
  374. package/src/tools/oapi/im/time-utils.js +212 -0
  375. package/src/tools/oapi/im/user-name-uat.d.ts +26 -0
  376. package/src/tools/oapi/im/user-name-uat.js +143 -0
  377. package/src/tools/oapi/index.d.ts +11 -0
  378. package/src/tools/oapi/index.js +64 -0
  379. package/src/tools/oapi/sdk-types.d.ts +96 -0
  380. package/src/tools/oapi/sdk-types.js +13 -0
  381. package/src/tools/oapi/search/doc-search.d.ts +13 -0
  382. package/src/tools/oapi/search/doc-search.js +194 -0
  383. package/src/tools/oapi/search/index.d.ts +12 -0
  384. package/src/tools/oapi/search/index.js +37 -0
  385. package/src/tools/oapi/sheets/index.d.ts +12 -0
  386. package/src/tools/oapi/sheets/index.js +35 -0
  387. package/src/tools/oapi/sheets/sheet.d.ts +16 -0
  388. package/src/tools/oapi/sheets/sheet.js +688 -0
  389. package/src/tools/oapi/task/attachment.d.ts +18 -0
  390. package/src/tools/oapi/task/attachment.js +107 -0
  391. package/src/tools/oapi/task/comment.d.ts +15 -0
  392. package/src/tools/oapi/task/comment.js +149 -0
  393. package/src/tools/oapi/task/index.d.ts +11 -0
  394. package/src/tools/oapi/task/index.js +21 -0
  395. package/src/tools/oapi/task/section.d.ts +17 -0
  396. package/src/tools/oapi/task/section.js +293 -0
  397. package/src/tools/oapi/task/subtask.d.ts +14 -0
  398. package/src/tools/oapi/task/subtask.js +171 -0
  399. package/src/tools/oapi/task/task.d.ts +19 -0
  400. package/src/tools/oapi/task/task.js +512 -0
  401. package/src/tools/oapi/task/task_agent.d.ts +14 -0
  402. package/src/tools/oapi/task/task_agent.js +108 -0
  403. package/src/tools/oapi/task/tasklist.d.ts +19 -0
  404. package/src/tools/oapi/task/tasklist.js +276 -0
  405. package/src/tools/oapi/wiki/index.d.ts +12 -0
  406. package/src/tools/oapi/wiki/index.js +42 -0
  407. package/src/tools/oapi/wiki/space-node.d.ts +17 -0
  408. package/src/tools/oapi/wiki/space-node.js +233 -0
  409. package/src/tools/oapi/wiki/space.d.ts +15 -0
  410. package/src/tools/oapi/wiki/space.js +133 -0
  411. package/src/tools/oauth-batch-auth.d.ts +11 -0
  412. package/src/tools/oauth-batch-auth.js +150 -0
  413. package/src/tools/oauth-cards.d.ts +39 -0
  414. package/src/tools/oauth-cards.js +324 -0
  415. package/src/tools/oauth.d.ts +47 -0
  416. package/src/tools/oauth.js +592 -0
  417. package/src/tools/onboarding-auth.d.ts +27 -0
  418. package/src/tools/onboarding-auth.js +133 -0
  419. package/src/tools/tat/im/index.d.ts +15 -0
  420. package/src/tools/tat/im/index.js +22 -0
  421. package/src/tools/tat/im/resource.d.ts +15 -0
  422. package/src/tools/tat/im/resource.js +192 -0
  423. package/tsdown.config.d.ts +2 -0
  424. package/tsdown.config.js +25 -0
  425. package/vitest.config.d.ts +2 -0
  426. package/vitest.config.js +12 -0
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Inbound message handling pipeline for the Lark/Feishu channel plugin.
7
+ *
8
+ * Orchestrates a nine-stage pipeline:
9
+ * 1. Account resolution
10
+ * 2. Event parsing → parse.ts (merge_forward expanded in-place)
11
+ * 3. Empty-message guard → early return for text-less, media-less messages
12
+ * 4. Sender enrichment → enrich.ts (lightweight, before gate)
13
+ * 5. Policy gate → gate.ts
14
+ * 6. User name prefetch → enrich.ts (batch cache warm-up)
15
+ * 7. Content resolution → enrich.ts (media / quote, parallel)
16
+ * 8. Command authorization → plugin-sdk/command-auth
17
+ * 9. Agent dispatch → dispatch.ts
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.handleFeishuMessage = handleFeishuMessage;
21
+ const reply_history_1 = require("openclaw/plugin-sdk/reply-history");
22
+ const command_auth_1 = require("openclaw/plugin-sdk/command-auth");
23
+ const allow_from_1 = require("openclaw/plugin-sdk/allow-from");
24
+ const accounts_1 = require("../../core/accounts.js");
25
+ const lark_client_1 = require("../../core/lark-client.js");
26
+ const lark_logger_1 = require("../../core/lark-logger.js");
27
+ const lark_ticket_1 = require("../../core/lark-ticket.js");
28
+ const chat_queue_1 = require("../../channel/chat-queue.js");
29
+ const send_1 = require("../outbound/send.js");
30
+ const parse_1 = require("./parse.js");
31
+ const enrich_1 = require("./enrich.js");
32
+ const gate_1 = require("./gate.js");
33
+ const handler_registry_1 = require("./handler-registry.js");
34
+ const dispatch_1 = require("./dispatch.js");
35
+ const policy_1 = require("./policy.js");
36
+ const mention_registry_1 = require("./mention-registry.js");
37
+ const bot_loop_guard_1 = require("./bot-loop-guard.js");
38
+ const logger = (0, lark_logger_1.larkLogger)('inbound/handler');
39
+ // ---------------------------------------------------------------------------
40
+ // Public: handle inbound message
41
+ // ---------------------------------------------------------------------------
42
+ async function handleFeishuMessage(params) {
43
+ const { cfg, event, botOpenId, runtime, chatHistories, accountId, replyToMessageId, forceMention, skipTyping } = params;
44
+ // 1. Account resolution
45
+ const account = (0, accounts_1.getLarkAccount)(cfg, accountId);
46
+ const accountFeishuCfg = account.config;
47
+ // ★ 多账号配置隔离:构造 account 级别的 ClawdbotConfig
48
+ //
49
+ // 在多账号场景下,每个 account 可以独立配置 groupPolicy / requireMention
50
+ // 等策略。但 SDK 的 resolveGroupPolicy / resolveRequireMention 等函数从
51
+ // cfg.channels.feishu 读取配置,而 cfg 是顶层全局配置,不包含 per-account
52
+ // 的覆盖值。
53
+ //
54
+ // 这里将 cfg.channels.feishu 替换为经过 getLarkAccount() 合并后的
55
+ // accountFeishuCfg(= base config + account override),确保下游所有 SDK 调用
56
+ // 都能正确读取当前 account 的配置。
57
+ const accountScopedCfg = {
58
+ ...cfg,
59
+ channels: { ...cfg.channels, feishu: accountFeishuCfg },
60
+ };
61
+ const log = runtime?.log ?? ((...args) => logger.info(args.map(String).join(' ')));
62
+ const error = runtime?.error ?? ((...args) => logger.error(args.map(String).join(' ')));
63
+ // 2. Parse event → MessageContext (merge_forward expanded in-place)
64
+ let ctx = await (0, parse_1.parseMessageEvent)(event, botOpenId, {
65
+ cfg: accountScopedCfg,
66
+ accountId: account.accountId,
67
+ });
68
+ // Self-echo hard filter — drop messages authored by this very bot before
69
+ // enrichment, gating, or dispatch. Mirrors the channel-layer filter in
70
+ // event-handlers.ts so alternate entrypoints into handleFeishuMessage
71
+ // (synthetic messages, replays, tests) don't bypass it. Skipped when
72
+ // botOpenId is not yet populated (startup race before bot probe resolves);
73
+ // the channel-layer filter and downstream bot-sender gate act as fallback.
74
+ if (botOpenId && ctx.senderId && ctx.senderId === botOpenId) {
75
+ log(`feishu[${account.accountId}]: drop self-echo message ${ctx.messageId}`);
76
+ return;
77
+ }
78
+ // 3. Early reject: skip empty-text messages with no media resources.
79
+ // OpenClaw 2026.4.29 adds a core-side guard for this (##74634), but
80
+ // rejecting here avoids wasting cycles on enrichment, gate, and
81
+ // dispatch for messages that would be silently dropped at the deliver
82
+ // callback anyway.
83
+ // A "bare @" (only a mention, no text/media) is a valid ping in
84
+ // bot-at-bot flows — treat it as an intentional wake-up rather than an
85
+ // empty message. Only drop messages that carry no text, no media, AND
86
+ // no mention at all.
87
+ if (!ctx.content.trim() && ctx.resources.length === 0 && ctx.mentions.length === 0 && !ctx.mentionAll) {
88
+ log(`feishu[${account.accountId}]: empty message ${ctx.messageId} (no text, no media, no mention), skipping`);
89
+ return;
90
+ }
91
+ // 4. Enrich (lightweight): sender name + permission error tracking
92
+ const { ctx: enrichedCtx, permissionError } = await (0, enrich_1.resolveSenderInfo)({
93
+ ctx,
94
+ account,
95
+ log,
96
+ });
97
+ ctx = enrichedCtx;
98
+ // Feed the per-chat name→openId registry that the outbound layer uses to
99
+ // turn "@Name" in LLM output into a real <at user_id="ou_xxx"> element.
100
+ // Both the sender and any @-target observed here are valuable signal —
101
+ // recording them now (before the gate) means we keep learning names even
102
+ // for messages the gate rejects.
103
+ if (ctx.senderId && ctx.senderName) {
104
+ (0, mention_registry_1.recordSender)(ctx.chatId, ctx.senderId, ctx.senderName);
105
+ }
106
+ for (const m of ctx.mentions) {
107
+ if (m.openId && m.name)
108
+ (0, mention_registry_1.recordMention)(ctx.chatId, m.openId, m.name);
109
+ }
110
+ // Bot-loop guard: a human turn re-arms the consecutive bot-turn budget so a
111
+ // fresh human-driven exchange always starts clean (counter checked below,
112
+ // after the gate, only for bot senders).
113
+ if (!ctx.senderIsBot) {
114
+ (0, bot_loop_guard_1.resetBotLoop)(ctx.chatId, ctx.threadId ?? ctx.rootId);
115
+ }
116
+ log(`feishu[${account.accountId}]: received message from ${ctx.senderId} in ${ctx.chatId} (${ctx.chatType})`);
117
+ logger.info(`received from ${ctx.senderId} in ${ctx.chatId} (${ctx.chatType})`);
118
+ const historyLimit = Math.max(0, accountFeishuCfg?.historyLimit ?? accountScopedCfg.messages?.groupChat?.historyLimit ?? reply_history_1.DEFAULT_GROUP_HISTORY_LIMIT);
119
+ // 5. Gate: policy / access-control checks (skipped for synthetic messages)
120
+ const gate = forceMention
121
+ ? { allowed: true }
122
+ : await (0, gate_1.checkMessageGate)({ ctx, accountFeishuCfg, account, accountScopedCfg, log });
123
+ if (!gate.allowed) {
124
+ if (gate.reason === 'no_mention') {
125
+ logger.info(`rejected: no bot mention in group ${ctx.chatId}`);
126
+ }
127
+ // Record history entry if the gate produced one (group no-mention case)
128
+ if (gate.historyEntry && chatHistories) {
129
+ const historyKey = (0, chat_queue_1.threadScopedKey)(ctx.chatId, ctx.threadId);
130
+ (0, reply_history_1.recordPendingHistoryEntryIfEnabled)({
131
+ historyMap: chatHistories,
132
+ historyKey,
133
+ limit: historyLimit,
134
+ entry: gate.historyEntry,
135
+ });
136
+ }
137
+ return;
138
+ }
139
+ // 6. Batch pre-warm user name cache (sender + mentions)
140
+ await (0, enrich_1.prefetchUserNames)({ ctx, account, log });
141
+ // 7. Enrich (heavyweight, after gate — parallel where possible)
142
+ const enrichParams = { ctx, accountScopedCfg, account, log };
143
+ const [mediaResult, quotedContent] = await Promise.all([
144
+ (0, enrich_1.resolveMedia)(enrichParams),
145
+ (0, enrich_1.resolveQuotedContent)(enrichParams),
146
+ ]);
147
+ // 7b. Replace Feishu file-key placeholders in content with local
148
+ // file paths so the SDK can detect images for native vision and
149
+ // the AI receives meaningful file references.
150
+ if (mediaResult.mediaList.length > 0) {
151
+ ctx = {
152
+ ...ctx,
153
+ content: (0, enrich_1.substituteMediaPaths)(ctx.content, mediaResult.mediaList),
154
+ };
155
+ }
156
+ // 8. Compute commandAuthorized via SDK access group command gating
157
+ const core = lark_client_1.LarkClient.runtime;
158
+ const isGroup = ctx.chatType === 'group';
159
+ const dmPolicy = accountFeishuCfg?.dmPolicy ?? 'pairing';
160
+ // Resolve per-group config early — shared by both command authorization
161
+ // and dispatch (step 8).
162
+ const groupConfig = isGroup ? (0, policy_1.resolveFeishuGroupConfig)({ cfg: accountFeishuCfg, groupId: ctx.chatId }) : undefined;
163
+ const defaultGroupConfig = isGroup ? accountFeishuCfg?.groups?.['*'] : undefined;
164
+ // Build the sender allowlist for command authorization in group context.
165
+ // Excludes legacy oc_xxx chat-id entries (group admission, not sender identity).
166
+ //
167
+ // When the explicit group sender policy is "open", pass ["*"] to align
168
+ // command authorization with chat access (if you can chat, you can run
169
+ // commands). When no policy is configured (undefined fallback), default to
170
+ // allowlist behaviour — only users in accountFeishuCfg.allowFrom (owner list) or
171
+ // an explicit groupAllowFrom/per-group allowFrom can run commands.
172
+ const configuredGroupAllowFrom = (() => {
173
+ if (!isGroup)
174
+ return undefined;
175
+ // Exclude legacy oc_xxx chat-id entries from groupAllowFrom (sender filter only).
176
+ const { senderAllowFrom } = (0, policy_1.splitLegacyGroupAllowFrom)(accountFeishuCfg?.groupAllowFrom ?? []);
177
+ const senderGroupAllowFrom = senderAllowFrom;
178
+ const perGroupAllowFrom = (groupConfig?.allowFrom ?? []).map(String);
179
+ const defaultSenderAllowFrom = !groupConfig && defaultGroupConfig?.allowFrom ? defaultGroupConfig.allowFrom.map(String) : [];
180
+ const combined = [...senderGroupAllowFrom, ...perGroupAllowFrom, ...defaultSenderAllowFrom];
181
+ if (combined.length > 0)
182
+ return combined;
183
+ // No allowFrom list configured — check if sender policy is explicitly "open".
184
+ // Do NOT fall back to "open" as a default: unset policy → allowlist behaviour.
185
+ const explicitSenderPolicy = groupConfig?.groupPolicy ?? defaultGroupConfig?.groupPolicy ?? accountFeishuCfg?.groupPolicy;
186
+ return explicitSenderPolicy === 'open' ? ['*'] : [];
187
+ })();
188
+ const { commandAuthorized } = await (0, command_auth_1.resolveSenderCommandAuthorization)({
189
+ rawBody: ctx.content,
190
+ cfg: accountScopedCfg,
191
+ isGroup,
192
+ dmPolicy,
193
+ configuredAllowFrom: (accountFeishuCfg?.allowFrom ?? []).map(String),
194
+ configuredGroupAllowFrom,
195
+ senderId: ctx.senderId,
196
+ isSenderAllowed: (senderId, allowFrom) => (0, allow_from_1.isNormalizedSenderAllowed)({ senderId, allowFrom }),
197
+ readAllowFromStore: () => (0, gate_1.readFeishuAllowFromStore)(account.accountId),
198
+ shouldComputeCommandAuthorized: core.channel.commands.shouldComputeCommandAuthorized,
199
+ resolveCommandAuthorizedFromAuthorizers: core.channel.commands.resolveCommandAuthorizedFromAuthorizers,
200
+ });
201
+ // Bot-loop guard: cap consecutive bot↔bot turns so a runaway debate stops
202
+ // itself. Only bot senders count; a human turn already reset the counter
203
+ // above. Checked after the gate so only messages we would actually act on
204
+ // are counted.
205
+ if (ctx.senderIsBot) {
206
+ // Use root_id when thread_id is absent: in topic groups, reply events
207
+ // often carry only root_id (thread_id is inferred later in dispatch), and
208
+ // the queue/dispatch key uses the same fallback. Without it, all topics in
209
+ // a chat share one chat-level counter and one topic's cutoff suppresses
210
+ // the others.
211
+ const verdict = (0, bot_loop_guard_1.noteBotTurnAndCheck)(ctx.chatId, ctx.threadId ?? ctx.rootId);
212
+ if (!verdict.allowed) {
213
+ log(`feishu[${account.accountId}]: bot-loop guard tripped ` +
214
+ `(${verdict.count}/${verdict.limit} consecutive bot turns) in ${ctx.chatId}, suppressing reply`);
215
+ // Surface the cutoff to humans ONCE, on the first over-cap turn, so the
216
+ // conversation doesn't just go silent. Plain text with no @ — so it
217
+ // neither wakes the peer bot (allowBots='mentions') nor extends the
218
+ // loop. A human message resets the counter and re-arms auto-reply.
219
+ //
220
+ // Deliver it where the debate actually is: reply to the triggering
221
+ // message. Thread the notice only when the bot↔bot reply body would also
222
+ // be threaded — i.e. mirror resolveFeishuReplyRouting's effective
223
+ // replyInThread for a bot turn: a real thread_id is present AND threading
224
+ // is opted in (threadSession or replyInThread). Crucially we must NOT
225
+ // treat root_id as a thread here: a plain bot↔bot quote-reply chain in a
226
+ // normal group carries root_id but no thread_id, and reply_in_thread=true
227
+ // on such a message makes Feishu mint a brand-new topic for just the
228
+ // notice — pulling it into a thread the debate itself was never in.
229
+ if (verdict.count === verdict.limit + 1) {
230
+ try {
231
+ // Localized via i18nTexts so the viewer's Feishu client renders the
232
+ // notice in its own language (same mechanism as /help, /doctor).
233
+ // replyInThread precedence matches dispatch (group > default > account).
234
+ const replyInThreadCfg = groupConfig?.replyInThread ??
235
+ defaultGroupConfig?.replyInThread ??
236
+ account.config?.replyInThread;
237
+ const inThread = Boolean(ctx.threadId) &&
238
+ (account.config?.threadSession === true || replyInThreadCfg === true);
239
+ await (0, send_1.sendMessageFeishu)({
240
+ cfg: accountScopedCfg,
241
+ to: ctx.chatId,
242
+ text: `⏸️ 已达连续 ${verdict.limit} 轮自动对话上限,已暂停自动回复。需要继续请在群里发一条消息。`,
243
+ i18nTexts: {
244
+ zh_cn: `⏸️ 已达连续 ${verdict.limit} 轮自动对话上限,已暂停自动回复。需要继续请在群里发一条消息。`,
245
+ en_us: `⏸️ Reached the limit of ${verdict.limit} consecutive automated turns; auto-replies are paused. Send a message in the chat to continue.`,
246
+ },
247
+ accountId: account.accountId,
248
+ replyToMessageId: replyToMessageId ?? ctx.messageId,
249
+ replyInThread: inThread,
250
+ threadId: inThread ? ctx.threadId : undefined,
251
+ });
252
+ }
253
+ catch (err) {
254
+ log(`feishu[${account.accountId}]: failed to send loop-guard notice: ${String(err)}`);
255
+ }
256
+ }
257
+ return;
258
+ }
259
+ }
260
+ // 9. Dispatch to agent
261
+ // groupConfig and defaultGroupConfig are already resolved above.
262
+ try {
263
+ await (0, dispatch_1.dispatchToAgent)({
264
+ ctx,
265
+ permissionError,
266
+ mediaPayload: mediaResult.payload,
267
+ quotedContent,
268
+ account,
269
+ accountScopedCfg,
270
+ runtime,
271
+ chatHistories,
272
+ historyLimit,
273
+ replyToMessageId,
274
+ commandAuthorized,
275
+ groupConfig,
276
+ defaultGroupConfig,
277
+ skipTyping,
278
+ botOpenId,
279
+ });
280
+ }
281
+ catch (err) {
282
+ error(`feishu[${account.accountId}]: failed to dispatch message: ${String(err)}`);
283
+ logger.error(`dispatch failed: ${String(err)} (elapsed=${(0, lark_ticket_1.ticketElapsed)()}ms)`);
284
+ }
285
+ }
286
+ (0, handler_registry_1.injectInboundHandler)(handleFeishuMessage);
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Media resolution and payload building for inbound Feishu messages.
6
+ *
7
+ * Downloads media files based on ResourceDescriptors extracted during
8
+ * the content converter phase, and builds the payload object spread
9
+ * into the agent envelope.
10
+ */
11
+ import type { ClawdbotConfig } from 'openclaw/plugin-sdk';
12
+ import type { FeishuMediaInfo, ResourceDescriptor } from '../types';
13
+ /**
14
+ * Download media files based on pre-extracted ResourceDescriptors from
15
+ * the converter phase.
16
+ */
17
+ export declare function downloadResources(params: {
18
+ cfg: ClawdbotConfig;
19
+ messageId: string;
20
+ resources: ResourceDescriptor[];
21
+ maxBytes: number;
22
+ log?: (msg: string) => void;
23
+ accountId?: string;
24
+ }): Promise<FeishuMediaInfo[]>;
25
+ export declare function buildFeishuMediaPayload(mediaList: FeishuMediaInfo[]): {
26
+ MediaPath?: string;
27
+ MediaType?: string;
28
+ MediaUrl?: string;
29
+ MediaPaths?: string[];
30
+ MediaUrls?: string[];
31
+ MediaTypes?: string[];
32
+ };
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Media resolution and payload building for inbound Feishu messages.
7
+ *
8
+ * Downloads media files based on ResourceDescriptors extracted during
9
+ * the content converter phase, and builds the payload object spread
10
+ * into the agent envelope.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.downloadResources = downloadResources;
14
+ exports.buildFeishuMediaPayload = buildFeishuMediaPayload;
15
+ const lark_client_1 = require("../../core/lark-client.js");
16
+ const media_1 = require("../outbound/media.js");
17
+ // ---------------------------------------------------------------------------
18
+ // Resource-descriptor-based download
19
+ // ---------------------------------------------------------------------------
20
+ /**
21
+ * Download media files based on pre-extracted ResourceDescriptors from
22
+ * the converter phase.
23
+ */
24
+ async function downloadResources(params) {
25
+ const { cfg, messageId, resources, maxBytes, log, accountId } = params;
26
+ if (resources.length === 0)
27
+ return [];
28
+ const out = [];
29
+ const core = lark_client_1.LarkClient.runtime;
30
+ for (const res of resources) {
31
+ try {
32
+ const resourceType = res.type === 'image' ? 'image' : 'file';
33
+ const result = await (0, media_1.downloadMessageResourceFeishu)({
34
+ cfg,
35
+ messageId,
36
+ fileKey: res.fileKey,
37
+ type: resourceType,
38
+ accountId,
39
+ });
40
+ let contentType = result.contentType;
41
+ if (!contentType) {
42
+ contentType = await core.media.detectMime({ buffer: result.buffer });
43
+ }
44
+ const fileName = result.fileName || res.fileName;
45
+ const saved = await core.channel.media.saveMediaBuffer(result.buffer, contentType, 'inbound', maxBytes, fileName);
46
+ const placeholder = inferPlaceholderFromType(res.type);
47
+ out.push({
48
+ path: saved.path,
49
+ contentType: saved.contentType,
50
+ placeholder,
51
+ fileKey: res.fileKey,
52
+ resourceType: res.type,
53
+ });
54
+ log?.(`feishu: downloaded ${res.type} resource ${res.fileKey}, saved to ${saved.path}`);
55
+ }
56
+ catch (err) {
57
+ log?.(`feishu: failed to download ${res.type} resource ${res.fileKey}: ${String(err)}`);
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+ function inferPlaceholderFromType(type) {
63
+ switch (type) {
64
+ case 'image':
65
+ return '<media:image>';
66
+ case 'file':
67
+ return '<media:document>';
68
+ case 'audio':
69
+ return '<media:audio>';
70
+ case 'video':
71
+ return '<media:video>';
72
+ case 'sticker':
73
+ return '<media:sticker>';
74
+ }
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Media payload builder
78
+ // ---------------------------------------------------------------------------
79
+ function buildFeishuMediaPayload(mediaList) {
80
+ const first = mediaList[0];
81
+ const mediaPaths = mediaList.map((m) => m.path);
82
+ const mediaTypes = mediaList.map((m) => m.contentType).filter(Boolean);
83
+ return {
84
+ MediaPath: first?.path,
85
+ MediaType: first?.contentType,
86
+ MediaUrl: first?.path,
87
+ MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined,
88
+ MediaUrls: mediaPaths.length > 0 ? mediaPaths : undefined,
89
+ MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined,
90
+ };
91
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Per-chat name → openId registry.
6
+ *
7
+ * Backs `normalizeOutboundMentions` / `ensureMention` on the outbound side:
8
+ * when the LLM writes "@Alice" in a reply, the outbound layer needs to look
9
+ * up which `ou_xxx` "Alice" maps to in this specific chat to produce a real
10
+ * Feishu `<at user_id="ou_xxx">` element that actually triggers delivery.
11
+ *
12
+ * Two ingestion paths feed the registry:
13
+ * 1. `recordSender(chatId, openId, name)` — every inbound message's sender
14
+ * 2. `recordMention(chatId, openId, name)` — every @-target in inbound text
15
+ *
16
+ * Entries decay with TTL so stale display names (renames, departed members)
17
+ * don't pollute future lookups indefinitely. The registry is process-local;
18
+ * it's a best-effort cache, not a source of truth, and a cache miss simply
19
+ * means the outbound mention falls back to plain "@Name" text (which Feishu
20
+ * won't deliver as a notification — that's a graceful degradation, not a
21
+ * functional failure).
22
+ */
23
+ /**
24
+ * Record an @-mention target observed in inbound text.
25
+ *
26
+ * The name passed here should be the human-readable display name as parsed
27
+ * out of the Feishu mention element, not the raw `@user_xxx` placeholder.
28
+ */
29
+ export declare function recordMention(chatId: string, openId: string, name: string): void;
30
+ /**
31
+ * Record the sender of an inbound message.
32
+ *
33
+ * Even when the sender is never @-mentioned, recording lets the outbound
34
+ * layer @ them back by name. In bot↔bot flows this is the only way the
35
+ * receiving bot learns the peer bot's name → openId mapping.
36
+ */
37
+ export declare function recordSender(chatId: string, openId: string, name: string): void;
38
+ /**
39
+ * Look up the openId for a name in a given chat. Returns `undefined` when
40
+ * the name has never been seen, or when the entry has aged out past TTL.
41
+ *
42
+ * Caller may pass either the raw mention spelling ("alice", "Alice", "
43
+ * Alice ") — name comparison is case-insensitive and ignores surrounding
44
+ * whitespace.
45
+ */
46
+ export declare function lookupByName(chatId: string, name: string, opts?: {
47
+ ttlMs?: number;
48
+ }): string | undefined;
49
+ /**
50
+ * Reset the registry. Intended for tests; production code should rely on
51
+ * TTL expiry instead.
52
+ */
53
+ export declare function resetMentionRegistry(): void;
54
+ /**
55
+ * Drop all entries older than `ttlMs` across every chat. Optional helper
56
+ * for callers that want a deterministic cleanup tick — `lookupByName`
57
+ * already does lazy eviction on read.
58
+ */
59
+ export declare function purgeStaleEntries(ttlMs?: number): void;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Per-chat name → openId registry.
7
+ *
8
+ * Backs `normalizeOutboundMentions` / `ensureMention` on the outbound side:
9
+ * when the LLM writes "@Alice" in a reply, the outbound layer needs to look
10
+ * up which `ou_xxx` "Alice" maps to in this specific chat to produce a real
11
+ * Feishu `<at user_id="ou_xxx">` element that actually triggers delivery.
12
+ *
13
+ * Two ingestion paths feed the registry:
14
+ * 1. `recordSender(chatId, openId, name)` — every inbound message's sender
15
+ * 2. `recordMention(chatId, openId, name)` — every @-target in inbound text
16
+ *
17
+ * Entries decay with TTL so stale display names (renames, departed members)
18
+ * don't pollute future lookups indefinitely. The registry is process-local;
19
+ * it's a best-effort cache, not a source of truth, and a cache miss simply
20
+ * means the outbound mention falls back to plain "@Name" text (which Feishu
21
+ * won't deliver as a notification — that's a graceful degradation, not a
22
+ * functional failure).
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.recordMention = recordMention;
26
+ exports.recordSender = recordSender;
27
+ exports.lookupByName = lookupByName;
28
+ exports.resetMentionRegistry = resetMentionRegistry;
29
+ exports.purgeStaleEntries = purgeStaleEntries;
30
+ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000; // 24h
31
+ // chatId -> lowercased name -> Entry
32
+ const registry = new Map();
33
+ function normalizeName(name) {
34
+ return name.trim().toLowerCase();
35
+ }
36
+ function getChatMap(chatId) {
37
+ let chatMap = registry.get(chatId);
38
+ if (!chatMap) {
39
+ chatMap = new Map();
40
+ registry.set(chatId, chatMap);
41
+ }
42
+ return chatMap;
43
+ }
44
+ function recordEntry(chatId, openId, name) {
45
+ if (!chatId || !openId || !name)
46
+ return;
47
+ const key = normalizeName(name);
48
+ if (!key)
49
+ return;
50
+ getChatMap(chatId).set(key, { openId, name, recordedAt: Date.now() });
51
+ }
52
+ /**
53
+ * Record an @-mention target observed in inbound text.
54
+ *
55
+ * The name passed here should be the human-readable display name as parsed
56
+ * out of the Feishu mention element, not the raw `@user_xxx` placeholder.
57
+ */
58
+ function recordMention(chatId, openId, name) {
59
+ recordEntry(chatId, openId, name);
60
+ }
61
+ /**
62
+ * Record the sender of an inbound message.
63
+ *
64
+ * Even when the sender is never @-mentioned, recording lets the outbound
65
+ * layer @ them back by name. In bot↔bot flows this is the only way the
66
+ * receiving bot learns the peer bot's name → openId mapping.
67
+ */
68
+ function recordSender(chatId, openId, name) {
69
+ recordEntry(chatId, openId, name);
70
+ }
71
+ /**
72
+ * Look up the openId for a name in a given chat. Returns `undefined` when
73
+ * the name has never been seen, or when the entry has aged out past TTL.
74
+ *
75
+ * Caller may pass either the raw mention spelling ("alice", "Alice", "
76
+ * Alice ") — name comparison is case-insensitive and ignores surrounding
77
+ * whitespace.
78
+ */
79
+ function lookupByName(chatId, name, opts = {}) {
80
+ const chatMap = registry.get(chatId);
81
+ if (!chatMap)
82
+ return undefined;
83
+ const entry = chatMap.get(normalizeName(name));
84
+ if (!entry)
85
+ return undefined;
86
+ const ttl = opts.ttlMs ?? DEFAULT_TTL_MS;
87
+ if (Date.now() - entry.recordedAt > ttl) {
88
+ chatMap.delete(normalizeName(name));
89
+ return undefined;
90
+ }
91
+ return entry.openId;
92
+ }
93
+ /**
94
+ * Reset the registry. Intended for tests; production code should rely on
95
+ * TTL expiry instead.
96
+ */
97
+ function resetMentionRegistry() {
98
+ registry.clear();
99
+ }
100
+ /**
101
+ * Drop all entries older than `ttlMs` across every chat. Optional helper
102
+ * for callers that want a deterministic cleanup tick — `lookupByName`
103
+ * already does lazy eviction on read.
104
+ */
105
+ function purgeStaleEntries(ttlMs = DEFAULT_TTL_MS) {
106
+ const cutoff = Date.now() - ttlMs;
107
+ for (const [chatId, chatMap] of registry) {
108
+ for (const [key, entry] of chatMap) {
109
+ if (entry.recordedAt < cutoff)
110
+ chatMap.delete(key);
111
+ }
112
+ if (chatMap.size === 0)
113
+ registry.delete(chatId);
114
+ }
115
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * @mention utilities for the Lark/Feishu channel plugin.
6
+ *
7
+ * All logic is based on `MentionInfo[]` from `MessageContext.mentions`.
8
+ * Provides:
9
+ * - Derive helpers: `mentionedBot()`, `nonBotMentions()`
10
+ * - Format helpers for outbound text and card messages.
11
+ */
12
+ import type { MentionInfo, MessageContext } from '../types';
13
+ export type { MentionInfo } from '../types';
14
+ /**
15
+ * Detect whether a raw mention entry represents @all / @所有人.
16
+ *
17
+ * Feishu @all mentions have `key: "@_all"` and empty ID fields.
18
+ * We match on `key` as the primary signal (most stable across locales).
19
+ */
20
+ export declare function isMentionAll(mention: {
21
+ key: string;
22
+ }): boolean;
23
+ /** Whether the receiving bot itself was @-mentioned. */
24
+ export declare function mentionedBot(ctx: MessageContext): boolean;
25
+ /** All mentions excluding the receiving bot itself. */
26
+ export declare function nonBotMentions(ctx: MessageContext): MentionInfo[];
27
+ /**
28
+ * Remove all @mention placeholder keys from the message text.
29
+ */
30
+ export declare function extractMessageBody(text: string, allMentionKeys: string[]): string;
31
+ /**
32
+ * Format a mention for a Feishu text / post message.
33
+ * @returns e.g. `<at user_id="ou_xxx">Alice</at>`
34
+ */
35
+ export declare function formatMentionForText(target: MentionInfo): string;
36
+ /** Format an @everyone mention for text / post. */
37
+ export declare function formatMentionAllForText(): string;
38
+ /**
39
+ * Format a mention for a Feishu Interactive Card.
40
+ * @returns e.g. `<at id=ou_xxx></at>`
41
+ */
42
+ export declare function formatMentionForCard(target: MentionInfo): string;
43
+ /** Format an @everyone mention for card. */
44
+ export declare function formatMentionAllForCard(): string;
45
+ /** Prepend @mention tags (text format) to a message body. */
46
+ export declare function buildMentionedMessage(targets: MentionInfo[], message: string): string;
47
+ /** Prepend @mention tags (card format) to card markdown content. */
48
+ export declare function buildMentionedCardContent(targets: MentionInfo[], message: string): string;