@lark-project/openclaw-lark-project 2026.3.131

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 (368) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/README.zh.md +80 -0
  4. package/dist/index.js +172 -0
  5. package/dist/index.js.map +7 -0
  6. package/dist/skills/feishu-bitable/SKILL.md +248 -0
  7. package/dist/skills/feishu-bitable/references/examples.md +813 -0
  8. package/dist/skills/feishu-bitable/references/field-properties.md +763 -0
  9. package/dist/skills/feishu-bitable/references/record-values.md +911 -0
  10. package/dist/skills/feishu-calendar/SKILL.md +244 -0
  11. package/dist/skills/feishu-channel-rules/SKILL.md +18 -0
  12. package/dist/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  13. package/dist/skills/feishu-create-doc/SKILL.md +719 -0
  14. package/dist/skills/feishu-fetch-doc/SKILL.md +93 -0
  15. package/dist/skills/feishu-im-read/SKILL.md +163 -0
  16. package/dist/skills/feishu-project/SKILL.md +122 -0
  17. package/dist/skills/feishu-task/SKILL.md +293 -0
  18. package/dist/skills/feishu-troubleshoot/SKILL.md +70 -0
  19. package/dist/skills/feishu-update-doc/SKILL.md +285 -0
  20. package/dist/src/card/builder.js +293 -0
  21. package/dist/src/card/builder.js.map +7 -0
  22. package/dist/src/card/cardkit.js +126 -0
  23. package/dist/src/card/cardkit.js.map +7 -0
  24. package/dist/src/card/flush-controller.js +107 -0
  25. package/dist/src/card/flush-controller.js.map +7 -0
  26. package/dist/src/card/markdown-style.js +57 -0
  27. package/dist/src/card/markdown-style.js.map +7 -0
  28. package/dist/src/card/reply-dispatcher-types.js +39 -0
  29. package/dist/src/card/reply-dispatcher-types.js.map +7 -0
  30. package/dist/src/card/reply-dispatcher.js +245 -0
  31. package/dist/src/card/reply-dispatcher.js.map +7 -0
  32. package/dist/src/card/reply-mode.js +29 -0
  33. package/dist/src/card/reply-mode.js.map +7 -0
  34. package/dist/src/card/streaming-card-controller.js +653 -0
  35. package/dist/src/card/streaming-card-controller.js.map +7 -0
  36. package/dist/src/card/unavailable-guard.js +76 -0
  37. package/dist/src/card/unavailable-guard.js.map +7 -0
  38. package/dist/src/channel/abort-detect.js +79 -0
  39. package/dist/src/channel/abort-detect.js.map +7 -0
  40. package/dist/src/channel/chat-queue.js +50 -0
  41. package/dist/src/channel/chat-queue.js.map +7 -0
  42. package/dist/src/channel/config-adapter.js +89 -0
  43. package/dist/src/channel/config-adapter.js.map +7 -0
  44. package/dist/src/channel/directory.js +133 -0
  45. package/dist/src/channel/directory.js.map +7 -0
  46. package/dist/src/channel/event-handlers.js +175 -0
  47. package/dist/src/channel/event-handlers.js.map +7 -0
  48. package/dist/src/channel/monitor.js +108 -0
  49. package/dist/src/channel/monitor.js.map +7 -0
  50. package/dist/src/channel/onboarding-config.js +76 -0
  51. package/dist/src/channel/onboarding-config.js.map +7 -0
  52. package/dist/src/channel/onboarding-migrate.js +55 -0
  53. package/dist/src/channel/onboarding-migrate.js.map +7 -0
  54. package/dist/src/channel/onboarding.js +285 -0
  55. package/dist/src/channel/onboarding.js.map +7 -0
  56. package/dist/src/channel/plugin.js +260 -0
  57. package/dist/src/channel/plugin.js.map +7 -0
  58. package/dist/src/channel/probe.js +14 -0
  59. package/dist/src/channel/probe.js.map +7 -0
  60. package/dist/src/channel/types.js +1 -0
  61. package/dist/src/channel/types.js.map +7 -0
  62. package/dist/src/commands/auth.js +73 -0
  63. package/dist/src/commands/auth.js.map +7 -0
  64. package/dist/src/commands/diagnose.js +658 -0
  65. package/dist/src/commands/diagnose.js.map +7 -0
  66. package/dist/src/commands/doctor.js +327 -0
  67. package/dist/src/commands/doctor.js.map +7 -0
  68. package/dist/src/commands/index.js +124 -0
  69. package/dist/src/commands/index.js.map +7 -0
  70. package/dist/src/core/accounts.js +129 -0
  71. package/dist/src/core/accounts.js.map +7 -0
  72. package/dist/src/core/agent-config.js +60 -0
  73. package/dist/src/core/agent-config.js.map +7 -0
  74. package/dist/src/core/api-error.js +55 -0
  75. package/dist/src/core/api-error.js.map +7 -0
  76. package/dist/src/core/app-owner-fallback.js +17 -0
  77. package/dist/src/core/app-owner-fallback.js.map +7 -0
  78. package/dist/src/core/app-scope-checker.js +95 -0
  79. package/dist/src/core/app-scope-checker.js.map +7 -0
  80. package/dist/src/core/auth-errors.js +120 -0
  81. package/dist/src/core/auth-errors.js.map +7 -0
  82. package/dist/src/core/chat-info-cache.js +102 -0
  83. package/dist/src/core/chat-info-cache.js.map +7 -0
  84. package/dist/src/core/config-schema.js +150 -0
  85. package/dist/src/core/config-schema.js.map +7 -0
  86. package/dist/src/core/device-flow.js +174 -0
  87. package/dist/src/core/device-flow.js.map +7 -0
  88. package/dist/src/core/feishu-fetch.js +12 -0
  89. package/dist/src/core/feishu-fetch.js.map +7 -0
  90. package/dist/src/core/footer-config.js +16 -0
  91. package/dist/src/core/footer-config.js.map +7 -0
  92. package/dist/src/core/lark-client.js +322 -0
  93. package/dist/src/core/lark-client.js.map +7 -0
  94. package/dist/src/core/lark-logger.js +92 -0
  95. package/dist/src/core/lark-logger.js.map +7 -0
  96. package/dist/src/core/lark-ticket.js +18 -0
  97. package/dist/src/core/lark-ticket.js.map +7 -0
  98. package/dist/src/core/message-unavailable.js +119 -0
  99. package/dist/src/core/message-unavailable.js.map +7 -0
  100. package/dist/src/core/owner-policy.js +25 -0
  101. package/dist/src/core/owner-policy.js.map +7 -0
  102. package/dist/src/core/permission-url.js +37 -0
  103. package/dist/src/core/permission-url.js.map +7 -0
  104. package/dist/src/core/project-auth.js +177 -0
  105. package/dist/src/core/project-auth.js.map +7 -0
  106. package/dist/src/core/project-oauth-flow.js +124 -0
  107. package/dist/src/core/project-oauth-flow.js.map +7 -0
  108. package/dist/src/core/project-token-store.js +172 -0
  109. package/dist/src/core/project-token-store.js.map +7 -0
  110. package/dist/src/core/raw-request.js +45 -0
  111. package/dist/src/core/raw-request.js.map +7 -0
  112. package/dist/src/core/scope-manager.js +62 -0
  113. package/dist/src/core/scope-manager.js.map +7 -0
  114. package/dist/src/core/security-check.js +118 -0
  115. package/dist/src/core/security-check.js.map +7 -0
  116. package/dist/src/core/shutdown-hooks.js +37 -0
  117. package/dist/src/core/shutdown-hooks.js.map +7 -0
  118. package/dist/src/core/targets.js +55 -0
  119. package/dist/src/core/targets.js.map +7 -0
  120. package/dist/src/core/token-store.js +215 -0
  121. package/dist/src/core/token-store.js.map +7 -0
  122. package/dist/src/core/tool-client.js +335 -0
  123. package/dist/src/core/tool-client.js.map +7 -0
  124. package/dist/src/core/tool-scopes.js +207 -0
  125. package/dist/src/core/tool-scopes.js.map +7 -0
  126. package/dist/src/core/tools-config.js +57 -0
  127. package/dist/src/core/tools-config.js.map +7 -0
  128. package/dist/src/core/types.js +1 -0
  129. package/dist/src/core/types.js.map +7 -0
  130. package/dist/src/core/uat-client.js +124 -0
  131. package/dist/src/core/uat-client.js.map +7 -0
  132. package/dist/src/core/version.js +27 -0
  133. package/dist/src/core/version.js.map +7 -0
  134. package/dist/src/messaging/converters/audio.js +19 -0
  135. package/dist/src/messaging/converters/audio.js.map +7 -0
  136. package/dist/src/messaging/converters/calendar.js +46 -0
  137. package/dist/src/messaging/converters/calendar.js.map +7 -0
  138. package/dist/src/messaging/converters/content-converter.js +61 -0
  139. package/dist/src/messaging/converters/content-converter.js.map +7 -0
  140. package/dist/src/messaging/converters/file.js +18 -0
  141. package/dist/src/messaging/converters/file.js.map +7 -0
  142. package/dist/src/messaging/converters/folder.js +18 -0
  143. package/dist/src/messaging/converters/folder.js.map +7 -0
  144. package/dist/src/messaging/converters/hongbao.js +14 -0
  145. package/dist/src/messaging/converters/hongbao.js.map +7 -0
  146. package/dist/src/messaging/converters/image.js +16 -0
  147. package/dist/src/messaging/converters/image.js.map +7 -0
  148. package/dist/src/messaging/converters/index.js +48 -0
  149. package/dist/src/messaging/converters/index.js.map +7 -0
  150. package/dist/src/messaging/converters/interactive/card-converter.js +1040 -0
  151. package/dist/src/messaging/converters/interactive/card-converter.js.map +7 -0
  152. package/dist/src/messaging/converters/interactive/card-utils.js +36 -0
  153. package/dist/src/messaging/converters/interactive/card-utils.js.map +7 -0
  154. package/dist/src/messaging/converters/interactive/index.js +19 -0
  155. package/dist/src/messaging/converters/interactive/index.js.map +7 -0
  156. package/dist/src/messaging/converters/interactive/legacy.js +53 -0
  157. package/dist/src/messaging/converters/interactive/legacy.js.map +7 -0
  158. package/dist/src/messaging/converters/interactive/types.js +23 -0
  159. package/dist/src/messaging/converters/interactive/types.js.map +7 -0
  160. package/dist/src/messaging/converters/location.js +17 -0
  161. package/dist/src/messaging/converters/location.js.map +7 -0
  162. package/dist/src/messaging/converters/merge-forward.js +143 -0
  163. package/dist/src/messaging/converters/merge-forward.js.map +7 -0
  164. package/dist/src/messaging/converters/post.js +113 -0
  165. package/dist/src/messaging/converters/post.js.map +7 -0
  166. package/dist/src/messaging/converters/share.js +22 -0
  167. package/dist/src/messaging/converters/share.js.map +7 -0
  168. package/dist/src/messaging/converters/sticker.js +16 -0
  169. package/dist/src/messaging/converters/sticker.js.map +7 -0
  170. package/dist/src/messaging/converters/system.js +25 -0
  171. package/dist/src/messaging/converters/system.js.map +7 -0
  172. package/dist/src/messaging/converters/text.js +12 -0
  173. package/dist/src/messaging/converters/text.js.map +7 -0
  174. package/dist/src/messaging/converters/todo.js +37 -0
  175. package/dist/src/messaging/converters/todo.js.map +7 -0
  176. package/dist/src/messaging/converters/types.js +1 -0
  177. package/dist/src/messaging/converters/types.js.map +7 -0
  178. package/dist/src/messaging/converters/unknown.js +13 -0
  179. package/dist/src/messaging/converters/unknown.js.map +7 -0
  180. package/dist/src/messaging/converters/utils.js +35 -0
  181. package/dist/src/messaging/converters/utils.js.map +7 -0
  182. package/dist/src/messaging/converters/video-chat.js +21 -0
  183. package/dist/src/messaging/converters/video-chat.js.map +7 -0
  184. package/dist/src/messaging/converters/video.js +30 -0
  185. package/dist/src/messaging/converters/video.js.map +7 -0
  186. package/dist/src/messaging/converters/vote.js +24 -0
  187. package/dist/src/messaging/converters/vote.js.map +7 -0
  188. package/dist/src/messaging/inbound/dedup.js +82 -0
  189. package/dist/src/messaging/inbound/dedup.js.map +7 -0
  190. package/dist/src/messaging/inbound/dispatch-builders.js +98 -0
  191. package/dist/src/messaging/inbound/dispatch-builders.js.map +7 -0
  192. package/dist/src/messaging/inbound/dispatch-commands.js +94 -0
  193. package/dist/src/messaging/inbound/dispatch-commands.js.map +7 -0
  194. package/dist/src/messaging/inbound/dispatch-context.js +96 -0
  195. package/dist/src/messaging/inbound/dispatch-context.js.map +7 -0
  196. package/dist/src/messaging/inbound/dispatch.js +150 -0
  197. package/dist/src/messaging/inbound/dispatch.js.map +7 -0
  198. package/dist/src/messaging/inbound/enrich.js +137 -0
  199. package/dist/src/messaging/inbound/enrich.js.map +7 -0
  200. package/dist/src/messaging/inbound/gate-effects.js +28 -0
  201. package/dist/src/messaging/inbound/gate-effects.js.map +7 -0
  202. package/dist/src/messaging/inbound/gate.js +163 -0
  203. package/dist/src/messaging/inbound/gate.js.map +7 -0
  204. package/dist/src/messaging/inbound/handler.js +132 -0
  205. package/dist/src/messaging/inbound/handler.js.map +7 -0
  206. package/dist/src/messaging/inbound/media-resolver.js +70 -0
  207. package/dist/src/messaging/inbound/media-resolver.js.map +7 -0
  208. package/dist/src/messaging/inbound/mention.js +50 -0
  209. package/dist/src/messaging/inbound/mention.js.map +7 -0
  210. package/dist/src/messaging/inbound/parse-io.js +41 -0
  211. package/dist/src/messaging/inbound/parse-io.js.map +7 -0
  212. package/dist/src/messaging/inbound/parse.js +79 -0
  213. package/dist/src/messaging/inbound/parse.js.map +7 -0
  214. package/dist/src/messaging/inbound/permission.js +30 -0
  215. package/dist/src/messaging/inbound/permission.js.map +7 -0
  216. package/dist/src/messaging/inbound/policy.js +83 -0
  217. package/dist/src/messaging/inbound/policy.js.map +7 -0
  218. package/dist/src/messaging/inbound/reaction-handler.js +162 -0
  219. package/dist/src/messaging/inbound/reaction-handler.js.map +7 -0
  220. package/dist/src/messaging/inbound/user-name-cache.js +172 -0
  221. package/dist/src/messaging/inbound/user-name-cache.js.map +7 -0
  222. package/dist/src/messaging/outbound/actions.js +239 -0
  223. package/dist/src/messaging/outbound/actions.js.map +7 -0
  224. package/dist/src/messaging/outbound/chat-manage.js +74 -0
  225. package/dist/src/messaging/outbound/chat-manage.js.map +7 -0
  226. package/dist/src/messaging/outbound/deliver.js +162 -0
  227. package/dist/src/messaging/outbound/deliver.js.map +7 -0
  228. package/dist/src/messaging/outbound/fetch.js +7 -0
  229. package/dist/src/messaging/outbound/fetch.js.map +7 -0
  230. package/dist/src/messaging/outbound/forward.js +31 -0
  231. package/dist/src/messaging/outbound/forward.js.map +7 -0
  232. package/dist/src/messaging/outbound/media-url-utils.js +101 -0
  233. package/dist/src/messaging/outbound/media-url-utils.js.map +7 -0
  234. package/dist/src/messaging/outbound/media.js +463 -0
  235. package/dist/src/messaging/outbound/media.js.map +7 -0
  236. package/dist/src/messaging/outbound/outbound.js +95 -0
  237. package/dist/src/messaging/outbound/outbound.js.map +7 -0
  238. package/dist/src/messaging/outbound/reactions.js +312 -0
  239. package/dist/src/messaging/outbound/reactions.js.map +7 -0
  240. package/dist/src/messaging/outbound/send.js +194 -0
  241. package/dist/src/messaging/outbound/send.js.map +7 -0
  242. package/dist/src/messaging/outbound/typing.js +77 -0
  243. package/dist/src/messaging/outbound/typing.js.map +7 -0
  244. package/dist/src/messaging/shared/message-lookup.js +84 -0
  245. package/dist/src/messaging/shared/message-lookup.js.map +7 -0
  246. package/dist/src/messaging/types.js +1 -0
  247. package/dist/src/messaging/types.js.map +7 -0
  248. package/dist/src/tools/auto-auth.js +714 -0
  249. package/dist/src/tools/auto-auth.js.map +7 -0
  250. package/dist/src/tools/helpers.js +133 -0
  251. package/dist/src/tools/helpers.js.map +7 -0
  252. package/dist/src/tools/mcp/doc/create.js +35 -0
  253. package/dist/src/tools/mcp/doc/create.js.map +7 -0
  254. package/dist/src/tools/mcp/doc/fetch.js +33 -0
  255. package/dist/src/tools/mcp/doc/fetch.js.map +7 -0
  256. package/dist/src/tools/mcp/doc/index.js +32 -0
  257. package/dist/src/tools/mcp/doc/index.js.map +7 -0
  258. package/dist/src/tools/mcp/doc/update.js +61 -0
  259. package/dist/src/tools/mcp/doc/update.js.map +7 -0
  260. package/dist/src/tools/mcp/project/endpoint.js +25 -0
  261. package/dist/src/tools/mcp/project/endpoint.js.map +7 -0
  262. package/dist/src/tools/mcp/project/index.js +27 -0
  263. package/dist/src/tools/mcp/project/index.js.map +7 -0
  264. package/dist/src/tools/mcp/project/tools.js +579 -0
  265. package/dist/src/tools/mcp/project/tools.js.map +7 -0
  266. package/dist/src/tools/mcp/shared.js +170 -0
  267. package/dist/src/tools/mcp/shared.js.map +7 -0
  268. package/dist/src/tools/oapi/bitable/app-table-field.js +244 -0
  269. package/dist/src/tools/oapi/bitable/app-table-field.js.map +7 -0
  270. package/dist/src/tools/oapi/bitable/app-table-record.js +501 -0
  271. package/dist/src/tools/oapi/bitable/app-table-record.js.map +7 -0
  272. package/dist/src/tools/oapi/bitable/app-table-view.js +226 -0
  273. package/dist/src/tools/oapi/bitable/app-table-view.js.map +7 -0
  274. package/dist/src/tools/oapi/bitable/app-table.js +278 -0
  275. package/dist/src/tools/oapi/bitable/app-table.js.map +7 -0
  276. package/dist/src/tools/oapi/bitable/app.js +200 -0
  277. package/dist/src/tools/oapi/bitable/app.js.map +7 -0
  278. package/dist/src/tools/oapi/bitable/index.js +13 -0
  279. package/dist/src/tools/oapi/bitable/index.js.map +7 -0
  280. package/dist/src/tools/oapi/calendar/calendar.js +131 -0
  281. package/dist/src/tools/oapi/calendar/calendar.js.map +7 -0
  282. package/dist/src/tools/oapi/calendar/event-attendee.js +301 -0
  283. package/dist/src/tools/oapi/calendar/event-attendee.js.map +7 -0
  284. package/dist/src/tools/oapi/calendar/event.js +834 -0
  285. package/dist/src/tools/oapi/calendar/event.js.map +7 -0
  286. package/dist/src/tools/oapi/calendar/freebusy.js +111 -0
  287. package/dist/src/tools/oapi/calendar/freebusy.js.map +7 -0
  288. package/dist/src/tools/oapi/calendar/index.js +11 -0
  289. package/dist/src/tools/oapi/calendar/index.js.map +7 -0
  290. package/dist/src/tools/oapi/chat/chat.js +132 -0
  291. package/dist/src/tools/oapi/chat/chat.js.map +7 -0
  292. package/dist/src/tools/oapi/chat/index.js +11 -0
  293. package/dist/src/tools/oapi/chat/index.js.map +7 -0
  294. package/dist/src/tools/oapi/chat/members.js +83 -0
  295. package/dist/src/tools/oapi/chat/members.js.map +7 -0
  296. package/dist/src/tools/oapi/common/get-user.js +95 -0
  297. package/dist/src/tools/oapi/common/get-user.js.map +7 -0
  298. package/dist/src/tools/oapi/common/index.js +7 -0
  299. package/dist/src/tools/oapi/common/index.js.map +7 -0
  300. package/dist/src/tools/oapi/common/search-user.js +67 -0
  301. package/dist/src/tools/oapi/common/search-user.js.map +7 -0
  302. package/dist/src/tools/oapi/drive/doc-comments.js +310 -0
  303. package/dist/src/tools/oapi/drive/doc-comments.js.map +7 -0
  304. package/dist/src/tools/oapi/drive/doc-media.js +314 -0
  305. package/dist/src/tools/oapi/drive/doc-media.js.map +7 -0
  306. package/dist/src/tools/oapi/drive/file.js +548 -0
  307. package/dist/src/tools/oapi/drive/file.js.map +7 -0
  308. package/dist/src/tools/oapi/drive/index.js +29 -0
  309. package/dist/src/tools/oapi/drive/index.js.map +7 -0
  310. package/dist/src/tools/oapi/helpers.js +199 -0
  311. package/dist/src/tools/oapi/helpers.js.map +7 -0
  312. package/dist/src/tools/oapi/im/format-messages.js +128 -0
  313. package/dist/src/tools/oapi/im/format-messages.js.map +7 -0
  314. package/dist/src/tools/oapi/im/index.js +15 -0
  315. package/dist/src/tools/oapi/im/index.js.map +7 -0
  316. package/dist/src/tools/oapi/im/message-read.js +404 -0
  317. package/dist/src/tools/oapi/im/message-read.js.map +7 -0
  318. package/dist/src/tools/oapi/im/message.js +179 -0
  319. package/dist/src/tools/oapi/im/message.js.map +7 -0
  320. package/dist/src/tools/oapi/im/resource.js +126 -0
  321. package/dist/src/tools/oapi/im/resource.js.map +7 -0
  322. package/dist/src/tools/oapi/im/time-utils.js +169 -0
  323. package/dist/src/tools/oapi/im/time-utils.js.map +7 -0
  324. package/dist/src/tools/oapi/im/user-name-uat.js +103 -0
  325. package/dist/src/tools/oapi/im/user-name-uat.js.map +7 -0
  326. package/dist/src/tools/oapi/index.js +56 -0
  327. package/dist/src/tools/oapi/index.js.map +7 -0
  328. package/dist/src/tools/oapi/sdk-types.js +1 -0
  329. package/dist/src/tools/oapi/sdk-types.js.map +7 -0
  330. package/dist/src/tools/oapi/search/doc-search.js +215 -0
  331. package/dist/src/tools/oapi/search/doc-search.js.map +7 -0
  332. package/dist/src/tools/oapi/search/index.js +25 -0
  333. package/dist/src/tools/oapi/search/index.js.map +7 -0
  334. package/dist/src/tools/oapi/sheets/index.js +25 -0
  335. package/dist/src/tools/oapi/sheets/index.js.map +7 -0
  336. package/dist/src/tools/oapi/sheets/sheet.js +652 -0
  337. package/dist/src/tools/oapi/sheets/sheet.js.map +7 -0
  338. package/dist/src/tools/oapi/task/comment.js +151 -0
  339. package/dist/src/tools/oapi/task/comment.js.map +7 -0
  340. package/dist/src/tools/oapi/task/index.js +11 -0
  341. package/dist/src/tools/oapi/task/index.js.map +7 -0
  342. package/dist/src/tools/oapi/task/subtask.js +175 -0
  343. package/dist/src/tools/oapi/task/subtask.js.map +7 -0
  344. package/dist/src/tools/oapi/task/task.js +405 -0
  345. package/dist/src/tools/oapi/task/task.js.map +7 -0
  346. package/dist/src/tools/oapi/task/tasklist.js +366 -0
  347. package/dist/src/tools/oapi/task/tasklist.js.map +7 -0
  348. package/dist/src/tools/oapi/wiki/index.js +27 -0
  349. package/dist/src/tools/oapi/wiki/index.js.map +7 -0
  350. package/dist/src/tools/oapi/wiki/space-node.js +311 -0
  351. package/dist/src/tools/oapi/wiki/space-node.js.map +7 -0
  352. package/dist/src/tools/oapi/wiki/space.js +148 -0
  353. package/dist/src/tools/oapi/wiki/space.js.map +7 -0
  354. package/dist/src/tools/oauth-batch-auth.js +125 -0
  355. package/dist/src/tools/oauth-batch-auth.js.map +7 -0
  356. package/dist/src/tools/oauth-cards.js +269 -0
  357. package/dist/src/tools/oauth-cards.js.map +7 -0
  358. package/dist/src/tools/oauth.js +538 -0
  359. package/dist/src/tools/oauth.js.map +7 -0
  360. package/dist/src/tools/onboarding-auth.js +101 -0
  361. package/dist/src/tools/onboarding-auth.js.map +7 -0
  362. package/dist/src/tools/project-oauth.js +305 -0
  363. package/dist/src/tools/project-oauth.js.map +7 -0
  364. package/dist/src/tools/tat/im/index.js +9 -0
  365. package/dist/src/tools/tat/im/index.js.map +7 -0
  366. package/dist/src/tools/tat/im/resource.js +123 -0
  367. package/dist/src/tools/tat/im/resource.js.map +7 -0
  368. package/package.json +64 -0
