@yanhaidao/wecom 2.4.120 → 2.5.110

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 (323) hide show
  1. package/README.md +4 -5
  2. package/dist/index.js +68 -0
  3. package/dist/src/accounts.js +20 -0
  4. package/dist/src/agent/handler.js +895 -0
  5. package/dist/src/agent/index.js +5 -0
  6. package/dist/src/app/account-runtime.js +216 -0
  7. package/dist/src/app/bootstrap.js +19 -0
  8. package/dist/src/app/index.js +118 -0
  9. package/dist/src/capability/agent/delivery-service.js +63 -0
  10. package/dist/src/capability/agent/fallback-policy.js +6 -0
  11. package/dist/src/capability/agent/ingress-service.js +33 -0
  12. package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
  13. package/dist/src/capability/bot/dispatch-config.js +45 -0
  14. package/dist/src/capability/bot/fallback-delivery.js +147 -0
  15. package/dist/src/capability/bot/local-path-delivery.js +178 -0
  16. package/dist/src/capability/bot/sandbox-media.js +138 -0
  17. package/dist/src/capability/bot/service.js +49 -0
  18. package/dist/src/capability/bot/stream-delivery.js +321 -0
  19. package/dist/src/capability/bot/stream-finalizer.js +81 -0
  20. package/dist/src/capability/bot/stream-orchestrator.js +318 -0
  21. package/dist/src/capability/bot/types.js +1 -0
  22. package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
  23. package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
  24. package/dist/src/capability/calendar/tool.js +365 -0
  25. package/dist/src/capability/calendar/types.js +12 -0
  26. package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
  27. package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
  28. package/dist/src/capability/doc/tool.js +1556 -0
  29. package/dist/src/capability/doc/types.js +113 -0
  30. package/dist/src/capability/mcp/index.js +3 -0
  31. package/dist/src/capability/mcp/schema.js +102 -0
  32. package/dist/src/capability/mcp/tool.js +146 -0
  33. package/dist/src/capability/mcp/transport.js +293 -0
  34. package/dist/src/channel.js +224 -0
  35. package/dist/src/config/accounts.js +236 -0
  36. package/dist/src/config/derived-paths.js +31 -0
  37. package/dist/src/config/index.js +7 -0
  38. package/dist/src/config/media.js +110 -0
  39. package/dist/src/config/network.js +32 -0
  40. package/dist/src/config/routing.js +20 -0
  41. package/dist/src/config/runtime-config.js +25 -0
  42. package/dist/src/config/schema.js +4 -0
  43. package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
  44. package/dist/src/context-store.js +219 -0
  45. package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
  46. package/dist/src/crypto/index.js +9 -0
  47. package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
  48. package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
  49. package/dist/src/crypto.js +145 -0
  50. package/dist/src/domain/models.js +1 -0
  51. package/dist/src/domain/policies.js +32 -0
  52. package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
  53. package/dist/src/gateway-monitor.js +139 -0
  54. package/dist/src/http.js +114 -0
  55. package/{src/media.ts → dist/src/media.js} +21 -40
  56. package/dist/src/monitor/limits.js +7 -0
  57. package/dist/src/monitor/state.js +28 -0
  58. package/dist/src/monitor.js +84 -0
  59. package/dist/src/observability/audit-log.js +30 -0
  60. package/dist/src/observability/legacy-operational-event-store.js +22 -0
  61. package/dist/src/observability/raw-envelope-log.js +24 -0
  62. package/dist/src/observability/status-registry.js +9 -0
  63. package/dist/src/observability/transport-session-view.js +14 -0
  64. package/dist/src/onboarding.js +546 -0
  65. package/dist/src/outbound.js +557 -0
  66. package/dist/src/runtime/dispatcher.js +57 -0
  67. package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
  68. package/dist/src/runtime/outbound-intent.js +1 -0
  69. package/dist/src/runtime/reply-orchestrator.js +38 -0
  70. package/dist/src/runtime/routing-bridge.js +26 -0
  71. package/dist/src/runtime/session-manager.js +112 -0
  72. package/dist/src/runtime/source-registry.js +174 -0
  73. package/dist/src/runtime.js +1 -0
  74. package/dist/src/shared/command-auth.js +57 -0
  75. package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
  76. package/dist/src/shared/media-asset.js +65 -0
  77. package/dist/src/shared/media-service.js +59 -0
  78. package/dist/src/shared/media-types.js +1 -0
  79. package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
  80. package/dist/src/store/active-reply-store.js +41 -0
  81. package/dist/src/store/interfaces.js +1 -0
  82. package/dist/src/store/memory-store.js +33 -0
  83. package/dist/src/store/stream-batch-store.js +319 -0
  84. package/{src/target.ts → dist/src/target.js} +15 -48
  85. package/dist/src/transport/agent-api/client.js +168 -0
  86. package/dist/src/transport/agent-api/core.js +337 -0
  87. package/dist/src/transport/agent-api/delivery.js +28 -0
  88. package/dist/src/transport/agent-api/media-upload.js +4 -0
  89. package/dist/src/transport/agent-api/reply.js +24 -0
  90. package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
  91. package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
  92. package/dist/src/transport/agent-api/upstream-reply.js +26 -0
  93. package/dist/src/transport/agent-callback/http-handler.js +30 -0
  94. package/dist/src/transport/agent-callback/inbound.js +4 -0
  95. package/dist/src/transport/agent-callback/reply.js +8 -0
  96. package/dist/src/transport/agent-callback/request-handler.js +189 -0
  97. package/dist/src/transport/agent-callback/session.js +15 -0
  98. package/dist/src/transport/bot-webhook/active-reply.js +27 -0
  99. package/dist/src/transport/bot-webhook/http-handler.js +31 -0
  100. package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
  101. package/dist/src/transport/bot-webhook/inbound.js +4 -0
  102. package/dist/src/transport/bot-webhook/message-shape.js +98 -0
  103. package/dist/src/transport/bot-webhook/protocol.js +124 -0
  104. package/dist/src/transport/bot-webhook/reply.js +9 -0
  105. package/dist/src/transport/bot-webhook/request-handler.js +285 -0
  106. package/dist/src/transport/bot-webhook/session.js +15 -0
  107. package/dist/src/transport/bot-ws/inbound.js +147 -0
  108. package/dist/src/transport/bot-ws/media.js +236 -0
  109. package/dist/src/transport/bot-ws/reply.js +310 -0
  110. package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
  111. package/dist/src/transport/bot-ws/session.js +15 -0
  112. package/dist/src/transport/http/common.js +78 -0
  113. package/dist/src/transport/http/registry.js +71 -0
  114. package/dist/src/transport/http/request-handler.js +51 -0
  115. package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
  116. package/dist/src/types/account.js +1 -0
  117. package/dist/src/types/config.js +1 -0
  118. package/dist/src/types/constants.js +28 -0
  119. package/dist/src/types/events.js +1 -0
  120. package/dist/src/types/index.js +1 -0
  121. package/dist/src/types/legacy-stream.js +1 -0
  122. package/dist/src/types/message.js +5 -0
  123. package/dist/src/types/runtime-context.js +1 -0
  124. package/dist/src/types/runtime.js +1 -0
  125. package/dist/src/types.js +1 -0
  126. package/dist/src/upstream/index.js +111 -0
  127. package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
  128. package/openclaw.plugin.json +15 -0
  129. package/package.json +18 -1
  130. package/.github/workflows/release.yml +0 -143
  131. package/GOVERNANCE.md +0 -26
  132. package/MENU_EVENT_CONF.md +0 -500
  133. package/MENU_EVENT_PLAN.md +0 -440
  134. package/SKILLS_CAL.md +0 -895
  135. package/SKILLS_DOC.md +0 -2288
  136. package/UPSTREAM_CONFIG.md +0 -170
  137. package/UPSTREAM_PLAN.md +0 -175
  138. package/assets/01.bot-add.png +0 -0
  139. package/assets/01.bot-setp2.png +0 -0
  140. package/assets/01.image.jpg +0 -0
  141. package/assets/02.agent.add.png +0 -0
  142. package/assets/02.agent.api-set.png +0 -0
  143. package/assets/02.image.jpg +0 -0
  144. package/assets/03.agent.page.png +0 -0
  145. package/assets/03.bot.page.png +0 -0
  146. package/assets/link-me.jpg +0 -0
  147. package/assets/register.png +0 -0
  148. package/changelog/v2.2.28.md +0 -70
  149. package/changelog/v2.3.10.md +0 -17
  150. package/changelog/v2.3.11.md +0 -19
  151. package/changelog/v2.3.12.md +0 -25
  152. package/changelog/v2.3.13.md +0 -19
  153. package/changelog/v2.3.14.md +0 -48
  154. package/changelog/v2.3.15.md +0 -15
  155. package/changelog/v2.3.16.md +0 -11
  156. package/changelog/v2.3.18.md +0 -22
  157. package/changelog/v2.3.19.md +0 -73
  158. package/changelog/v2.3.2.md +0 -28
  159. package/changelog/v2.3.26.md +0 -21
  160. package/changelog/v2.3.27.md +0 -33
  161. package/changelog/v2.3.4.md +0 -20
  162. package/changelog/v2.3.9.md +0 -22
  163. package/changelog/v2.4.12.md +0 -37
  164. package/compat-single-account.md +0 -148
  165. package/index.test.ts +0 -38
  166. package/scripts/test-proxy.ts +0 -70
  167. package/scripts/wecom/README.md +0 -123
  168. package/scripts/wecom/menu-click-help.js +0 -59
  169. package/scripts/wecom/menu-click-help.py +0 -55
  170. package/src/accounts.ts +0 -34
  171. package/src/agent/api-client.upload.test.ts +0 -109
  172. package/src/agent/event-router.test.ts +0 -421
  173. package/src/agent/event-router.ts +0 -272
  174. package/src/agent/handler.event-filter.test.ts +0 -135
  175. package/src/agent/handler.ts +0 -1250
  176. package/src/agent/index.ts +0 -12
  177. package/src/agent/script-runner.ts +0 -186
  178. package/src/agent/test-fixtures/invalid-json-script.mjs +0 -1
  179. package/src/agent/test-fixtures/reply-event-script.mjs +0 -29
  180. package/src/agent/test-fixtures/reply-event-script.py +0 -17
  181. package/src/app/account-runtime.ts +0 -276
  182. package/src/app/bootstrap.ts +0 -29
  183. package/src/app/index.ts +0 -192
  184. package/src/capability/agent/delivery-service.ts +0 -87
  185. package/src/capability/agent/fallback-policy.ts +0 -13
  186. package/src/capability/agent/ingress-service.ts +0 -38
  187. package/src/capability/agent/upstream-delivery-service.ts +0 -96
  188. package/src/capability/bot/dispatch-config.ts +0 -47
  189. package/src/capability/bot/fallback-delivery.ts +0 -178
  190. package/src/capability/bot/local-path-delivery.ts +0 -215
  191. package/src/capability/bot/sandbox-media.test.ts +0 -221
  192. package/src/capability/bot/sandbox-media.ts +0 -176
  193. package/src/capability/bot/service.ts +0 -56
  194. package/src/capability/bot/stream-delivery.ts +0 -379
  195. package/src/capability/bot/stream-finalizer.ts +0 -120
  196. package/src/capability/bot/stream-orchestrator.ts +0 -371
  197. package/src/capability/bot/types.ts +0 -8
  198. package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
  199. package/src/capability/calendar/tool.ts +0 -417
  200. package/src/capability/calendar/types.ts +0 -309
  201. package/src/capability/doc/tool.ts +0 -1629
  202. package/src/capability/doc/types.ts +0 -792
  203. package/src/capability/mcp/index.ts +0 -10
  204. package/src/capability/mcp/schema.ts +0 -107
  205. package/src/capability/mcp/tool.ts +0 -174
  206. package/src/capability/mcp/transport.ts +0 -394
  207. package/src/channel.config.test.ts +0 -180
  208. package/src/channel.lifecycle.test.ts +0 -255
  209. package/src/channel.meta.test.ts +0 -26
  210. package/src/channel.ts +0 -256
  211. package/src/config/accounts.resolve.test.ts +0 -75
  212. package/src/config/accounts.ts +0 -312
  213. package/src/config/derived-paths.test.ts +0 -111
  214. package/src/config/derived-paths.ts +0 -41
  215. package/src/config/index.ts +0 -22
  216. package/src/config/media.test.ts +0 -113
  217. package/src/config/media.ts +0 -139
  218. package/src/config/network.ts +0 -20
  219. package/src/config/routing.test.ts +0 -88
  220. package/src/config/routing.ts +0 -26
  221. package/src/config/runtime-config.ts +0 -46
  222. package/src/config/schema.ts +0 -144
  223. package/src/context-store.ts +0 -297
  224. package/src/crypto/index.ts +0 -24
  225. package/src/crypto.test.ts +0 -32
  226. package/src/crypto.ts +0 -176
  227. package/src/domain/models.ts +0 -7
  228. package/src/domain/policies.ts +0 -36
  229. package/src/dynamic-agent.account-scope.test.ts +0 -17
  230. package/src/gateway-monitor.ts +0 -181
  231. package/src/http.ts +0 -137
  232. package/src/media.test.ts +0 -82
  233. package/src/monitor/limits.ts +0 -7
  234. package/src/monitor/state.queue.test.ts +0 -185
  235. package/src/monitor/state.ts +0 -34
  236. package/src/monitor.active.test.ts +0 -245
  237. package/src/monitor.inbound-filter.test.ts +0 -63
  238. package/src/monitor.integration.test.ts +0 -208
  239. package/src/monitor.ts +0 -121
  240. package/src/monitor.webhook.test.ts +0 -774
  241. package/src/observability/audit-log.ts +0 -48
  242. package/src/observability/legacy-operational-event-store.ts +0 -36
  243. package/src/observability/raw-envelope-log.ts +0 -28
  244. package/src/observability/status-registry.ts +0 -13
  245. package/src/observability/transport-session-view.ts +0 -14
  246. package/src/onboarding.test.ts +0 -336
  247. package/src/onboarding.ts +0 -704
  248. package/src/outbound.test.ts +0 -1271
  249. package/src/outbound.ts +0 -746
  250. package/src/runtime/dispatcher.ts +0 -71
  251. package/src/runtime/outbound-intent.ts +0 -4
  252. package/src/runtime/reply-orchestrator.test.ts +0 -71
  253. package/src/runtime/reply-orchestrator.ts +0 -67
  254. package/src/runtime/routing-bridge.test.ts +0 -115
  255. package/src/runtime/routing-bridge.ts +0 -44
  256. package/src/runtime/session-manager.test.ts +0 -174
  257. package/src/runtime/session-manager.ts +0 -139
  258. package/src/runtime/source-registry.ts +0 -249
  259. package/src/runtime.ts +0 -14
  260. package/src/shared/command-auth.ts +0 -87
  261. package/src/shared/media-asset.ts +0 -78
  262. package/src/shared/media-service.test.ts +0 -111
  263. package/src/shared/media-service.ts +0 -84
  264. package/src/shared/media-types.ts +0 -5
  265. package/src/shared/xml-parser.test.ts +0 -50
  266. package/src/store/active-reply-store.ts +0 -42
  267. package/src/store/interfaces.ts +0 -11
  268. package/src/store/memory-store.ts +0 -43
  269. package/src/store/stream-batch-store.ts +0 -350
  270. package/src/transport/agent-api/client.ts +0 -277
  271. package/src/transport/agent-api/core.ts +0 -463
  272. package/src/transport/agent-api/delivery.ts +0 -41
  273. package/src/transport/agent-api/media-upload.ts +0 -11
  274. package/src/transport/agent-api/reply.ts +0 -39
  275. package/src/transport/agent-api/upstream-delivery.ts +0 -45
  276. package/src/transport/agent-api/upstream-media-upload.ts +0 -70
  277. package/src/transport/agent-api/upstream-reply.ts +0 -43
  278. package/src/transport/agent-callback/http-handler.ts +0 -47
  279. package/src/transport/agent-callback/inbound.ts +0 -5
  280. package/src/transport/agent-callback/reply.ts +0 -13
  281. package/src/transport/agent-callback/request-handler.ts +0 -244
  282. package/src/transport/agent-callback/session.ts +0 -23
  283. package/src/transport/bot-webhook/active-reply.ts +0 -39
  284. package/src/transport/bot-webhook/http-handler.ts +0 -48
  285. package/src/transport/bot-webhook/inbound-normalizer.ts +0 -371
  286. package/src/transport/bot-webhook/inbound.ts +0 -5
  287. package/src/transport/bot-webhook/message-shape.ts +0 -89
  288. package/src/transport/bot-webhook/protocol.ts +0 -148
  289. package/src/transport/bot-webhook/reply.ts +0 -15
  290. package/src/transport/bot-webhook/request-handler.ts +0 -394
  291. package/src/transport/bot-webhook/session.ts +0 -23
  292. package/src/transport/bot-ws/inbound.test.ts +0 -96
  293. package/src/transport/bot-ws/inbound.ts +0 -116
  294. package/src/transport/bot-ws/media.test.ts +0 -44
  295. package/src/transport/bot-ws/media.ts +0 -321
  296. package/src/transport/bot-ws/reply.test.ts +0 -450
  297. package/src/transport/bot-ws/reply.ts +0 -365
  298. package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
  299. package/src/transport/bot-ws/sdk-adapter.ts +0 -314
  300. package/src/transport/bot-ws/session.ts +0 -28
  301. package/src/transport/http/common.ts +0 -109
  302. package/src/transport/http/registry.ts +0 -92
  303. package/src/transport/http/request-handler.ts +0 -84
  304. package/src/types/account.ts +0 -72
  305. package/src/types/config.ts +0 -166
  306. package/src/types/constants.ts +0 -31
  307. package/src/types/events.ts +0 -21
  308. package/src/types/global.d.ts +0 -9
  309. package/src/types/index.ts +0 -17
  310. package/src/types/legacy-stream.ts +0 -50
  311. package/src/types/message.ts +0 -187
  312. package/src/types/runtime-context.ts +0 -28
  313. package/src/types/runtime.ts +0 -165
  314. package/src/types.ts +0 -41
  315. package/src/upstream/index.ts +0 -150
  316. package/src/upstream.test.ts +0 -84
  317. package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
  318. package/tsconfig.json +0 -22
  319. package/vitest.config.ts +0 -26
  320. /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
  321. /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
  322. /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
  323. /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
