@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
@@ -1,71 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
-
3
- import { prepareInboundSession } from "./session-manager.js";
4
- import { dispatchRuntimeReply } from "./reply-orchestrator.js";
5
- import type { RuntimeStore } from "../store/interfaces.js";
6
- import type { WecomAuditLog } from "../observability/audit-log.js";
7
- import { buildRawEnvelopeSummary } from "../observability/raw-envelope-log.js";
8
- import type { ReplyHandle, UnifiedInboundEvent } from "../types/index.js";
9
- import type { WecomMediaService } from "../shared/media-service.js";
10
- import { registerActiveBotWsReplyHandle, unregisterActiveBotWsReplyHandle } from "../runtime.js";
11
-
12
- export async function dispatchInboundEvent(params: {
13
- core: PluginRuntime;
14
- cfg: OpenClawConfig;
15
- store: RuntimeStore;
16
- auditLog: WecomAuditLog;
17
- mediaService: WecomMediaService;
18
- event: UnifiedInboundEvent;
19
- replyHandle: ReplyHandle;
20
- }): Promise<void> {
21
- const { core, cfg, store, auditLog, mediaService, event, replyHandle } = params;
22
- if (!store.markInboundSeen(event)) {
23
- auditLog.appendOperational({
24
- accountId: event.accountId,
25
- transport: event.transport,
26
- category: "duplicate-inbound",
27
- messageId: event.messageId,
28
- summary: buildRawEnvelopeSummary(event),
29
- raw: event.raw,
30
- });
31
- return;
32
- }
33
- auditLog.appendInbound({
34
- accountId: event.accountId,
35
- transport: event.transport,
36
- messageId: event.messageId,
37
- summary: buildRawEnvelopeSummary(event),
38
- raw: event.raw,
39
- });
40
- store.writeReplyContext(event.messageId, event.replyContext);
41
- const session = await prepareInboundSession({
42
- core,
43
- cfg,
44
- event,
45
- mediaService,
46
- });
47
- const sessionKey = session.ctx.SessionKey ?? session.route.sessionKey;
48
- registerActiveBotWsReplyHandle({
49
- accountId: event.accountId,
50
- sessionKey,
51
- peerKind: event.conversation.peerKind,
52
- peerId: event.conversation.peerId,
53
- handle: replyHandle,
54
- });
55
- try {
56
- await dispatchRuntimeReply({
57
- core,
58
- cfg,
59
- session,
60
- replyHandle,
61
- });
62
- } finally {
63
- unregisterActiveBotWsReplyHandle({
64
- accountId: event.accountId,
65
- sessionKey,
66
- peerKind: event.conversation.peerKind,
67
- peerId: event.conversation.peerId,
68
- handle: replyHandle,
69
- });
70
- }
71
- }
@@ -1,4 +0,0 @@
1
- export type OutboundIntent =
2
- | { kind: "text"; text: string }
3
- | { kind: "media"; mediaUrl: string; text?: string }
4
- | { kind: "card"; card: Record<string, unknown> };
@@ -1,71 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { dispatchRuntimeReply } from "./reply-orchestrator.js";
3
-
4
- describe("dispatchRuntimeReply", () => {
5
- it("enables block streaming for bot-ws replies", async () => {
6
- const dispatchReplyWithBufferedBlockDispatcher = vi
7
- .fn()
8
- .mockResolvedValue({ queuedFinal: false, counts: { block: 0, final: 0, tool: 0 } });
9
- const core = {
10
- channel: {
11
- reply: {
12
- dispatchReplyWithBufferedBlockDispatcher,
13
- },
14
- },
15
- } as any;
16
-
17
- await dispatchRuntimeReply({
18
- core,
19
- cfg: {} as any,
20
- session: { ctx: { SessionKey: "session-a" } } as any,
21
- replyHandle: {
22
- context: {
23
- transport: "bot-ws",
24
- accountId: "default",
25
- raw: { transport: "bot-ws", envelopeType: "ws", body: {} },
26
- },
27
- deliver: vi.fn(),
28
- } as any,
29
- });
30
-
31
- expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledWith(
32
- expect.objectContaining({
33
- replyOptions: expect.objectContaining({
34
- disableBlockStreaming: false,
35
- }),
36
- }),
37
- );
38
- });
39
-
40
- it("synthesizes a final close for bot-ws when only block replies were queued", async () => {
41
- const dispatchReplyWithBufferedBlockDispatcher = vi.fn().mockResolvedValue({
42
- queuedFinal: false,
43
- counts: { block: 1, final: 0, tool: 0 },
44
- });
45
- const deliver = vi.fn().mockResolvedValue(undefined);
46
- const core = {
47
- channel: {
48
- reply: {
49
- dispatchReplyWithBufferedBlockDispatcher,
50
- },
51
- },
52
- } as any;
53
-
54
- await dispatchRuntimeReply({
55
- core,
56
- cfg: {} as any,
57
- session: { ctx: { SessionKey: "session-a" } } as any,
58
- replyHandle: {
59
- context: {
60
- transport: "bot-ws",
61
- accountId: "default",
62
- raw: { transport: "bot-ws", envelopeType: "ws", body: {} },
63
- },
64
- deliver,
65
- } as any,
66
- });
67
-
68
- expect(deliver).toHaveBeenCalledTimes(1);
69
- expect(deliver).toHaveBeenCalledWith({ text: "" }, { kind: "final" });
70
- });
71
- });
@@ -1,67 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
- import type { ReplyHandle } from "../types/index.js";
3
- import type { PreparedSession } from "./session-manager.js";
4
-
5
- export async function dispatchReplyPayload(params: {
6
- replyHandle: ReplyHandle;
7
- payload: {
8
- text?: string;
9
- mediaUrl?: string;
10
- mediaUrls?: string[];
11
- replyToId?: string;
12
- replyToTag?: boolean;
13
- replyToCurrent?: boolean;
14
- audioAsVoice?: boolean;
15
- isError?: boolean;
16
- isReasoning?: boolean;
17
- channelData?: Record<string, unknown>;
18
- };
19
- kind: "block" | "final";
20
- }): Promise<void> {
21
- await params.replyHandle.deliver(params.payload, { kind: params.kind });
22
- }
23
-
24
- export async function dispatchRuntimeReply(params: {
25
- core: PluginRuntime;
26
- cfg: OpenClawConfig;
27
- session: PreparedSession;
28
- replyHandle: ReplyHandle;
29
- }): Promise<void> {
30
- const { core, cfg, session, replyHandle } = params;
31
- const result = await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
32
- ctx: session.ctx,
33
- cfg,
34
- replyOptions:
35
- replyHandle.context.transport === "bot-ws"
36
- ? {
37
- // WS bot replies should emit block updates instead of waiting for a final-only flush.
38
- disableBlockStreaming: false,
39
- }
40
- : undefined,
41
- dispatcherOptions: {
42
- deliver: async (payload, info) => {
43
- await dispatchReplyPayload({
44
- replyHandle,
45
- payload,
46
- kind: info?.kind === "final" ? "final" : "block",
47
- });
48
- },
49
- onError: async (error) => {
50
- await replyHandle.fail?.(error);
51
- },
52
- },
53
- });
54
-
55
- if (
56
- replyHandle.context.transport === "bot-ws" &&
57
- result &&
58
- result.queuedFinal !== true &&
59
- (result.counts?.block ?? 0) > 0
60
- ) {
61
- await dispatchReplyPayload({
62
- replyHandle,
63
- payload: { text: "" },
64
- kind: "final",
65
- });
66
- }
67
- }
@@ -1,115 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
3
-
4
- import { resolveRuntimeRoute } from "./routing-bridge.js";
5
- import { ensureDynamicAgentListed } from "../dynamic-agent.js";
6
- import type { UnifiedInboundEvent } from "../types/index.js";
7
-
8
- vi.mock("../dynamic-agent.js", async () => {
9
- const actual = await vi.importActual<typeof import("../dynamic-agent.js")>(
10
- "../dynamic-agent.js",
11
- );
12
- return {
13
- ...actual,
14
- ensureDynamicAgentListed: vi.fn().mockResolvedValue(undefined),
15
- };
16
- });
17
-
18
- describe("resolveRuntimeRoute", () => {
19
- beforeEach(() => {
20
- vi.mocked(ensureDynamicAgentListed).mockClear();
21
- });
22
-
23
- it("overrides WS runtime routes with dynamic agent routing for direct chats", () => {
24
- const baseRoute = {
25
- agentId: "main",
26
- channel: "wecom",
27
- accountId: "acct-ws",
28
- sessionKey: "agent:main",
29
- mainSessionKey: "agent:main:main",
30
- lastRoutePolicy: "session" as const,
31
- matchedBy: "default" as const,
32
- };
33
- const resolveAgentRoute = vi.fn().mockReturnValue({ ...baseRoute });
34
- const core = {
35
- channel: {
36
- routing: {
37
- resolveAgentRoute,
38
- },
39
- },
40
- } as unknown as PluginRuntime;
41
- const cfg = {
42
- channels: {
43
- wecom: {
44
- dynamicAgents: {
45
- enabled: true,
46
- },
47
- },
48
- },
49
- } as OpenClawConfig;
50
- const event = {
51
- accountId: "acct-ws",
52
- conversation: {
53
- accountId: "acct-ws",
54
- peerKind: "direct",
55
- peerId: "HiDaoMax",
56
- senderId: "HiDaoMax",
57
- },
58
- } as UnifiedInboundEvent;
59
-
60
- const route = resolveRuntimeRoute({ core, cfg, event });
61
-
62
- expect(resolveAgentRoute).toHaveBeenCalledOnce();
63
- expect(route.agentId).toBe("wecom-acct-ws-dm-hidaomax");
64
- expect(route.sessionKey).toBe(
65
- "agent:wecom-acct-ws-dm-hidaomax:wecom:acct-ws:dm:HiDaoMax",
66
- );
67
- expect(vi.mocked(ensureDynamicAgentListed)).toHaveBeenCalledWith(
68
- "wecom-acct-ws-dm-hidaomax",
69
- core,
70
- );
71
- });
72
-
73
- it("keeps the resolved core route when dynamic agent routing is disabled for the sender", () => {
74
- const baseRoute = {
75
- agentId: "main",
76
- channel: "wecom",
77
- accountId: "acct-ws",
78
- sessionKey: "agent:main",
79
- mainSessionKey: "agent:main:main",
80
- lastRoutePolicy: "session" as const,
81
- matchedBy: "binding.account" as const,
82
- };
83
- const core = {
84
- channel: {
85
- routing: {
86
- resolveAgentRoute: vi.fn().mockReturnValue({ ...baseRoute }),
87
- },
88
- },
89
- } as unknown as PluginRuntime;
90
- const cfg = {
91
- channels: {
92
- wecom: {
93
- dynamicAgents: {
94
- enabled: true,
95
- adminUsers: ["HiDaoMax"],
96
- },
97
- },
98
- },
99
- } as OpenClawConfig;
100
- const event = {
101
- accountId: "acct-ws",
102
- conversation: {
103
- accountId: "acct-ws",
104
- peerKind: "direct",
105
- peerId: "HiDaoMax",
106
- senderId: "HiDaoMax",
107
- },
108
- } as UnifiedInboundEvent;
109
-
110
- const route = resolveRuntimeRoute({ core, cfg, event });
111
-
112
- expect(route).toEqual(baseRoute);
113
- expect(vi.mocked(ensureDynamicAgentListed)).not.toHaveBeenCalled();
114
- });
115
- });
@@ -1,44 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
-
3
- import {
4
- ensureDynamicAgentListed,
5
- generateAgentId,
6
- shouldUseDynamicAgent,
7
- } from "../dynamic-agent.js";
8
- import type { UnifiedInboundEvent } from "../types/index.js";
9
-
10
- export function resolveRuntimeRoute(params: {
11
- core: PluginRuntime;
12
- cfg: OpenClawConfig;
13
- event: UnifiedInboundEvent;
14
- }) {
15
- const route = params.core.channel.routing.resolveAgentRoute({
16
- cfg: params.cfg,
17
- channel: "wecom",
18
- accountId: params.event.accountId,
19
- peer: {
20
- kind: params.event.conversation.peerKind,
21
- id: params.event.conversation.peerId,
22
- },
23
- });
24
-
25
- const chatType = params.event.conversation.peerKind === "group" ? "group" : "dm";
26
- const useDynamicAgent = shouldUseDynamicAgent({
27
- chatType,
28
- senderId: params.event.conversation.senderId,
29
- config: params.cfg,
30
- });
31
- if (!useDynamicAgent) {
32
- return route;
33
- }
34
-
35
- const targetAgentId = generateAgentId(
36
- chatType,
37
- params.event.conversation.peerId,
38
- params.event.accountId,
39
- );
40
- route.agentId = targetAgentId;
41
- route.sessionKey = `agent:${targetAgentId}:wecom:${params.event.accountId}:${chatType}:${params.event.conversation.peerId}`;
42
- ensureDynamicAgentListed(targetAgentId, params.core).catch(() => {});
43
- return route;
44
- }
@@ -1,174 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { prepareInboundSession } from "./session-manager.js";
3
-
4
- const {
5
- resolveRuntimeRoute,
6
- getPeerContextToken,
7
- registerWecomSourceSnapshot,
8
- } = vi.hoisted(() => ({
9
- resolveRuntimeRoute: vi.fn(),
10
- getPeerContextToken: vi.fn(),
11
- registerWecomSourceSnapshot: vi.fn(),
12
- }));
13
-
14
- vi.mock("./routing-bridge.js", () => ({
15
- resolveRuntimeRoute,
16
- }));
17
-
18
- vi.mock("../context-store.js", () => ({
19
- getPeerContextToken,
20
- }));
21
-
22
- vi.mock("./source-registry.js", () => ({
23
- registerWecomSourceSnapshot,
24
- }));
25
-
26
- function createCore() {
27
- const finalizeInboundContext = vi.fn((ctx) => ctx);
28
- const recordInboundSession = vi.fn(async () => {});
29
-
30
- return {
31
- core: {
32
- channel: {
33
- session: {
34
- resolveStorePath: vi.fn(() => "/tmp/wecom-session-store"),
35
- readSessionUpdatedAt: vi.fn(() => 1234567890),
36
- recordInboundSession,
37
- },
38
- reply: {
39
- resolveEnvelopeFormatOptions: vi.fn(() => ({ envelope: "default" })),
40
- formatAgentEnvelope: vi.fn(() => "formatted-body"),
41
- finalizeInboundContext,
42
- },
43
- },
44
- } as any,
45
- finalizeInboundContext,
46
- recordInboundSession,
47
- };
48
- }
49
-
50
- function createMediaService() {
51
- return {
52
- normalizeFirstAttachment: vi.fn(async () => undefined),
53
- saveInboundAttachment: vi.fn(async () => undefined),
54
- } as any;
55
- }
56
-
57
- describe("prepareInboundSession", () => {
58
- beforeEach(() => {
59
- vi.clearAllMocks();
60
- resolveRuntimeRoute.mockReturnValue({
61
- sessionKey: "agent:ops_bot:wecom:direct:hidaomax",
62
- agentId: "ops_bot",
63
- accountId: "default",
64
- });
65
- });
66
-
67
- it("marks bot-ws turns as the current wecom surface", async () => {
68
- getPeerContextToken.mockReturnValue("ctx-bot");
69
- const { core, finalizeInboundContext, recordInboundSession } = createCore();
70
-
71
- const result = await prepareInboundSession({
72
- core,
73
- cfg: {} as any,
74
- event: {
75
- accountId: "default",
76
- transport: "bot-ws",
77
- messageId: "msg-bot-1",
78
- conversation: {
79
- peerKind: "direct",
80
- peerId: "HiDaoMax",
81
- senderId: "HiDaoMax",
82
- },
83
- senderName: "HiDaoMax",
84
- text: "hello",
85
- } as any,
86
- mediaService: createMediaService(),
87
- });
88
-
89
- expect(finalizeInboundContext).toHaveBeenCalledWith(
90
- expect.objectContaining({
91
- Provider: "wecom",
92
- Surface: "wecom",
93
- OriginatingChannel: "wecom",
94
- OriginatingTo: "wecom:context:ctx-bot",
95
- To: "wecom:user:HiDaoMax",
96
- }),
97
- );
98
- expect(recordInboundSession).toHaveBeenCalledTimes(1);
99
- expect(result.ctx.Provider).toBe("wecom");
100
- expect(result.ctx.Surface).toBe("wecom");
101
- });
102
-
103
- it("keeps agent-callback turns on provider-only context", async () => {
104
- getPeerContextToken.mockReturnValue(undefined);
105
- const { core, finalizeInboundContext } = createCore();
106
-
107
- const result = await prepareInboundSession({
108
- core,
109
- cfg: {} as any,
110
- event: {
111
- accountId: "default",
112
- transport: "agent-callback",
113
- messageId: "msg-agent-1",
114
- conversation: {
115
- peerKind: "direct",
116
- peerId: "HiDaoMax",
117
- senderId: "HiDaoMax",
118
- },
119
- senderName: "HiDaoMax",
120
- text: "hello",
121
- } as any,
122
- mediaService: createMediaService(),
123
- });
124
-
125
- expect(finalizeInboundContext).toHaveBeenCalledWith(
126
- expect.objectContaining({
127
- Provider: "wecom",
128
- OriginatingChannel: "wecom",
129
- OriginatingTo: "wecom:user:HiDaoMax",
130
- }),
131
- );
132
- expect(result.ctx.Provider).toBe("wecom");
133
- expect(result.ctx).not.toHaveProperty("Surface");
134
- });
135
-
136
- it("registers SessionId for source lookups after context finalization", async () => {
137
- getPeerContextToken.mockReturnValue(undefined);
138
- const { core } = createCore();
139
- core.channel.reply.finalizeInboundContext = vi.fn((ctx) => ({
140
- ...ctx,
141
- SessionId: "sess-agent-1",
142
- }));
143
-
144
- await prepareInboundSession({
145
- core,
146
- cfg: {} as any,
147
- event: {
148
- accountId: "default",
149
- transport: "agent-callback",
150
- messageId: "msg-agent-2",
151
- conversation: {
152
- peerKind: "direct",
153
- peerId: "HiDaoMax",
154
- senderId: "HiDaoMax",
155
- },
156
- senderName: "HiDaoMax",
157
- text: "hello",
158
- } as any,
159
- mediaService: createMediaService(),
160
- });
161
-
162
- expect(registerWecomSourceSnapshot).toHaveBeenLastCalledWith(
163
- expect.objectContaining({
164
- accountId: "default",
165
- source: "agent-callback",
166
- messageId: "msg-agent-2",
167
- sessionKey: "agent:ops_bot:wecom:direct:hidaomax",
168
- sessionId: "sess-agent-1",
169
- peerKind: "direct",
170
- peerId: "HiDaoMax",
171
- }),
172
- );
173
- });
174
- });
@@ -1,139 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
- import type { WecomMediaService } from "../shared/media-service.js";
3
- import type { UnifiedInboundEvent } from "../types/index.js";
4
- import { getPeerContextToken } from "../context-store.js";
5
- import { buildWecomContextTarget } from "../target.js";
6
- import { resolveRuntimeRoute } from "./routing-bridge.js";
7
- import { registerWecomSourceSnapshot } from "./source-registry.js";
8
-
9
- export type PreparedSession = {
10
- route: ReturnType<typeof resolveRuntimeRoute>;
11
- ctx: ReturnType<PluginRuntime["channel"]["reply"]["finalizeInboundContext"]>;
12
- storePath: string;
13
- };
14
-
15
- function readContextSessionId(ctx: { SessionId?: string } | Record<string, unknown>): string | undefined {
16
- const sessionId = "SessionId" in ctx ? ctx.SessionId : undefined;
17
- return typeof sessionId === "string" && sessionId.trim() ? sessionId.trim() : undefined;
18
- }
19
-
20
- export async function prepareInboundSession(params: {
21
- core: PluginRuntime;
22
- cfg: OpenClawConfig;
23
- event: UnifiedInboundEvent;
24
- mediaService: WecomMediaService;
25
- }): Promise<PreparedSession> {
26
- const { core, cfg, event, mediaService } = params;
27
- const route = resolveRuntimeRoute({ core, cfg, event });
28
- const source =
29
- event.transport === "bot-ws"
30
- ? "bot-ws"
31
- : event.transport === "agent-callback"
32
- ? "agent-callback"
33
- : undefined;
34
- if (source) {
35
- registerWecomSourceSnapshot({
36
- accountId: event.accountId,
37
- source,
38
- messageId: event.messageId,
39
- sessionKey: route.sessionKey,
40
- peerKind: event.conversation.peerKind,
41
- peerId: event.conversation.peerId,
42
- });
43
- }
44
- const storePath = core.channel.session.resolveStorePath(cfg.session?.store, {
45
- agentId: route.agentId,
46
- });
47
- const previousTimestamp = core.channel.session.readSessionUpdatedAt({
48
- storePath,
49
- sessionKey: route.sessionKey,
50
- });
51
- const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
52
- const body = core.channel.reply.formatAgentEnvelope({
53
- channel: "WeCom",
54
- from: `${event.conversation.peerKind}:${event.conversation.peerId}`,
55
- previousTimestamp,
56
- envelope: envelopeOptions,
57
- body: event.text,
58
- });
59
-
60
- const firstAttachment = await mediaService.normalizeFirstAttachment(event);
61
- const mediaPath = firstAttachment
62
- ? await mediaService.saveInboundAttachment(event, firstAttachment)
63
- : undefined;
64
- const defaultOriginatingTo =
65
- event.conversation.peerKind === "group"
66
- ? `wecom:group:${event.conversation.peerId}`
67
- : `wecom:user:${event.conversation.peerId}`;
68
- const contextToken =
69
- event.transport === "bot-ws"
70
- ? getPeerContextToken(event.accountId, event.conversation.peerId)
71
- : undefined;
72
- const originatingTo = contextToken
73
- ? buildWecomContextTarget(contextToken)
74
- : defaultOriginatingTo;
75
- const providerContext =
76
- event.transport === "bot-ws"
77
- ? {
78
- // Bot WS inbound turns already have a live reply handle bound to the
79
- // current req_id. Mark the current surface as WeCom so core final text
80
- // stays on that handle and replaces the placeholder instead of being
81
- // re-routed as a second active-push message.
82
- Provider: "wecom" as const,
83
- Surface: "wecom" as const,
84
- }
85
- : {
86
- Provider: "wecom" as const,
87
- };
88
-
89
- const ctx = core.channel.reply.finalizeInboundContext({
90
- Body: body,
91
- RawBody: event.text,
92
- CommandBody: event.text,
93
- From:
94
- event.conversation.peerKind === "group"
95
- ? `wecom:group:${event.conversation.peerId}`
96
- : `wecom:user:${event.conversation.senderId}`,
97
- To:
98
- event.conversation.peerKind === "group"
99
- ? `wecom:group:${event.conversation.peerId}`
100
- : `wecom:user:${event.conversation.peerId}`,
101
- SessionKey: route.sessionKey,
102
- AccountId: route.accountId,
103
- ChatType: event.conversation.peerKind,
104
- ConversationLabel: `${event.conversation.peerKind}:${event.conversation.peerId}`,
105
- SenderName: event.senderName ?? event.conversation.senderId,
106
- SenderId: event.conversation.senderId,
107
- // Keep Originating* populated so explicit route-to-origin flows and message
108
- // tools can still resolve the active peer context when needed.
109
- ...providerContext,
110
- OriginatingChannel: "wecom",
111
- OriginatingTo: originatingTo,
112
- MessageSid: event.messageId,
113
- CommandAuthorized: true,
114
- MediaPath: mediaPath,
115
- MediaUrl: mediaPath,
116
- MediaType: firstAttachment?.contentType,
117
- });
118
-
119
- if (source) {
120
- registerWecomSourceSnapshot({
121
- accountId: event.accountId,
122
- source,
123
- messageId: event.messageId,
124
- sessionKey: ctx.SessionKey ?? route.sessionKey,
125
- sessionId: readContextSessionId(ctx),
126
- peerKind: event.conversation.peerKind,
127
- peerId: event.conversation.peerId,
128
- });
129
- }
130
-
131
- await core.channel.session.recordInboundSession({
132
- storePath,
133
- sessionKey: ctx.SessionKey ?? route.sessionKey,
134
- ctx,
135
- onRecordError: () => {},
136
- });
137
-
138
- return { route, ctx, storePath };
139
- }