@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,1045 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Streaming card controller for the Lark/Feishu channel plugin.
7
+ *
8
+ * Manages the full lifecycle of a streaming CardKit card:
9
+ * idle → creating → streaming → completed / aborted / terminated.
10
+ *
11
+ * Delegates throttling to FlushController and message-unavailable
12
+ * detection to UnavailableGuard.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.StreamingCardController = void 0;
16
+ exports.prepareTerminalCardContent = prepareTerminalCardContent;
17
+ const promises_1 = require("node:fs/promises");
18
+ const agent_runtime_1 = require("openclaw/plugin-sdk/agent-runtime");
19
+ const reply_runtime_1 = require("openclaw/plugin-sdk/reply-runtime");
20
+ const api_error_1 = require("../core/api-error.js");
21
+ const lark_logger_1 = require("../core/lark-logger.js");
22
+ const lark_client_1 = require("../core/lark-client.js");
23
+ const shutdown_hooks_1 = require("../core/shutdown-hooks.js");
24
+ const send_1 = require("../messaging/outbound/send.js");
25
+ const builder_1 = require("./builder.js");
26
+ const card_error_1 = require("./card-error.js");
27
+ const cardkit_1 = require("./cardkit.js");
28
+ const flush_controller_1 = require("./flush-controller.js");
29
+ const image_resolver_1 = require("./image-resolver.js");
30
+ const markdown_style_1 = require("./markdown-style.js");
31
+ const tool_use_display_1 = require("./tool-use-display.js");
32
+ const tool_use_trace_store_1 = require("./tool-use-trace-store.js");
33
+ const reply_dispatcher_types_1 = require("./reply-dispatcher-types.js");
34
+ const unavailable_guard_1 = require("./unavailable-guard.js");
35
+ const log = (0, lark_logger_1.larkLogger)('card/streaming');
36
+ // ---------------------------------------------------------------------------
37
+ // StreamingCardController
38
+ // ---------------------------------------------------------------------------
39
+ class StreamingCardController {
40
+ // ---- Explicit state machine ----
41
+ phase = 'idle';
42
+ // ---- Structured state ----
43
+ cardKit = {
44
+ cardKitCardId: null,
45
+ originalCardKitCardId: null,
46
+ cardKitSequence: 0,
47
+ cardMessageId: null,
48
+ };
49
+ text = {
50
+ accumulatedText: '',
51
+ completedText: '',
52
+ streamingPrefix: '',
53
+ lastPartialText: '',
54
+ lastFlushedText: '',
55
+ };
56
+ reasoning = {
57
+ accumulatedReasoningText: '',
58
+ reasoningStartTime: null,
59
+ reasoningElapsedMs: 0,
60
+ isReasoningPhase: false,
61
+ };
62
+ toolUse = {
63
+ startedAt: null,
64
+ elapsedMs: 0,
65
+ isActive: false,
66
+ };
67
+ // ---- Sub-controllers ----
68
+ flush;
69
+ guard;
70
+ imageResolver;
71
+ // ---- Lifecycle ----
72
+ createEpoch = 0;
73
+ _terminalReason = null;
74
+ dispatchFullyComplete = false;
75
+ cardCreationPromise = null;
76
+ disposeShutdownHook = null;
77
+ dispatchStartTime = Date.now();
78
+ // ---- Injected dependencies ----
79
+ deps;
80
+ elapsed() {
81
+ return Date.now() - this.dispatchStartTime;
82
+ }
83
+ needsFooterMetrics() {
84
+ const footer = this.deps.resolvedFooter;
85
+ return footer.tokens || footer.cache || footer.context || footer.model;
86
+ }
87
+ async getFooterSessionMetrics() {
88
+ try {
89
+ const runtime = lark_client_1.LarkClient.runtime;
90
+ if (!runtime)
91
+ return undefined;
92
+ const cfgWithSession = this.deps.cfg;
93
+ const sessionStorePath = cfgWithSession.sessions?.store ?? cfgWithSession.session?.store;
94
+ const key = this.deps.sessionKey.trim().toLowerCase();
95
+ // WORKAROUND: SDK session key round-trip bug.
96
+ // The SDK's toAgentRequestSessionKey() strips the agent scope from keys
97
+ // like "agent:hr:main" → "main", then toAgentStoreSessionKey() rebuilds
98
+ // using the default agent ID → "agent:main:main". This means metrics
99
+ // written by the SDK always land under "agent:<defaultAgentId>:…"
100
+ // regardless of the account-scoped agent ID the plugin routing generated.
101
+ // Fallback: when the primary key misses, try replacing the agent-id
102
+ // segment with the resolved default agent ID.
103
+ // TODO: remove once the SDK preserves the original agent ID during the
104
+ // request→store key round-trip.
105
+ const defaultAgentId = (0, agent_runtime_1.resolveDefaultAgentId)(this.deps.cfg);
106
+ const fallbackKey = key.replace(/^(agent):[^:]+:/, `$1:${defaultAgentId}:`);
107
+ const candidateKeys = fallbackKey !== key ? [key, fallbackKey] : [key];
108
+ const sessionApi = runtime.agent?.session;
109
+ if (sessionApi?.resolveStorePath && sessionApi?.loadSessionStore) {
110
+ const storePath = sessionApi.resolveStorePath(sessionStorePath, { agentId: this.deps.agentId });
111
+ const store = sessionApi.loadSessionStore(storePath);
112
+ let entry;
113
+ let matchedKey;
114
+ for (const candidate of candidateKeys) {
115
+ const val = store[candidate];
116
+ if (val && typeof val === 'object') {
117
+ entry = val;
118
+ matchedKey = candidate;
119
+ break;
120
+ }
121
+ }
122
+ if (!entry) {
123
+ log.debug('footer metrics lookup: session entry missing', {
124
+ sessionKey: this.deps.sessionKey,
125
+ candidateKeys,
126
+ storePath,
127
+ source: 'runtime.agent.session',
128
+ });
129
+ return undefined;
130
+ }
131
+ const metrics = {
132
+ inputTokens: typeof entry.inputTokens === 'number' ? entry.inputTokens : undefined,
133
+ outputTokens: typeof entry.outputTokens === 'number' ? entry.outputTokens : undefined,
134
+ cacheRead: typeof entry.cacheRead === 'number' ? entry.cacheRead : undefined,
135
+ cacheWrite: typeof entry.cacheWrite === 'number' ? entry.cacheWrite : undefined,
136
+ totalTokens: typeof entry.totalTokens === 'number' ? entry.totalTokens : undefined,
137
+ totalTokensFresh: typeof entry.totalTokensFresh === 'boolean' ? entry.totalTokensFresh : undefined,
138
+ contextTokens: typeof entry.contextTokens === 'number' ? entry.contextTokens : undefined,
139
+ model: typeof entry.model === 'string' ? entry.model : undefined,
140
+ };
141
+ log.debug('footer metrics lookup: session entry found', {
142
+ sessionKey: this.deps.sessionKey,
143
+ matchedKey,
144
+ storePath,
145
+ source: 'runtime.agent.session',
146
+ });
147
+ return metrics;
148
+ }
149
+ const channelSession = runtime.channel?.session;
150
+ if (!channelSession?.resolveStorePath) {
151
+ return undefined;
152
+ }
153
+ const storePath = channelSession.resolveStorePath(sessionStorePath, { agentId: this.deps.agentId });
154
+ const raw = await (0, promises_1.readFile)(storePath, 'utf8');
155
+ const parsed = JSON.parse(raw);
156
+ const store = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
157
+ ? parsed
158
+ : {};
159
+ let entry;
160
+ let matchedKey;
161
+ for (const candidate of candidateKeys) {
162
+ const val = store[candidate];
163
+ if (val && typeof val === 'object') {
164
+ entry = val;
165
+ matchedKey = candidate;
166
+ break;
167
+ }
168
+ }
169
+ if (!entry) {
170
+ log.debug('footer metrics lookup: session entry missing', {
171
+ sessionKey: this.deps.sessionKey,
172
+ candidateKeys,
173
+ storePath,
174
+ source: 'channel.session.file',
175
+ });
176
+ return undefined;
177
+ }
178
+ const metrics = {
179
+ inputTokens: typeof entry.inputTokens === 'number' ? entry.inputTokens : undefined,
180
+ outputTokens: typeof entry.outputTokens === 'number' ? entry.outputTokens : undefined,
181
+ cacheRead: typeof entry.cacheRead === 'number' ? entry.cacheRead : undefined,
182
+ cacheWrite: typeof entry.cacheWrite === 'number' ? entry.cacheWrite : undefined,
183
+ totalTokens: typeof entry.totalTokens === 'number' ? entry.totalTokens : undefined,
184
+ totalTokensFresh: typeof entry.totalTokensFresh === 'boolean' ? entry.totalTokensFresh : undefined,
185
+ contextTokens: typeof entry.contextTokens === 'number' ? entry.contextTokens : undefined,
186
+ model: typeof entry.model === 'string' ? entry.model : undefined,
187
+ };
188
+ log.debug('footer metrics lookup: session entry found', {
189
+ sessionKey: this.deps.sessionKey,
190
+ matchedKey,
191
+ storePath,
192
+ source: 'channel.session.file',
193
+ });
194
+ return metrics;
195
+ }
196
+ catch (err) {
197
+ log.warn('footer metrics lookup failed', { error: String(err), sessionKey: this.deps.sessionKey });
198
+ return undefined;
199
+ }
200
+ }
201
+ constructor(deps) {
202
+ this.deps = deps;
203
+ this.guard = new unavailable_guard_1.UnavailableGuard({
204
+ replyToMessageId: deps.replyToMessageId,
205
+ getCardMessageId: () => this.cardKit.cardMessageId,
206
+ onTerminate: () => {
207
+ this.transition('terminated', 'UnavailableGuard', 'unavailable');
208
+ },
209
+ });
210
+ this.flush = new flush_controller_1.FlushController(() => this.performFlush());
211
+ this.imageResolver = new image_resolver_1.ImageResolver({
212
+ cfg: deps.cfg,
213
+ accountId: deps.accountId,
214
+ onImageResolved: () => {
215
+ if (!this.isTerminalPhase && this.cardKit.cardMessageId) {
216
+ void this.throttledCardUpdate();
217
+ }
218
+ },
219
+ });
220
+ }
221
+ // ------------------------------------------------------------------
222
+ // Public accessors
223
+ // ------------------------------------------------------------------
224
+ get cardMessageId() {
225
+ return this.cardKit.cardMessageId;
226
+ }
227
+ get isTerminalPhase() {
228
+ return reply_dispatcher_types_1.TERMINAL_PHASES.has(this.phase);
229
+ }
230
+ /**
231
+ * Whether the card has been explicitly aborted (via abortCard()).
232
+ *
233
+ * Distinct from isTerminalPhase — creation_failed is NOT an abort;
234
+ * it should allow fallthrough to static delivery in the factory.
235
+ */
236
+ get isAborted() {
237
+ return this.phase === 'aborted';
238
+ }
239
+ /** Whether the reply pipeline was terminated due to an unavailable message. */
240
+ get isTerminated() {
241
+ return this.guard.isTerminated;
242
+ }
243
+ /** Check if the pipeline should skip further operations for this source. */
244
+ shouldSkipForUnavailable(source) {
245
+ return this.guard.shouldSkip(source);
246
+ }
247
+ /** Attempt to terminate the pipeline due to an unavailable message error. */
248
+ terminateIfUnavailable(source, err) {
249
+ return this.guard.terminate(source, err);
250
+ }
251
+ /** Why the controller entered a terminal phase, or null if still active. */
252
+ get terminalReason() {
253
+ return this._terminalReason;
254
+ }
255
+ /** @internal — exposed for test assertions only. */
256
+ get currentPhase() {
257
+ return this.phase;
258
+ }
259
+ get shouldDisplayToolUse() {
260
+ return this.deps.toolUseDisplay.showToolUse;
261
+ }
262
+ computeToolUseDisplay() {
263
+ if (!this.shouldDisplayToolUse)
264
+ return null;
265
+ const traceSteps = (0, tool_use_trace_store_1.getToolUseTraceSteps)(this.deps.sessionKey);
266
+ return (0, tool_use_display_1.normalizeToolUseDisplay)({
267
+ traceSteps,
268
+ showFullPaths: this.deps.toolUseDisplay.showFullPaths,
269
+ showResultDetails: this.deps.toolUseDisplay.showToolResultDetails,
270
+ });
271
+ }
272
+ get visibleToolUseElapsedMs() {
273
+ if (!this.shouldDisplayToolUse || !this.toolUse.startedAt) {
274
+ return undefined;
275
+ }
276
+ return this.toolUse.elapsedMs || Date.now() - this.toolUse.startedAt;
277
+ }
278
+ computeToolUseTitleSuffix(display) {
279
+ if (!this.shouldDisplayToolUse)
280
+ return undefined;
281
+ const stepCount = display?.stepCount ?? 0;
282
+ return stepCount > 0 ? (0, tool_use_display_1.buildToolUseTitleSuffix)({ stepCount }) : undefined;
283
+ }
284
+ // ------------------------------------------------------------------
285
+ // Unified callback guard
286
+ // ------------------------------------------------------------------
287
+ /**
288
+ * Unified callback guard — returns true if the pipeline is active
289
+ * and the callback should proceed.
290
+ *
291
+ * Combines three checks:
292
+ * 1. guard.isTerminated — message recalled/deleted
293
+ * 2. guard.shouldSkip(source) — eagerly detect unavailable messages
294
+ * 3. isTerminalPhase — completed/aborted/terminated/creation_failed
295
+ */
296
+ shouldProceed(source) {
297
+ if (this.guard.isTerminated || this.guard.shouldSkip(source))
298
+ return false;
299
+ return !this.isTerminalPhase;
300
+ }
301
+ // ------------------------------------------------------------------
302
+ // State machine
303
+ // ------------------------------------------------------------------
304
+ isStaleCreate(epoch) {
305
+ return epoch !== this.createEpoch;
306
+ }
307
+ transition(to, source, reason) {
308
+ const from = this.phase;
309
+ if (from === to)
310
+ return false;
311
+ if (!reply_dispatcher_types_1.PHASE_TRANSITIONS[from].has(to)) {
312
+ log.warn('phase transition rejected', { from, to, source });
313
+ return false;
314
+ }
315
+ this.phase = to;
316
+ log.info('phase transition', { from, to, source, reason });
317
+ if (reply_dispatcher_types_1.TERMINAL_PHASES.has(to)) {
318
+ this._terminalReason = reason ?? null;
319
+ this.onEnterTerminalPhase();
320
+ }
321
+ return true;
322
+ }
323
+ onEnterTerminalPhase() {
324
+ this.createEpoch += 1;
325
+ this.flush.cancelPendingFlush();
326
+ this.flush.complete();
327
+ this.disposeShutdownHook?.();
328
+ this.disposeShutdownHook = null;
329
+ if (this.phase === 'terminated' || this.phase === 'creation_failed') {
330
+ (0, tool_use_trace_store_1.clearToolUseTraceRun)(this.deps.sessionKey);
331
+ }
332
+ }
333
+ markToolUseActivity() {
334
+ if (!this.toolUse.startedAt) {
335
+ this.toolUse.startedAt = Date.now();
336
+ }
337
+ this.toolUse.elapsedMs = Date.now() - this.toolUse.startedAt;
338
+ this.toolUse.isActive = true;
339
+ }
340
+ captureToolUseElapsed() {
341
+ if (!this.toolUse.startedAt)
342
+ return;
343
+ this.toolUse.elapsedMs = Date.now() - this.toolUse.startedAt;
344
+ this.toolUse.isActive = false;
345
+ }
346
+ // ------------------------------------------------------------------
347
+ // SDK callback bindings
348
+ // ------------------------------------------------------------------
349
+ /**
350
+ * Handle a deliver() call in streaming card mode.
351
+ *
352
+ * Accumulates text from the SDK's deliver callbacks to build the
353
+ * authoritative "completedText" for the final card.
354
+ */
355
+ async onDeliver(payload) {
356
+ if (!this.shouldProceed('onDeliver'))
357
+ return;
358
+ const text = payload.text ?? '';
359
+ if (!text.trim())
360
+ return;
361
+ await this.ensureCardCreated();
362
+ if (!this.shouldProceed('onDeliver.postCreate'))
363
+ return;
364
+ if (!this.cardKit.cardMessageId)
365
+ return;
366
+ this.captureToolUseElapsed();
367
+ const split = (0, builder_1.splitReasoningText)(text);
368
+ if (split.reasoningText && !split.answerText) {
369
+ // Pure reasoning payload
370
+ this.reasoning.reasoningElapsedMs = this.reasoning.reasoningStartTime
371
+ ? Date.now() - this.reasoning.reasoningStartTime
372
+ : 0;
373
+ this.reasoning.accumulatedReasoningText = split.reasoningText;
374
+ this.reasoning.isReasoningPhase = true;
375
+ await this.throttledCardUpdate();
376
+ return;
377
+ }
378
+ // Answer payload (may also contain inline reasoning from tags)
379
+ this.reasoning.isReasoningPhase = false;
380
+ if (split.reasoningText) {
381
+ this.reasoning.accumulatedReasoningText = split.reasoningText;
382
+ }
383
+ const answerText = split.answerText ?? text;
384
+ // 累积 deliver 文本用于最终卡片
385
+ this.text.completedText += (this.text.completedText ? '\n\n' : '') + answerText;
386
+ // 没有流式数据时,用 deliver 文本显示在卡片上
387
+ if (!this.text.lastPartialText && !this.text.streamingPrefix) {
388
+ this.text.accumulatedText += (this.text.accumulatedText ? '\n\n' : '') + answerText;
389
+ this.text.streamingPrefix = this.text.accumulatedText;
390
+ await this.throttledCardUpdate();
391
+ }
392
+ }
393
+ async onReasoningStream(payload) {
394
+ if (!this.shouldProceed('onReasoningStream'))
395
+ return;
396
+ await this.ensureCardCreated();
397
+ if (!this.shouldProceed('onReasoningStream.postCreate'))
398
+ return;
399
+ if (!this.cardKit.cardMessageId)
400
+ return;
401
+ const rawText = payload.text ?? '';
402
+ if (!rawText)
403
+ return;
404
+ if (!this.reasoning.reasoningStartTime) {
405
+ this.reasoning.reasoningStartTime = Date.now();
406
+ }
407
+ this.reasoning.isReasoningPhase = true;
408
+ const split = (0, builder_1.splitReasoningText)(rawText);
409
+ this.reasoning.accumulatedReasoningText = split.reasoningText ?? rawText;
410
+ await this.throttledCardUpdate();
411
+ }
412
+ async onToolStart(payload) {
413
+ if (!this.shouldProceed('onToolStart'))
414
+ return;
415
+ if (!this.shouldDisplayToolUse)
416
+ return;
417
+ if (payload.phase && payload.phase !== 'start')
418
+ return;
419
+ this.markToolUseActivity();
420
+ await this.ensureCardCreated();
421
+ if (!this.shouldProceed('onToolStart.postCreate'))
422
+ return;
423
+ if (!this.cardKit.cardMessageId)
424
+ return;
425
+ if (!this.text.accumulatedText && this.cardKit.cardKitCardId) {
426
+ await this.throttledToolUseStatusUpdate();
427
+ return;
428
+ }
429
+ await this.throttledCardUpdate();
430
+ }
431
+ async onToolPayload(_payload) {
432
+ if (!this.shouldProceed('onToolPayload'))
433
+ return;
434
+ if (!this.shouldDisplayToolUse)
435
+ return;
436
+ this.markToolUseActivity();
437
+ await this.ensureCardCreated();
438
+ if (!this.shouldProceed('onToolPayload.postCreate'))
439
+ return;
440
+ if (!this.cardKit.cardMessageId)
441
+ return;
442
+ if (!this.text.accumulatedText && this.cardKit.cardKitCardId) {
443
+ await this.throttledToolUseStatusUpdate();
444
+ return;
445
+ }
446
+ await this.throttledCardUpdate();
447
+ }
448
+ async onPartialReply(payload) {
449
+ if (!this.shouldProceed('onPartialReply'))
450
+ return;
451
+ // Use splitReasoningText (consistent with onDeliver/onReasoningStream)
452
+ // to extract <think> tag content before stripping it from the answer.
453
+ // Previously only stripReasoningTags was called, silently discarding
454
+ // any thinking content that the LLM wrapped in <think> tags.
455
+ const rawText = payload.text ?? '';
456
+ const split = (0, builder_1.splitReasoningText)(rawText);
457
+ if (split.reasoningText) {
458
+ if (!this.reasoning.reasoningStartTime) {
459
+ this.reasoning.reasoningStartTime = Date.now();
460
+ }
461
+ this.reasoning.accumulatedReasoningText = split.reasoningText;
462
+ this.reasoning.isReasoningPhase = true;
463
+ }
464
+ const text = split.answerText ?? (0, builder_1.stripReasoningTags)(rawText);
465
+ log.debug('onPartialReply', { len: text.length });
466
+ if (!text)
467
+ return;
468
+ this.captureToolUseElapsed();
469
+ if (!this.reasoning.reasoningStartTime) {
470
+ this.reasoning.reasoningStartTime = Date.now();
471
+ }
472
+ if (this.reasoning.isReasoningPhase) {
473
+ this.reasoning.isReasoningPhase = false;
474
+ this.reasoning.reasoningElapsedMs = this.reasoning.reasoningStartTime
475
+ ? Date.now() - this.reasoning.reasoningStartTime
476
+ : 0;
477
+ }
478
+ // 检测回复边界:文本长度缩短 → 新回复开始
479
+ if (this.text.lastPartialText && text.length < this.text.lastPartialText.length) {
480
+ this.text.streamingPrefix += (this.text.streamingPrefix ? '\n\n' : '') + this.text.lastPartialText;
481
+ }
482
+ this.text.lastPartialText = text;
483
+ this.text.accumulatedText = this.text.streamingPrefix ? this.text.streamingPrefix + '\n\n' + text : text;
484
+ // NO_REPLY 缓冲
485
+ if (!this.text.streamingPrefix && reply_runtime_1.SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim())) {
486
+ log.debug('onPartialReply: buffering NO_REPLY prefix');
487
+ return;
488
+ }
489
+ await this.ensureCardCreated();
490
+ if (!this.shouldProceed('onPartialReply.postCreate'))
491
+ return;
492
+ if (!this.cardKit.cardMessageId)
493
+ return;
494
+ await this.throttledCardUpdate();
495
+ }
496
+ async onError(err, info) {
497
+ if (this.guard.terminate('onError', err))
498
+ return;
499
+ log.error(`${info.kind} reply failed`, { error: String(err) });
500
+ this.captureToolUseElapsed();
501
+ this.finalizeCard('onError', 'error');
502
+ await this.flush.waitForFlush();
503
+ if (this.cardCreationPromise)
504
+ await this.cardCreationPromise;
505
+ const errorEffectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
506
+ const footerMetrics = this.needsFooterMetrics() ? await this.getFooterSessionMetrics() : undefined;
507
+ const toolUseDisplay = this.computeToolUseDisplay();
508
+ try {
509
+ if (this.cardKit.cardMessageId) {
510
+ const rawErrorText = this.text.accumulatedText
511
+ ? `${this.text.accumulatedText}\n\n---\n**Error**: An error occurred while generating the response.`
512
+ : '**Error**: An error occurred while generating the response.';
513
+ const terminalContent = prepareTerminalCardContent({
514
+ text: rawErrorText,
515
+ reasoningText: this.reasoning.accumulatedReasoningText || undefined,
516
+ }, this.imageResolver);
517
+ const errorCard = (0, builder_1.buildCardContent)('complete', {
518
+ text: terminalContent.text,
519
+ reasoningText: terminalContent.reasoningText,
520
+ reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,
521
+ toolUseSteps: toolUseDisplay?.steps,
522
+ toolUseTitleSuffix: this.computeToolUseTitleSuffix(toolUseDisplay),
523
+ toolUseElapsedMs: this.visibleToolUseElapsedMs,
524
+ showToolUse: this.deps.toolUseDisplay.showToolUse,
525
+ elapsedMs: this.elapsed(),
526
+ isError: true,
527
+ footer: this.deps.resolvedFooter,
528
+ footerMetrics,
529
+ });
530
+ if (errorEffectiveCardId) {
531
+ await this.closeStreamingAndUpdate(errorEffectiveCardId, errorCard, 'onError');
532
+ }
533
+ else {
534
+ await (0, send_1.updateCardFeishu)({
535
+ cfg: this.deps.cfg,
536
+ messageId: this.cardKit.cardMessageId,
537
+ card: errorCard,
538
+ accountId: this.deps.accountId,
539
+ });
540
+ }
541
+ }
542
+ }
543
+ catch {
544
+ // Ignore update failures during error handling
545
+ }
546
+ finally {
547
+ (0, tool_use_trace_store_1.clearToolUseTraceRun)(this.deps.sessionKey);
548
+ }
549
+ }
550
+ async onIdle() {
551
+ if (this.guard.isTerminated || this.guard.shouldSkip('onIdle'))
552
+ return;
553
+ if (!this.dispatchFullyComplete)
554
+ return;
555
+ if (this.isTerminalPhase)
556
+ return;
557
+ this.captureToolUseElapsed();
558
+ this.finalizeCard('onIdle', 'normal');
559
+ await this.flush.waitForFlush();
560
+ if (this.cardCreationPromise) {
561
+ await this.cardCreationPromise;
562
+ await new Promise((resolve) => setTimeout(resolve, 0));
563
+ await this.flush.waitForFlush();
564
+ }
565
+ const idleEffectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
566
+ try {
567
+ if (this.cardKit.cardMessageId) {
568
+ if (idleEffectiveCardId) {
569
+ const seqBeforeClose = this.cardKit.cardKitSequence;
570
+ this.cardKit.cardKitSequence += 1;
571
+ log.info('onIdle: closing streaming mode', {
572
+ seqBefore: seqBeforeClose,
573
+ seqAfter: this.cardKit.cardKitSequence,
574
+ });
575
+ await (0, cardkit_1.setCardStreamingMode)({
576
+ cfg: this.deps.cfg,
577
+ cardId: idleEffectiveCardId,
578
+ streamingMode: false,
579
+ sequence: this.cardKit.cardKitSequence,
580
+ accountId: this.deps.accountId,
581
+ });
582
+ }
583
+ const isNoReplyLeak = !this.text.completedText && reply_runtime_1.SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim());
584
+ const displayText = this.text.completedText || (isNoReplyLeak ? '' : this.text.accumulatedText) || reply_dispatcher_types_1.EMPTY_REPLY_FALLBACK_TEXT;
585
+ if (!this.text.completedText && !this.text.accumulatedText) {
586
+ log.warn('reply completed without visible text, using empty-reply fallback');
587
+ }
588
+ // 等待图片异步解析(最多 15s),避免终态卡片留占位符
589
+ const resolvedDisplayText = await this.imageResolver.resolveImagesAwait(displayText, 15_000);
590
+ const idleToolUseDisplay = this.computeToolUseDisplay();
591
+ const terminalContent = prepareTerminalCardContent({
592
+ text: resolvedDisplayText,
593
+ reasoningText: this.reasoning.accumulatedReasoningText || undefined,
594
+ }, this.imageResolver);
595
+ const footerMetrics = this.needsFooterMetrics() ? await this.getFooterSessionMetrics() : undefined;
596
+ const completeCard = (0, builder_1.buildCardContent)('complete', {
597
+ text: terminalContent.text,
598
+ reasoningText: terminalContent.reasoningText,
599
+ reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,
600
+ toolUseSteps: idleToolUseDisplay?.steps,
601
+ toolUseTitleSuffix: this.computeToolUseTitleSuffix(idleToolUseDisplay),
602
+ toolUseElapsedMs: this.visibleToolUseElapsedMs,
603
+ showToolUse: this.deps.toolUseDisplay.showToolUse,
604
+ elapsedMs: this.elapsed(),
605
+ footer: this.deps.resolvedFooter,
606
+ footerMetrics,
607
+ });
608
+ if (idleEffectiveCardId) {
609
+ const seqBeforeUpdate = this.cardKit.cardKitSequence;
610
+ this.cardKit.cardKitSequence += 1;
611
+ log.info('onIdle: updating final card', {
612
+ seqBefore: seqBeforeUpdate,
613
+ seqAfter: this.cardKit.cardKitSequence,
614
+ });
615
+ await (0, cardkit_1.updateCardKitCard)({
616
+ cfg: this.deps.cfg,
617
+ cardId: idleEffectiveCardId,
618
+ card: (0, builder_1.toCardKit2)(completeCard),
619
+ sequence: this.cardKit.cardKitSequence,
620
+ accountId: this.deps.accountId,
621
+ });
622
+ }
623
+ else {
624
+ await (0, send_1.updateCardFeishu)({
625
+ cfg: this.deps.cfg,
626
+ messageId: this.cardKit.cardMessageId,
627
+ card: completeCard,
628
+ accountId: this.deps.accountId,
629
+ });
630
+ }
631
+ log.info('reply completed, card finalized', {
632
+ elapsedMs: this.elapsed(),
633
+ isCardKit: !!idleEffectiveCardId,
634
+ });
635
+ }
636
+ }
637
+ catch (err) {
638
+ log.warn('final card update failed', { error: String(err) });
639
+ }
640
+ finally {
641
+ (0, tool_use_trace_store_1.clearToolUseTraceRun)(this.deps.sessionKey);
642
+ }
643
+ }
644
+ // ------------------------------------------------------------------
645
+ // External control
646
+ // ------------------------------------------------------------------
647
+ markFullyComplete() {
648
+ log.debug('markFullyComplete', {
649
+ completedTextLen: this.text.completedText.length,
650
+ accumulatedTextLen: this.text.accumulatedText.length,
651
+ });
652
+ this.dispatchFullyComplete = true;
653
+ }
654
+ async abortCard() {
655
+ try {
656
+ this.captureToolUseElapsed();
657
+ if (!this.transition('aborted', 'abortCard', 'abort'))
658
+ return;
659
+ // transition() already executed onEnterTerminalPhase (cancel + complete + dispose hook)
660
+ // Only need to wait for any in-flight flush to finish
661
+ await this.flush.waitForFlush();
662
+ if (this.cardCreationPromise)
663
+ await this.cardCreationPromise;
664
+ const effectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
665
+ const elapsedMs = Date.now() - this.dispatchStartTime;
666
+ const abortToolUseDisplay = this.computeToolUseDisplay();
667
+ const terminalContent = prepareTerminalCardContent({
668
+ text: this.text.accumulatedText || 'Aborted.',
669
+ reasoningText: this.reasoning.accumulatedReasoningText || undefined,
670
+ }, this.imageResolver);
671
+ const footerMetrics = this.needsFooterMetrics() ? await this.getFooterSessionMetrics() : undefined;
672
+ if (effectiveCardId) {
673
+ const abortCardContent = (0, builder_1.buildCardContent)('complete', {
674
+ text: terminalContent.text,
675
+ reasoningText: terminalContent.reasoningText,
676
+ reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,
677
+ toolUseSteps: abortToolUseDisplay?.steps,
678
+ toolUseTitleSuffix: this.computeToolUseTitleSuffix(abortToolUseDisplay),
679
+ toolUseElapsedMs: this.visibleToolUseElapsedMs,
680
+ showToolUse: this.deps.toolUseDisplay.showToolUse,
681
+ elapsedMs,
682
+ isAborted: true,
683
+ footer: this.deps.resolvedFooter,
684
+ footerMetrics,
685
+ });
686
+ await this.closeStreamingAndUpdate(effectiveCardId, abortCardContent, 'abortCard');
687
+ log.info('abortCard completed', { effectiveCardId });
688
+ }
689
+ else if (this.cardKit.cardMessageId) {
690
+ // IM fallback: 卡片不是通过 CardKit 发的,用 im.message.patch 更新
691
+ const abortCard = (0, builder_1.buildCardContent)('complete', {
692
+ text: terminalContent.text,
693
+ reasoningText: terminalContent.reasoningText,
694
+ reasoningElapsedMs: this.reasoning.reasoningElapsedMs || undefined,
695
+ toolUseSteps: abortToolUseDisplay?.steps,
696
+ toolUseTitleSuffix: this.computeToolUseTitleSuffix(abortToolUseDisplay),
697
+ toolUseElapsedMs: this.visibleToolUseElapsedMs,
698
+ showToolUse: this.deps.toolUseDisplay.showToolUse,
699
+ elapsedMs,
700
+ isAborted: true,
701
+ footer: this.deps.resolvedFooter,
702
+ footerMetrics,
703
+ });
704
+ await (0, send_1.updateCardFeishu)({
705
+ cfg: this.deps.cfg,
706
+ messageId: this.cardKit.cardMessageId,
707
+ card: abortCard,
708
+ accountId: this.deps.accountId,
709
+ });
710
+ log.info('abortCard completed (IM fallback)', {
711
+ messageId: this.cardKit.cardMessageId,
712
+ });
713
+ }
714
+ }
715
+ catch (err) {
716
+ log.warn('abortCard failed', { error: String(err) });
717
+ }
718
+ finally {
719
+ (0, tool_use_trace_store_1.clearToolUseTraceRun)(this.deps.sessionKey);
720
+ }
721
+ }
722
+ // ------------------------------------------------------------------
723
+ // Internal: card creation
724
+ // ------------------------------------------------------------------
725
+ async ensureCardCreated() {
726
+ if (this.guard.shouldSkip('ensureCardCreated.precheck'))
727
+ return;
728
+ if (this.cardKit.cardMessageId || this.phase === 'creation_failed' || this.isTerminalPhase) {
729
+ return;
730
+ }
731
+ if (this.cardCreationPromise) {
732
+ await this.cardCreationPromise;
733
+ return;
734
+ }
735
+ if (!this.transition('creating', 'ensureCardCreated'))
736
+ return;
737
+ this.createEpoch += 1;
738
+ const epoch = this.createEpoch;
739
+ this.cardCreationPromise = (async () => {
740
+ try {
741
+ try {
742
+ // Step 1: Create card entity
743
+ const cId = await (0, cardkit_1.createCardEntity)({
744
+ cfg: this.deps.cfg,
745
+ card: (0, builder_1.buildStreamingThinkingCard)(this.deps.toolUseDisplay.showToolUse),
746
+ accountId: this.deps.accountId,
747
+ });
748
+ if (this.isStaleCreate(epoch)) {
749
+ log.info('ensureCardCreated: stale epoch after createCardEntity, bailing out', {
750
+ epoch,
751
+ phase: this.phase,
752
+ });
753
+ return;
754
+ }
755
+ if (cId) {
756
+ this.cardKit.cardKitCardId = cId;
757
+ this.cardKit.originalCardKitCardId = cId;
758
+ this.cardKit.cardKitSequence = 1;
759
+ this.disposeShutdownHook = (0, shutdown_hooks_1.registerShutdownHook)(`streaming-card:${cId}`, () => this.abortCard());
760
+ log.info('created CardKit entity', {
761
+ cardId: cId,
762
+ initialSequence: this.cardKit.cardKitSequence,
763
+ });
764
+ // Step 2: Send IM message referencing card_id
765
+ const result = await (0, cardkit_1.sendCardByCardId)({
766
+ cfg: this.deps.cfg,
767
+ to: this.deps.chatId,
768
+ cardId: cId,
769
+ replyToMessageId: this.deps.replyToMessageId,
770
+ replyInThread: this.deps.replyInThread,
771
+ accountId: this.deps.accountId,
772
+ });
773
+ if (this.isStaleCreate(epoch)) {
774
+ log.info('ensureCardCreated: stale epoch after sendCardByCardId, bailing out', {
775
+ epoch,
776
+ phase: this.phase,
777
+ });
778
+ this.disposeShutdownHook?.();
779
+ this.disposeShutdownHook = null;
780
+ return;
781
+ }
782
+ this.cardKit.cardMessageId = result.messageId;
783
+ this.flush.setCardMessageReady(true);
784
+ if (!this.transition('streaming', 'ensureCardCreated.cardkit')) {
785
+ this.disposeShutdownHook?.();
786
+ this.disposeShutdownHook = null;
787
+ return;
788
+ }
789
+ log.info('sent CardKit card', { messageId: result.messageId });
790
+ }
791
+ else {
792
+ throw new Error('card.create returned empty card_id');
793
+ }
794
+ }
795
+ catch (cardKitErr) {
796
+ if (this.isStaleCreate(epoch))
797
+ return;
798
+ if (this.guard.terminate('ensureCardCreated.cardkitFlow', cardKitErr)) {
799
+ return;
800
+ }
801
+ // CardKit flow failed — fall back to regular IM card
802
+ const apiDetail = extractApiDetail(cardKitErr);
803
+ log.warn('CardKit flow failed, falling back to IM', { apiDetail });
804
+ this.cardKit.cardKitCardId = null;
805
+ this.cardKit.originalCardKitCardId = null;
806
+ const fallbackCard = (0, builder_1.buildCardContent)('streaming', {
807
+ showToolUse: this.deps.toolUseDisplay.showToolUse,
808
+ });
809
+ const result = await (0, send_1.sendCardFeishu)({
810
+ cfg: this.deps.cfg,
811
+ to: this.deps.chatId,
812
+ card: fallbackCard,
813
+ replyToMessageId: this.deps.replyToMessageId,
814
+ replyInThread: this.deps.replyInThread,
815
+ accountId: this.deps.accountId,
816
+ });
817
+ if (this.isStaleCreate(epoch)) {
818
+ log.info('ensureCardCreated: stale epoch after IM fallback send, bailing out', {
819
+ epoch,
820
+ phase: this.phase,
821
+ });
822
+ return;
823
+ }
824
+ this.cardKit.cardMessageId = result.messageId;
825
+ this.flush.setCardMessageReady(true);
826
+ if (!this.transition('streaming', 'ensureCardCreated.imFallback')) {
827
+ return;
828
+ }
829
+ log.info('sent fallback IM card', { messageId: result.messageId });
830
+ }
831
+ }
832
+ catch (err) {
833
+ if (this.isStaleCreate(epoch))
834
+ return;
835
+ if (this.guard.terminate('ensureCardCreated.outer', err)) {
836
+ return;
837
+ }
838
+ log.warn('thinking card failed, falling back to static', {
839
+ error: String(err),
840
+ });
841
+ this.transition('creation_failed', 'ensureCardCreated.outer', 'creation_failed');
842
+ }
843
+ })();
844
+ await this.cardCreationPromise;
845
+ }
846
+ // ------------------------------------------------------------------
847
+ // Internal: flush
848
+ // ------------------------------------------------------------------
849
+ async performFlush() {
850
+ if (!this.cardKit.cardMessageId || this.isTerminalPhase)
851
+ return;
852
+ // v2 CardKit 卡片不能走 IM patch,如果流式 CardKit 已禁用但 originalCardKitCardId
853
+ // 仍在,说明卡片是通过 CardKit 发的——跳过中间态更新,等终态用 originalCardKitCardId 收尾
854
+ if (!this.cardKit.cardKitCardId && this.cardKit.originalCardKitCardId) {
855
+ log.debug('performFlush: skipping (CardKit streaming disabled, awaiting final update)');
856
+ return;
857
+ }
858
+ log.debug('flushCardUpdate: enter', {
859
+ seq: this.cardKit.cardKitSequence,
860
+ isCardKit: !!this.cardKit.cardKitCardId,
861
+ });
862
+ try {
863
+ const displayText = this.buildDisplayText();
864
+ // 流式中间帧使用同步 resolveImages(不等待异步上传)
865
+ const resolvedText = this.imageResolver.resolveImages(displayText);
866
+ if (this.cardKit.cardKitCardId) {
867
+ if (resolvedText !== this.text.lastFlushedText) {
868
+ const prevSeq = this.cardKit.cardKitSequence;
869
+ this.cardKit.cardKitSequence += 1;
870
+ log.debug('flushCardUpdate: answer seq bump', {
871
+ seqBefore: prevSeq,
872
+ seqAfter: this.cardKit.cardKitSequence,
873
+ });
874
+ await (0, cardkit_1.streamCardContent)({
875
+ cfg: this.deps.cfg,
876
+ cardId: this.cardKit.cardKitCardId,
877
+ elementId: builder_1.STREAMING_ELEMENT_ID,
878
+ content: (0, markdown_style_1.optimizeMarkdownStyle)(resolvedText),
879
+ sequence: this.cardKit.cardKitSequence,
880
+ accountId: this.deps.accountId,
881
+ });
882
+ this.text.lastFlushedText = resolvedText;
883
+ }
884
+ }
885
+ else {
886
+ log.debug('flushCardUpdate: IM patch fallback');
887
+ const flushDisplay = this.computeToolUseDisplay();
888
+ const card = (0, builder_1.buildCardContent)('streaming', {
889
+ text: this.reasoning.isReasoningPhase ? '' : resolvedText,
890
+ reasoningText: this.reasoning.isReasoningPhase ? this.reasoning.accumulatedReasoningText : undefined,
891
+ toolUseSteps: flushDisplay?.steps,
892
+ toolUseTitleSuffix: this.computeToolUseTitleSuffix(flushDisplay),
893
+ showToolUse: this.deps.toolUseDisplay.showToolUse,
894
+ });
895
+ await (0, send_1.updateCardFeishu)({
896
+ cfg: this.deps.cfg,
897
+ messageId: this.cardKit.cardMessageId,
898
+ card: card,
899
+ accountId: this.deps.accountId,
900
+ });
901
+ }
902
+ }
903
+ catch (err) {
904
+ if (this.guard.terminate('flushCardUpdate', err))
905
+ return;
906
+ const apiCode = (0, api_error_1.extractLarkApiCode)(err);
907
+ // 速率限制(230020)— 跳过此帧,不降级
908
+ if ((0, card_error_1.isCardRateLimitError)(err)) {
909
+ log.info('flushCardUpdate: rate limited (230020), skipping', {
910
+ seq: this.cardKit.cardKitSequence,
911
+ });
912
+ return;
913
+ }
914
+ // 卡片表格数超出飞书限制(230099/11310)— 禁用 CardKit 流式,
915
+ // 保留 originalCardKitCardId 供 onIdle 做最终 CardKit 更新
916
+ if ((0, card_error_1.isCardTableLimitError)(err)) {
917
+ log.warn('flushCardUpdate: card table limit exceeded (230099/11310), disabling CardKit streaming', {
918
+ seq: this.cardKit.cardKitSequence,
919
+ });
920
+ this.cardKit.cardKitCardId = null;
921
+ return;
922
+ }
923
+ const apiDetail = extractApiDetail(err);
924
+ log.error('card stream update failed', {
925
+ apiCode,
926
+ seq: this.cardKit.cardKitSequence,
927
+ apiDetail,
928
+ });
929
+ if (this.cardKit.cardKitCardId) {
930
+ log.warn('disabling CardKit streaming, falling back to im.message.patch');
931
+ this.cardKit.cardKitCardId = null;
932
+ }
933
+ }
934
+ }
935
+ buildDisplayText() {
936
+ if (this.reasoning.isReasoningPhase && this.reasoning.accumulatedReasoningText) {
937
+ const reasoningDisplay = `💭 **Thinking...**\n\n${this.reasoning.accumulatedReasoningText}`;
938
+ return this.text.accumulatedText ? this.text.accumulatedText + '\n\n' + reasoningDisplay : reasoningDisplay;
939
+ }
940
+ return this.text.accumulatedText;
941
+ }
942
+ async throttledCardUpdate() {
943
+ if (this.guard.shouldSkip('throttledCardUpdate'))
944
+ return;
945
+ const throttleMs = this.cardKit.cardKitCardId ? reply_dispatcher_types_1.THROTTLE_CONSTANTS.CARDKIT_MS : reply_dispatcher_types_1.THROTTLE_CONSTANTS.PATCH_MS;
946
+ await this.flush.throttledUpdate(throttleMs);
947
+ }
948
+ // ---- Tool-use status streaming (pre-answer phase) ----
949
+ lastToolUseStatusUpdateTime = 0;
950
+ async throttledToolUseStatusUpdate() {
951
+ if (!this.cardKit.cardKitCardId)
952
+ return;
953
+ const now = Date.now();
954
+ if (now - this.lastToolUseStatusUpdateTime < reply_dispatcher_types_1.THROTTLE_CONSTANTS.REASONING_STATUS_MS)
955
+ return;
956
+ this.lastToolUseStatusUpdateTime = now;
957
+ await this.updateToolUseStatus();
958
+ }
959
+ async updateToolUseStatus() {
960
+ if (!this.cardKit.cardKitCardId || this.isTerminalPhase)
961
+ return;
962
+ try {
963
+ const display = this.computeToolUseDisplay();
964
+ const card = (0, builder_1.buildStreamingPreAnswerCard)({
965
+ steps: display?.steps,
966
+ elapsedMs: this.visibleToolUseElapsedMs,
967
+ showToolUse: this.shouldDisplayToolUse,
968
+ });
969
+ this.cardKit.cardKitSequence += 1;
970
+ await (0, cardkit_1.updateCardKitCard)({
971
+ cfg: this.deps.cfg,
972
+ cardId: this.cardKit.cardKitCardId,
973
+ card,
974
+ sequence: this.cardKit.cardKitSequence,
975
+ accountId: this.deps.accountId,
976
+ });
977
+ }
978
+ catch (err) {
979
+ log.debug('updateToolUseStatus failed', { error: String(err) });
980
+ }
981
+ }
982
+ // ------------------------------------------------------------------
983
+ // Internal: lifecycle helpers
984
+ // ------------------------------------------------------------------
985
+ finalizeCard(source, reason) {
986
+ this.transition('completed', source, reason);
987
+ }
988
+ /**
989
+ * Close streaming mode then update card content (shared by onError and abortCard).
990
+ */
991
+ async closeStreamingAndUpdate(cardId, card, label) {
992
+ const seqBeforeClose = this.cardKit.cardKitSequence;
993
+ this.cardKit.cardKitSequence += 1;
994
+ log.info(`${label}: closing streaming mode`, {
995
+ seqBefore: seqBeforeClose,
996
+ seqAfter: this.cardKit.cardKitSequence,
997
+ });
998
+ await (0, cardkit_1.setCardStreamingMode)({
999
+ cfg: this.deps.cfg,
1000
+ cardId,
1001
+ streamingMode: false,
1002
+ sequence: this.cardKit.cardKitSequence,
1003
+ accountId: this.deps.accountId,
1004
+ });
1005
+ const seqBeforeUpdate = this.cardKit.cardKitSequence;
1006
+ this.cardKit.cardKitSequence += 1;
1007
+ log.info(`${label}: updating card`, {
1008
+ seqBefore: seqBeforeUpdate,
1009
+ seqAfter: this.cardKit.cardKitSequence,
1010
+ });
1011
+ await (0, cardkit_1.updateCardKitCard)({
1012
+ cfg: this.deps.cfg,
1013
+ cardId,
1014
+ card: (0, builder_1.toCardKit2)(card),
1015
+ sequence: this.cardKit.cardKitSequence,
1016
+ accountId: this.deps.accountId,
1017
+ });
1018
+ }
1019
+ }
1020
+ exports.StreamingCardController = StreamingCardController;
1021
+ // ---------------------------------------------------------------------------
1022
+ // Error detail extraction helpers (replacing `any` casts)
1023
+ // ---------------------------------------------------------------------------
1024
+ /**
1025
+ * 终态卡片的正文和 reasoning 都会被飞书按 markdown 渲染,
1026
+ * 因此两者都要先做图片替换与表格降级,避免再次撞到 230099/11310。
1027
+ */
1028
+ function prepareTerminalCardContent(content, imageResolver, tableLimit = card_error_1.FEISHU_CARD_TABLE_LIMIT) {
1029
+ const resolvedReasoningText = content.reasoningText ? imageResolver.resolveImages(content.reasoningText) : undefined;
1030
+ const resolvedText = imageResolver.resolveImages(content.text);
1031
+ const sanitizedSegments = (0, card_error_1.sanitizeTextSegmentsForCard)(resolvedReasoningText ? [resolvedReasoningText, resolvedText] : [resolvedText], tableLimit);
1032
+ if (resolvedReasoningText) {
1033
+ return {
1034
+ reasoningText: sanitizedSegments[0],
1035
+ text: sanitizedSegments[1],
1036
+ };
1037
+ }
1038
+ return { text: sanitizedSegments[0] };
1039
+ }
1040
+ function extractApiDetail(err) {
1041
+ if (!err || typeof err !== 'object')
1042
+ return String(err);
1043
+ const e = err;
1044
+ return e.response?.data ? JSON.stringify(e.response.data) : String(err);
1045
+ }