@@ -0,0 +1,98 @@
1
+ import { buildPendingHistoryContextFromMap } from "openclaw/plugin-sdk";
2
+ import { nonBotMentions } from "./mention";
3
+ import { threadScopedKey } from "../../channel/chat-queue";
4
+ function buildMentionAnnotation(ctx) {
5
+ const mentions = nonBotMentions(ctx);
6
+ if (mentions.length === 0) return void 0;
7
+ const mentionDetails = mentions.map((t) => `${t.name} (open_id: ${t.openId})`).join(", ");
8
+ return `[System: This message @mentions the following users: ${mentionDetails}. Use these open_ids when performing actions involving these users.]`;
9
+ }
10
+ function buildMessageBody(ctx, quotedContent) {
11
+ let messageBody = ctx.content;
12
+ if (quotedContent) {
13
+ messageBody = `[Replying to: "${quotedContent}"]
14
+
15
+ ${ctx.content}`;
16
+ }
17
+ const speaker = ctx.senderName ?? ctx.senderId;
18
+ messageBody = `${speaker}: ${messageBody}`;
19
+ const mentionAnnotation = buildMentionAnnotation(ctx);
20
+ if (mentionAnnotation) {
21
+ messageBody += `
22
+
23
+ ${mentionAnnotation}`;
24
+ }
25
+ return messageBody;
26
+ }
27
+ function buildBodyForAgent(ctx) {
28
+ const mentionAnnotation = buildMentionAnnotation(ctx);
29
+ if (mentionAnnotation) {
30
+ return `${ctx.content}
31
+
32
+ ${mentionAnnotation}`;
33
+ }
34
+ return ctx.content;
35
+ }
36
+ function buildInboundPayload(dc, opts) {
37
+ return dc.core.channel.reply.finalizeInboundContext({
38
+ // extraFields first — fixed fields below always take precedence
39
+ ...opts.extraFields,
40
+ Body: opts.body,
41
+ BodyForAgent: opts.bodyForAgent,
42
+ RawBody: opts.rawBody,
43
+ CommandBody: opts.commandBody,
44
+ From: dc.feishuFrom,
45
+ To: dc.feishuTo,
46
+ SessionKey: dc.threadSessionKey ?? dc.route.sessionKey,
47
+ AccountId: dc.route.accountId,
48
+ ChatType: dc.isGroup ? "group" : "direct",
49
+ GroupSubject: dc.isGroup ? dc.ctx.chatId : void 0,
50
+ SenderName: opts.senderName,
51
+ SenderId: opts.senderId,
52
+ Provider: "feishu",
53
+ Surface: "feishu",
54
+ MessageSid: opts.messageSid,
55
+ ReplyToBody: opts.replyToBody,
56
+ InboundHistory: opts.inboundHistory,
57
+ Timestamp: Date.now(),
58
+ WasMentioned: opts.wasMentioned,
59
+ CommandAuthorized: dc.commandAuthorized,
60
+ OriginatingChannel: "feishu",
61
+ OriginatingTo: dc.feishuTo
62
+ });
63
+ }
64
+ function buildEnvelopeWithHistory(dc, messageBody, chatHistories, historyLimit) {
65
+ const body = dc.core.channel.reply.formatAgentEnvelope({
66
+ channel: "Feishu",
67
+ from: dc.envelopeFrom,
68
+ timestamp: /* @__PURE__ */ new Date(),
69
+ envelope: dc.envelopeOptions,
70
+ body: messageBody
71
+ });
72
+ let combinedBody = body;
73
+ const historyKey = dc.isGroup ? threadScopedKey(dc.ctx.chatId, dc.isThread ? dc.ctx.threadId : void 0) : void 0;
74
+ if (dc.isGroup && historyKey && chatHistories) {
75
+ combinedBody = buildPendingHistoryContextFromMap({
76
+ historyMap: chatHistories,
77
+ historyKey,
78
+ limit: historyLimit,
79
+ currentMessage: combinedBody,
80
+ formatEntry: (entry) => dc.core.channel.reply.formatAgentEnvelope({
81
+ channel: "Feishu",
82
+ from: `${dc.ctx.chatId}:${entry.sender}`,
83
+ timestamp: entry.timestamp,
84
+ body: entry.body,
85
+ envelope: dc.envelopeOptions
86
+ })
87
+ });
88
+ }
89
+ return { combinedBody, historyKey };
90
+ }
91
+ export {
92
+ buildBodyForAgent,
93
+ buildEnvelopeWithHistory,
94
+ buildInboundPayload,
95
+ buildMentionAnnotation,
96
+ buildMessageBody
97
+ };
98
+ //# sourceMappingURL=dispatch-builders.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/messaging/inbound/dispatch-builders.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Pure construction functions for the agent dispatch pipeline.\n *\n * All functions in this module are side-effect-free: they build data\n * structures (message bodies, envelope payloads, inbound context) but\n * never perform I/O, send messages, or mutate external state.\n */\n\nimport type { HistoryEntry } from 'openclaw/plugin-sdk';\nimport { buildPendingHistoryContextFromMap } from 'openclaw/plugin-sdk';\nimport type { MessageContext } from '../types';\nimport type { DispatchContext } from './dispatch-context';\nimport { LarkClient } from '../../core/lark-client';\nimport { nonBotMentions } from './mention';\nimport { threadScopedKey } from '../../channel/chat-queue';\n\n// ---------------------------------------------------------------------------\n// Mention annotation\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `[System: ...]` mention annotation when the message @-mentions\n * non-bot users. Returns `undefined` when there are no user mentions.\n *\n * Sender identity / chat metadata are handled by the SDK's own\n * `buildInboundUserContextPrefix` (via SenderId, SenderName, ReplyToBody,\n * InboundHistory, etc.), so we only inject the mention data that the SDK\n * does not natively support.\n */\nexport function buildMentionAnnotation(ctx: MessageContext): string | undefined {\n const mentions = nonBotMentions(ctx);\n if (mentions.length === 0) return undefined;\n const mentionDetails = mentions.map((t) => `${t.name} (open_id: ${t.openId})`).join(', ');\n return `[System: This message @mentions the following users: ${mentionDetails}. Use these open_ids when performing actions involving these users.]`;\n}\n\n// ---------------------------------------------------------------------------\n// Message body builders\n// ---------------------------------------------------------------------------\n\n/**\n * Pure function: build the annotated message body with optional quote,\n * speaker prefix, and mention annotation (for the envelope Body).\n *\n * Note: message_id and reply_to are now conveyed via system-event tags\n * (msg:om_xxx, reply_to:om_yyy) instead of inline annotations, keeping\n * the body cleaner and avoiding misleading heuristics for non-text\n * message types (merge_forward, interactive cards, etc.).\n */\nexport function buildMessageBody(ctx: MessageContext, quotedContent?: string): string {\n let messageBody = ctx.content;\n if (quotedContent) {\n messageBody = `[Replying to: \"${quotedContent}\"]\\n\\n${ctx.content}`;\n }\n\n const speaker = ctx.senderName ?? ctx.senderId;\n messageBody = `${speaker}: ${messageBody}`;\n\n const mentionAnnotation = buildMentionAnnotation(ctx);\n if (mentionAnnotation) {\n messageBody += `\\n\\n${mentionAnnotation}`;\n }\n\n return messageBody;\n}\n\n/**\n * Build the BodyForAgent value: the clean message content plus an\n * optional mention annotation.\n *\n * SDK >= 2026.2.10 changed the BodyForAgent fallback chain from\n * `BodyForAgent ?? Body` to `BodyForAgent ?? CommandBody ?? RawBody ?? Body`,\n * so annotations embedded only in Body never reach the AI. Setting\n * BodyForAgent explicitly ensures the mention annotation survives.\n *\n * Sender identity, reply context, and chat history are NOT duplicated\n * here \u2014 they are injected by the SDK's `buildInboundUserContextPrefix`\n * via the standard fields (SenderId, SenderName, ReplyToBody,\n * InboundHistory) that we pass in buildInboundPayload.\n *\n * Note: media file paths are substituted into `ctx.content` upstream\n * (handler.ts -> substituteMediaPaths) before this function is called.\n * The SDK's `detectAndLoadPromptImages` will discover image paths from\n * the text and inject them as multimodal content blocks.\n */\nexport function buildBodyForAgent(ctx: MessageContext): string {\n const mentionAnnotation = buildMentionAnnotation(ctx);\n if (mentionAnnotation) {\n return `${ctx.content}\\n\\n${mentionAnnotation}`;\n }\n return ctx.content;\n}\n\n// ---------------------------------------------------------------------------\n// Inbound payload builder\n// ---------------------------------------------------------------------------\n\n/**\n * Unified call to `finalizeInboundContext`, eliminating the duplicated\n * field-mapping between permission notification and main message paths.\n */\nexport function buildInboundPayload(\n dc: DispatchContext,\n opts: {\n body: string;\n bodyForAgent: string;\n rawBody: string;\n commandBody: string;\n senderName: string;\n senderId: string;\n messageSid: string;\n wasMentioned: boolean;\n replyToBody?: string;\n inboundHistory?: { sender: string; body: string; timestamp: number }[];\n extraFields?: Record<string, unknown>;\n },\n): ReturnType<typeof LarkClient.runtime.channel.reply.finalizeInboundContext> {\n return dc.core.channel.reply.finalizeInboundContext({\n // extraFields first \u2014 fixed fields below always take precedence\n ...opts.extraFields,\n Body: opts.body,\n BodyForAgent: opts.bodyForAgent,\n RawBody: opts.rawBody,\n CommandBody: opts.commandBody,\n From: dc.feishuFrom,\n To: dc.feishuTo,\n SessionKey: dc.threadSessionKey ?? dc.route.sessionKey,\n AccountId: dc.route.accountId,\n ChatType: dc.isGroup ? 'group' : 'direct',\n GroupSubject: dc.isGroup ? dc.ctx.chatId : undefined,\n SenderName: opts.senderName,\n SenderId: opts.senderId,\n Provider: 'feishu' as const,\n Surface: 'feishu' as const,\n MessageSid: opts.messageSid,\n ReplyToBody: opts.replyToBody,\n InboundHistory: opts.inboundHistory,\n Timestamp: Date.now(),\n WasMentioned: opts.wasMentioned,\n CommandAuthorized: dc.commandAuthorized,\n OriginatingChannel: 'feishu' as const,\n OriginatingTo: dc.feishuTo,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Envelope + history builder\n// ---------------------------------------------------------------------------\n\n/**\n * Format the agent envelope and prepend group chat history if applicable.\n * Returns the combined body and the history key (undefined for DMs).\n */\nexport function buildEnvelopeWithHistory(\n dc: DispatchContext,\n messageBody: string,\n chatHistories: Map<string, HistoryEntry[]> | undefined,\n historyLimit: number,\n): { combinedBody: string; historyKey: string | undefined } {\n const body = dc.core.channel.reply.formatAgentEnvelope({\n channel: 'Feishu',\n from: dc.envelopeFrom,\n timestamp: new Date(),\n envelope: dc.envelopeOptions,\n body: messageBody,\n });\n\n let combinedBody = body;\n const historyKey = dc.isGroup ? threadScopedKey(dc.ctx.chatId, dc.isThread ? dc.ctx.threadId : undefined) : undefined;\n\n if (dc.isGroup && historyKey && chatHistories) {\n combinedBody = buildPendingHistoryContextFromMap({\n historyMap: chatHistories,\n historyKey,\n limit: historyLimit,\n currentMessage: combinedBody,\n formatEntry: (entry) =>\n dc.core.channel.reply.formatAgentEnvelope({\n channel: 'Feishu',\n from: `${dc.ctx.chatId}:${entry.sender}`,\n timestamp: entry.timestamp,\n body: entry.body,\n envelope: dc.envelopeOptions,\n }),\n });\n }\n\n return { combinedBody, historyKey };\n}\n"],
5
+ "mappings": "AAYA,SAAS,yCAAyC;AAIlD,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAezB,SAAS,uBAAuB,KAAyC;AAC9E,QAAM,WAAW,eAAe,GAAG;AACnC,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAM,iBAAiB,SAAS,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,cAAc,EAAE,MAAM,GAAG,EAAE,KAAK,IAAI;AACxF,SAAO,wDAAwD,cAAc;AAC/E;AAeO,SAAS,iBAAiB,KAAqB,eAAgC;AACpF,MAAI,cAAc,IAAI;AACtB,MAAI,eAAe;AACjB,kBAAc,kBAAkB,aAAa;AAAA;AAAA,EAAS,IAAI,OAAO;AAAA,EACnE;AAEA,QAAM,UAAU,IAAI,cAAc,IAAI;AACtC,gBAAc,GAAG,OAAO,KAAK,WAAW;AAExC,QAAM,oBAAoB,uBAAuB,GAAG;AACpD,MAAI,mBAAmB;AACrB,mBAAe;AAAA;AAAA,EAAO,iBAAiB;AAAA,EACzC;AAEA,SAAO;AACT;AAqBO,SAAS,kBAAkB,KAA6B;AAC7D,QAAM,oBAAoB,uBAAuB,GAAG;AACpD,MAAI,mBAAmB;AACrB,WAAO,GAAG,IAAI,OAAO;AAAA;AAAA,EAAO,iBAAiB;AAAA,EAC/C;AACA,SAAO,IAAI;AACb;AAUO,SAAS,oBACd,IACA,MAa4E;AAC5E,SAAO,GAAG,KAAK,QAAQ,MAAM,uBAAuB;AAAA;AAAA,IAElD,GAAG,KAAK;AAAA,IACR,MAAM,KAAK;AAAA,IACX,cAAc,KAAK;AAAA,IACnB,SAAS,KAAK;AAAA,IACd,aAAa,KAAK;AAAA,IAClB,MAAM,GAAG;AAAA,IACT,IAAI,GAAG;AAAA,IACP,YAAY,GAAG,oBAAoB,GAAG,MAAM;AAAA,IAC5C,WAAW,GAAG,MAAM;AAAA,IACpB,UAAU,GAAG,UAAU,UAAU;AAAA,IACjC,cAAc,GAAG,UAAU,GAAG,IAAI,SAAS;AAAA,IAC3C,YAAY,KAAK;AAAA,IACjB,UAAU,KAAK;AAAA,IACf,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,IAClB,gBAAgB,KAAK;AAAA,IACrB,WAAW,KAAK,IAAI;AAAA,IACpB,cAAc,KAAK;AAAA,IACnB,mBAAmB,GAAG;AAAA,IACtB,oBAAoB;AAAA,IACpB,eAAe,GAAG;AAAA,EACpB,CAAC;AACH;AAUO,SAAS,yBACd,IACA,aACA,eACA,cAC0D;AAC1D,QAAM,OAAO,GAAG,KAAK,QAAQ,MAAM,oBAAoB;AAAA,IACrD,SAAS;AAAA,IACT,MAAM,GAAG;AAAA,IACT,WAAW,oBAAI,KAAK;AAAA,IACpB,UAAU,GAAG;AAAA,IACb,MAAM;AAAA,EACR,CAAC;AAED,MAAI,eAAe;AACnB,QAAM,aAAa,GAAG,UAAU,gBAAgB,GAAG,IAAI,QAAQ,GAAG,WAAW,GAAG,IAAI,WAAW,MAAS,IAAI;AAE5G,MAAI,GAAG,WAAW,cAAc,eAAe;AAC7C,mBAAe,kCAAkC;AAAA,MAC/C,YAAY;AAAA,MACZ;AAAA,MACA,OAAO;AAAA,MACP,gBAAgB;AAAA,MAChB,aAAa,CAAC,UACZ,GAAG,KAAK,QAAQ,MAAM,oBAAoB;AAAA,QACxC,SAAS;AAAA,QACT,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,MAAM,MAAM;AAAA,QACtC,WAAW,MAAM;AAAA,QACjB,MAAM,MAAM;AAAA,QACZ,UAAU,GAAG;AAAA,MACf,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,cAAc,WAAW;AACpC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,94 @@
1
+ import { larkLogger } from "../../core/lark-logger";
2
+ import { ticketElapsed } from "../../core/lark-ticket";
3
+ import { createFeishuReplyDispatcher } from "../../card/reply-dispatcher";
4
+ import { sendMessageFeishu } from "../outbound/send";
5
+ import { buildInboundPayload } from "./dispatch-builders";
6
+ const log = larkLogger("inbound/dispatch-commands");
7
+ async function dispatchPermissionNotification(dc, permissionError, replyToMessageId) {
8
+ const grantUrl = permissionError.grantUrl ?? "";
9
+ 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}]`;
10
+ const permBody = dc.core.channel.reply.formatAgentEnvelope({
11
+ channel: "Feishu",
12
+ from: dc.envelopeFrom,
13
+ timestamp: /* @__PURE__ */ new Date(),
14
+ envelope: dc.envelopeOptions,
15
+ body: permissionNotifyBody
16
+ });
17
+ const permCtx = buildInboundPayload(dc, {
18
+ body: permBody,
19
+ bodyForAgent: permissionNotifyBody,
20
+ rawBody: permissionNotifyBody,
21
+ commandBody: permissionNotifyBody,
22
+ senderName: "system",
23
+ senderId: "system",
24
+ messageSid: `${dc.ctx.messageId}:permission-error`,
25
+ wasMentioned: false
26
+ });
27
+ const {
28
+ dispatcher: permDispatcher,
29
+ replyOptions: permReplyOptions,
30
+ markDispatchIdle: markPermIdle,
31
+ markFullyComplete: markPermComplete
32
+ } = createFeishuReplyDispatcher({
33
+ cfg: dc.accountScopedCfg,
34
+ agentId: dc.route.agentId,
35
+ chatId: dc.ctx.chatId,
36
+ replyToMessageId: replyToMessageId ?? dc.ctx.messageId,
37
+ accountId: dc.account.accountId,
38
+ chatType: dc.ctx.chatType,
39
+ replyInThread: dc.isThread
40
+ });
41
+ dc.log(`feishu[${dc.account.accountId}]: dispatching permission error notification to agent`);
42
+ await dc.core.channel.reply.dispatchReplyFromConfig({
43
+ ctx: permCtx,
44
+ cfg: dc.accountScopedCfg,
45
+ dispatcher: permDispatcher,
46
+ replyOptions: permReplyOptions
47
+ });
48
+ await permDispatcher.waitForIdle();
49
+ markPermComplete();
50
+ markPermIdle();
51
+ }
52
+ async function dispatchSystemCommand(dc, ctxPayload, suppressReply = false, replyToMessageId) {
53
+ let delivered = false;
54
+ dc.log(
55
+ `feishu[${dc.account.accountId}]: detected system command, using plain-text dispatch${suppressReply ? " (reply suppressed)" : ""}`
56
+ );
57
+ log.info(`system command detected, plain-text dispatch${suppressReply ? ", reply suppressed" : ""}`);
58
+ await dc.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
59
+ ctx: ctxPayload,
60
+ cfg: dc.accountScopedCfg,
61
+ dispatcherOptions: {
62
+ deliver: async (payload) => {
63
+ if (suppressReply) return;
64
+ const text = payload.text?.trim() ?? "";
65
+ if (!text) return;
66
+ await sendMessageFeishu({
67
+ cfg: dc.accountScopedCfg,
68
+ to: dc.ctx.chatId,
69
+ text,
70
+ replyToMessageId: replyToMessageId ?? dc.ctx.messageId,
71
+ accountId: dc.account.accountId,
72
+ replyInThread: dc.isThread
73
+ });
74
+ delivered = true;
75
+ },
76
+ onSkip: (_payload, info) => {
77
+ if (info.reason !== "silent") {
78
+ dc.log(`feishu[${dc.account.accountId}]: command reply skipped (reason=${info.reason})`);
79
+ }
80
+ },
81
+ onError: (err, info) => {
82
+ dc.error(`feishu[${dc.account.accountId}]: command ${info.kind} reply failed: ${String(err)}`);
83
+ }
84
+ },
85
+ replyOptions: {}
86
+ });
87
+ dc.log(`feishu[${dc.account.accountId}]: system command dispatched (delivered=${delivered})`);
88
+ log.info(`system command dispatched (delivered=${delivered}, elapsed=${ticketElapsed()}ms)`);
89
+ }
90
+ export {
91
+ dispatchPermissionNotification,
92
+ dispatchSystemCommand
93
+ };
94
+ //# sourceMappingURL=dispatch-commands.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/messaging/inbound/dispatch-commands.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * System command and permission notification dispatch for inbound messages.\n *\n * Handles control commands (/help, /reset, etc.) via plain-text delivery\n * and permission-error notifications via the streaming card flow.\n */\n\nimport type { DispatchContext } from './dispatch-context';\nimport type { PermissionError } from './permission';\nimport { LarkClient } from '../../core/lark-client';\nimport { larkLogger } from '../../core/lark-logger';\nimport { ticketElapsed } from '../../core/lark-ticket';\nimport { createFeishuReplyDispatcher } from '../../card/reply-dispatcher';\nimport { sendMessageFeishu } from '../outbound/send';\nimport { buildInboundPayload } from './dispatch-builders';\n\nconst log = larkLogger('inbound/dispatch-commands');\n\n// ---------------------------------------------------------------------------\n// Permission error notification\n// ---------------------------------------------------------------------------\n\n/**\n * Dispatch a permission-error notification to the agent so it can\n * inform the user about the missing Feishu API scope.\n */\nexport async function dispatchPermissionNotification(\n dc: DispatchContext,\n permissionError: PermissionError,\n replyToMessageId?: string,\n): Promise<void> {\n const grantUrl = permissionError.grantUrl ?? '';\n 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}]`;\n\n const permBody = dc.core.channel.reply.formatAgentEnvelope({\n channel: 'Feishu',\n from: dc.envelopeFrom,\n timestamp: new Date(),\n envelope: dc.envelopeOptions,\n body: permissionNotifyBody,\n });\n\n const permCtx = buildInboundPayload(dc, {\n body: permBody,\n bodyForAgent: permissionNotifyBody,\n rawBody: permissionNotifyBody,\n commandBody: permissionNotifyBody,\n senderName: 'system',\n senderId: 'system',\n messageSid: `${dc.ctx.messageId}:permission-error`,\n wasMentioned: false,\n });\n\n const {\n dispatcher: permDispatcher,\n replyOptions: permReplyOptions,\n markDispatchIdle: markPermIdle,\n markFullyComplete: markPermComplete,\n } = createFeishuReplyDispatcher({\n cfg: dc.accountScopedCfg,\n agentId: dc.route.agentId,\n chatId: dc.ctx.chatId,\n replyToMessageId: replyToMessageId ?? dc.ctx.messageId,\n accountId: dc.account.accountId,\n chatType: dc.ctx.chatType,\n replyInThread: dc.isThread,\n });\n\n dc.log(`feishu[${dc.account.accountId}]: dispatching permission error notification to agent`);\n\n await dc.core.channel.reply.dispatchReplyFromConfig({\n ctx: permCtx,\n cfg: dc.accountScopedCfg,\n dispatcher: permDispatcher,\n replyOptions: permReplyOptions,\n });\n\n await permDispatcher.waitForIdle();\n markPermComplete();\n markPermIdle();\n}\n\n// ---------------------------------------------------------------------------\n// System command dispatch\n// ---------------------------------------------------------------------------\n\n/**\n * Dispatch a system command (/help, /reset, etc.) via plain-text delivery.\n * No streaming card, no \"Processing...\" state.\n *\n * When `suppressReply` is true the agent still runs (e.g. reads workspace\n * files) but its text output is not forwarded to Feishu. This is used for\n * bare /new and /reset commands: the SDK already sends a \"done\" notice\n * via its own route, so the AI greeting would be redundant.\n */\nexport async function dispatchSystemCommand(\n dc: DispatchContext,\n ctxPayload: ReturnType<typeof LarkClient.runtime.channel.reply.finalizeInboundContext>,\n suppressReply = false,\n replyToMessageId?: string,\n): Promise<void> {\n let delivered = false;\n\n dc.log(\n `feishu[${dc.account.accountId}]: detected system command, using plain-text dispatch${suppressReply ? ' (reply suppressed)' : ''}`,\n );\n log.info(`system command detected, plain-text dispatch${suppressReply ? ', reply suppressed' : ''}`);\n\n await dc.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({\n ctx: ctxPayload,\n cfg: dc.accountScopedCfg,\n dispatcherOptions: {\n deliver: async (payload) => {\n if (suppressReply) return;\n const text = payload.text?.trim() ?? '';\n if (!text) return;\n await sendMessageFeishu({\n cfg: dc.accountScopedCfg,\n to: dc.ctx.chatId,\n text,\n replyToMessageId: replyToMessageId ?? dc.ctx.messageId,\n accountId: dc.account.accountId,\n replyInThread: dc.isThread,\n });\n delivered = true;\n },\n onSkip: (_payload, info) => {\n if (info.reason !== 'silent') {\n dc.log(`feishu[${dc.account.accountId}]: command reply skipped (reason=${info.reason})`);\n }\n },\n onError: (err, info) => {\n dc.error(`feishu[${dc.account.accountId}]: command ${info.kind} reply failed: ${String(err)}`);\n },\n },\n replyOptions: {},\n });\n\n dc.log(`feishu[${dc.account.accountId}]: system command dispatched (delivered=${delivered})`);\n log.info(`system command dispatched (delivered=${delivered}, elapsed=${ticketElapsed()}ms)`);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,96 @@
1
+ import { resolveThreadSessionKeys } from "openclaw/plugin-sdk";
2
+ import { LarkClient } from "../../core/lark-client";
3
+ import { larkLogger } from "../../core/lark-logger";
4
+ import { isThreadCapableGroup } from "../../core/chat-info-cache";
5
+ const log = larkLogger("inbound/dispatch-context");
6
+ function ensureRuntime(runtime) {
7
+ if (runtime) return runtime;
8
+ return {
9
+ log: (...args) => log.info(args.map(String).join(" ")),
10
+ error: (...args) => log.error(args.map(String).join(" ")),
11
+ exit: (code) => process.exit(code)
12
+ };
13
+ }
14
+ function buildDispatchContext(params) {
15
+ const { ctx, account, accountScopedCfg } = params;
16
+ const runtime = ensureRuntime(params.runtime);
17
+ const log2 = runtime.log;
18
+ const error = runtime.error;
19
+ const isGroup = ctx.chatType === "group";
20
+ const isThread = isGroup && Boolean(ctx.threadId);
21
+ const core = LarkClient.runtime;
22
+ const feishuFrom = `feishu:${ctx.senderId}`;
23
+ const feishuTo = isGroup ? `chat:${ctx.chatId}` : `user:${ctx.senderId}`;
24
+ const envelopeFrom = isGroup ? `${ctx.chatId}:${ctx.senderId}` : ctx.senderId;
25
+ const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(accountScopedCfg);
26
+ const route = core.channel.routing.resolveAgentRoute({
27
+ cfg: accountScopedCfg,
28
+ channel: "feishu",
29
+ accountId: account.accountId,
30
+ peer: {
31
+ kind: isGroup ? "group" : "direct",
32
+ id: isGroup ? ctx.chatId : ctx.senderId
33
+ }
34
+ });
35
+ const sender = ctx.senderName ? `${ctx.senderName} (${ctx.senderId})` : ctx.senderId;
36
+ const location = isGroup ? `group ${ctx.chatId}` : "DM";
37
+ const tags = [];
38
+ tags.push(`msg:${ctx.messageId}`);
39
+ if (ctx.parentId) tags.push(`reply_to:${ctx.parentId}`);
40
+ if (ctx.contentType !== "text") tags.push(ctx.contentType);
41
+ if (ctx.mentions.some((m) => m.isBot)) tags.push("@bot");
42
+ if (ctx.threadId) tags.push(`thread:${ctx.threadId}`);
43
+ if (ctx.resources.length > 0) {
44
+ tags.push(`${ctx.resources.length} attachment(s)`);
45
+ }
46
+ const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
47
+ core.system.enqueueSystemEvent(`Feishu[${account.accountId}] ${location} | ${sender}${tagStr}`, {
48
+ sessionKey: route.sessionKey,
49
+ contextKey: `feishu:message:${ctx.chatId}:${ctx.messageId}`
50
+ });
51
+ return {
52
+ ctx,
53
+ accountScopedCfg,
54
+ account,
55
+ runtime,
56
+ log: log2,
57
+ error,
58
+ core,
59
+ isGroup,
60
+ isThread,
61
+ feishuFrom,
62
+ feishuTo,
63
+ envelopeFrom,
64
+ envelopeOptions,
65
+ route,
66
+ threadSessionKey: void 0,
67
+ commandAuthorized: params.commandAuthorized
68
+ };
69
+ }
70
+ async function resolveThreadSessionKey(params) {
71
+ const { accountScopedCfg, account, chatId, threadId, baseSessionKey } = params;
72
+ if (account.config?.threadSession !== true) return void 0;
73
+ const threadCapable = await isThreadCapableGroup({
74
+ cfg: accountScopedCfg,
75
+ chatId,
76
+ accountId: account.accountId
77
+ });
78
+ if (!threadCapable) {
79
+ log.info(`thread session skipped: group ${chatId} is not topic/thread mode`);
80
+ return void 0;
81
+ }
82
+ const { sessionKey } = resolveThreadSessionKeys({
83
+ baseSessionKey,
84
+ threadId,
85
+ parentSessionKey: baseSessionKey,
86
+ normalizeThreadId: (id) => id
87
+ // 飞书 thread ID (omt_xxx) 区分大小写,不做 lowercase
88
+ });
89
+ return sessionKey;
90
+ }
91
+ export {
92
+ buildDispatchContext,
93
+ ensureRuntime,
94
+ resolveThreadSessionKey
95
+ };
96
+ //# sourceMappingURL=dispatch-context.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/messaging/inbound/dispatch-context.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Dispatch context construction for the inbound agent dispatch pipeline.\n *\n * Derives all shared values needed by downstream dispatch helpers:\n * logging, addressing, route resolution, thread session, and system\n * event emission.\n */\n\nimport type { ClawdbotConfig, RuntimeEnv } from 'openclaw/plugin-sdk';\nimport { resolveThreadSessionKeys } from 'openclaw/plugin-sdk';\nimport type { MessageContext } from '../types';\nimport type { LarkAccount } from '../../core/types';\nimport { LarkClient } from '../../core/lark-client';\nimport { larkLogger } from '../../core/lark-logger';\nimport { isThreadCapableGroup } from '../../core/chat-info-cache';\n\nconst log = larkLogger('inbound/dispatch-context');\n\n// ---------------------------------------------------------------------------\n// DispatchContext type\n// ---------------------------------------------------------------------------\n\nexport interface DispatchContext {\n ctx: MessageContext;\n /** account \u7EA7\u522B\u7684 ClawdbotConfig\uFF08channels.feishu \u5DF2\u66FF\u6362\u4E3A per-account \u5408\u5E76\u540E\u7684\u914D\u7F6E\uFF09 */\n accountScopedCfg: ClawdbotConfig;\n account: LarkAccount;\n runtime: RuntimeEnv;\n log: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n core: typeof LarkClient.runtime;\n isGroup: boolean;\n isThread: boolean;\n feishuFrom: string;\n feishuTo: string;\n envelopeFrom: string;\n envelopeOptions: ReturnType<typeof LarkClient.runtime.channel.reply.resolveEnvelopeFormatOptions>;\n route: ReturnType<typeof LarkClient.runtime.channel.routing.resolveAgentRoute>;\n threadSessionKey?: string;\n commandAuthorized?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// RuntimeEnv fallback\n// ---------------------------------------------------------------------------\n\n/**\n * Provide a safe RuntimeEnv fallback when the caller did not supply one.\n * Replaces the previous unsafe `runtime as RuntimeEnv` casts.\n */\nexport function ensureRuntime(runtime: RuntimeEnv | undefined): RuntimeEnv {\n if (runtime) return runtime;\n return {\n log: (...args: unknown[]) => log.info(args.map(String).join(' ')),\n error: (...args: unknown[]) => log.error(args.map(String).join(' ')),\n exit: (code: number) => process.exit(code) as never,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Context construction\n// ---------------------------------------------------------------------------\n\n/**\n * Derive all shared values needed by downstream helpers:\n * logging, addressing, route resolution, and system event emission.\n */\nexport function buildDispatchContext(params: {\n ctx: MessageContext;\n account: LarkAccount;\n accountScopedCfg: ClawdbotConfig;\n runtime?: RuntimeEnv;\n commandAuthorized?: boolean;\n}): DispatchContext {\n const { ctx, account, accountScopedCfg } = params;\n\n const runtime = ensureRuntime(params.runtime);\n const log = runtime.log;\n const error = runtime.error;\n const isGroup = ctx.chatType === 'group';\n const isThread = isGroup && Boolean(ctx.threadId);\n const core = LarkClient.runtime;\n\n const feishuFrom = `feishu:${ctx.senderId}`;\n const feishuTo = isGroup ? `chat:${ctx.chatId}` : `user:${ctx.senderId}`;\n\n const envelopeFrom = isGroup ? `${ctx.chatId}:${ctx.senderId}` : ctx.senderId;\n\n const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(accountScopedCfg);\n\n // ---- Route resolution ----\n const route = core.channel.routing.resolveAgentRoute({\n cfg: accountScopedCfg,\n channel: 'feishu',\n accountId: account.accountId,\n peer: {\n kind: isGroup ? 'group' : 'direct',\n id: isGroup ? ctx.chatId : ctx.senderId,\n },\n });\n\n // ---- System event ----\n const sender = ctx.senderName ? `${ctx.senderName} (${ctx.senderId})` : ctx.senderId;\n const location = isGroup ? `group ${ctx.chatId}` : 'DM';\n\n const tags: string[] = [];\n tags.push(`msg:${ctx.messageId}`);\n if (ctx.parentId) tags.push(`reply_to:${ctx.parentId}`);\n if (ctx.contentType !== 'text') tags.push(ctx.contentType);\n if (ctx.mentions.some((m) => m.isBot)) tags.push('@bot');\n if (ctx.threadId) tags.push(`thread:${ctx.threadId}`);\n if (ctx.resources.length > 0) {\n tags.push(`${ctx.resources.length} attachment(s)`);\n }\n const tagStr = tags.length > 0 ? ` [${tags.join(', ')}]` : '';\n\n core.system.enqueueSystemEvent(`Feishu[${account.accountId}] ${location} | ${sender}${tagStr}`, {\n sessionKey: route.sessionKey,\n contextKey: `feishu:message:${ctx.chatId}:${ctx.messageId}`,\n });\n\n return {\n ctx,\n accountScopedCfg,\n account,\n runtime,\n log,\n error,\n core,\n isGroup,\n isThread,\n feishuFrom,\n feishuTo,\n envelopeFrom,\n envelopeOptions,\n route,\n threadSessionKey: undefined,\n commandAuthorized: params.commandAuthorized,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Thread session resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve thread session key for thread-capable groups.\n *\n * Returns a thread-scoped session key when ALL conditions are met:\n * 1. `threadSession` config is enabled on the account\n * 2. The group is a topic group (chat_mode=topic) or uses thread\n * message mode (group_message_type=thread)\n *\n * The group info is fetched via `im.chat.get` with a 1-hour LRU cache\n * to minimise OAPI calls.\n */\nexport async function resolveThreadSessionKey(params: {\n accountScopedCfg: ClawdbotConfig;\n account: LarkAccount;\n chatId: string;\n threadId: string;\n baseSessionKey: string;\n}): Promise<string | undefined> {\n const { accountScopedCfg, account, chatId, threadId, baseSessionKey } = params;\n\n if (account.config?.threadSession !== true) return undefined;\n\n const threadCapable = await isThreadCapableGroup({\n cfg: accountScopedCfg,\n chatId,\n accountId: account.accountId,\n });\n if (!threadCapable) {\n log.info(`thread session skipped: group ${chatId} is not topic/thread mode`);\n return undefined;\n }\n\n // \u4F7F\u7528 SDK \u6807\u51C6\u51FD\u6570\uFF0C\u4FDD\u8BC1\u5206\u9694\u7B26\u683C\u5F0F\u4E0E resolveThreadParentSessionKey \u517C\u5BB9\n const { sessionKey } = resolveThreadSessionKeys({\n baseSessionKey,\n threadId,\n parentSessionKey: baseSessionKey,\n normalizeThreadId: (id) => id, // \u98DE\u4E66 thread ID (omt_xxx) \u533A\u5206\u5927\u5C0F\u5199\uFF0C\u4E0D\u505A lowercase\n });\n return sessionKey;\n}\n"],
5
+ "mappings": "AAYA,SAAS,gCAAgC;AAGzC,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AAErC,MAAM,MAAM,WAAW,0BAA0B;AAkC1C,SAAS,cAAc,SAA6C;AACzE,MAAI,QAAS,QAAO;AACpB,SAAO;AAAA,IACL,KAAK,IAAI,SAAoB,IAAI,KAAK,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,IAChE,OAAO,IAAI,SAAoB,IAAI,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,IACnE,MAAM,CAAC,SAAiB,QAAQ,KAAK,IAAI;AAAA,EAC3C;AACF;AAUO,SAAS,qBAAqB,QAMjB;AAClB,QAAM,EAAE,KAAK,SAAS,iBAAiB,IAAI;AAE3C,QAAM,UAAU,cAAc,OAAO,OAAO;AAC5C,QAAMA,OAAM,QAAQ;AACpB,QAAM,QAAQ,QAAQ;AACtB,QAAM,UAAU,IAAI,aAAa;AACjC,QAAM,WAAW,WAAW,QAAQ,IAAI,QAAQ;AAChD,QAAM,OAAO,WAAW;AAExB,QAAM,aAAa,UAAU,IAAI,QAAQ;AACzC,QAAM,WAAW,UAAU,QAAQ,IAAI,MAAM,KAAK,QAAQ,IAAI,QAAQ;AAEtE,QAAM,eAAe,UAAU,GAAG,IAAI,MAAM,IAAI,IAAI,QAAQ,KAAK,IAAI;AAErE,QAAM,kBAAkB,KAAK,QAAQ,MAAM,6BAA6B,gBAAgB;AAGxF,QAAM,QAAQ,KAAK,QAAQ,QAAQ,kBAAkB;AAAA,IACnD,KAAK;AAAA,IACL,SAAS;AAAA,IACT,WAAW,QAAQ;AAAA,IACnB,MAAM;AAAA,MACJ,MAAM,UAAU,UAAU;AAAA,MAC1B,IAAI,UAAU,IAAI,SAAS,IAAI;AAAA,IACjC;AAAA,EACF,CAAC;AAGD,QAAM,SAAS,IAAI,aAAa,GAAG,IAAI,UAAU,KAAK,IAAI,QAAQ,MAAM,IAAI;AAC5E,QAAM,WAAW,UAAU,SAAS,IAAI,MAAM,KAAK;AAEnD,QAAM,OAAiB,CAAC;AACxB,OAAK,KAAK,OAAO,IAAI,SAAS,EAAE;AAChC,MAAI,IAAI,SAAU,MAAK,KAAK,YAAY,IAAI,QAAQ,EAAE;AACtD,MAAI,IAAI,gBAAgB,OAAQ,MAAK,KAAK,IAAI,WAAW;AACzD,MAAI,IAAI,SAAS,KAAK,CAAC,MAAM,EAAE,KAAK,EAAG,MAAK,KAAK,MAAM;AACvD,MAAI,IAAI,SAAU,MAAK,KAAK,UAAU,IAAI,QAAQ,EAAE;AACpD,MAAI,IAAI,UAAU,SAAS,GAAG;AAC5B,SAAK,KAAK,GAAG,IAAI,UAAU,MAAM,gBAAgB;AAAA,EACnD;AACA,QAAM,SAAS,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,IAAI,CAAC,MAAM;AAE3D,OAAK,OAAO,mBAAmB,UAAU,QAAQ,SAAS,KAAK,QAAQ,MAAM,MAAM,GAAG,MAAM,IAAI;AAAA,IAC9F,YAAY,MAAM;AAAA,IAClB,YAAY,kBAAkB,IAAI,MAAM,IAAI,IAAI,SAAS;AAAA,EAC3D,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,IAClB,mBAAmB,OAAO;AAAA,EAC5B;AACF;AAiBA,eAAsB,wBAAwB,QAMd;AAC9B,QAAM,EAAE,kBAAkB,SAAS,QAAQ,UAAU,eAAe,IAAI;AAExE,MAAI,QAAQ,QAAQ,kBAAkB,KAAM,QAAO;AAEnD,QAAM,gBAAgB,MAAM,qBAAqB;AAAA,IAC/C,KAAK;AAAA,IACL;AAAA,IACA,WAAW,QAAQ;AAAA,EACrB,CAAC;AACD,MAAI,CAAC,eAAe;AAClB,QAAI,KAAK,iCAAiC,MAAM,2BAA2B;AAC3E,WAAO;AAAA,EACT;AAGA,QAAM,EAAE,WAAW,IAAI,yBAAyB;AAAA,IAC9C;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,IAClB,mBAAmB,CAAC,OAAO;AAAA;AAAA,EAC7B,CAAC;AACD,SAAO;AACT;",
6
+ "names": ["log"]
7
+ }
@@ -0,0 +1,150 @@
1
+ import { clearHistoryEntriesIfEnabled } from "openclaw/plugin-sdk";
2
+ import { larkLogger } from "../../core/lark-logger";
3
+ import { ticketElapsed } from "../../core/lark-ticket";
4
+ import { createFeishuReplyDispatcher } from "../../card/reply-dispatcher";
5
+ import { mentionedBot } from "./mention";
6
+ import {
7
+ buildQueueKey,
8
+ threadScopedKey,
9
+ registerActiveDispatcher,
10
+ unregisterActiveDispatcher
11
+ } from "../../channel/chat-queue";
12
+ import { isLikelyAbortText } from "../../channel/abort-detect";
13
+ import { buildDispatchContext, resolveThreadSessionKey } from "./dispatch-context";
14
+ import {
15
+ buildMessageBody,
16
+ buildBodyForAgent,
17
+ buildInboundPayload,
18
+ buildEnvelopeWithHistory
19
+ } from "./dispatch-builders";
20
+ import { dispatchPermissionNotification, dispatchSystemCommand } from "./dispatch-commands";
21
+ const log = larkLogger("inbound/dispatch");
22
+ async function dispatchNormalMessage(dc, ctxPayload, chatHistories, historyKey, historyLimit, replyToMessageId, skillFilter, skipTyping) {
23
+ if (isLikelyAbortText(dc.ctx.content?.trim() ?? "")) {
24
+ dc.log(`feishu[${dc.account.accountId}]: abort message detected, using plain-text dispatch`);
25
+ log.info("abort message detected, using plain-text dispatch");
26
+ await dispatchSystemCommand(dc, ctxPayload, false, replyToMessageId);
27
+ return;
28
+ }
29
+ const { dispatcher, replyOptions, markDispatchIdle, markFullyComplete, abortCard } = createFeishuReplyDispatcher({
30
+ cfg: dc.accountScopedCfg,
31
+ agentId: dc.route.agentId,
32
+ chatId: dc.ctx.chatId,
33
+ replyToMessageId: replyToMessageId ?? dc.ctx.messageId,
34
+ accountId: dc.account.accountId,
35
+ chatType: dc.ctx.chatType,
36
+ skipTyping,
37
+ replyInThread: dc.isThread
38
+ });
39
+ const abortController = new AbortController();
40
+ const queueKey = buildQueueKey(dc.account.accountId, dc.ctx.chatId, dc.ctx.threadId);
41
+ registerActiveDispatcher(queueKey, { abortCard, abortController });
42
+ const effectiveSessionKey = dc.threadSessionKey ?? dc.route.sessionKey;
43
+ dc.log(`feishu[${dc.account.accountId}]: dispatching to agent (session=${effectiveSessionKey})`);
44
+ log.info(`dispatching to agent (session=${effectiveSessionKey})`);
45
+ try {
46
+ const { queuedFinal, counts } = await dc.core.channel.reply.dispatchReplyFromConfig({
47
+ ctx: ctxPayload,
48
+ cfg: dc.accountScopedCfg,
49
+ dispatcher,
50
+ replyOptions: {
51
+ ...replyOptions,
52
+ abortSignal: abortController.signal,
53
+ ...skillFilter ? { skillFilter } : {}
54
+ }
55
+ });
56
+ await dispatcher.waitForIdle();
57
+ markFullyComplete();
58
+ markDispatchIdle();
59
+ if (dc.isGroup && historyKey && chatHistories) {
60
+ clearHistoryEntriesIfEnabled({
61
+ historyMap: chatHistories,
62
+ historyKey,
63
+ limit: historyLimit
64
+ });
65
+ }
66
+ dc.log(`feishu[${dc.account.accountId}]: dispatch complete (queuedFinal=${queuedFinal}, replies=${counts.final})`);
67
+ log.info(`dispatch complete (replies=${counts.final}, elapsed=${ticketElapsed()}ms)`);
68
+ } finally {
69
+ unregisterActiveDispatcher(queueKey);
70
+ }
71
+ }
72
+ async function dispatchToAgent(params) {
73
+ const dc = buildDispatchContext(params);
74
+ if (dc.isThread && dc.ctx.threadId) {
75
+ dc.threadSessionKey = await resolveThreadSessionKey({
76
+ accountScopedCfg: dc.accountScopedCfg,
77
+ account: dc.account,
78
+ chatId: dc.ctx.chatId,
79
+ threadId: dc.ctx.threadId,
80
+ baseSessionKey: dc.route.sessionKey
81
+ });
82
+ }
83
+ const messageBody = buildMessageBody(params.ctx, params.quotedContent);
84
+ if (params.permissionError) {
85
+ try {
86
+ await dispatchPermissionNotification(dc, params.permissionError, params.replyToMessageId);
87
+ } catch (err) {
88
+ dc.error(`feishu[${dc.account.accountId}]: permission notification failed, continuing: ${String(err)}`);
89
+ }
90
+ }
91
+ const { combinedBody, historyKey } = buildEnvelopeWithHistory(
92
+ dc,
93
+ messageBody,
94
+ params.chatHistories,
95
+ params.historyLimit
96
+ );
97
+ const bodyForAgent = buildBodyForAgent(params.ctx);
98
+ const threadHistoryKey = threadScopedKey(dc.ctx.chatId, dc.isThread ? dc.ctx.threadId : void 0);
99
+ const inboundHistory = dc.isGroup && params.chatHistories && params.historyLimit > 0 ? (params.chatHistories.get(threadHistoryKey) ?? []).map((entry) => ({
100
+ sender: entry.sender,
101
+ body: entry.body,
102
+ timestamp: entry.timestamp ?? Date.now()
103
+ })) : void 0;
104
+ const isBareNewOrReset = /^\/(?:new|reset)\s*$/i.test((params.ctx.content ?? "").trim());
105
+ const groupSystemPrompt = dc.isGroup ? params.groupConfig?.systemPrompt?.trim() || params.defaultGroupConfig?.systemPrompt?.trim() || void 0 : void 0;
106
+ const ctxPayload = buildInboundPayload(dc, {
107
+ body: combinedBody,
108
+ bodyForAgent,
109
+ rawBody: params.ctx.content,
110
+ commandBody: params.ctx.content,
111
+ senderName: params.ctx.senderName ?? params.ctx.senderId,
112
+ senderId: params.ctx.senderId,
113
+ messageSid: params.ctx.messageId,
114
+ wasMentioned: mentionedBot(params.ctx),
115
+ replyToBody: params.quotedContent,
116
+ inboundHistory,
117
+ extraFields: {
118
+ ...params.mediaPayload,
119
+ ...groupSystemPrompt ? { GroupSystemPrompt: groupSystemPrompt } : {},
120
+ ...dc.ctx.threadId ? { MessageThreadId: dc.ctx.threadId } : {}
121
+ }
122
+ });
123
+ const isCommand = dc.core.channel.commands.isControlCommandMessage(params.ctx.content, params.accountScopedCfg);
124
+ const skillFilter = dc.isGroup ? params.groupConfig?.skills ?? params.defaultGroupConfig?.skills : void 0;
125
+ if (isCommand) {
126
+ await dispatchSystemCommand(dc, ctxPayload, isBareNewOrReset, params.replyToMessageId);
127
+ if (isBareNewOrReset && dc.isGroup && historyKey && params.chatHistories) {
128
+ clearHistoryEntriesIfEnabled({
129
+ historyMap: params.chatHistories,
130
+ historyKey,
131
+ limit: params.historyLimit
132
+ });
133
+ }
134
+ } else {
135
+ await dispatchNormalMessage(
136
+ dc,
137
+ ctxPayload,
138
+ params.chatHistories,
139
+ historyKey,
140
+ params.historyLimit,
141
+ params.replyToMessageId,
142
+ skillFilter,
143
+ params.skipTyping
144
+ );
145
+ }
146
+ }
147
+ export {
148
+ dispatchToAgent
149
+ };
150
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/messaging/inbound/dispatch.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Agent dispatch for inbound Feishu messages.\n *\n * Builds the agent envelope, prepends chat history context, and\n * dispatches through the appropriate reply path (system command\n * vs. normal streaming/static flow).\n *\n * Implementation details are split across focused modules:\n * - dispatch-context.ts \u2014 DispatchContext type, route/session/event\n * - dispatch-builders.ts \u2014 pure payload/body/envelope construction\n * - dispatch-commands.ts \u2014 system command & permission notification\n */\n\nimport type { RuntimeEnv, HistoryEntry } from 'openclaw/plugin-sdk';\nimport { clearHistoryEntriesIfEnabled } from 'openclaw/plugin-sdk';\nimport type { MessageContext } from '../types';\nimport type { LarkAccount } from '../../core/types';\nimport type { FeishuGroupConfig } from '../../core/types';\nimport type { PermissionError } from './permission';\nimport { larkLogger } from '../../core/lark-logger';\nimport { ticketElapsed } from '../../core/lark-ticket';\nimport { createFeishuReplyDispatcher } from '../../card/reply-dispatcher';\nimport { mentionedBot } from './mention';\nimport {\n buildQueueKey,\n threadScopedKey,\n registerActiveDispatcher,\n unregisterActiveDispatcher,\n} from '../../channel/chat-queue';\nimport { isLikelyAbortText } from '../../channel/abort-detect';\nimport { type DispatchContext, buildDispatchContext, resolveThreadSessionKey } from './dispatch-context';\nimport {\n buildMessageBody,\n buildBodyForAgent,\n buildInboundPayload,\n buildEnvelopeWithHistory,\n} from './dispatch-builders';\nimport { dispatchPermissionNotification, dispatchSystemCommand } from './dispatch-commands';\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport { LarkClient } from '../../core/lark-client';\n\nconst log = larkLogger('inbound/dispatch');\n\n// ---------------------------------------------------------------------------\n// Internal: normal message dispatch\n// ---------------------------------------------------------------------------\n\n/**\n * Dispatch a normal (non-command) message via the streaming card flow.\n * Cleans up consumed history entries after dispatch completes.\n *\n * Note: history cleanup is intentionally placed here and NOT in the\n * system-command path \u2014 command handlers don't consume history context,\n * so the entries should be preserved for the next normal message.\n */\nasync function dispatchNormalMessage(\n dc: DispatchContext,\n ctxPayload: ReturnType<typeof LarkClient.runtime.channel.reply.finalizeInboundContext>,\n chatHistories: Map<string, HistoryEntry[]> | undefined,\n historyKey: string | undefined,\n historyLimit: number,\n replyToMessageId?: string,\n skillFilter?: string[],\n skipTyping?: boolean,\n): Promise<void> {\n // Abort messages should never create streaming cards \u2014 dispatch via the\n // plain-text system-command path so the SDK's abort handler can reply\n // without touching CardKit.\n if (isLikelyAbortText(dc.ctx.content?.trim() ?? '')) {\n dc.log(`feishu[${dc.account.accountId}]: abort message detected, using plain-text dispatch`);\n log.info('abort message detected, using plain-text dispatch');\n await dispatchSystemCommand(dc, ctxPayload, false, replyToMessageId);\n return;\n }\n\n const { dispatcher, replyOptions, markDispatchIdle, markFullyComplete, abortCard } = createFeishuReplyDispatcher({\n cfg: dc.accountScopedCfg,\n agentId: dc.route.agentId,\n chatId: dc.ctx.chatId,\n replyToMessageId: replyToMessageId ?? dc.ctx.messageId,\n accountId: dc.account.accountId,\n chatType: dc.ctx.chatType,\n skipTyping,\n replyInThread: dc.isThread,\n });\n\n // Create an AbortController so the abort fast-path can cancel the\n // underlying LLM request (not just the streaming card UI).\n const abortController = new AbortController();\n\n // Register the active dispatcher so the monitor abort fast-path can\n // terminate the streaming card before this task completes.\n const queueKey = buildQueueKey(dc.account.accountId, dc.ctx.chatId, dc.ctx.threadId);\n registerActiveDispatcher(queueKey, { abortCard, abortController });\n\n const effectiveSessionKey = dc.threadSessionKey ?? dc.route.sessionKey;\n dc.log(`feishu[${dc.account.accountId}]: dispatching to agent (session=${effectiveSessionKey})`);\n log.info(`dispatching to agent (session=${effectiveSessionKey})`);\n\n try {\n const { queuedFinal, counts } = await dc.core.channel.reply.dispatchReplyFromConfig({\n ctx: ctxPayload,\n cfg: dc.accountScopedCfg,\n dispatcher,\n replyOptions: {\n ...replyOptions,\n abortSignal: abortController.signal,\n ...(skillFilter ? { skillFilter } : {}),\n },\n });\n\n // Wait for all enqueued deliver() calls in the SDK's sendChain to\n // complete before marking the dispatch as done. Without this,\n // dispatchReplyFromConfig() may return while the final deliver() is\n // still pending in the Promise chain, causing markFullyComplete() to\n // block it and leaving completedText incomplete \u2014 which in turn makes\n // the streaming card's final update show truncated content.\n await dispatcher.waitForIdle();\n\n markFullyComplete();\n markDispatchIdle();\n\n // Clean up consumed history entries\n if (dc.isGroup && historyKey && chatHistories) {\n clearHistoryEntriesIfEnabled({\n historyMap: chatHistories,\n historyKey,\n limit: historyLimit,\n });\n }\n\n dc.log(`feishu[${dc.account.accountId}]: dispatch complete (queuedFinal=${queuedFinal}, replies=${counts.final})`);\n log.info(`dispatch complete (replies=${counts.final}, elapsed=${ticketElapsed()}ms)`);\n } finally {\n unregisterActiveDispatcher(queueKey);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport async function dispatchToAgent(params: {\n ctx: MessageContext;\n permissionError?: PermissionError;\n mediaPayload: Record<string, unknown>;\n quotedContent?: string;\n account: LarkAccount;\n /** account \u7EA7\u522B\u7684 ClawdbotConfig\uFF08channels.feishu \u5DF2\u66FF\u6362\u4E3A per-account \u5408\u5E76\u540E\u7684\u914D\u7F6E\uFF09 */\n accountScopedCfg: ClawdbotConfig;\n runtime?: RuntimeEnv;\n chatHistories?: Map<string, HistoryEntry[]>;\n historyLimit: number;\n /** Override the message ID used for reply threading. When set, the\n * reply-dispatcher uses this ID for typing indicators and card replies\n * instead of ctx.messageId (which may be a synthetic ID). */\n replyToMessageId?: string;\n /** When set, controls whether the sender is authorized to execute\n * control commands. Computed by the handler via the SDK's access\n * group command gating system. */\n commandAuthorized?: boolean;\n /** Per-group configuration for skills, systemPrompt, etc. */\n groupConfig?: FeishuGroupConfig;\n /** Default group configuration from the \"*\" wildcard entry. */\n defaultGroupConfig?: FeishuGroupConfig;\n /** When true, the reply dispatcher skips typing indicators. */\n skipTyping?: boolean;\n}): Promise<void> {\n // 1. Derive shared context (including route resolution + system event)\n const dc = buildDispatchContext(params);\n\n // 1b. Resolve thread session isolation (async: may query group info API)\n if (dc.isThread && dc.ctx.threadId) {\n dc.threadSessionKey = await resolveThreadSessionKey({\n accountScopedCfg: dc.accountScopedCfg,\n account: dc.account,\n chatId: dc.ctx.chatId,\n threadId: dc.ctx.threadId,\n baseSessionKey: dc.route.sessionKey,\n });\n }\n\n // 2. Build annotated message body\n const messageBody = buildMessageBody(params.ctx, params.quotedContent);\n\n // 3. Permission-error notification (optional side-effect).\n // Isolated so a failure here does not block the main message dispatch.\n if (params.permissionError) {\n try {\n await dispatchPermissionNotification(dc, params.permissionError, params.replyToMessageId);\n } catch (err) {\n dc.error(`feishu[${dc.account.accountId}]: permission notification failed, continuing: ${String(err)}`);\n }\n }\n\n // 4. Build main envelope (with group chat history)\n const { combinedBody, historyKey } = buildEnvelopeWithHistory(\n dc,\n messageBody,\n params.chatHistories,\n params.historyLimit,\n );\n\n // 5. Build BodyForAgent with mention annotation (if any).\n // SDK >= 2026.2.10 no longer falls back to Body for BodyForAgent,\n // so we must set it explicitly to preserve the annotation.\n const bodyForAgent = buildBodyForAgent(params.ctx);\n\n // 6. Build InboundHistory for SDK metadata injection (>= 2026.2.10).\n // The SDK's buildInboundUserContextPrefix renders these as structured\n // JSON blocks; earlier SDK versions simply ignore unknown fields.\n const threadHistoryKey = threadScopedKey(dc.ctx.chatId, dc.isThread ? dc.ctx.threadId : undefined);\n const inboundHistory =\n dc.isGroup && params.chatHistories && params.historyLimit > 0\n ? (params.chatHistories.get(threadHistoryKey) ?? []).map((entry) => ({\n sender: entry.sender,\n body: entry.body,\n timestamp: entry.timestamp ?? Date.now(),\n }))\n : undefined;\n\n // 7. Build inbound context payload\n const isBareNewOrReset = /^\\/(?:new|reset)\\s*$/i.test((params.ctx.content ?? '').trim());\n const groupSystemPrompt = dc.isGroup\n ? params.groupConfig?.systemPrompt?.trim() || params.defaultGroupConfig?.systemPrompt?.trim() || undefined\n : undefined;\n const ctxPayload = buildInboundPayload(dc, {\n body: combinedBody,\n bodyForAgent,\n rawBody: params.ctx.content,\n commandBody: params.ctx.content,\n senderName: params.ctx.senderName ?? params.ctx.senderId,\n senderId: params.ctx.senderId,\n messageSid: params.ctx.messageId,\n wasMentioned: mentionedBot(params.ctx),\n replyToBody: params.quotedContent,\n inboundHistory,\n extraFields: {\n ...params.mediaPayload,\n ...(groupSystemPrompt ? { GroupSystemPrompt: groupSystemPrompt } : {}),\n ...(dc.ctx.threadId ? { MessageThreadId: dc.ctx.threadId } : {}),\n },\n });\n\n // 8. Dispatch: system command vs. normal message\n const isCommand = dc.core.channel.commands.isControlCommandMessage(params.ctx.content, params.accountScopedCfg);\n\n // Resolve per-group skill filter (per-group > default \"*\")\n const skillFilter = dc.isGroup ? (params.groupConfig?.skills ?? params.defaultGroupConfig?.skills) : undefined;\n\n if (isCommand) {\n await dispatchSystemCommand(dc, ctxPayload, isBareNewOrReset, params.replyToMessageId);\n // /new and /reset explicitly start a new session \u2014 clear pending history\n if (isBareNewOrReset && dc.isGroup && historyKey && params.chatHistories) {\n clearHistoryEntriesIfEnabled({\n historyMap: params.chatHistories,\n historyKey,\n limit: params.historyLimit,\n });\n }\n } else {\n // Normal message dispatch; history cleanup happens inside.\n // System commands intentionally skip history cleanup \u2014 command handlers\n // don't consume history context, so entries are preserved for the next\n // normal message.\n await dispatchNormalMessage(\n dc,\n ctxPayload,\n params.chatHistories,\n historyKey,\n params.historyLimit,\n params.replyToMessageId,\n skillFilter,\n params.skipTyping,\n );\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }