ylib-wecom-openclaw-plugin 2026.4.29

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 (180) hide show
  1. package/README.md +596 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.js +99 -0
  4. package/dist/src/accounts.d.ts +57 -0
  5. package/dist/src/accounts.js +247 -0
  6. package/dist/src/agent/api-client.d.ts +95 -0
  7. package/dist/src/agent/api-client.js +425 -0
  8. package/dist/src/agent/handler.d.ts +64 -0
  9. package/dist/src/agent/handler.js +731 -0
  10. package/dist/src/agent/index.d.ts +5 -0
  11. package/dist/src/agent/index.js +21 -0
  12. package/dist/src/agent/webhook.d.ts +25 -0
  13. package/dist/src/agent/webhook.js +294 -0
  14. package/dist/src/agent/xml.d.ts +21 -0
  15. package/dist/src/agent/xml.js +43 -0
  16. package/dist/src/channel.d.ts +5 -0
  17. package/dist/src/channel.js +815 -0
  18. package/dist/src/chat-queue.d.ts +31 -0
  19. package/dist/src/chat-queue.js +53 -0
  20. package/dist/src/config-schema.d.ts +587 -0
  21. package/dist/src/config-schema.js +146 -0
  22. package/dist/src/const.d.ts +128 -0
  23. package/dist/src/const.js +168 -0
  24. package/dist/src/dm-policy.d.ts +29 -0
  25. package/dist/src/dm-policy.js +146 -0
  26. package/dist/src/dynamic-agent.d.ts +37 -0
  27. package/dist/src/dynamic-agent.js +67 -0
  28. package/dist/src/dynamic-routing.d.ts +65 -0
  29. package/dist/src/dynamic-routing.js +62 -0
  30. package/dist/src/endpoint-dispatch.d.ts +54 -0
  31. package/dist/src/endpoint-dispatch.js +967 -0
  32. package/dist/src/endpoint-event-adapter.d.ts +15 -0
  33. package/dist/src/endpoint-event-adapter.js +427 -0
  34. package/dist/src/group-policy.d.ts +30 -0
  35. package/dist/src/group-policy.js +126 -0
  36. package/dist/src/http.d.ts +27 -0
  37. package/dist/src/http.js +168 -0
  38. package/dist/src/im-runtime-telemetry.d.ts +25 -0
  39. package/dist/src/im-runtime-telemetry.js +68 -0
  40. package/dist/src/interface.d.ts +192 -0
  41. package/dist/src/interface.js +5 -0
  42. package/dist/src/markdown-chunk.d.ts +1 -0
  43. package/dist/src/markdown-chunk.js +396 -0
  44. package/dist/src/mcp/index.d.ts +6 -0
  45. package/dist/src/mcp/index.js +28 -0
  46. package/dist/src/mcp/interceptors/biz-error.d.ts +11 -0
  47. package/dist/src/mcp/interceptors/biz-error.js +73 -0
  48. package/dist/src/mcp/interceptors/doc-auth-error.d.ts +10 -0
  49. package/dist/src/mcp/interceptors/doc-auth-error.js +235 -0
  50. package/dist/src/mcp/interceptors/index.d.ts +35 -0
  51. package/dist/src/mcp/interceptors/index.js +143 -0
  52. package/dist/src/mcp/interceptors/msg-media.d.ts +11 -0
  53. package/dist/src/mcp/interceptors/msg-media.js +201 -0
  54. package/dist/src/mcp/interceptors/smartpage-create.d.ts +30 -0
  55. package/dist/src/mcp/interceptors/smartpage-create.js +252 -0
  56. package/dist/src/mcp/interceptors/smartpage-export.d.ts +17 -0
  57. package/dist/src/mcp/interceptors/smartpage-export.js +135 -0
  58. package/dist/src/mcp/interceptors/smartsheet-upload.d.ts +22 -0
  59. package/dist/src/mcp/interceptors/smartsheet-upload.js +388 -0
  60. package/dist/src/mcp/interceptors/types.d.ts +64 -0
  61. package/dist/src/mcp/interceptors/types.js +8 -0
  62. package/dist/src/mcp/schema.d.ts +11 -0
  63. package/dist/src/mcp/schema.js +115 -0
  64. package/dist/src/mcp/tool.d.ts +63 -0
  65. package/dist/src/mcp/tool.js +318 -0
  66. package/dist/src/mcp/transport.d.ts +94 -0
  67. package/dist/src/mcp/transport.js +702 -0
  68. package/dist/src/media-handler.d.ts +55 -0
  69. package/dist/src/media-handler.js +306 -0
  70. package/dist/src/media-uploader.d.ts +142 -0
  71. package/dist/src/media-uploader.js +446 -0
  72. package/dist/src/message-parser.d.ts +104 -0
  73. package/dist/src/message-parser.js +232 -0
  74. package/dist/src/message-sender.d.ts +54 -0
  75. package/dist/src/message-sender.js +210 -0
  76. package/dist/src/monitor.d.ts +69 -0
  77. package/dist/src/monitor.js +1846 -0
  78. package/dist/src/onboarding.d.ts +8 -0
  79. package/dist/src/onboarding.js +248 -0
  80. package/dist/src/openclaw-compat.d.ts +148 -0
  81. package/dist/src/openclaw-compat.js +839 -0
  82. package/dist/src/proactive-markdown-send.d.ts +14 -0
  83. package/dist/src/proactive-markdown-send.js +205 -0
  84. package/dist/src/reqid-store.d.ts +23 -0
  85. package/dist/src/reqid-store.js +136 -0
  86. package/dist/src/runtime.d.ts +2 -0
  87. package/dist/src/runtime.js +7 -0
  88. package/dist/src/shared/command-auth.d.ts +23 -0
  89. package/dist/src/shared/command-auth.js +112 -0
  90. package/dist/src/shared/xml-parser.d.ts +46 -0
  91. package/dist/src/shared/xml-parser.js +228 -0
  92. package/dist/src/state-dir-resolve.d.ts +2 -0
  93. package/dist/src/state-dir-resolve.js +33 -0
  94. package/dist/src/state-manager.d.ts +115 -0
  95. package/dist/src/state-manager.js +413 -0
  96. package/dist/src/target.d.ts +35 -0
  97. package/dist/src/target.js +71 -0
  98. package/dist/src/template-card-manager.d.ts +55 -0
  99. package/dist/src/template-card-manager.js +316 -0
  100. package/dist/src/template-card-parser.d.ts +37 -0
  101. package/dist/src/template-card-parser.js +672 -0
  102. package/dist/src/timeout.d.ts +20 -0
  103. package/dist/src/timeout.js +57 -0
  104. package/dist/src/types/account.d.ts +29 -0
  105. package/dist/src/types/account.js +5 -0
  106. package/dist/src/types/config.d.ts +98 -0
  107. package/dist/src/types/config.js +8 -0
  108. package/dist/src/types/constants.d.ts +42 -0
  109. package/dist/src/types/constants.js +45 -0
  110. package/dist/src/types/index.d.ts +7 -0
  111. package/dist/src/types/index.js +17 -0
  112. package/dist/src/types/message.d.ts +238 -0
  113. package/dist/src/types/message.js +6 -0
  114. package/dist/src/utils.d.ts +148 -0
  115. package/dist/src/utils.js +92 -0
  116. package/dist/src/version.d.ts +2 -0
  117. package/dist/src/version.js +28 -0
  118. package/dist/src/webhook/command-auth.d.ts +47 -0
  119. package/dist/src/webhook/command-auth.js +137 -0
  120. package/dist/src/webhook/gateway.d.ts +36 -0
  121. package/dist/src/webhook/gateway.js +297 -0
  122. package/dist/src/webhook/handler.d.ts +19 -0
  123. package/dist/src/webhook/handler.js +481 -0
  124. package/dist/src/webhook/helpers.d.ts +157 -0
  125. package/dist/src/webhook/helpers.js +936 -0
  126. package/dist/src/webhook/http.d.ts +27 -0
  127. package/dist/src/webhook/http.js +168 -0
  128. package/dist/src/webhook/index.d.ts +11 -0
  129. package/dist/src/webhook/index.js +43 -0
  130. package/dist/src/webhook/media.d.ts +30 -0
  131. package/dist/src/webhook/media.js +152 -0
  132. package/dist/src/webhook/monitor.d.ts +59 -0
  133. package/dist/src/webhook/monitor.js +1672 -0
  134. package/dist/src/webhook/state.d.ts +220 -0
  135. package/dist/src/webhook/state.js +568 -0
  136. package/dist/src/webhook/target.d.ts +41 -0
  137. package/dist/src/webhook/target.js +165 -0
  138. package/dist/src/webhook/types.d.ts +348 -0
  139. package/dist/src/webhook/types.js +36 -0
  140. package/dist/src/webhook/video-frame.d.ts +13 -0
  141. package/dist/src/webhook/video-frame.js +108 -0
  142. package/openclaw.plugin.json +19 -0
  143. package/package.json +96 -0
  144. package/schema.json +534 -0
  145. package/scripts/generate-schema.mjs +33 -0
  146. package/skills/wecom-contact/SKILL.md +162 -0
  147. package/skills/wecom-doc/SKILL.md +162 -0
  148. package/skills/wecom-doc/references/create-doc.md +56 -0
  149. package/skills/wecom-doc/references/edit-doc-content.md +68 -0
  150. package/skills/wecom-doc/references/get-doc-content.md +88 -0
  151. package/skills/wecom-doc/references/smartpage-create.md +125 -0
  152. package/skills/wecom-doc/references/smartpage-export.md +160 -0
  153. package/skills/wecom-meeting/SKILL.md +441 -0
  154. package/skills/wecom-meeting/references/example-full.md +30 -0
  155. package/skills/wecom-meeting/references/example-reminder.md +46 -0
  156. package/skills/wecom-meeting/references/example-security.md +22 -0
  157. package/skills/wecom-meeting/references/response-get-meeting-info.md +148 -0
  158. package/skills/wecom-msg/SKILL.md +157 -0
  159. package/skills/wecom-msg/references/api-get-messages.md +93 -0
  160. package/skills/wecom-msg/references/api-get-msg-chat-list.md +58 -0
  161. package/skills/wecom-msg/references/api-get-msg-media.md +44 -0
  162. package/skills/wecom-msg/references/api-send-message.md +39 -0
  163. package/skills/wecom-preflight/SKILL.md +141 -0
  164. package/skills/wecom-schedule/SKILL.md +161 -0
  165. package/skills/wecom-schedule/references/api-check-availability.md +56 -0
  166. package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
  167. package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
  168. package/skills/wecom-schedule/references/api-update-schedule.md +32 -0
  169. package/skills/wecom-schedule/references/ref-reminders.md +24 -0
  170. package/skills/wecom-send-media/SKILL.md +68 -0
  171. package/skills/wecom-send-template-card/SKILL.md +157 -0
  172. package/skills/wecom-send-template-card/references/api-template-card-types.md +358 -0
  173. package/skills/wecom-smartsheet/SKILL.md +164 -0
  174. package/skills/wecom-smartsheet/references/smartsheet-cell-value-formats.md +163 -0
  175. package/skills/wecom-smartsheet/references/smartsheet-field-types.md +44 -0
  176. package/skills/wecom-smartsheet/references/smartsheet-get-records.md +96 -0
  177. package/skills/wecom-smartsheet/references/webhook-examples.md +185 -0
  178. package/skills/wecom-smartsheet/references/webhook-fallback.md +184 -0
  179. package/skills/wecom-todo/SKILL.md +392 -0
  180. package/skills/wecom-todo/examples/workflows.md +163 -0