@@ -0,0 +1,557 @@
1
+ import { WecomAgentDeliveryService } from "./capability/agent/index.js";
2
+ import { WecomUpstreamAgentDeliveryService } from "./capability/agent/upstream-delivery-service.js";
3
+ import { resolveWecomMergedMediaLocalRoots, resolveWecomMediaMaxBytes, resolveWecomAccount, resolveWecomAccountConflict, resolveWecomAccounts, } from "./config/index.js";
4
+ import { getAccountRuntime, getActiveBotWsReplyHandle, getBotWsPushHandle, getWecomRuntime, } from "./runtime.js";
5
+ import { getPeerUpstreamCorpId } from "./context-store.js";
6
+ import { resolveWecomSourceSnapshot } from "./runtime/source-registry.js";
7
+ import { resolveOutboundMediaAsset } from "./shared/media-asset.js";
8
+ import { resolveScopedWecomTarget } from "./target.js";
9
+ import { toWeComMarkdownV2 } from "./wecom_msg_adapter/markdown_adapter.js";
10
+ import { parseUpstreamAgentSessionTarget, createUpstreamAgentConfig, resolveUpstreamCorpConfig } from "./upstream/index.js";
11
+ function resolveOutboundContext(params) {
12
+ const rawTo = String(params.to ?? "").trim();
13
+ const fallbackAccountId = params.accountId?.trim();
14
+ const scoped = resolveScopedWecomTarget(params.to, fallbackAccountId);
15
+ const scopedAccountId = scoped?.accountId?.trim() || fallbackAccountId;
16
+ const peerId = scoped?.target.touser?.trim() || scoped?.target.chatid?.trim();
17
+ const peerKind = scoped?.target.chatid ? "group" : scoped?.target.touser ? "direct" : undefined;
18
+ const source = scopedAccountId
19
+ ? resolveWecomSourceSnapshot({
20
+ accountId: scopedAccountId,
21
+ sessionKey: params.sessionKey,
22
+ peerKind,
23
+ peerId,
24
+ })
25
+ : undefined;
26
+ const peerUpstreamCorpId = scopedAccountId && peerKind === "direct" && peerId
27
+ ? getPeerUpstreamCorpId(scopedAccountId, peerId)?.trim()
28
+ : undefined;
29
+ return {
30
+ rawTo,
31
+ explicitAgentTarget: isExplicitAgentTarget(params.to),
32
+ scopedAccountId,
33
+ peerKind,
34
+ peerId,
35
+ source,
36
+ peerUpstreamCorpId,
37
+ };
38
+ }
39
+ function logOutboundDecision(params) {
40
+ const resolved = resolveOutboundContext(params);
41
+ const runtimeAccountId = resolved.scopedAccountId || params.accountId?.trim();
42
+ const logger = runtimeAccountId ? getAccountRuntime(runtimeAccountId)?.log.info : undefined;
43
+ logger?.(`[wecom-outbound] ${params.phase} rawTo=${resolved.rawTo || "N/A"} scopedAccount=${resolved.scopedAccountId ?? "N/A"} ` +
44
+ `peer=${resolved.peerKind && resolved.peerId ? `${resolved.peerKind}:${resolved.peerId}` : "N/A"} ` +
45
+ `explicitAgent=${String(resolved.explicitAgentTarget)} source=${resolved.source?.source ?? "none"} ` +
46
+ `sourceUpstreamCorpId=${resolved.source?.upstreamCorpId ?? "none"} peerUpstreamCorpId=${resolved.peerUpstreamCorpId ?? "none"} ` +
47
+ `sessionKey=${params.sessionKey?.trim() || "N/A"} textLen=${String(params.textLen ?? 0)} ` +
48
+ `mediaUrl=${params.mediaUrl ?? "N/A"}${params.extra ? ` ${params.extra}` : ""}`);
49
+ }
50
+ function resolveOutboundAccountOrThrow(params) {
51
+ const resolvedAccounts = resolveWecomAccounts(params.cfg);
52
+ const conflictAccountId = params.accountId?.trim() || resolvedAccounts.defaultAccountId;
53
+ const conflict = resolveWecomAccountConflict({
54
+ cfg: params.cfg,
55
+ accountId: conflictAccountId,
56
+ });
57
+ if (conflict) {
58
+ throw new Error(conflict.message);
59
+ }
60
+ const requestedAccountId = params.accountId?.trim();
61
+ if (requestedAccountId) {
62
+ if (!resolvedAccounts.accounts[requestedAccountId]) {
63
+ throw new Error(`WeCom outbound account "${requestedAccountId}" not found. Configure channels.wecom.accounts.${requestedAccountId} or use an existing accountId.`);
64
+ }
65
+ }
66
+ return resolveWecomAccount({
67
+ cfg: params.cfg,
68
+ accountId: params.accountId,
69
+ });
70
+ }
71
+ function resolveAgentConfigOrThrow(params) {
72
+ const account = resolveOutboundAccountOrThrow(params).agent;
73
+ if (!account?.apiConfigured) {
74
+ throw new Error(`WeCom outbound requires Agent mode for account=${params.accountId ?? "default"}. Configure channels.wecom.accounts.<accountId>.agent (or legacy channels.wecom.agent).`);
75
+ }
76
+ if (typeof account.agentId !== "number" || !Number.isFinite(account.agentId)) {
77
+ throw new Error(`WeCom outbound requires channels.wecom.accounts.<accountId>.agent.agentId (or legacy channels.wecom.agent.agentId) for account=${params.accountId ?? account.accountId}.`);
78
+ }
79
+ // 注意:不要在日志里输出 corpSecret 等敏感信息
80
+ getAccountRuntime(account.accountId)?.log.info?.(`[wecom-outbound] Using agent config: accountId=${account.accountId}, corpId=${account.corpId}, agentId=${account.agentId}`);
81
+ return account;
82
+ }
83
+ function isExplicitAgentTarget(raw) {
84
+ return /^wecom-agent(?:-upstream)?:/i.test(String(raw ?? "").trim());
85
+ }
86
+ function isAgentConversationTarget(params) {
87
+ if (isExplicitAgentTarget(params.to)) {
88
+ return true;
89
+ }
90
+ const fallbackAccountId = params.accountId?.trim();
91
+ const scoped = resolveScopedWecomTarget(params.to, fallbackAccountId);
92
+ const resolvedAccountId = scoped?.accountId?.trim() || fallbackAccountId;
93
+ if (!resolvedAccountId) {
94
+ return false;
95
+ }
96
+ const peerId = scoped?.target.touser?.trim() || scoped?.target.chatid?.trim();
97
+ const peerKind = scoped?.target.chatid ? "group" : scoped?.target.touser ? "direct" : undefined;
98
+ const source = resolveWecomSourceSnapshot({
99
+ accountId: resolvedAccountId,
100
+ sessionKey: params.sessionKey,
101
+ peerKind,
102
+ peerId,
103
+ });
104
+ return source?.source === "agent-callback";
105
+ }
106
+ /**
107
+ * 解析上下游目标,返回解析后的信息或 undefined
108
+ */
109
+ function resolveUpstreamTarget(params) {
110
+ const parsedExplicit = parseUpstreamAgentSessionTarget(params.to ?? "");
111
+ const isExplicitUpstreamTarget = Boolean(parsedExplicit);
112
+ const parsed = (() => {
113
+ if (parsedExplicit) {
114
+ return parsedExplicit;
115
+ }
116
+ const fallbackAccountId = params.accountId?.trim();
117
+ const scoped = resolveScopedWecomTarget(params.to, fallbackAccountId);
118
+ const toUser = scoped?.target.touser?.trim();
119
+ const resolvedAccountId = scoped?.accountId?.trim() || fallbackAccountId;
120
+ if (!toUser || !resolvedAccountId) {
121
+ return undefined;
122
+ }
123
+ const source = resolveWecomSourceSnapshot({
124
+ accountId: resolvedAccountId,
125
+ sessionKey: params.sessionKey,
126
+ peerKind: "direct",
127
+ peerId: toUser,
128
+ });
129
+ const upstreamCorpId = source?.upstreamCorpId?.trim() || getPeerUpstreamCorpId(resolvedAccountId, toUser)?.trim();
130
+ if (!upstreamCorpId) {
131
+ return undefined;
132
+ }
133
+ return {
134
+ accountId: resolvedAccountId,
135
+ upstreamCorpId,
136
+ userId: toUser,
137
+ };
138
+ })();
139
+ if (!parsed) {
140
+ return undefined;
141
+ }
142
+ const { accountId, upstreamCorpId, userId } = parsed;
143
+ const account = resolveOutboundAccountOrThrow({ cfg: params.cfg, accountId });
144
+ if (!account.agent?.apiConfigured) {
145
+ if (isExplicitUpstreamTarget) {
146
+ throw new Error(`WeCom upstream outbound requires Agent mode for account=${accountId}.`);
147
+ }
148
+ return undefined;
149
+ }
150
+ // 查找上下游配置
151
+ const upstreamConfig = resolveUpstreamCorpConfig({
152
+ upstreamCorpId,
153
+ upstreamCorps: account.agent.config.upstreamCorps,
154
+ });
155
+ if (!upstreamConfig) {
156
+ if (isExplicitUpstreamTarget) {
157
+ throw new Error(`WeCom upstream outbound: no upstream corp config found for corpId=${upstreamCorpId}. ` +
158
+ `Please configure channels.wecom.accounts.${accountId}.agent.upstreamCorps with corpId=${upstreamCorpId}.`);
159
+ }
160
+ return undefined;
161
+ }
162
+ // 创建上下游 Agent 配置
163
+ // 注意:使用下游企业的 corpId 和 agentId,但保持主企业的 corpSecret
164
+ const upstreamAgent = createUpstreamAgentConfig({
165
+ baseAgent: account.agent,
166
+ upstreamCorpId,
167
+ upstreamAgentId: upstreamConfig.agentId,
168
+ });
169
+ return { upstreamAgent, primaryAgent: account.agent, toUser: userId };
170
+ }
171
+ function resolveBotWsChatTarget(params) {
172
+ const scoped = resolveScopedWecomTarget(params.to, params.accountId);
173
+ if (!scoped) {
174
+ return undefined;
175
+ }
176
+ if (scoped.accountId && scoped.accountId !== params.accountId) {
177
+ throw new Error(`WeCom outbound account mismatch: target belongs to account=${scoped.accountId}, current account=${params.accountId}.`);
178
+ }
179
+ if (scoped.target.chatid) {
180
+ return scoped.target.chatid;
181
+ }
182
+ if (scoped.target.touser) {
183
+ return scoped.target.touser;
184
+ }
185
+ return undefined;
186
+ }
187
+ function resolveOutboundPeer(params) {
188
+ const scoped = resolveScopedWecomTarget(params.to, params.accountId);
189
+ if (!scoped) {
190
+ return undefined;
191
+ }
192
+ if (scoped.accountId && scoped.accountId !== params.accountId) {
193
+ return undefined;
194
+ }
195
+ if (scoped.target.chatid) {
196
+ return { peerKind: "group", peerId: scoped.target.chatid };
197
+ }
198
+ if (scoped.target.touser) {
199
+ return { peerKind: "direct", peerId: scoped.target.touser };
200
+ }
201
+ return undefined;
202
+ }
203
+ function shouldPreferBotWsOutbound(params) {
204
+ const account = resolveOutboundAccountOrThrow({
205
+ cfg: params.cfg,
206
+ accountId: params.accountId,
207
+ });
208
+ const peer = resolveOutboundPeer({
209
+ to: params.to,
210
+ accountId: account.accountId,
211
+ });
212
+ const source = resolveWecomSourceSnapshot({
213
+ accountId: account.accountId,
214
+ sessionKey: params.sessionKey,
215
+ peerKind: peer?.peerKind,
216
+ peerId: peer?.peerId,
217
+ });
218
+ const pinnedToAgent = source?.source === "agent-callback";
219
+ const pinnedToBotWs = source?.source === "bot-ws";
220
+ return {
221
+ preferred: !isExplicitAgentTarget(params.to) &&
222
+ !pinnedToAgent &&
223
+ Boolean(account.bot?.configured &&
224
+ account.bot.wsConfigured &&
225
+ (pinnedToBotWs || account.bot.primaryTransport === "ws")),
226
+ accountId: account.accountId,
227
+ };
228
+ }
229
+ function markActiveBotWsReplyHandleActivity(params) {
230
+ const peer = resolveOutboundPeer({
231
+ to: params.to,
232
+ accountId: params.accountId,
233
+ });
234
+ const handle = getActiveBotWsReplyHandle({
235
+ accountId: params.accountId,
236
+ sessionKey: params.sessionKey,
237
+ peerKind: peer?.peerKind,
238
+ peerId: peer?.peerId,
239
+ });
240
+ handle?.markExternalActivity?.();
241
+ }
242
+ async function sendTextViaBotWs(params) {
243
+ const { preferred, accountId } = shouldPreferBotWsOutbound(params);
244
+ if (!preferred) {
245
+ return false;
246
+ }
247
+ const chatId = resolveBotWsChatTarget({
248
+ to: params.to,
249
+ accountId,
250
+ });
251
+ if (!chatId) {
252
+ return false;
253
+ }
254
+ const handle = getBotWsPushHandle(accountId);
255
+ if (!handle) {
256
+ throw new Error(`WeCom outbound account=${accountId} is configured for Bot WS active push, but no live WS runtime is registered.`);
257
+ }
258
+ if (!handle.isConnected()) {
259
+ throw new Error(`WeCom outbound account=${accountId} is configured for Bot WS active push, but the WS transport is not connected.`);
260
+ }
261
+ const markdownText = toWeComMarkdownV2(params.text);
262
+ console.log(`[wecom-outbound] Sending Bot WS active message to target=${String(params.to ?? "")} chatId=${chatId} (len=${markdownText.length})`);
263
+ await handle.sendMarkdown(chatId, markdownText);
264
+ markActiveBotWsReplyHandleActivity({
265
+ accountId,
266
+ sessionKey: params.sessionKey,
267
+ to: params.to,
268
+ });
269
+ console.log(`[wecom-outbound] Successfully sent Bot WS active message to ${chatId}`);
270
+ return true;
271
+ }
272
+ async function sendMediaViaBotWs(params) {
273
+ const { preferred, accountId } = shouldPreferBotWsOutbound(params);
274
+ if (!preferred) {
275
+ return { attempted: false, sent: false };
276
+ }
277
+ const chatId = resolveBotWsChatTarget({
278
+ to: params.to,
279
+ accountId,
280
+ });
281
+ if (!chatId) {
282
+ return { attempted: false, sent: false };
283
+ }
284
+ const handle = getBotWsPushHandle(accountId);
285
+ if (!handle) {
286
+ throw new Error(`WeCom outbound account=${accountId} is configured for Bot WS active push, but no live WS runtime is registered.`);
287
+ }
288
+ if (!handle.isConnected()) {
289
+ throw new Error(`WeCom outbound account=${accountId} is configured for Bot WS active push, but the WS transport is not connected.`);
290
+ }
291
+ console.log(`[wecom-outbound] Sending Bot WS media to target=${String(params.to ?? "")} chatId=${chatId} media=${params.mediaUrl}`);
292
+ const effectiveMediaLocalRoots = resolveWecomMergedMediaLocalRoots({
293
+ cfg: params.cfg,
294
+ baseRoots: params.mediaLocalRoots,
295
+ });
296
+ const result = await handle.sendMedia({
297
+ chatId,
298
+ mediaUrl: params.mediaUrl,
299
+ text: params.text,
300
+ mediaLocalRoots: effectiveMediaLocalRoots,
301
+ maxBytes: resolveWecomMediaMaxBytes(params.cfg, accountId),
302
+ });
303
+ if (result.ok) {
304
+ markActiveBotWsReplyHandleActivity({
305
+ accountId,
306
+ sessionKey: params.sessionKey,
307
+ to: params.to,
308
+ });
309
+ console.log(`[wecom-outbound] Successfully sent Bot WS media to ${chatId}`);
310
+ return { attempted: true, sent: true };
311
+ }
312
+ const reason = result.rejectReason || result.error || "unknown";
313
+ console.warn(`[wecom-outbound] Bot WS media failed for ${chatId}: ${reason}`);
314
+ return { attempted: true, sent: false, reason };
315
+ }
316
+ export const wecomOutbound = {
317
+ deliveryMode: "direct",
318
+ chunkerMode: "text",
319
+ textChunkLimit: 20480,
320
+ chunker: (text, limit) => {
321
+ try {
322
+ return getWecomRuntime().channel.text.chunkText(text, limit);
323
+ }
324
+ catch {
325
+ return [text];
326
+ }
327
+ },
328
+ sendText: async ({ cfg, to, text, accountId, sessionKey }) => {
329
+ // signal removed - not supported in current SDK
330
+ // Defer Agent resolution until the Agent fallback path
331
+ // sendTextViaBotWs() can already deliver without Agent mode
332
+ // 体验优化:/new /reset 的“New session started”回执在 OpenClaw 核心里是英文固定文案,
333
+ // 且通过 routeReply 走 wecom outbound(Agent 主动发送)。
334
+ // 在 WeCom“双模式”场景下,这会造成:
335
+ // - 用户在 Bot 会话发 /new,但却收到一条 Agent 私信回执(双重回复/错会话)。
336
+ // 因此:
337
+ // - Bot 会话目标:抑制该回执(Bot 会话里由 wecom 插件补中文回执)。
338
+ // - Agent 会话目标(wecom-agent:):允许发送,但改写成中文。
339
+ let outgoingText = text;
340
+ const trimmed = String(outgoingText ?? "").trim();
341
+ logOutboundDecision({
342
+ phase: "sendText:start",
343
+ to,
344
+ accountId,
345
+ sessionKey,
346
+ textLen: trimmed.length,
347
+ });
348
+ const isAgentSessionTarget = isAgentConversationTarget({ to, accountId, sessionKey });
349
+ const looksLikeNewSessionAck = /new session started/i.test(trimmed) && /model:/i.test(trimmed);
350
+ if (looksLikeNewSessionAck) {
351
+ if (!isAgentSessionTarget) {
352
+ logOutboundDecision({
353
+ phase: "sendText:suppress-new-session-ack",
354
+ to,
355
+ accountId,
356
+ sessionKey,
357
+ textLen: trimmed.length,
358
+ });
359
+ // Suppress ack without agent resolution
360
+ return { channel: "wecom", messageId: `suppressed-${Date.now()}`, timestamp: Date.now() };
361
+ }
362
+ const modelLabel = (() => {
363
+ const m = trimmed.match(/model:\s*([^\n()]+)\s*/i);
364
+ return m?.[1]?.trim();
365
+ })();
366
+ const rewritten = modelLabel ? `✅ 已开启新会话(模型:${modelLabel})` : "✅ 已开启新会话。";
367
+ outgoingText = rewritten;
368
+ logOutboundDecision({
369
+ phase: "sendText:rewrite-new-session-ack",
370
+ to,
371
+ accountId,
372
+ sessionKey,
373
+ textLen: outgoingText.length,
374
+ });
375
+ }
376
+ let sentViaBotWs = false;
377
+ let agent = null;
378
+ let upstreamTarget;
379
+ try {
380
+ // 首先检查是否是上下游用户
381
+ upstreamTarget = resolveUpstreamTarget({ to, cfg, accountId, sessionKey });
382
+ if (upstreamTarget) {
383
+ logOutboundDecision({
384
+ phase: "sendText:path-upstream",
385
+ to,
386
+ accountId,
387
+ sessionKey,
388
+ textLen: outgoingText.length,
389
+ extra: `resolvedUser=${upstreamTarget.toUser} corpId=${upstreamTarget.upstreamAgent.corpId}`,
390
+ });
391
+ // 上下游用户使用专门的 DeliveryService 发送
392
+ getAccountRuntime(upstreamTarget.upstreamAgent.accountId)?.log.info?.(`[wecom-outbound] Sending text to upstream target corpId=${upstreamTarget.upstreamAgent.corpId} (len=${outgoingText.length})`);
393
+ const deliveryService = new WecomUpstreamAgentDeliveryService(upstreamTarget.upstreamAgent, upstreamTarget.primaryAgent);
394
+ await deliveryService.sendText({
395
+ to,
396
+ text: outgoingText,
397
+ });
398
+ return {
399
+ channel: "wecom",
400
+ messageId: `upstream-agent-${Date.now()}`,
401
+ timestamp: Date.now(),
402
+ };
403
+ }
404
+ sentViaBotWs = await sendTextViaBotWs({
405
+ cfg,
406
+ accountId,
407
+ to,
408
+ text: outgoingText,
409
+ sessionKey,
410
+ });
411
+ if (!sentViaBotWs) {
412
+ // Defer Agent resolution until needed for fallback
413
+ agent = resolveAgentConfigOrThrow({ cfg, accountId });
414
+ logOutboundDecision({
415
+ phase: "sendText:path-agent",
416
+ to,
417
+ accountId: agent.accountId,
418
+ sessionKey,
419
+ textLen: outgoingText.length,
420
+ });
421
+ getAccountRuntime(agent.accountId)?.log.info?.(`[wecom-outbound] Sending text to target=${String(to ?? "")} (len=${outgoingText.length})`);
422
+ const deliveryService = new WecomAgentDeliveryService(agent);
423
+ await deliveryService.sendText({
424
+ to,
425
+ text: outgoingText,
426
+ });
427
+ }
428
+ else {
429
+ logOutboundDecision({
430
+ phase: "sendText:path-bot-ws",
431
+ to,
432
+ accountId,
433
+ sessionKey,
434
+ textLen: outgoingText.length,
435
+ });
436
+ }
437
+ }
438
+ catch (err) {
439
+ console.error(`[wecom-outbound] FAILED to send: ${err instanceof Error ? err.message : String(err)}`);
440
+ if (agent) {
441
+ getAccountRuntime(agent.accountId)?.log.error?.(`[wecom-outbound] Failed to send text to ${String(to ?? "")}: ${err instanceof Error ? err.message : String(err)}`);
442
+ }
443
+ throw err;
444
+ }
445
+ return {
446
+ channel: "wecom",
447
+ messageId: `${sentViaBotWs ? "bot-ws" : "agent"}-${Date.now()}`,
448
+ timestamp: Date.now(),
449
+ };
450
+ },
451
+ sendMedia: async ({ cfg, to, text, mediaUrl, accountId, mediaLocalRoots, sessionKey, }) => {
452
+ // signal removed - not supported in current SDK
453
+ if (!mediaUrl) {
454
+ throw new Error("WeCom outbound requires mediaUrl.");
455
+ }
456
+ logOutboundDecision({
457
+ phase: "sendMedia:start",
458
+ to,
459
+ accountId,
460
+ sessionKey,
461
+ textLen: String(text ?? "").trim().length,
462
+ mediaUrl,
463
+ });
464
+ // 首先检查是否是上下游用户
465
+ const upstreamTarget = resolveUpstreamTarget({ to, cfg, accountId, sessionKey });
466
+ if (upstreamTarget) {
467
+ logOutboundDecision({
468
+ phase: "sendMedia:path-upstream",
469
+ to,
470
+ accountId,
471
+ sessionKey,
472
+ textLen: String(text ?? "").trim().length,
473
+ mediaUrl,
474
+ extra: `resolvedUser=${upstreamTarget.toUser} corpId=${upstreamTarget.upstreamAgent.corpId}`,
475
+ });
476
+ getAccountRuntime(upstreamTarget.upstreamAgent.accountId)?.log.info?.(`[wecom-outbound] Sending media to upstream target corpId=${upstreamTarget.upstreamAgent.corpId} (filename=${mediaUrl})`);
477
+ const { buffer, contentType, filename } = await resolveOutboundMediaAsset({
478
+ mediaUrl,
479
+ network: upstreamTarget.upstreamAgent.network,
480
+ });
481
+ const deliveryService = new WecomUpstreamAgentDeliveryService(upstreamTarget.upstreamAgent, upstreamTarget.primaryAgent);
482
+ await deliveryService.sendMedia({
483
+ to,
484
+ text,
485
+ buffer,
486
+ filename,
487
+ contentType,
488
+ });
489
+ return {
490
+ channel: "wecom",
491
+ messageId: `upstream-agent-media-${Date.now()}`,
492
+ timestamp: Date.now(),
493
+ };
494
+ }
495
+ const botWs = await sendMediaViaBotWs({
496
+ cfg,
497
+ accountId,
498
+ to,
499
+ text,
500
+ mediaUrl,
501
+ mediaLocalRoots,
502
+ sessionKey,
503
+ });
504
+ if (botWs.sent) {
505
+ logOutboundDecision({
506
+ phase: "sendMedia:path-bot-ws",
507
+ to,
508
+ accountId,
509
+ sessionKey,
510
+ textLen: String(text ?? "").trim().length,
511
+ mediaUrl,
512
+ });
513
+ return {
514
+ channel: "wecom",
515
+ messageId: `bot-ws-media-${Date.now()}`,
516
+ timestamp: Date.now(),
517
+ };
518
+ }
519
+ if (botWs.attempted) {
520
+ throw new Error(`WeCom Bot WS media delivery failed for ${String(to ?? "")}: ${botWs.reason ?? "unknown"}`);
521
+ }
522
+ const agent = resolveAgentConfigOrThrow({ cfg, accountId });
523
+ logOutboundDecision({
524
+ phase: "sendMedia:path-agent",
525
+ to,
526
+ accountId: agent.accountId,
527
+ sessionKey,
528
+ textLen: String(text ?? "").trim().length,
529
+ mediaUrl,
530
+ });
531
+ const deliveryService = new WecomAgentDeliveryService(agent);
532
+ const { buffer, contentType, filename } = await resolveOutboundMediaAsset({
533
+ mediaUrl,
534
+ network: agent.network,
535
+ });
536
+ console.log(`[wecom-outbound] Sending media to ${String(to ?? "")} (filename=${filename}, contentType=${contentType})`);
537
+ try {
538
+ await deliveryService.sendMedia({
539
+ to,
540
+ text,
541
+ buffer,
542
+ filename,
543
+ contentType,
544
+ });
545
+ console.log(`[wecom-outbound] Successfully sent media to ${String(to ?? "")}`);
546
+ }
547
+ catch (err) {
548
+ console.error(`[wecom-outbound] Failed to send media to ${String(to ?? "")}:`, err);
549
+ throw err;
550
+ }
551
+ return {
552
+ channel: "wecom",
553
+ messageId: `${botWs.attempted ? "agent-fallback-media" : "agent-media"}-${Date.now()}`,
554
+ timestamp: Date.now(),
555
+ };
556
+ },
557
+ };
@@ -0,0 +1,57 @@
1
+ import { prepareInboundSession } from "./session-manager.js";
2
+ import { dispatchRuntimeReply } from "./reply-orchestrator.js";
3
+ import { buildRawEnvelopeSummary } from "../observability/raw-envelope-log.js";
4
+ import { registerActiveBotWsReplyHandle, unregisterActiveBotWsReplyHandle } from "../runtime.js";
5
+ export async function dispatchInboundEvent(params) {
6
+ const { core, cfg, store, auditLog, mediaService, event, replyHandle } = params;
7
+ if (!store.markInboundSeen(event)) {
8
+ auditLog.appendOperational({
9
+ accountId: event.accountId,
10
+ transport: event.transport,
11
+ category: "duplicate-inbound",
12
+ messageId: event.messageId,
13
+ summary: buildRawEnvelopeSummary(event),
14
+ raw: event.raw,
15
+ });
16
+ return;
17
+ }
18
+ auditLog.appendInbound({
19
+ accountId: event.accountId,
20
+ transport: event.transport,
21
+ messageId: event.messageId,
22
+ summary: buildRawEnvelopeSummary(event),
23
+ raw: event.raw,
24
+ });
25
+ store.writeReplyContext(event.messageId, event.replyContext);
26
+ const session = await prepareInboundSession({
27
+ core,
28
+ cfg,
29
+ event,
30
+ mediaService,
31
+ });
32
+ const sessionKey = session.ctx.SessionKey ?? session.route.sessionKey;
33
+ registerActiveBotWsReplyHandle({
34
+ accountId: event.accountId,
35
+ sessionKey,
36
+ peerKind: event.conversation.peerKind,
37
+ peerId: event.conversation.peerId,
38
+ handle: replyHandle,
39
+ });
40
+ try {
41
+ await dispatchRuntimeReply({
42
+ core,
43
+ cfg,
44
+ session,
45
+ replyHandle,
46
+ });
47
+ }
48
+ finally {
49
+ unregisterActiveBotWsReplyHandle({
50
+ accountId: event.accountId,
51
+ sessionKey,
52
+ peerKind: event.conversation.peerKind,
53
+ peerId: event.conversation.peerId,
54
+ handle: replyHandle,
55
+ });
56
+ }
57
+ }
@@ -1,4 +1,3 @@
1
1
  export { dispatchInboundEvent } from "./dispatcher.js";
2
2
  export { prepareInboundSession } from "./session-manager.js";
3
3
  export { dispatchRuntimeReply } from "./reply-orchestrator.js";
4
- export type { OutboundIntent } from "./outbound-intent.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ export async function dispatchReplyPayload(params) {
2
+ await params.replyHandle.deliver(params.payload, { kind: params.kind });
3
+ }
4
+ export async function dispatchRuntimeReply(params) {
5
+ const { core, cfg, session, replyHandle } = params;
6
+ const result = await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
7
+ ctx: session.ctx,
8
+ cfg,
9
+ replyOptions: replyHandle.context.transport === "bot-ws"
10
+ ? {
11
+ // WS bot replies should emit block updates instead of waiting for a final-only flush.
12
+ disableBlockStreaming: false,
13
+ }
14
+ : undefined,
15
+ dispatcherOptions: {
16
+ deliver: async (payload, info) => {
17
+ await dispatchReplyPayload({
18
+ replyHandle,
19
+ payload,
20
+ kind: info?.kind === "final" ? "final" : "block",
21
+ });
22
+ },
23
+ onError: async (error) => {
24
+ await replyHandle.fail?.(error);
25
+ },
26
+ },
27
+ });
28
+ if (replyHandle.context.transport === "bot-ws" &&
29
+ result &&
30
+ result.queuedFinal !== true &&
31
+ (result.counts?.block ?? 0) > 0) {
32
+ await dispatchReplyPayload({
33
+ replyHandle,
34
+ payload: { text: "" },
35
+ kind: "final",
36
+ });
37
+ }
38
+ }
@@ -0,0 +1,26 @@
1
+ import { ensureDynamicAgentListed, generateAgentId, shouldUseDynamicAgent, } from "../dynamic-agent.js";
2
+ export function resolveRuntimeRoute(params) {
3
+ const route = params.core.channel.routing.resolveAgentRoute({
4
+ cfg: params.cfg,
5
+ channel: "wecom",
6
+ accountId: params.event.accountId,
7
+ peer: {
8
+ kind: params.event.conversation.peerKind,
9
+ id: params.event.conversation.peerId,
10
+ },
11
+ });
12
+ const chatType = params.event.conversation.peerKind === "group" ? "group" : "dm";
13
+ const useDynamicAgent = shouldUseDynamicAgent({
14
+ chatType,
15
+ senderId: params.event.conversation.senderId,
16
+ config: params.cfg,
17
+ });
18
+ if (!useDynamicAgent) {
19
+ return route;
20
+ }
21
+ const targetAgentId = generateAgentId(chatType, params.event.conversation.peerId, params.event.accountId);
22
+ route.agentId = targetAgentId;
23
+ route.sessionKey = `agent:${targetAgentId}:wecom:${params.event.accountId}:${chatType}:${params.event.conversation.peerId}`;
24
+ ensureDynamicAgentListed(targetAgentId, params.core).catch(() => { });
25
+ return route;
26
+ }