@@ -0,0 +1,1846 @@
1
+ "use strict";
2
+ /**
3
+ * 企业微信 WebSocket 监控器主模块
4
+ *
5
+ * 负责:
6
+ * - 建立和管理 WebSocket 连接
7
+ * - 协调消息处理流程(解析→策略检查→下载图片→路由回复)
8
+ * - 资源生命周期管理
9
+ *
10
+ * 子模块:
11
+ * - message-parser.ts : 消息内容解析
12
+ * - message-sender.ts : 消息发送(带超时保护)
13
+ * - media-handler.ts : 图片下载和保存(带超时保护)
14
+ * - group-policy.ts : 群组访问控制
15
+ * - dm-policy.ts : 私聊访问控制
16
+ * - state-manager.ts : 全局状态管理(带 TTL 清理)
17
+ * - timeout.ts : 超时工具
18
+ */
19
+ var __assign = (this && this.__assign) || function () {
20
+ __assign = Object.assign || function(t) {
21
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
22
+ s = arguments[i];
23
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
24
+ t[p] = s[p];
25
+ }
26
+ return t;
27
+ };
28
+ return __assign.apply(this, arguments);
29
+ };
30
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
33
+ }) : (function(o, m, k, k2) {
34
+ if (k2 === undefined) k2 = k;
35
+ o[k2] = m[k];
36
+ }));
37
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
38
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
39
+ }) : function(o, v) {
40
+ o["default"] = v;
41
+ });
42
+ var __importStar = (this && this.__importStar) || function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
50
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
51
+ return new (P || (P = Promise))(function (resolve, reject) {
52
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
53
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
54
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
55
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
56
+ });
57
+ };
58
+ var __generator = (this && this.__generator) || function (thisArg, body) {
59
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
60
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
61
+ function verb(n) { return function (v) { return step([n, v]); }; }
62
+ function step(op) {
63
+ if (f) throw new TypeError("Generator is already executing.");
64
+ while (_) try {
65
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
66
+ if (y = 0, t) op = [op[0] & 2, t.value];
67
+ switch (op[0]) {
68
+ case 0: case 1: t = op; break;
69
+ case 4: _.label++; return { value: op[1], done: false };
70
+ case 5: _.label++; y = op[1]; op = [0]; continue;
71
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
72
+ default:
73
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
74
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
75
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
76
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
77
+ if (t[2]) _.ops.pop();
78
+ _.trys.pop(); continue;
79
+ }
80
+ op = body.call(thisArg, _);
81
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
82
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
83
+ }
84
+ };
85
+ var __spreadArrays = (this && this.__spreadArrays) || function () {
86
+ for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
87
+ for (var r = Array(s), k = 0, i = 0; i < il; i++)
88
+ for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
89
+ r[k] = a[j];
90
+ return r;
91
+ };
92
+ exports.__esModule = true;
93
+ exports.__monitorTestables = exports.monitorWeComProvider = void 0;
94
+ var os = __importStar(require("os"));
95
+ var path = __importStar(require("path"));
96
+ var aibot_node_sdk_1 = require("@wecom/aibot-node-sdk");
97
+ var const_js_1 = require("./const.js");
98
+ var dm_policy_js_1 = require("./dm-policy.js");
99
+ var dynamic_routing_js_1 = require("./dynamic-routing.js");
100
+ var group_policy_js_1 = require("./group-policy.js");
101
+ var media_handler_js_1 = require("./media-handler.js");
102
+ var media_uploader_js_1 = require("./media-uploader.js");
103
+ var message_parser_js_1 = require("./message-parser.js");
104
+ var message_sender_js_1 = require("./message-sender.js");
105
+ var openclaw_compat_js_1 = require("./openclaw-compat.js");
106
+ var runtime_js_1 = require("./runtime.js");
107
+ var state_manager_js_1 = require("./state-manager.js");
108
+ var template_card_manager_js_1 = require("./template-card-manager.js");
109
+ var template_card_parser_js_1 = require("./template-card-parser.js");
110
+ var utils_js_1 = require("./utils.js");
111
+ var version_js_1 = require("./version.js");
112
+ var endpoint_event_adapter_js_1 = require("./endpoint-event-adapter.js");
113
+ var endpoint_dispatch_js_1 = require("./endpoint-dispatch.js");
114
+ var im_runtime_telemetry_js_1 = require("./im-runtime-telemetry.js");
115
+ var proactive_markdown_send_js_1 = require("./proactive-markdown-send.js");
116
+ // ============================================================================
117
+ // 附件超限提示文案
118
+ // ============================================================================
119
+ /**
120
+ * 构造「附件超过 OpenClaw 大小限制」的中文提示文案。
121
+ */
122
+ function buildMediaOversizeHintText(err) {
123
+ var maxMb = err.maxBytes / 1024 / 1024;
124
+ return "\u5F53\u524DOpenClaw\u9650\u5236\u6587\u4EF6\u4E0D\u8D85\u8FC7" + maxMb + "MB\uFF0C\u8BF7\u4FEE\u6539OpenClaw\u914D\u7F6E\u3002";
125
+ }
126
+ // ============================================================================
127
+ // 媒体本地路径白名单扩展
128
+ // ============================================================================
129
+ /**
130
+ * 在 getDefaultMediaLocalRoots() 基础上,将 stateDir 本身也加入白名单,
131
+ * 并合并用户在 WeComConfig 中配置的自定义 mediaLocalRoots。
132
+ *
133
+ * getDefaultMediaLocalRoots() 仅包含 stateDir 下的子目录(media/agents/workspace/sandboxes),
134
+ * 但 agent 生成的文件可能直接放在 stateDir 根目录下(如 ~/.openclaw-dev/1.png),
135
+ * 因此需要将 stateDir 本身也加入白名单以避免 LocalMediaAccessError。
136
+ *
137
+ * 用户可在 openclaw.json 中配置:
138
+ * {
139
+ * "channels": {
140
+ * "wecom": {
141
+ * "mediaLocalRoots": ["~/Downloads", "~/Documents"]
142
+ * }
143
+ * }
144
+ * }
145
+ */
146
+ function getExtendedMediaLocalRoots(config) {
147
+ return __awaiter(this, void 0, void 0, function () {
148
+ var defaults, roots, stateDir, _i, _a, r, resolved;
149
+ return __generator(this, function (_b) {
150
+ switch (_b.label) {
151
+ case 0: return [4 /*yield*/, openclaw_compat_js_1.getDefaultMediaLocalRoots()];
152
+ case 1:
153
+ defaults = _b.sent();
154
+ roots = __spreadArrays(defaults);
155
+ stateDir = path.resolve(openclaw_compat_js_1.resolveStateDir());
156
+ if (!roots.includes(stateDir)) {
157
+ roots.push(stateDir);
158
+ }
159
+ // 合并用户在 WeComConfig 中配置的自定义路径
160
+ if (config === null || config === void 0 ? void 0 : config.mediaLocalRoots) {
161
+ for (_i = 0, _a = config.mediaLocalRoots; _i < _a.length; _i++) {
162
+ r = _a[_i];
163
+ resolved = path.resolve(r.replace(/^~(?=\/|$)/, os.homedir()));
164
+ if (!roots.includes(resolved)) {
165
+ roots.push(resolved);
166
+ }
167
+ }
168
+ }
169
+ return [2 /*return*/, roots];
170
+ }
171
+ });
172
+ });
173
+ }
174
+ // ============================================================================
175
+ // 媒体发送错误提示
176
+ // ============================================================================
177
+ /**
178
+ * 根据媒体发送结果生成纯文本错误摘要(用于替换 thinking 流式消息展示给用户)。
179
+ *
180
+ * 使用纯文本而非 markdown 格式,因为 replyStream 只支持纯文本。
181
+ */
182
+ function buildMediaErrorSummary(mediaUrl, result) {
183
+ var _a;
184
+ if ((_a = result.error) === null || _a === void 0 ? void 0 : _a.includes("LocalMediaAccessError")) {
185
+ return "\u26A0\uFE0F \u6587\u4EF6\u53D1\u9001\u5931\u8D25\uFF1A\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u8DEF\u5F84 " + mediaUrl + "\n\u8BF7\u5728 openclaw.json \u7684 mediaLocalRoots \u4E2D\u6DFB\u52A0\u8BE5\u8DEF\u5F84\u7684\u7236\u76EE\u5F55\u540E\u91CD\u542F\u751F\u6548\u3002";
186
+ }
187
+ if (result.rejectReason) {
188
+ return "\u26A0\uFE0F \u6587\u4EF6\u53D1\u9001\u5931\u8D25\uFF1A" + result.rejectReason;
189
+ }
190
+ return "\u26A0\uFE0F \u6587\u4EF6\u53D1\u9001\u5931\u8D25\uFF1A\u65E0\u6CD5\u5904\u7406\u6587\u4EF6 " + mediaUrl + "\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002";
191
+ }
192
+ function extractUserFacingErrorMessage(raw) {
193
+ var text = String(raw || "").trim();
194
+ if (!text)
195
+ return "";
196
+ var codeErrorPrefix = "endpoint code error:";
197
+ if (text.toLowerCase().startsWith(codeErrorPrefix)) {
198
+ return text.slice(codeErrorPrefix.length).trim();
199
+ }
200
+ var endpointErrorPrefix = "endpoint error ";
201
+ if (text.toLowerCase().startsWith(endpointErrorPrefix)) {
202
+ return text;
203
+ }
204
+ return text;
205
+ }
206
+ var const_js_2 = require("./const.js");
207
+ __createBinding(exports, const_js_2, "WeComCommand");
208
+ var state_manager_js_2 = require("./state-manager.js");
209
+ __createBinding(exports, state_manager_js_2, "getWeComWebSocket");
210
+ __createBinding(exports, state_manager_js_2, "setReqIdForChat");
211
+ __createBinding(exports, state_manager_js_2, "getReqIdForChatAsync");
212
+ __createBinding(exports, state_manager_js_2, "getReqIdForChat");
213
+ __createBinding(exports, state_manager_js_2, "deleteReqIdForChat");
214
+ __createBinding(exports, state_manager_js_2, "warmupReqIdStore");
215
+ __createBinding(exports, state_manager_js_2, "flushReqIdStore");
216
+ var message_sender_js_2 = require("./message-sender.js");
217
+ __createBinding(exports, message_sender_js_2, "sendWeComReply");
218
+ // ============================================================================
219
+ // 消息上下文构建
220
+ // ============================================================================
221
+ function pickSenderDisplayNameAndFields(body) {
222
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
223
+ var raw = body;
224
+ var from = ((_a = body.from) !== null && _a !== void 0 ? _a : {});
225
+ var fields = {
226
+ from_name: (_b = from.name) !== null && _b !== void 0 ? _b : null,
227
+ from_userid: (_c = from.userid) !== null && _c !== void 0 ? _c : null,
228
+ sender_name: (_d = raw.sender_name) !== null && _d !== void 0 ? _d : null,
229
+ sender_nickname: (_e = raw.sender_nickname) !== null && _e !== void 0 ? _e : null,
230
+ sender_nick: (_f = raw.sender_nick) !== null && _f !== void 0 ? _f : null,
231
+ display_name: (_g = raw.display_name) !== null && _g !== void 0 ? _g : null,
232
+ user_name: (_h = raw.user_name) !== null && _h !== void 0 ? _h : null,
233
+ nickname: (_j = raw.nickname) !== null && _j !== void 0 ? _j : null,
234
+ name: (_k = raw.name) !== null && _k !== void 0 ? _k : null
235
+ };
236
+ var candidates = [
237
+ fields.from_name,
238
+ fields.sender_name,
239
+ fields.sender_nickname,
240
+ fields.sender_nick,
241
+ fields.display_name,
242
+ fields.user_name,
243
+ fields.nickname,
244
+ fields.name,
245
+ fields.from_userid,
246
+ ];
247
+ for (var _i = 0, candidates_1 = candidates; _i < candidates_1.length; _i++) {
248
+ var item = candidates_1[_i];
249
+ var text = String(item !== null && item !== void 0 ? item : "").trim();
250
+ if (text) {
251
+ return { selected: text, fields: fields };
252
+ }
253
+ }
254
+ return { selected: "", fields: fields };
255
+ }
256
+ /**
257
+ * 构建消息上下文
258
+ * @returns 消息上下文对象
259
+ */
260
+ function buildMessageContext(frame, account, config, text, mediaList, quoteContent, runtime) {
261
+ var _a, _b, _c, _d;
262
+ var core = runtime_js_1.getWeComRuntime();
263
+ var body = frame.body;
264
+ var chatId = body.chatid || body.from.userid;
265
+ var chatType = body.chattype === "group" ? "group" : "direct";
266
+ var senderId = String(body.from.userid || "").trim();
267
+ var sessionPeerId = utils_js_1.resolveWecomSessionPeerId({
268
+ chatType: chatType,
269
+ chatId: chatId,
270
+ senderId: senderId,
271
+ separateSessionByConversation: account.config.separateSessionByConversation,
272
+ groupSessionScope: account.config.groupSessionScope
273
+ });
274
+ var senderInfo = pickSenderDisplayNameAndFields(body);
275
+ var selectedSenderName = senderInfo.selected || String(body.from.userid || "").trim();
276
+ (_a = runtime === null || runtime === void 0 ? void 0 : runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom][inbound][sender-name] account=" + account.accountId + " from=" + String(body.from.userid || "").trim() + " selected=\"" + selectedSenderName + "\" fields=" + JSON.stringify(senderInfo.fields));
277
+ // 解析路由信息
278
+ var route = core.channel.routing.resolveAgentRoute({
279
+ cfg: config,
280
+ channel: const_js_1.CHANNEL_ID,
281
+ accountId: account.accountId,
282
+ peer: {
283
+ kind: chatType,
284
+ id: sessionPeerId
285
+ }
286
+ });
287
+ // ===== 动态 Agent 路由注入 =====
288
+ var routingResult = dynamic_routing_js_1.processDynamicRouting({
289
+ route: route,
290
+ config: config,
291
+ core: core,
292
+ accountId: account.accountId,
293
+ chatType: chatType === "group" ? "group" : "dm",
294
+ chatId: chatId,
295
+ senderId: body.from.userid,
296
+ log: (runtime === null || runtime === void 0 ? void 0 : runtime.log) ? function () {
297
+ var _a;
298
+ var args = [];
299
+ for (var _i = 0; _i < arguments.length; _i++) {
300
+ args[_i] = arguments[_i];
301
+ }
302
+ return (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
303
+ } : undefined,
304
+ error: (runtime === null || runtime === void 0 ? void 0 : runtime.error) ? function () {
305
+ var _a;
306
+ var args = [];
307
+ for (var _i = 0; _i < arguments.length; _i++) {
308
+ args[_i] = arguments[_i];
309
+ }
310
+ return (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
311
+ } : undefined
312
+ });
313
+ // 应用动态路由结果
314
+ if (routingResult.routeModified) {
315
+ route.agentId = routingResult.finalAgentId;
316
+ route.sessionKey = routingResult.finalSessionKey;
317
+ }
318
+ // ===== 动态 Agent 路由注入结束 =====
319
+ // 构建会话标签
320
+ var fromLabel = chatType === "group" ? "group:" + chatId : "user:" + body.from.userid;
321
+ // 当只有媒体没有文本时,使用占位符标识媒体类型
322
+ var hasImages = mediaList.some(function (m) { var _a; return (_a = m.contentType) === null || _a === void 0 ? void 0 : _a.startsWith("image/"); });
323
+ var messageBody = text ||
324
+ (mediaList.length > 0
325
+ ? hasImages
326
+ ? const_js_1.MEDIA_IMAGE_PLACEHOLDER
327
+ : const_js_1.MEDIA_DOCUMENT_PLACEHOLDER
328
+ : "");
329
+ // 构建多媒体数组
330
+ var mediaPaths = mediaList.length > 0 ? mediaList.map(function (m) { return m.path; }) : undefined;
331
+ var mediaTypes = mediaList.length > 0
332
+ ? mediaList.map(function (m) { return m.contentType; }).filter(Boolean)
333
+ : undefined;
334
+ // 使用 route.agentId 解析 storePath(多 agent 场景下 session 路径隔离)
335
+ var storePath = core.channel.session.resolveStorePath((_b = config.session) === null || _b === void 0 ? void 0 : _b.store, {
336
+ agentId: route.agentId
337
+ });
338
+ // 构建标准消息上下文
339
+ var ctxPayload = core.channel.reply.finalizeInboundContext({
340
+ Body: messageBody,
341
+ RawBody: messageBody,
342
+ CommandBody: messageBody,
343
+ MessageSid: body.msgid,
344
+ From: chatType === "group" ? const_js_1.CHANNEL_ID + ":group:" + chatId : const_js_1.CHANNEL_ID + ":" + body.from.userid,
345
+ To: const_js_1.CHANNEL_ID + ":" + chatId,
346
+ SenderId: body.from.userid,
347
+ SenderName: selectedSenderName,
348
+ SessionKey: route.sessionKey,
349
+ AccountId: route.accountId,
350
+ ChatType: chatType,
351
+ ConversationLabel: fromLabel,
352
+ Timestamp: Date.now(),
353
+ Provider: const_js_1.CHANNEL_ID,
354
+ Surface: const_js_1.CHANNEL_ID,
355
+ OriginatingChannel: const_js_1.CHANNEL_ID,
356
+ OriginatingTo: const_js_1.CHANNEL_ID + ":" + chatId,
357
+ CommandAuthorized: true,
358
+ ResponseUrl: body.response_url,
359
+ ReqId: frame.headers.req_id,
360
+ WeComFrame: frame,
361
+ MediaPath: (_c = mediaList[0]) === null || _c === void 0 ? void 0 : _c.path,
362
+ MediaType: (_d = mediaList[0]) === null || _d === void 0 ? void 0 : _d.contentType,
363
+ MediaPaths: mediaPaths,
364
+ MediaTypes: mediaTypes,
365
+ MediaUrls: mediaPaths,
366
+ ReplyToBody: quoteContent
367
+ });
368
+ return { ctxPayload: ctxPayload, route: route, storePath: storePath, chatId: chatId, chatType: chatType };
369
+ }
370
+ /**
371
+ * 发送"思考中"消息
372
+ */
373
+ function sendThinkingReply(params) {
374
+ var _a, _b;
375
+ return __awaiter(this, void 0, void 0, function () {
376
+ var wsClient, frame, streamId, runtime, state, accountId, err_1;
377
+ return __generator(this, function (_c) {
378
+ switch (_c.label) {
379
+ case 0:
380
+ wsClient = params.wsClient, frame = params.frame, streamId = params.streamId, runtime = params.runtime, state = params.state, accountId = params.accountId;
381
+ _c.label = 1;
382
+ case 1:
383
+ _c.trys.push([1, 3, , 4]);
384
+ return [4 /*yield*/, message_sender_js_1.sendWeComReply({
385
+ wsClient: wsClient,
386
+ frame: frame,
387
+ text: const_js_1.THINKING_MESSAGE,
388
+ runtime: runtime,
389
+ finish: false,
390
+ streamId: streamId,
391
+ accountId: accountId
392
+ })];
393
+ case 2:
394
+ _c.sent();
395
+ return [3 /*break*/, 4];
396
+ case 3:
397
+ err_1 = _c.sent();
398
+ if (err_1 instanceof message_sender_js_1.StreamExpiredError && state) {
399
+ state.streamExpired = true;
400
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] Stream expired during thinking reply, will fallback to proactive send");
401
+ }
402
+ else {
403
+ (_b = runtime.error) === null || _b === void 0 ? void 0 : _b.call(runtime, "[wecom] Failed to send thinking message: " + String(err_1));
404
+ }
405
+ return [3 /*break*/, 4];
406
+ case 4: return [2 /*return*/];
407
+ }
408
+ });
409
+ });
410
+ }
411
+ function isStreamReplyLocallyExpired(state, now) {
412
+ if (now === void 0) { now = Date.now(); }
413
+ if (!state.streamId || !state.streamStartedAt)
414
+ return false;
415
+ return now - state.streamStartedAt >= const_js_1.STREAM_REPLY_LOCAL_TTL_MS;
416
+ }
417
+ function markStreamReplyLocallyExpired(state, runtime, reason) {
418
+ var _a, _b, _c;
419
+ if (state.streamExpired)
420
+ return true;
421
+ if (!isStreamReplyLocallyExpired(state))
422
+ return false;
423
+ state.streamExpired = true;
424
+ var ageMs = Date.now() - ((_a = state.streamStartedAt) !== null && _a !== void 0 ? _a : Date.now());
425
+ (_b = runtime.log) === null || _b === void 0 ? void 0 : _b.call(runtime, "[wecom] Stream local TTL reached, switch to proactive send: reason=" + reason + ", ageMs=" + ageMs + ", ttlMs=" + const_js_1.STREAM_REPLY_LOCAL_TTL_MS + ", streamId=" + ((_c = state.streamId) !== null && _c !== void 0 ? _c : ""));
426
+ return true;
427
+ }
428
+ function sendStreamExpiryNotice(ctx, reason) {
429
+ var _a, _b, _c;
430
+ return __awaiter(this, void 0, void 0, function () {
431
+ var wsClient, frame, state, account, runtime, body, chatId, noticeText, err_2;
432
+ return __generator(this, function (_d) {
433
+ switch (_d.label) {
434
+ case 0:
435
+ wsClient = ctx.wsClient, frame = ctx.frame, state = ctx.state, account = ctx.account, runtime = ctx.runtime;
436
+ if (state.streamExpiryNoticeSent)
437
+ return [2 /*return*/];
438
+ state.streamExpiryNoticeSent = true;
439
+ body = frame.body;
440
+ chatId = body.chatid || body.from.userid;
441
+ noticeText = "本次回复处理时间较长,企业微信流式消息已到达时间限制,后续内容将改为普通消息继续发送。";
442
+ _d.label = 1;
443
+ case 1:
444
+ _d.trys.push([1, 3, , 4]);
445
+ return [4 /*yield*/, proactive_markdown_send_js_1.sendWeComMarkdownMessageChunks({
446
+ wsClient: wsClient,
447
+ chatId: chatId,
448
+ text: noticeText,
449
+ runtime: runtime,
450
+ accountId: account.accountId,
451
+ label: "stream-expiry-notice"
452
+ })];
453
+ case 2:
454
+ _d.sent();
455
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] Stream expiry notice sent: reason=" + reason + ", streamId=" + ((_b = state.streamId) !== null && _b !== void 0 ? _b : ""));
456
+ return [3 /*break*/, 4];
457
+ case 3:
458
+ err_2 = _d.sent();
459
+ (_c = runtime.error) === null || _c === void 0 ? void 0 : _c.call(runtime, "[wecom] Failed to send stream expiry notice: " + String(err_2));
460
+ return [3 /*break*/, 4];
461
+ case 4: return [2 /*return*/];
462
+ }
463
+ });
464
+ });
465
+ }
466
+ /**
467
+ * 上传并发送一批媒体文件(统一走主动发送通道)
468
+ *
469
+ * replyMedia(被动回复)无法覆盖 replyStream 发出的 thinking 流式消息,
470
+ * 因此所有媒体统一走 aibot_send_msg 主动发送。
471
+ */
472
+ function deriveMediaDisplayFileName(mediaUrl) {
473
+ var raw = String(mediaUrl || "").trim();
474
+ if (!raw)
475
+ return "resource";
476
+ try {
477
+ var u = new URL(raw);
478
+ var name = decodeURIComponent(u.pathname.split("/").pop() || "");
479
+ if (name)
480
+ return name;
481
+ }
482
+ catch (_a) {
483
+ var parts = raw.split("/");
484
+ var name = parts[parts.length - 1] || "";
485
+ if (name)
486
+ return name;
487
+ }
488
+ return "resource";
489
+ }
490
+ function formatMediaSendResultList(items) {
491
+ if (!items.length)
492
+ return "";
493
+ var lines = ["附件发送结果:"];
494
+ for (var _i = 0, items_1 = items; _i < items_1.length; _i++) {
495
+ var item = items_1[_i];
496
+ if (item.ok) {
497
+ lines.push("- \u6210\u529F: " + item.fileName);
498
+ }
499
+ else {
500
+ lines.push("- \u5931\u8D25: " + item.fileName + "\uFF08\u539F\u56E0: " + (item.reason || "未知错误") + "\uFF09");
501
+ }
502
+ }
503
+ return lines.join("\n");
504
+ }
505
+ function sendMediaBatch(ctx, mediaUrls) {
506
+ var _a, _b;
507
+ return __awaiter(this, void 0, void 0, function () {
508
+ var wsClient, frame, state, account, runtime, body, chatId, mediaLocalRoots, items, _i, mediaUrls_1, mediaUrl, result, summary;
509
+ return __generator(this, function (_c) {
510
+ switch (_c.label) {
511
+ case 0:
512
+ wsClient = ctx.wsClient, frame = ctx.frame, state = ctx.state, account = ctx.account, runtime = ctx.runtime;
513
+ body = frame.body;
514
+ chatId = body.chatid || body.from.userid;
515
+ return [4 /*yield*/, getExtendedMediaLocalRoots(account.config)];
516
+ case 1:
517
+ mediaLocalRoots = _c.sent();
518
+ items = [];
519
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom][debug] mediaLocalRoots=" + JSON.stringify(mediaLocalRoots) + ", mediaUrls=" + JSON.stringify(mediaUrls));
520
+ _i = 0, mediaUrls_1 = mediaUrls;
521
+ _c.label = 2;
522
+ case 2:
523
+ if (!(_i < mediaUrls_1.length)) return [3 /*break*/, 5];
524
+ mediaUrl = mediaUrls_1[_i];
525
+ return [4 /*yield*/, media_uploader_js_1.uploadAndSendMedia({
526
+ wsClient: wsClient,
527
+ mediaUrl: mediaUrl,
528
+ chatId: chatId,
529
+ mediaLocalRoots: mediaLocalRoots,
530
+ gatewayToken: account.config.gatewayToken,
531
+ gatewayBaseUrl: account.config.gatewayBaseUrl,
532
+ log: function () {
533
+ var _a;
534
+ var args = [];
535
+ for (var _i = 0; _i < arguments.length; _i++) {
536
+ args[_i] = arguments[_i];
537
+ }
538
+ return (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
539
+ },
540
+ errorLog: function () {
541
+ var _a;
542
+ var args = [];
543
+ for (var _i = 0; _i < arguments.length; _i++) {
544
+ args[_i] = arguments[_i];
545
+ }
546
+ return (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
547
+ }
548
+ })];
549
+ case 3:
550
+ result = _c.sent();
551
+ if (result.ok) {
552
+ im_runtime_telemetry_js_1.imRuntimeRecordChannelActivity("wecom", account.accountId, "outbound");
553
+ state.hasMedia = true;
554
+ items.push({
555
+ url: mediaUrl,
556
+ fileName: result.fileName || deriveMediaDisplayFileName(mediaUrl),
557
+ ok: true
558
+ });
559
+ }
560
+ else {
561
+ state.hasMediaFailed = true;
562
+ (_b = runtime.error) === null || _b === void 0 ? void 0 : _b.call(runtime, "[wecom] Media send failed: url=" + mediaUrl + ", reason=" + (result.rejectReason || result.error));
563
+ summary = buildMediaErrorSummary(mediaUrl, result);
564
+ state.mediaErrorSummary = state.mediaErrorSummary
565
+ ? state.mediaErrorSummary + "\n\n" + summary
566
+ : summary;
567
+ items.push({
568
+ url: mediaUrl,
569
+ fileName: result.fileName || deriveMediaDisplayFileName(mediaUrl),
570
+ ok: false,
571
+ reason: result.rejectReason || result.error || "发送失败"
572
+ });
573
+ }
574
+ _c.label = 4;
575
+ case 4:
576
+ _i++;
577
+ return [3 /*break*/, 2];
578
+ case 5: return [2 /*return*/, items];
579
+ }
580
+ });
581
+ });
582
+ }
583
+ /**
584
+ * 关闭 thinking 流(发送 finish=true 的流式消息)
585
+ *
586
+ * thinking 是通过 replyStream 用 streamId 发的流式消息,
587
+ * 只有同一 streamId 的 replyStream(finish=true) 才能关闭它。
588
+ *
589
+ * ⚠️ 注意:企微会忽略空格等不可见内容,必须用有可见字符的文案才能真正
590
+ * 替换掉 thinking 动画,否则 thinking 会一直残留。
591
+ *
592
+ * 关闭策略(按优先级):
593
+ * 1. 有可见文本 → 用完整文本关闭
594
+ * 2. 有模板卡片发送成功 → "📋 卡片消息已发送。"
595
+ * 3. 有媒体成功发送(通过 deliver 回调) → 用友好提示"文件已发送"
596
+ * 4. 媒体发送失败 → 直接用错误摘要替换 thinking
597
+ *
598
+ * 降级策略:
599
+ * - 当 streamExpired=true(errcode 846608 或本地 TTL 保护触发)时,流式通道不可用,
600
+ * 改用 wsClient.sendMessage 主动发送完整文本。
601
+ *
602
+ * 注意:模板卡片的检测和发送已在 finishThinkingStream 之前由
603
+ * processTemplateCardsIfNeeded 完成,此处只关心最后的消息发送。
604
+ */
605
+ function finishThinkingStream(ctx) {
606
+ var _a;
607
+ return __awaiter(this, void 0, void 0, function () {
608
+ var wsClient, frame, state, account, runtime, body, chatId, visibleText, finishText, outboundText, expired, err_3;
609
+ return __generator(this, function (_b) {
610
+ switch (_b.label) {
611
+ case 0:
612
+ wsClient = ctx.wsClient, frame = ctx.frame, state = ctx.state, account = ctx.account, runtime = ctx.runtime;
613
+ body = frame.body;
614
+ chatId = body.chatid || body.from.userid;
615
+ visibleText = state.accumulatedText;
616
+ finishText = state.accumulatedText;
617
+ if (visibleText) {
618
+ finishText = state.accumulatedText;
619
+ }
620
+ else if (state.hasTemplateCard) {
621
+ finishText = "📋 卡片消息已发送。";
622
+ }
623
+ else if (state.hasMedia) {
624
+ if (state.hasMediaFailed && state.mediaErrorSummary) {
625
+ finishText = finishText
626
+ ? finishText + "\n\n" + state.mediaErrorSummary
627
+ : state.mediaErrorSummary;
628
+ }
629
+ else if (!finishText) {
630
+ finishText = "📎 文件已发送,请查收。";
631
+ }
632
+ }
633
+ outboundText = finishText || "当前没有可展示的回复内容";
634
+ if (!outboundText) return [3 /*break*/, 7];
635
+ expired = state.streamExpired || markStreamReplyLocallyExpired(state, runtime, "finish");
636
+ if (!!expired) return [3 /*break*/, 4];
637
+ _b.label = 1;
638
+ case 1:
639
+ _b.trys.push([1, 3, , 4]);
640
+ return [4 /*yield*/, message_sender_js_1.sendWeComReply({
641
+ wsClient: wsClient,
642
+ frame: frame,
643
+ text: outboundText,
644
+ runtime: runtime,
645
+ finish: true,
646
+ streamId: state.streamId,
647
+ accountId: account.accountId
648
+ })];
649
+ case 2:
650
+ _b.sent();
651
+ return [3 /*break*/, 4];
652
+ case 3:
653
+ err_3 = _b.sent();
654
+ if (err_3 instanceof message_sender_js_1.StreamExpiredError) {
655
+ expired = true;
656
+ }
657
+ else {
658
+ throw err_3;
659
+ }
660
+ return [3 /*break*/, 4];
661
+ case 4:
662
+ if (!expired) return [3 /*break*/, 7];
663
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] Stream expired, sending final text via sendMessage (proactive)");
664
+ return [4 /*yield*/, sendStreamExpiryNotice(ctx, "finish")];
665
+ case 5:
666
+ _b.sent();
667
+ return [4 /*yield*/, proactive_markdown_send_js_1.sendWeComMarkdownMessageChunks({
668
+ wsClient: wsClient,
669
+ chatId: chatId,
670
+ text: outboundText,
671
+ runtime: runtime,
672
+ accountId: account.accountId,
673
+ label: "stream-expired-final"
674
+ })];
675
+ case 6:
676
+ _b.sent();
677
+ _b.label = 7;
678
+ case 7: return [2 /*return*/];
679
+ }
680
+ });
681
+ });
682
+ }
683
+ /**
684
+ * 路由消息到核心处理流程并处理回复
685
+ */
686
+ function routeAndDispatchMessage(params) {
687
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
688
+ return __awaiter(this, void 0, void 0, function () {
689
+ var ctxPayload, route, storePath, chatId, chatType, config, account, wsClient, frame, state, runtime, onCleanup, core, ctx, handledConfirmationKeys, subAgentRunIds, announcedSubAgentRunIds, errorPrefixSent, cleanedUp, safeCleanup, isShowThink, bindingScope, endpoint, body, senderId, senderName, chatName, platformId, dispatchResult, mediaSendItems, mediaResultText, mediaErr_1, errMsg, summary, cardResult_1, cardResult, err_4, userFacingError, cardResult, finishErr_1;
690
+ var _this = this;
691
+ return __generator(this, function (_k) {
692
+ switch (_k.label) {
693
+ case 0:
694
+ ctxPayload = params.ctxPayload, route = params.route, storePath = params.storePath, chatId = params.chatId, chatType = params.chatType, config = params.config, account = params.account, wsClient = params.wsClient, frame = params.frame, state = params.state, runtime = params.runtime, onCleanup = params.onCleanup;
695
+ core = runtime_js_1.getWeComRuntime();
696
+ ctx = { wsClient: wsClient, frame: frame, state: state, account: account, runtime: runtime };
697
+ handledConfirmationKeys = new Set();
698
+ subAgentRunIds = new Set();
699
+ announcedSubAgentRunIds = new Set();
700
+ errorPrefixSent = false;
701
+ cleanedUp = false;
702
+ safeCleanup = function () {
703
+ if (!cleanedUp) {
704
+ cleanedUp = true;
705
+ onCleanup();
706
+ }
707
+ };
708
+ isShowThink = !((_a = account.sendThinkingMessage) !== null && _a !== void 0 ? _a : true);
709
+ _k.label = 1;
710
+ case 1:
711
+ _k.trys.push([1, 16, , 22]);
712
+ // 记录 inbound session 元数据(session 追踪)
713
+ return [4 /*yield*/, core.channel.session.recordInboundSession({
714
+ storePath: storePath,
715
+ sessionKey: (_b = ctxPayload.SessionKey) !== null && _b !== void 0 ? _b : route.sessionKey,
716
+ ctx: ctxPayload,
717
+ updateLastRoute: chatType !== "group"
718
+ ? {
719
+ sessionKey: route.mainSessionKey,
720
+ channel: const_js_1.CHANNEL_ID,
721
+ to: const_js_1.CHANNEL_ID + ":" + chatId,
722
+ accountId: route.accountId
723
+ }
724
+ : undefined,
725
+ onRecordError: function (err) {
726
+ var _a;
727
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] failed updating session meta: " + String(err));
728
+ }
729
+ })];
730
+ case 2:
731
+ // 记录 inbound session 元数据(session 追踪)
732
+ _k.sent();
733
+ bindingScope = endpoint_dispatch_js_1.resolveWecomScopeByBindings({
734
+ cfg: config,
735
+ accountId: account.accountId
736
+ });
737
+ endpoint = endpoint_dispatch_js_1.resolveWecomEndpointConfig(account.config, bindingScope);
738
+ if (!endpoint) return [3 /*break*/, 12];
739
+ (_c = runtime.log) === null || _c === void 0 ? void 0 : _c.call(runtime, "[wecom][endpoint] scope source account.workspaceId=" + String(account.config.workspaceId || account.config.workspace_id || account.config.projectId || account.config.project_id || "") + " binding.workspaceId=" + String((bindingScope === null || bindingScope === void 0 ? void 0 : bindingScope.workspaceId) || "") + " effective.workspaceId=" + endpoint.workspaceId);
740
+ if (!(!isShowThink && state.streamId && !state.accumulatedText)) return [3 /*break*/, 4];
741
+ return [4 /*yield*/, sendThinkingReply({
742
+ wsClient: wsClient,
743
+ frame: frame,
744
+ streamId: state.streamId,
745
+ runtime: runtime,
746
+ state: state,
747
+ accountId: account.accountId
748
+ })];
749
+ case 3:
750
+ _k.sent();
751
+ isShowThink = true;
752
+ _k.label = 4;
753
+ case 4:
754
+ body = frame.body;
755
+ senderId = String(((_d = body.from) === null || _d === void 0 ? void 0 : _d.userid) || "").trim();
756
+ senderName = String(((_e = ctxPayload) === null || _e === void 0 ? void 0 : _e.SenderName) || "").trim();
757
+ chatName = String(((_f = body) === null || _f === void 0 ? void 0 : _f.chatname) || "").trim();
758
+ platformId = String(account.botId || route.accountId || "").trim();
759
+ return [4 /*yield*/, endpoint_dispatch_js_1.dispatchWecomViaEndpoint({
760
+ endpointConfig: endpoint,
761
+ routeAgentId: route.agentId,
762
+ accountId: account.accountId,
763
+ platformId: platformId,
764
+ chatId: chatId,
765
+ chatType: chatType === "group" ? "group" : "direct",
766
+ senderId: senderId,
767
+ senderName: senderName,
768
+ chatName: chatName,
769
+ userContent: String(ctxPayload.Body || "").trim(),
770
+ mediaPath: String(ctxPayload.MediaPath || "").trim() || undefined,
771
+ mediaType: String(ctxPayload.MediaType || "").trim() || undefined,
772
+ onConfirmationAction: function (confirmationText) { return __awaiter(_this, void 0, void 0, function () {
773
+ return __generator(this, function (_a) {
774
+ switch (_a.label) {
775
+ case 0: return [4 /*yield*/, proactive_markdown_send_js_1.sendWeComMarkdownMessageChunks({
776
+ wsClient: wsClient,
777
+ chatId: chatId,
778
+ text: confirmationText,
779
+ runtime: runtime,
780
+ accountId: account.accountId,
781
+ label: "endpoint-confirmation"
782
+ })];
783
+ case 1:
784
+ _a.sent();
785
+ return [2 /*return*/];
786
+ }
787
+ });
788
+ }); },
789
+ onChunk: function (_chunk, accumulated) { return __awaiter(_this, void 0, void 0, function () {
790
+ var displayText, err_5;
791
+ var _a;
792
+ return __generator(this, function (_b) {
793
+ switch (_b.label) {
794
+ case 0:
795
+ state.accumulatedText = accumulated;
796
+ if (!state.streamId || state.streamExpired)
797
+ return [2 /*return*/];
798
+ if (!markStreamReplyLocallyExpired(state, runtime, "endpoint_partial")) return [3 /*break*/, 2];
799
+ return [4 /*yield*/, sendStreamExpiryNotice(ctx, "endpoint_partial_local_ttl")];
800
+ case 1:
801
+ _b.sent();
802
+ return [2 /*return*/];
803
+ case 2:
804
+ _b.trys.push([2, 4, , 8]);
805
+ displayText = template_card_parser_js_1.maskTemplateCardBlocks(state.accumulatedText, function () {
806
+ var _a;
807
+ var args = [];
808
+ for (var _i = 0; _i < arguments.length; _i++) {
809
+ args[_i] = arguments[_i];
810
+ }
811
+ return (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
812
+ });
813
+ return [4 /*yield*/, message_sender_js_1.sendWeComReply({
814
+ wsClient: wsClient,
815
+ frame: frame,
816
+ text: displayText,
817
+ runtime: runtime,
818
+ finish: false,
819
+ streamId: state.streamId,
820
+ accountId: account.accountId
821
+ })];
822
+ case 3:
823
+ _b.sent();
824
+ return [3 /*break*/, 8];
825
+ case 4:
826
+ err_5 = _b.sent();
827
+ if (!(err_5 instanceof message_sender_js_1.StreamExpiredError)) return [3 /*break*/, 6];
828
+ state.streamExpired = true;
829
+ return [4 /*yield*/, sendStreamExpiryNotice(ctx, "endpoint_partial_846608")];
830
+ case 5:
831
+ _b.sent();
832
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] Stream expired during endpoint partial reply, will fallback to proactive send");
833
+ return [3 /*break*/, 7];
834
+ case 6: throw err_5;
835
+ case 7: return [3 /*break*/, 8];
836
+ case 8: return [2 /*return*/];
837
+ }
838
+ });
839
+ }); },
840
+ runtimeLog: function () {
841
+ var _a;
842
+ var args = [];
843
+ for (var _i = 0; _i < arguments.length; _i++) {
844
+ args[_i] = arguments[_i];
845
+ }
846
+ return (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
847
+ },
848
+ runtimeError: function () {
849
+ var _a;
850
+ var args = [];
851
+ for (var _i = 0; _i < arguments.length; _i++) {
852
+ args[_i] = arguments[_i];
853
+ }
854
+ return (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
855
+ }
856
+ })];
857
+ case 5:
858
+ dispatchResult = _k.sent();
859
+ state.accumulatedText = dispatchResult.outboundText;
860
+ if (!(dispatchResult.mediaUrls.length > 0)) return [3 /*break*/, 9];
861
+ _k.label = 6;
862
+ case 6:
863
+ _k.trys.push([6, 8, , 9]);
864
+ return [4 /*yield*/, sendMediaBatch(ctx, dispatchResult.mediaUrls)];
865
+ case 7:
866
+ mediaSendItems = _k.sent();
867
+ mediaResultText = formatMediaSendResultList(mediaSendItems);
868
+ if (mediaResultText) {
869
+ state.accumulatedText = [
870
+ String(state.accumulatedText || "").trim(),
871
+ mediaResultText.trim(),
872
+ ]
873
+ .filter(Boolean)
874
+ .join("\n\n")
875
+ .trim();
876
+ }
877
+ return [3 /*break*/, 9];
878
+ case 8:
879
+ mediaErr_1 = _k.sent();
880
+ state.hasMediaFailed = true;
881
+ errMsg = String(mediaErr_1);
882
+ summary = "\u26A0\uFE0F \u6587\u4EF6\u53D1\u9001\u5931\u8D25\uFF1A\u5185\u90E8\u5904\u7406\u5F02\u5E38\uFF0C\u8BF7\u5347\u7EA7 openclaw \u5230\u6700\u65B0\u7248\u672C\u540E\u91CD\u8BD5\u3002\n\u9519\u8BEF\u8BE6\u60C5\uFF1A" + errMsg;
883
+ state.mediaErrorSummary = state.mediaErrorSummary
884
+ ? state.mediaErrorSummary + "\n\n" + summary
885
+ : summary;
886
+ (_g = runtime.error) === null || _g === void 0 ? void 0 : _g.call(runtime, "[wecom] endpoint sendMediaBatch threw: " + errMsg);
887
+ return [3 /*break*/, 9];
888
+ case 9: return [4 /*yield*/, template_card_manager_js_1.processTemplateCardsIfNeeded({
889
+ wsClient: wsClient,
890
+ frame: frame,
891
+ state: state,
892
+ account: account,
893
+ runtime: runtime
894
+ })];
895
+ case 10:
896
+ cardResult_1 = _k.sent();
897
+ if (cardResult_1) {
898
+ state.accumulatedText = cardResult_1.remainingText;
899
+ }
900
+ return [4 /*yield*/, finishThinkingStream(ctx)];
901
+ case 11:
902
+ _k.sent();
903
+ safeCleanup();
904
+ return [2 /*return*/];
905
+ case 12: return [4 /*yield*/, core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
906
+ ctx: ctxPayload,
907
+ cfg: config,
908
+ replyOptions: {
909
+ // 打印 LLM 返回的原始分片内容(在 openclaw 核心对 MEDIA: 指令解析之前),
910
+ // 用于排查流式分片导致 MEDIA 指令被切断、识别丢失等问题
911
+ // onPartialReply: (payload: unknown) => {
912
+ // runtime.log?.(`[openclaw -> plugin][partial] payload=${JSON.stringify(payload)}`);
913
+ // },
914
+ },
915
+ dispatcherOptions: {
916
+ onReplyStart: function () { return __awaiter(_this, void 0, void 0, function () {
917
+ var e_1;
918
+ var _a;
919
+ return __generator(this, function (_b) {
920
+ switch (_b.label) {
921
+ case 0:
922
+ if (!(!isShowThink && state.streamId && !state.accumulatedText)) return [3 /*break*/, 5];
923
+ _b.label = 1;
924
+ case 1:
925
+ _b.trys.push([1, 3, , 4]);
926
+ return [4 /*yield*/, sendThinkingReply({
927
+ wsClient: wsClient,
928
+ frame: frame,
929
+ streamId: state.streamId,
930
+ runtime: runtime,
931
+ state: state,
932
+ accountId: account.accountId
933
+ })];
934
+ case 2:
935
+ _b.sent();
936
+ return [3 /*break*/, 4];
937
+ case 3:
938
+ e_1 = _b.sent();
939
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] sendThinkingReply threw err: " + String(e_1));
940
+ return [3 /*break*/, 4];
941
+ case 4:
942
+ isShowThink = true;
943
+ _b.label = 5;
944
+ case 5: return [2 /*return*/];
945
+ }
946
+ });
947
+ }); },
948
+ deliver: function (payload, info) { return __awaiter(_this, void 0, void 0, function () {
949
+ var adapted, announceKey, outputText, mediaUrls, mediaErr_2, errMsg, summary, displayText, err_6;
950
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
951
+ return __generator(this, function (_k) {
952
+ switch (_k.label) {
953
+ case 0:
954
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[openclaw -> plugin] kind=" + info.kind + ", payload=" + JSON.stringify(payload) + ", info=" + JSON.stringify(info));
955
+ adapted = endpoint_event_adapter_js_1.adaptEndpointEvent({
956
+ rawText: (_b = payload.text) !== null && _b !== void 0 ? _b : "",
957
+ currentEventType: info.kind
958
+ });
959
+ if (adapted.subAgentStartLine) {
960
+ // call_sub_agent 是子代理边界起点;后续同 run_id 的事件按子代理内部过程过滤。
961
+ if (adapted.subAgentRunId)
962
+ subAgentRunIds.add(adapted.subAgentRunId);
963
+ announceKey = adapted.subAgentRunId || adapted.subAgentStartLine;
964
+ // final-only 模式严格只展示最终结论/error/主流程 confirm,因此不展示 sub agent 调用提示。
965
+ // 但上面的 run_id 仍必须记录,用来继续过滤该 sub agent 后续内部事件。
966
+ if (account.config.showFinalAnswerOnly !== true && !announcedSubAgentRunIds.has(announceKey)) {
967
+ announcedSubAgentRunIds.add(announceKey);
968
+ state.accumulatedText += adapted.subAgentStartLine;
969
+ }
970
+ return [2 /*return*/];
971
+ }
972
+ if (adapted.subAgentRunId &&
973
+ subAgentRunIds.has(adapted.subAgentRunId) &&
974
+ adapted.eventType !== "error") {
975
+ // 子代理内部 content/action/confirm 不进入聊天;error 不能在这里过滤,后面会正常输出。
976
+ // sub agent 理论上不应该发起 confirm;这里把 confirm 一并挡住只是防御性兜底,避免内部确认意外打断主会话。
977
+ (_c = runtime.log) === null || _c === void 0 ? void 0 : _c.call(runtime, "[wecom] ignore sub-agent event: agent_run_id=" + adapted.subAgentRunId + " event=" + (adapted.eventType || info.kind));
978
+ return [2 /*return*/];
979
+ }
980
+ if (!adapted.confirmationText) return [3 /*break*/, 2];
981
+ // confirm 必须先于 final-only 过滤处理:主流程确认如果不主动兜底发送,agent 后续流程可能卡住。
982
+ // 子代理 confirm 已经在上面的 sub-agent 过滤分支被挡住,不会触发这里的兜底发送。
983
+ if (adapted.confirmationDedupKey && handledConfirmationKeys.has(adapted.confirmationDedupKey)) {
984
+ (_d = runtime.log) === null || _d === void 0 ? void 0 : _d.call(runtime, "[wecom] confirmation duplicate skipped: " + adapted.confirmationDedupKey);
985
+ return [2 /*return*/];
986
+ }
987
+ if (adapted.confirmationDedupKey)
988
+ handledConfirmationKeys.add(adapted.confirmationDedupKey);
989
+ return [4 /*yield*/, proactive_markdown_send_js_1.sendWeComMarkdownMessageChunks({
990
+ wsClient: wsClient,
991
+ chatId: chatId,
992
+ text: adapted.confirmationText,
993
+ runtime: runtime,
994
+ accountId: account.accountId,
995
+ label: "dispatcher-confirmation"
996
+ })];
997
+ case 1:
998
+ _k.sent();
999
+ return [2 /*return*/];
1000
+ case 2:
1001
+ if (adapted.errorText) {
1002
+ outputText = errorPrefixSent ? adapted.errorText : "\u9519\u8BEF\uFF1A" + adapted.errorText;
1003
+ errorPrefixSent = true;
1004
+ state.accumulatedText += outputText;
1005
+ }
1006
+ else if (account.config.showFinalAnswerOnly === true) {
1007
+ // final-only 模式只允许 final_answer 进入聊天;其他结构化过程事件只记日志或直接丢弃。
1008
+ if (adapted.finalAnswerText) {
1009
+ state.accumulatedText += adapted.finalAnswerText;
1010
+ }
1011
+ else if (adapted.eventType !== "message") {
1012
+ (_e = runtime.log) === null || _e === void 0 ? void 0 : _e.call(runtime, "[wecom] final-answer-only ignored event: " + (adapted.eventType || info.kind));
1013
+ return [2 /*return*/];
1014
+ }
1015
+ else if (adapted.parsed) {
1016
+ return [2 /*return*/];
1017
+ }
1018
+ }
1019
+ else if (adapted.finalAnswerText && adapted.eventType === "content") {
1020
+ // 默认模式下 final_answer 本质上仍是 content,按正常文本累积。
1021
+ state.accumulatedText += adapted.finalAnswerText;
1022
+ }
1023
+ if (adapted.ignoreAsNoise) {
1024
+ // upsert_record 等只用于补取语义信息,不能继续当普通文本透传到聊天。
1025
+ (_f = runtime.log) === null || _f === void 0 ? void 0 : _f.call(runtime, "[wecom] passthrough event ignored for outbound: " + (adapted.eventType || info.kind));
1026
+ return [2 /*return*/];
1027
+ }
1028
+ // 累积文本
1029
+ if (!adapted.errorText &&
1030
+ !adapted.finalAnswerText &&
1031
+ !(account.config.showFinalAnswerOnly === true && adapted.parsed) &&
1032
+ payload.text) {
1033
+ // 已被 adapter 语义化处理过的 final/error 不能再追加 raw payload,
1034
+ // 否则聊天里会出现原始 JSON 或重复文本。
1035
+ state.accumulatedText += "" + (payload.text || "");
1036
+ }
1037
+ mediaUrls = ((_g = payload.mediaUrls) === null || _g === void 0 ? void 0 : _g.length) ? payload.mediaUrls
1038
+ : payload.mediaUrl
1039
+ ? [payload.mediaUrl]
1040
+ : [];
1041
+ if (!(mediaUrls.length > 0)) return [3 /*break*/, 6];
1042
+ _k.label = 3;
1043
+ case 3:
1044
+ _k.trys.push([3, 5, , 6]);
1045
+ return [4 /*yield*/, sendMediaBatch(ctx, mediaUrls)];
1046
+ case 4:
1047
+ _k.sent();
1048
+ return [3 /*break*/, 6];
1049
+ case 5:
1050
+ mediaErr_2 = _k.sent();
1051
+ // sendMediaBatch 内部异常(如 getDefaultMediaLocalRoots 不可用等)
1052
+ // 必须标记 state,否则 finishThinkingStream 会显示"处理完成"误导用户
1053
+ state.hasMediaFailed = true;
1054
+ errMsg = String(mediaErr_2);
1055
+ summary = "\u26A0\uFE0F \u6587\u4EF6\u53D1\u9001\u5931\u8D25\uFF1A\u5185\u90E8\u5904\u7406\u5F02\u5E38\uFF0C\u8BF7\u5347\u7EA7 openclaw \u5230\u6700\u65B0\u7248\u672C\u540E\u91CD\u8BD5\u3002\n\u9519\u8BEF\u8BE6\u60C5\uFF1A" + errMsg;
1056
+ state.mediaErrorSummary = state.mediaErrorSummary
1057
+ ? state.mediaErrorSummary + "\n\n" + summary
1058
+ : summary;
1059
+ (_h = runtime.error) === null || _h === void 0 ? void 0 : _h.call(runtime, "[wecom] sendMediaBatch threw: " + errMsg);
1060
+ return [3 /*break*/, 6];
1061
+ case 6:
1062
+ if (!(state.accumulatedText && !state.streamExpired)) return [3 /*break*/, 14];
1063
+ if (!markStreamReplyLocallyExpired(state, runtime, "dispatcher_intermediate")) return [3 /*break*/, 8];
1064
+ return [4 /*yield*/, sendStreamExpiryNotice(ctx, "dispatcher_intermediate_local_ttl")];
1065
+ case 7:
1066
+ _k.sent();
1067
+ return [2 /*return*/];
1068
+ case 8:
1069
+ _k.trys.push([8, 10, , 14]);
1070
+ displayText = template_card_parser_js_1.maskTemplateCardBlocks(state.accumulatedText, function () {
1071
+ var _a;
1072
+ var args = [];
1073
+ for (var _i = 0; _i < arguments.length; _i++) {
1074
+ args[_i] = arguments[_i];
1075
+ }
1076
+ return (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
1077
+ });
1078
+ // if (displayText !== state.accumulatedText) {
1079
+ // runtime.log?.(`[wecom][template-card] Mid-frame masked: original=${state.accumulatedText.length}chars, masked=${displayText.length}chars`);
1080
+ // }
1081
+ return [4 /*yield*/, message_sender_js_1.sendWeComReply({
1082
+ wsClient: wsClient,
1083
+ frame: frame,
1084
+ text: displayText,
1085
+ runtime: runtime,
1086
+ finish: false,
1087
+ streamId: state.streamId,
1088
+ accountId: account.accountId
1089
+ })];
1090
+ case 9:
1091
+ // if (displayText !== state.accumulatedText) {
1092
+ // runtime.log?.(`[wecom][template-card] Mid-frame masked: original=${state.accumulatedText.length}chars, masked=${displayText.length}chars`);
1093
+ // }
1094
+ _k.sent();
1095
+ return [3 /*break*/, 14];
1096
+ case 10:
1097
+ err_6 = _k.sent();
1098
+ if (!(err_6 instanceof message_sender_js_1.StreamExpiredError)) return [3 /*break*/, 12];
1099
+ state.streamExpired = true;
1100
+ return [4 /*yield*/, sendStreamExpiryNotice(ctx, "dispatcher_intermediate_846608")];
1101
+ case 11:
1102
+ _k.sent();
1103
+ (_j = runtime.log) === null || _j === void 0 ? void 0 : _j.call(runtime, "[wecom] Stream expired during intermediate reply, will fallback to proactive send");
1104
+ return [3 /*break*/, 13];
1105
+ case 12: throw err_6;
1106
+ case 13: return [3 /*break*/, 14];
1107
+ case 14: return [2 /*return*/];
1108
+ }
1109
+ });
1110
+ }); },
1111
+ onError: function (err, info) {
1112
+ var _a;
1113
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] " + info.kind + " reply failed: " + String(err));
1114
+ }
1115
+ }
1116
+ })];
1117
+ case 13:
1118
+ _k.sent();
1119
+ return [4 /*yield*/, template_card_manager_js_1.processTemplateCardsIfNeeded({
1120
+ wsClient: wsClient,
1121
+ frame: frame,
1122
+ state: state,
1123
+ account: account,
1124
+ runtime: runtime
1125
+ })];
1126
+ case 14:
1127
+ cardResult = _k.sent();
1128
+ if (cardResult) {
1129
+ // 卡片已发送,用剩余文本替换累积文本
1130
+ state.accumulatedText = cardResult.remainingText;
1131
+ }
1132
+ // 关闭 thinking 流
1133
+ return [4 /*yield*/, finishThinkingStream(ctx)];
1134
+ case 15:
1135
+ // 关闭 thinking 流
1136
+ _k.sent();
1137
+ safeCleanup();
1138
+ return [3 /*break*/, 22];
1139
+ case 16:
1140
+ err_4 = _k.sent();
1141
+ (_h = runtime.error) === null || _h === void 0 ? void 0 : _h.call(runtime, "[wecom][plugin] Failed to process message: " + String(err_4));
1142
+ userFacingError = extractUserFacingErrorMessage(err_4 instanceof Error ? err_4.message : String(err_4));
1143
+ if (userFacingError) {
1144
+ state.accumulatedText = userFacingError;
1145
+ }
1146
+ _k.label = 17;
1147
+ case 17:
1148
+ _k.trys.push([17, 20, , 21]);
1149
+ return [4 /*yield*/, template_card_manager_js_1.processTemplateCardsIfNeeded({
1150
+ wsClient: wsClient,
1151
+ frame: frame,
1152
+ state: state,
1153
+ account: account,
1154
+ runtime: runtime
1155
+ })];
1156
+ case 18:
1157
+ cardResult = _k.sent();
1158
+ if (cardResult) {
1159
+ state.accumulatedText = cardResult.remainingText;
1160
+ }
1161
+ return [4 /*yield*/, finishThinkingStream(ctx)];
1162
+ case 19:
1163
+ _k.sent();
1164
+ return [3 /*break*/, 21];
1165
+ case 20:
1166
+ finishErr_1 = _k.sent();
1167
+ (_j = runtime.error) === null || _j === void 0 ? void 0 : _j.call(runtime, "[wecom] Failed to finish thinking stream after dispatch error: " + String(finishErr_1));
1168
+ return [3 /*break*/, 21];
1169
+ case 21:
1170
+ safeCleanup();
1171
+ return [3 /*break*/, 22];
1172
+ case 22: return [2 /*return*/];
1173
+ }
1174
+ });
1175
+ });
1176
+ }
1177
+ /**
1178
+ * 解析并校验企业微信消息(防抖前阶段:Step 1-4)
1179
+ *
1180
+ * 执行消息解析、策略检查、媒体下载等前置操作,
1181
+ * 返回一个可用于防抖缓冲的 entry,或 null(消息被过滤/跳过时)。
1182
+ */
1183
+ function prepareWeComMessage(params) {
1184
+ var _a, _b, _c, _d, _e;
1185
+ return __awaiter(this, void 0, void 0, function () {
1186
+ var frame, account, config, runtime, wsClient, body, chatId, chatType, messageId, reqId, _f, textParts, imageUrls, imageAesKeys, fileUrls, fileAesKeys, quoteContent, text, groupPolicyResult, dmPolicyResult, imageMediaList, fileMediaList, err_7, hintText, replyErr_1, mediaList;
1187
+ var _g;
1188
+ return __generator(this, function (_h) {
1189
+ switch (_h.label) {
1190
+ case 0:
1191
+ frame = params.frame, account = params.account, config = params.config, runtime = params.runtime, wsClient = params.wsClient;
1192
+ body = frame.body;
1193
+ chatId = body.chatid || body.from.userid;
1194
+ chatType = body.chattype === "group" ? "group" : "direct";
1195
+ messageId = body.msgid;
1196
+ reqId = frame.headers.req_id;
1197
+ _f = message_parser_js_1.parseMessageContent(body), textParts = _f.textParts, imageUrls = _f.imageUrls, imageAesKeys = _f.imageAesKeys, fileUrls = _f.fileUrls, fileAesKeys = _f.fileAesKeys, quoteContent = _f.quoteContent;
1198
+ text = textParts.join("\n").trim();
1199
+ // // 群聊中移除 @机器人 的提及标记
1200
+ // if (body.chattype === "group") {
1201
+ // text = text.replace(/@\S+/g, "").trim();
1202
+ // }
1203
+ // 如果文本为空但存在引用消息,使用引用消息内容
1204
+ if (!text && quoteContent) {
1205
+ text = quoteContent;
1206
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom][plugin] Using quote content as message body (user only mentioned bot)");
1207
+ }
1208
+ // 如果既没有文本也没有图片也没有文件也没有引用内容,则跳过
1209
+ if (!text && imageUrls.length === 0 && fileUrls.length === 0) {
1210
+ (_b = runtime.log) === null || _b === void 0 ? void 0 : _b.call(runtime, "[wecom][plugin] Skipping empty message (no text, image, file or quote)");
1211
+ return [2 /*return*/, null];
1212
+ }
1213
+ // Step 2: 群组策略检查(仅群聊)
1214
+ if (chatType === "group") {
1215
+ groupPolicyResult = group_policy_js_1.checkGroupPolicy({
1216
+ chatId: chatId,
1217
+ senderId: body.from.userid,
1218
+ account: account,
1219
+ config: config,
1220
+ runtime: runtime
1221
+ });
1222
+ if (!groupPolicyResult.allowed) {
1223
+ return [2 /*return*/, null];
1224
+ }
1225
+ }
1226
+ return [4 /*yield*/, dm_policy_js_1.checkDmPolicy({
1227
+ senderId: body.from.userid,
1228
+ isGroup: chatType === "group",
1229
+ account: account,
1230
+ wsClient: wsClient,
1231
+ frame: frame,
1232
+ runtime: runtime
1233
+ })];
1234
+ case 1:
1235
+ dmPolicyResult = _h.sent();
1236
+ if (!dmPolicyResult.allowed) {
1237
+ return [2 /*return*/, null];
1238
+ }
1239
+ _h.label = 2;
1240
+ case 2:
1241
+ _h.trys.push([2, 4, , 10]);
1242
+ return [4 /*yield*/, Promise.all([
1243
+ media_handler_js_1.downloadAndSaveImages({
1244
+ imageUrls: imageUrls,
1245
+ imageAesKeys: imageAesKeys,
1246
+ account: account,
1247
+ config: config,
1248
+ runtime: runtime,
1249
+ wsClient: wsClient
1250
+ }),
1251
+ media_handler_js_1.downloadAndSaveFiles({
1252
+ fileUrls: fileUrls,
1253
+ fileAesKeys: fileAesKeys,
1254
+ account: account,
1255
+ config: config,
1256
+ runtime: runtime,
1257
+ wsClient: wsClient
1258
+ }),
1259
+ ])];
1260
+ case 3:
1261
+ _g = _h.sent(), imageMediaList = _g[0], fileMediaList = _g[1];
1262
+ return [3 /*break*/, 10];
1263
+ case 4:
1264
+ err_7 = _h.sent();
1265
+ if (!(err_7 instanceof media_handler_js_1.MediaOversizeError)) return [3 /*break*/, 9];
1266
+ hintText = buildMediaOversizeHintText(err_7);
1267
+ (_c = runtime.error) === null || _c === void 0 ? void 0 : _c.call(runtime, "[wecom] Media oversize: kind=" + err_7.kind + ", size=" + err_7.sizeBytes + ", max=" + err_7.maxBytes + ", filename=" + ((_d = err_7.filename) !== null && _d !== void 0 ? _d : "(none)"));
1268
+ _h.label = 5;
1269
+ case 5:
1270
+ _h.trys.push([5, 7, , 8]);
1271
+ return [4 /*yield*/, message_sender_js_1.sendWeComReply({ wsClient: wsClient, frame: frame, text: hintText, runtime: runtime, finish: true, accountId: account.accountId })];
1272
+ case 6:
1273
+ _h.sent();
1274
+ return [3 /*break*/, 8];
1275
+ case 7:
1276
+ replyErr_1 = _h.sent();
1277
+ (_e = runtime.error) === null || _e === void 0 ? void 0 : _e.call(runtime, "[wecom] Failed to send oversize hint: " + String(replyErr_1));
1278
+ return [3 /*break*/, 8];
1279
+ case 8: return [2 /*return*/, null];
1280
+ case 9: throw err_7;
1281
+ case 10:
1282
+ mediaList = __spreadArrays(imageMediaList, fileMediaList);
1283
+ return [2 /*return*/, {
1284
+ frame: frame,
1285
+ account: account,
1286
+ config: config,
1287
+ runtime: runtime,
1288
+ wsClient: wsClient,
1289
+ text: text,
1290
+ mediaList: mediaList,
1291
+ quoteContent: quoteContent,
1292
+ messageId: messageId,
1293
+ chatId: chatId,
1294
+ reqId: reqId
1295
+ }];
1296
+ }
1297
+ });
1298
+ });
1299
+ }
1300
+ /**
1301
+ * 处理企业微信消息(Step 5-7)
1302
+ *
1303
+ * 接收解析后的消息数据,执行初始化状态、发送 thinking、路由到核心。
1304
+ * 同一会话中的消息通过串行队列保证按序执行。
1305
+ */
1306
+ function processWeComMessageNow(entry) {
1307
+ var _a;
1308
+ return __awaiter(this, void 0, void 0, function () {
1309
+ var frame, account, config, runtime, wsClient, text, mediaList, quoteContent, messageId, chatId, reqId, streamId, state, cleanupState, _b, ctxPayload, route, storePath, resolvedChatId, chatType, err_8;
1310
+ return __generator(this, function (_c) {
1311
+ switch (_c.label) {
1312
+ case 0:
1313
+ frame = entry.frame, account = entry.account, config = entry.config, runtime = entry.runtime, wsClient = entry.wsClient, text = entry.text, mediaList = entry.mediaList, quoteContent = entry.quoteContent, messageId = entry.messageId, chatId = entry.chatId, reqId = entry.reqId;
1314
+ // Step 5: 初始化消息状态
1315
+ state_manager_js_1.setReqIdForChat(chatId, reqId, account.accountId);
1316
+ streamId = aibot_node_sdk_1.generateReqId("stream");
1317
+ state = { accumulatedText: "", streamId: streamId, streamStartedAt: Date.now() };
1318
+ state_manager_js_1.setMessageState(messageId, state);
1319
+ cleanupState = function () {
1320
+ state_manager_js_1.deleteMessageState(messageId);
1321
+ };
1322
+ _b = buildMessageContext(frame, account, config, text, mediaList, quoteContent, runtime), ctxPayload = _b.ctxPayload, route = _b.route, storePath = _b.storePath, resolvedChatId = _b.chatId, chatType = _b.chatType;
1323
+ // 以 sessionKey 为键记录「原始大小写」的 chatId 与 chatType,
1324
+ // 供 MCP 工具工厂(index.ts:registerTool)在构造工具闭包时取回,
1325
+ // 进而传递给需要原始 chatId 的拦截器(如 doc-auth-error 发送 biz_msg)。
1326
+ //
1327
+ // 注意:不要使用 parseSessionKeyChat 反解 sessionKey —— OpenClaw core
1328
+ // 构建 sessionKey 时会将 peer.id 强制小写化,会导致企业微信
1329
+ // aibot_send_biz_msg 报 errcode=93006 invalid chatid。
1330
+ state_manager_js_1.setSessionChatInfo(route.sessionKey, {
1331
+ chatId: resolvedChatId,
1332
+ chatType: chatType === "group" ? "group" : "single"
1333
+ });
1334
+ _c.label = 1;
1335
+ case 1:
1336
+ _c.trys.push([1, 3, , 4]);
1337
+ return [4 /*yield*/, routeAndDispatchMessage({
1338
+ ctxPayload: ctxPayload,
1339
+ route: route,
1340
+ storePath: storePath,
1341
+ chatId: resolvedChatId,
1342
+ chatType: chatType,
1343
+ config: config,
1344
+ account: account,
1345
+ wsClient: wsClient,
1346
+ frame: frame,
1347
+ state: state,
1348
+ runtime: runtime,
1349
+ onCleanup: cleanupState
1350
+ })];
1351
+ case 2:
1352
+ _c.sent();
1353
+ return [3 /*break*/, 4];
1354
+ case 3:
1355
+ err_8 = _c.sent();
1356
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom][plugin] Message processing failed: " + String(err_8));
1357
+ cleanupState();
1358
+ return [3 /*break*/, 4];
1359
+ case 4: return [2 /*return*/];
1360
+ }
1361
+ });
1362
+ });
1363
+ }
1364
+ // ============================================================================
1365
+ // 创建 SDK Logger 适配器
1366
+ // ============================================================================
1367
+ /**
1368
+ * 创建适配 RuntimeEnv 的 Logger
1369
+ */
1370
+ function createSdkLogger(runtime, accountId) {
1371
+ return {
1372
+ debug: function (message) {
1373
+ var _a;
1374
+ var args = [];
1375
+ for (var _i = 1; _i < arguments.length; _i++) {
1376
+ args[_i - 1] = arguments[_i];
1377
+ }
1378
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime, "[" + accountId + "] " + message], args));
1379
+ },
1380
+ info: function (message) {
1381
+ var _a;
1382
+ var args = [];
1383
+ for (var _i = 1; _i < arguments.length; _i++) {
1384
+ args[_i - 1] = arguments[_i];
1385
+ }
1386
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime, "[" + accountId + "] " + message], args));
1387
+ },
1388
+ warn: function (message) {
1389
+ var _a;
1390
+ var args = [];
1391
+ for (var _i = 1; _i < arguments.length; _i++) {
1392
+ args[_i - 1] = arguments[_i];
1393
+ }
1394
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime, "[" + accountId + "] WARN: " + message], args));
1395
+ },
1396
+ error: function (message) {
1397
+ var _a;
1398
+ var args = [];
1399
+ for (var _i = 1; _i < arguments.length; _i++) {
1400
+ args[_i - 1] = arguments[_i];
1401
+ }
1402
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime, "[" + accountId + "] " + message], args));
1403
+ }
1404
+ };
1405
+ }
1406
+ // ============================================================================
1407
+ // 主函数
1408
+ // ============================================================================
1409
+ /**
1410
+ * 监听企业微信 WebSocket 连接
1411
+ * 使用 aibot-node-sdk 简化连接管理
1412
+ */
1413
+ function monitorWeComProvider(options) {
1414
+ var _a;
1415
+ return __awaiter(this, void 0, void 0, function () {
1416
+ var account, config, runtime, abortSignal, setStatus;
1417
+ var _this = this;
1418
+ return __generator(this, function (_b) {
1419
+ account = options.account, config = options.config, runtime = options.runtime, abortSignal = options.abortSignal, setStatus = options.setStatus;
1420
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] [" + version_js_1.PLUGIN_VERSION + "] Initializing WSClient with SDK...");
1421
+ // 启动消息状态定期清理
1422
+ state_manager_js_1.startMessageStateCleanup();
1423
+ return [2 /*return*/, new Promise(function (resolve, reject) {
1424
+ var _a;
1425
+ var logger = createSdkLogger(runtime, account.accountId);
1426
+ var wsClient = new aibot_node_sdk_1.WSClient({
1427
+ botId: account.botId,
1428
+ secret: account.secret,
1429
+ wsUrl: account.websocketUrl,
1430
+ logger: logger,
1431
+ heartbeatInterval: const_js_1.WS_HEARTBEAT_INTERVAL_MS,
1432
+ maxReconnectAttempts: const_js_1.WS_MAX_RECONNECT_ATTEMPTS,
1433
+ maxAuthFailureAttempts: const_js_1.WS_MAX_AUTH_FAILURE_ATTEMPTS,
1434
+ scene: const_js_1.SCENE_WECOM_OPENCLAW,
1435
+ plug_version: version_js_1.PLUGIN_VERSION
1436
+ });
1437
+ var updateLifecycleStatus = function (patch) {
1438
+ var now = typeof patch.lastEventAt === "number" ? patch.lastEventAt : Date.now();
1439
+ var connected = typeof patch.connected === "boolean" ? patch.connected : undefined;
1440
+ var authenticated = typeof patch.authenticated === "boolean" ? patch.authenticated : undefined;
1441
+ var running = typeof patch.running === "boolean" ? patch.running : undefined;
1442
+ var restartPending = typeof patch.restartPending === "boolean" ? patch.restartPending : undefined;
1443
+ var lastEvent = String(patch.lastEvent || "").trim() || null;
1444
+ var lastError = patch.lastError === null ? null : patch.lastError != null ? String(patch.lastError) : undefined;
1445
+ var reconnectAttempts = typeof patch.reconnectAttempts === "number" ? patch.reconnectAttempts : undefined;
1446
+ state_manager_js_1.updateWeComConnectionSnapshot(account.accountId, __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({}, (connected !== undefined ? { connected: connected } : {})), (authenticated !== undefined ? { authenticated: authenticated } : {})), (running !== undefined ? { running: running } : {})), (restartPending !== undefined ? { restartPending: restartPending } : {})), (lastEvent ? { lastEvent: lastEvent } : {})), { lastEventAt: now }), (lastError !== undefined ? { lastError: lastError } : {})), (typeof patch.lastStartAt === "number" ? { lastStartAt: patch.lastStartAt } : {})), (typeof patch.lastStopAt === "number" ? { lastStopAt: patch.lastStopAt } : {})), (reconnectAttempts !== undefined ? { reconnectAttempts: reconnectAttempts } : {})));
1447
+ im_runtime_telemetry_js_1.imRuntimeSetChannelStatus({
1448
+ platform: "wecom",
1449
+ botAccountId: account.accountId,
1450
+ linkStatus: connected === true ? "connected" : connected === false ? "disconnected" : undefined,
1451
+ lastError: lastError,
1452
+ lastEvent: lastEvent,
1453
+ lastEventAt: now,
1454
+ running: running,
1455
+ connected: connected,
1456
+ restartPending: restartPending,
1457
+ reconnectCount: reconnectAttempts
1458
+ });
1459
+ setStatus === null || setStatus === void 0 ? void 0 : setStatus(__assign({ accountId: account.accountId }, patch));
1460
+ };
1461
+ // 防止 cleanup 被多次调用(abort handler、error handler、disconnected_event 可能竞态触发)
1462
+ var cleanedUp = false;
1463
+ // 清理函数:确保所有资源被释放(幂等)
1464
+ var cleanup = function () { return __awaiter(_this, void 0, void 0, function () {
1465
+ return __generator(this, function (_a) {
1466
+ switch (_a.label) {
1467
+ case 0:
1468
+ if (cleanedUp)
1469
+ return [2 /*return*/];
1470
+ cleanedUp = true;
1471
+ state_manager_js_1.stopMessageStateCleanup();
1472
+ return [4 /*yield*/, state_manager_js_1.cleanupAccount(account.accountId)];
1473
+ case 1:
1474
+ _a.sent();
1475
+ return [2 /*return*/];
1476
+ }
1477
+ });
1478
+ }); };
1479
+ // 处理中止信号(框架 stopChannel 会触发 abort)
1480
+ // resolve() 让 Promise settle → 框架清理 store.tasks/store.aborts
1481
+ if (abortSignal) {
1482
+ abortSignal.addEventListener("abort", function () { return __awaiter(_this, void 0, void 0, function () {
1483
+ var _a;
1484
+ return __generator(this, function (_b) {
1485
+ switch (_b.label) {
1486
+ case 0:
1487
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] Connection aborted");
1488
+ updateLifecycleStatus({
1489
+ connected: false,
1490
+ authenticated: false,
1491
+ running: false,
1492
+ restartPending: false,
1493
+ lastEvent: "control_abort",
1494
+ lastError: "connection_aborted_by_control_plane",
1495
+ lastStopAt: Date.now(),
1496
+ lastEventAt: Date.now()
1497
+ });
1498
+ wsClient.disconnect();
1499
+ return [4 /*yield*/, cleanup()];
1500
+ case 1:
1501
+ _b.sent();
1502
+ resolve();
1503
+ return [2 /*return*/];
1504
+ }
1505
+ });
1506
+ }); });
1507
+ }
1508
+ // 监听连接事件
1509
+ wsClient.on("connected", function () {
1510
+ var _a;
1511
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] WebSocket connected");
1512
+ updateLifecycleStatus({
1513
+ connected: true,
1514
+ authenticated: false,
1515
+ running: true,
1516
+ restartPending: false,
1517
+ lastEvent: "websocket_connected",
1518
+ lastError: null,
1519
+ lastStartAt: Date.now(),
1520
+ lastEventAt: Date.now()
1521
+ });
1522
+ });
1523
+ // 监听认证成功事件
1524
+ wsClient.on("authenticated", function () {
1525
+ var _a;
1526
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] Authentication successful");
1527
+ state_manager_js_1.setWeComWebSocket(account.accountId, wsClient);
1528
+ updateLifecycleStatus({
1529
+ connected: true,
1530
+ authenticated: true,
1531
+ running: true,
1532
+ restartPending: false,
1533
+ lastEvent: "websocket_authenticated",
1534
+ lastError: null,
1535
+ lastStartAt: Date.now(),
1536
+ lastEventAt: Date.now()
1537
+ });
1538
+ });
1539
+ // 监听断开事件
1540
+ wsClient.on("disconnected", function (reason) {
1541
+ var _a;
1542
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] WebSocket disconnected: " + reason);
1543
+ updateLifecycleStatus({
1544
+ connected: false,
1545
+ authenticated: false,
1546
+ // disconnected 可能是短时网络抖动,优先让 SDK 自重连,保留 running=true。
1547
+ running: true,
1548
+ restartPending: true,
1549
+ lastEvent: "websocket_disconnected",
1550
+ lastError: String(reason || "websocket_disconnected"),
1551
+ lastEventAt: Date.now()
1552
+ });
1553
+ });
1554
+ // 监听被踢下线事件(服务端因新连接建立而主动断开旧连接)
1555
+ //
1556
+ // SDK 内部已设置 isManualClose=true 阻止 SDK 层自动重连,连接不会自行恢复。
1557
+ // **不 reject/resolve Promise**——保持 pending 以阻止框架层 auto-restart。
1558
+ //
1559
+ // 为什么不能 reject/resolve:
1560
+ // - reject → 框架 auto-restart 介入 → 新连接建立 → 又被踢 → 两个实例互踢无限循环
1561
+ // - resolve → 同上,框架 .then() 中的 auto-restart 也会触发
1562
+ //
1563
+ // Promise pending 的安全性:
1564
+ // - store.tasks.has(id) = true → 阻止 Health Monitor 直接 startChannel(startChannel 检查 tasks.has)
1565
+ // - 框架 stopChannel → abort() → abort handler 中 resolve() → tasks 正常清理
1566
+ // - 用户修改配置 → config reload → stopChannel + startChannel → 正常恢复
1567
+ //
1568
+ // 显式调用 wsClient.disconnect() 确保 SDK 内部资源(定时器、队列等)完全释放。
1569
+ wsClient.on("event.disconnected_event", function () { return __awaiter(_this, void 0, void 0, function () {
1570
+ var errorMsg;
1571
+ var _a;
1572
+ return __generator(this, function (_b) {
1573
+ switch (_b.label) {
1574
+ case 0:
1575
+ errorMsg = "Kicked by server: a new connection was established elsewhere. Auto-restart is suppressed to avoid mutual kicking. Please check for duplicate instances.";
1576
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] " + errorMsg);
1577
+ updateLifecycleStatus({
1578
+ connected: false,
1579
+ authenticated: false,
1580
+ running: false,
1581
+ restartPending: false,
1582
+ lastEvent: "transport_logged_out",
1583
+ lastError: errorMsg,
1584
+ lastStopAt: Date.now(),
1585
+ lastEventAt: Date.now()
1586
+ });
1587
+ im_runtime_telemetry_js_1.imRuntimeRecordTransportDisconnect({
1588
+ platform: "wecom",
1589
+ botAccountId: account.accountId,
1590
+ atMs: Date.now(),
1591
+ error: errorMsg,
1592
+ loggedOut: true
1593
+ });
1594
+ wsClient.disconnect();
1595
+ return [4 /*yield*/, cleanup()];
1596
+ case 1:
1597
+ _b.sent();
1598
+ return [2 /*return*/];
1599
+ }
1600
+ });
1601
+ }); });
1602
+ // 监听重连事件
1603
+ wsClient.on("reconnecting", function (attempt) {
1604
+ var _a;
1605
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] Reconnecting attempt " + attempt + "...");
1606
+ updateLifecycleStatus({
1607
+ connected: false,
1608
+ authenticated: false,
1609
+ running: true,
1610
+ restartPending: true,
1611
+ lastEvent: "websocket_reconnecting",
1612
+ reconnectAttempts: attempt,
1613
+ lastEventAt: Date.now()
1614
+ });
1615
+ });
1616
+ // 监听错误事件
1617
+ wsClient.on("error", function (error) { return __awaiter(_this, void 0, void 0, function () {
1618
+ var errorMsg;
1619
+ var _a, _b;
1620
+ return __generator(this, function (_c) {
1621
+ switch (_c.label) {
1622
+ case 0:
1623
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] WebSocket error: " + error.message);
1624
+ if (!(error instanceof aibot_node_sdk_1.WSAuthFailureError)) return [3 /*break*/, 2];
1625
+ errorMsg = "Auth failure attempts exhausted (" + const_js_1.WS_MAX_AUTH_FAILURE_ATTEMPTS + " attempts). Please check botId/secret configuration.";
1626
+ (_b = runtime.error) === null || _b === void 0 ? void 0 : _b.call(runtime, "[" + account.accountId + "] " + errorMsg);
1627
+ updateLifecycleStatus({
1628
+ connected: false,
1629
+ authenticated: false,
1630
+ running: false,
1631
+ restartPending: false,
1632
+ lastEvent: "auth_failure",
1633
+ lastError: errorMsg,
1634
+ lastStopAt: Date.now(),
1635
+ lastEventAt: Date.now()
1636
+ });
1637
+ im_runtime_telemetry_js_1.imRuntimeRecordTransportDisconnect({
1638
+ platform: "wecom",
1639
+ botAccountId: account.accountId,
1640
+ atMs: Date.now(),
1641
+ error: errorMsg,
1642
+ loggedOut: false
1643
+ });
1644
+ wsClient.disconnect();
1645
+ return [4 /*yield*/, cleanup()];
1646
+ case 1:
1647
+ _c.sent();
1648
+ return [2 /*return*/];
1649
+ case 2:
1650
+ if (error instanceof aibot_node_sdk_1.WSReconnectExhaustedError) {
1651
+ // 网络断线重连次数用尽(SDK 层已重试 WS_MAX_RECONNECT_ATTEMPTS 次)。
1652
+ // 通常是网络/服务端问题,框架 auto-restart 可能恢复。
1653
+ //
1654
+ // reject Promise → 框架 auto-restart 介入(最多 MAX_RESTART_ATTEMPTS=10 次)
1655
+ // 总连接尝试次数 = (1 首次 + WS_MAX_RECONNECT_ATTEMPTS 重连) × (1 首轮 + 10 auto-restart)
1656
+ // = 11 × 11 = 121 次
1657
+ //
1658
+ // 如果 Health Monitor 介入(每 5 分钟检查),会 resetRestartAttempts 重新计数,
1659
+ // 受限于 DEFAULT_MAX_RESTARTS_PER_HOUR=10,每小时最多额外 10 × 121 = 1210 次。
1660
+ // 但因网络断线通常是暂时性的,auto-restart + Health Monitor 的兜底机制是合理的。
1661
+ //
1662
+ // 显式调用 wsClient.disconnect() 确保 SDK 内部资源完全释放,
1663
+ // 避免旧实例的定时器/队列残留。
1664
+ im_runtime_telemetry_js_1.imRuntimeRecordTransportDisconnect({
1665
+ platform: "wecom",
1666
+ botAccountId: account.accountId,
1667
+ atMs: Date.now(),
1668
+ error: error.message,
1669
+ loggedOut: false
1670
+ });
1671
+ wsClient.disconnect();
1672
+ cleanup()["finally"](function () { return reject(error); });
1673
+ return [2 /*return*/];
1674
+ }
1675
+ return [2 /*return*/];
1676
+ }
1677
+ });
1678
+ }); });
1679
+ // 监听版本检查事件:收到 enter_check_update 时回复当前插件版本
1680
+ wsClient.on(const_js_1.EVENT_ENTER_CHECK_UPDATE, function (frame) { return __awaiter(_this, void 0, void 0, function () {
1681
+ var err_9;
1682
+ return __generator(this, function (_a) {
1683
+ switch (_a.label) {
1684
+ case 0:
1685
+ _a.trys.push([0, 2, , 3]);
1686
+ // runtime.log?.(`[${account.accountId}] Received enter_check_update, replying with version=${PLUGIN_VERSION}`);
1687
+ return [4 /*yield*/, wsClient.reply(frame, { version: version_js_1.PLUGIN_VERSION }, const_js_1.CMD_ENTER_EVENT_REPLY)];
1688
+ case 1:
1689
+ // runtime.log?.(`[${account.accountId}] Received enter_check_update, replying with version=${PLUGIN_VERSION}`);
1690
+ _a.sent();
1691
+ return [3 /*break*/, 3];
1692
+ case 2:
1693
+ err_9 = _a.sent();
1694
+ return [3 /*break*/, 3];
1695
+ case 3: return [2 /*return*/];
1696
+ }
1697
+ });
1698
+ }); });
1699
+ // 监听普通消息
1700
+ wsClient.on("message", function (frame) { return __awaiter(_this, void 0, void 0, function () {
1701
+ var entry, err_10;
1702
+ var _a;
1703
+ return __generator(this, function (_b) {
1704
+ switch (_b.label) {
1705
+ case 0:
1706
+ _b.trys.push([0, 3, , 4]);
1707
+ return [4 /*yield*/, prepareWeComMessage({
1708
+ frame: frame,
1709
+ account: account,
1710
+ config: config,
1711
+ runtime: runtime,
1712
+ wsClient: wsClient
1713
+ })];
1714
+ case 1:
1715
+ entry = _b.sent();
1716
+ if (!entry)
1717
+ return [2 /*return*/];
1718
+ im_runtime_telemetry_js_1.imRuntimeRecordChannelActivity("wecom", entry.account.accountId, "inbound");
1719
+ // 排队逻辑暂时关闭,直接处理消息
1720
+ // const { status } = enqueueWeComChatTask({
1721
+ // accountId: entry.account.accountId,
1722
+ // chatId: entry.chatId,
1723
+ // task: () => processWeComMessageNow(entry),
1724
+ // });
1725
+ //
1726
+ // if (status === "queued") {
1727
+ // runtime.log?.(`[wecom] Chat task queued for chat=${entry.chatId} (previous task still running)`);
1728
+ // }
1729
+ return [4 /*yield*/, processWeComMessageNow(entry)];
1730
+ case 2:
1731
+ // 排队逻辑暂时关闭,直接处理消息
1732
+ // const { status } = enqueueWeComChatTask({
1733
+ // accountId: entry.account.accountId,
1734
+ // chatId: entry.chatId,
1735
+ // task: () => processWeComMessageNow(entry),
1736
+ // });
1737
+ //
1738
+ // if (status === "queued") {
1739
+ // runtime.log?.(`[wecom] Chat task queued for chat=${entry.chatId} (previous task still running)`);
1740
+ // }
1741
+ _b.sent();
1742
+ return [3 /*break*/, 4];
1743
+ case 3:
1744
+ err_10 = _b.sent();
1745
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] Failed to process message: " + String(err_10));
1746
+ return [3 /*break*/, 4];
1747
+ case 4: return [2 /*return*/];
1748
+ }
1749
+ });
1750
+ }); });
1751
+ // 监听所有事件回调(aibot_event_callback)。
1752
+ // 这里使用通用 event 监听,再按 eventtype 分发,兼容不同 SDK 版本在细分事件名上的差异。
1753
+ wsClient.on("event", function (frame) { return __awaiter(_this, void 0, void 0, function () {
1754
+ var eventBody, eventType, templateCardEvent, updateErr_1, authChangeEvent, entry, err_11;
1755
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
1756
+ return __generator(this, function (_s) {
1757
+ switch (_s.label) {
1758
+ case 0:
1759
+ _s.trys.push([0, 10, , 11]);
1760
+ eventBody = frame.body;
1761
+ eventType = (_a = eventBody.event) === null || _a === void 0 ? void 0 : _a.eventtype;
1762
+ (_b = runtime.log) === null || _b === void 0 ? void 0 : _b.call(runtime, "[" + account.accountId + "] Received event callback: eventtype=" + (eventType !== null && eventType !== void 0 ? eventType : "") + ", msgid=" + ((_c = eventBody.msgid) !== null && _c !== void 0 ? _c : ""));
1763
+ if (!(eventType === "template_card_event")) return [3 /*break*/, 5];
1764
+ templateCardEvent = (_d = eventBody.event) === null || _d === void 0 ? void 0 : _d.template_card_event;
1765
+ (_e = runtime.log) === null || _e === void 0 ? void 0 : _e.call(runtime, "[" + account.accountId + "] Received template_card_event: event_key=" + ((_f = templateCardEvent === null || templateCardEvent === void 0 ? void 0 : templateCardEvent.event_key) !== null && _f !== void 0 ? _f : "") + ", task_id=" + ((_g = templateCardEvent === null || templateCardEvent === void 0 ? void 0 : templateCardEvent.task_id) !== null && _g !== void 0 ? _g : ""));
1766
+ _s.label = 1;
1767
+ case 1:
1768
+ _s.trys.push([1, 3, , 4]);
1769
+ return [4 /*yield*/, template_card_manager_js_1.updateTemplateCardOnEvent({
1770
+ frame: frame,
1771
+ accountId: account.accountId,
1772
+ runtime: runtime,
1773
+ wsClient: wsClient
1774
+ })];
1775
+ case 2:
1776
+ _s.sent();
1777
+ return [3 /*break*/, 4];
1778
+ case 3:
1779
+ updateErr_1 = _s.sent();
1780
+ (_h = runtime.error) === null || _h === void 0 ? void 0 : _h.call(runtime, "[" + account.accountId + "] [template-card-update] Failed to update template card: " + String(updateErr_1));
1781
+ return [3 /*break*/, 4];
1782
+ case 4: return [3 /*break*/, 6];
1783
+ case 5:
1784
+ if (eventType === "auth_change_event") {
1785
+ authChangeEvent = (_j = eventBody.event) === null || _j === void 0 ? void 0 : _j.auth_change_event;
1786
+ (_k = runtime.log) === null || _k === void 0 ? void 0 : _k.call(runtime, "[" + account.accountId + "] Received auth_change_event: auth_list=[" + ((_m = (_l = authChangeEvent === null || authChangeEvent === void 0 ? void 0 : authChangeEvent.auth_list) === null || _l === void 0 ? void 0 : _l.join(", ")) !== null && _m !== void 0 ? _m : "") + "]");
1787
+ }
1788
+ else {
1789
+ // 其他未识别的事件类型,跳过
1790
+ return [2 /*return*/];
1791
+ }
1792
+ _s.label = 6;
1793
+ case 6: return [4 /*yield*/, prepareWeComMessage({
1794
+ frame: frame,
1795
+ account: account,
1796
+ config: config,
1797
+ runtime: runtime,
1798
+ wsClient: wsClient
1799
+ })];
1800
+ case 7:
1801
+ entry = _s.sent();
1802
+ if (!entry) return [3 /*break*/, 9];
1803
+ im_runtime_telemetry_js_1.imRuntimeRecordChannelActivity("wecom", entry.account.accountId, "inbound");
1804
+ return [4 /*yield*/, processWeComMessageNow(entry)];
1805
+ case 8:
1806
+ _s.sent();
1807
+ _s.label = 9;
1808
+ case 9: return [3 /*break*/, 11];
1809
+ case 10:
1810
+ err_11 = _s.sent();
1811
+ (_o = runtime.error) === null || _o === void 0 ? void 0 : _o.call(runtime, "[" + account.accountId + "] Failed to process event callback (" + ((_r = (_q = (_p = frame.body) === null || _p === void 0 ? void 0 : _p.event) === null || _q === void 0 ? void 0 : _q.eventtype) !== null && _r !== void 0 ? _r : "unknown") + "): " + String(err_11));
1812
+ return [3 /*break*/, 11];
1813
+ case 11: return [2 /*return*/];
1814
+ }
1815
+ });
1816
+ }); });
1817
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] Event listeners attached: message + event(template_card_event, auth_change_event)");
1818
+ // 启动前预热 reqId 缓存,确保完成后再建立连接,避免 getSync 在预热完成前返回 undefined
1819
+ state_manager_js_1.warmupReqIdStore(account.accountId, function () {
1820
+ var _a;
1821
+ var args = [];
1822
+ for (var _i = 0; _i < arguments.length; _i++) {
1823
+ args[_i] = arguments[_i];
1824
+ }
1825
+ return (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call.apply(_a, __spreadArrays([runtime], args));
1826
+ })
1827
+ .then(function (count) {
1828
+ var _a;
1829
+ (_a = runtime.log) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] Warmed up " + count + " reqId entries from disk");
1830
+ })["catch"](function (err) {
1831
+ var _a;
1832
+ (_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[" + account.accountId + "] Failed to warmup reqId store: " + String(err));
1833
+ })["finally"](function () {
1834
+ // 无论预热成功或失败,都建立连接
1835
+ wsClient.connect();
1836
+ });
1837
+ })];
1838
+ });
1839
+ });
1840
+ }
1841
+ exports.monitorWeComProvider = monitorWeComProvider;
1842
+ exports.__monitorTestables = {
1843
+ finishThinkingStream: finishThinkingStream,
1844
+ isStreamReplyLocallyExpired: isStreamReplyLocallyExpired,
1845
+ markStreamReplyLocallyExpired: markStreamReplyLocallyExpired
1846
+ };