@dingxiang-me/openclaw-wechat 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.en.md +181 -14
  3. package/README.md +201 -16
  4. package/docs/channels/wecom.md +137 -1
  5. package/openclaw.plugin.json +688 -6
  6. package/package.json +204 -4
  7. package/scripts/wecom-agent-selfcheck.mjs +775 -0
  8. package/scripts/wecom-bot-longconn-probe.mjs +582 -0
  9. package/scripts/wecom-bot-selfcheck.mjs +952 -0
  10. package/scripts/wecom-callback-matrix.mjs +224 -0
  11. package/scripts/wecom-doctor.mjs +1407 -0
  12. package/scripts/wecom-e2e-scenario.mjs +333 -0
  13. package/scripts/wecom-migrate.mjs +261 -0
  14. package/scripts/wecom-quickstart.mjs +1824 -0
  15. package/scripts/wecom-release-check.mjs +232 -0
  16. package/scripts/wecom-remote-e2e.mjs +310 -0
  17. package/scripts/wecom-selfcheck.mjs +1255 -0
  18. package/scripts/wecom-smoke.sh +74 -0
  19. package/src/core/delivery-router.js +21 -0
  20. package/src/core.js +619 -30
  21. package/src/wecom/account-config-core.js +27 -1
  22. package/src/wecom/account-config.js +19 -2
  23. package/src/wecom/agent-dispatch-executor.js +11 -0
  24. package/src/wecom/agent-dispatch-handlers.js +61 -8
  25. package/src/wecom/agent-inbound-guards.js +24 -0
  26. package/src/wecom/agent-inbound-processor.js +34 -2
  27. package/src/wecom/agent-late-reply-runtime.js +30 -2
  28. package/src/wecom/agent-text-sender.js +2 -0
  29. package/src/wecom/api-client-core.js +27 -19
  30. package/src/wecom/api-client-media.js +16 -7
  31. package/src/wecom/api-client-send-text.js +4 -0
  32. package/src/wecom/api-client-send-typed.js +4 -1
  33. package/src/wecom/api-client-senders.js +41 -3
  34. package/src/wecom/api-client.js +1 -0
  35. package/src/wecom/bot-dispatch-fallback.js +18 -3
  36. package/src/wecom/bot-dispatch-handlers.js +47 -10
  37. package/src/wecom/bot-inbound-dispatch-runtime.js +3 -0
  38. package/src/wecom/bot-inbound-executor-helpers.js +11 -1
  39. package/src/wecom/bot-inbound-executor.js +24 -0
  40. package/src/wecom/bot-inbound-guards.js +31 -1
  41. package/src/wecom/channel-config-schema.js +132 -0
  42. package/src/wecom/channel-plugin.js +348 -7
  43. package/src/wecom/command-handlers.js +102 -11
  44. package/src/wecom/command-status-text.js +206 -0
  45. package/src/wecom/doc-client.js +7 -1
  46. package/src/wecom/inbound-content-handler-file-video-link.js +4 -0
  47. package/src/wecom/inbound-content-handler-image-voice.js +6 -0
  48. package/src/wecom/inbound-content.js +5 -0
  49. package/src/wecom/installer-api.js +910 -0
  50. package/src/wecom/media-download.js +2 -2
  51. package/src/wecom/migration-diagnostics.js +816 -0
  52. package/src/wecom/network-config.js +91 -0
  53. package/src/wecom/observability-metrics.js +9 -3
  54. package/src/wecom/outbound-agent-delivery.js +313 -0
  55. package/src/wecom/outbound-agent-media-sender.js +37 -7
  56. package/src/wecom/outbound-agent-push.js +1 -0
  57. package/src/wecom/outbound-delivery.js +129 -12
  58. package/src/wecom/outbound-stream-msg-item.js +25 -2
  59. package/src/wecom/outbound-webhook-delivery.js +19 -0
  60. package/src/wecom/outbound-webhook-media.js +30 -6
  61. package/src/wecom/pending-reply-manager.js +143 -0
  62. package/src/wecom/plugin-account-policy-services.js +26 -0
  63. package/src/wecom/plugin-base-services.js +58 -0
  64. package/src/wecom/plugin-constants.js +1 -1
  65. package/src/wecom/plugin-delivery-inbound-services.js +25 -0
  66. package/src/wecom/plugin-processing-deps.js +7 -0
  67. package/src/wecom/plugin-route-runtime-deps.js +1 -0
  68. package/src/wecom/plugin-services.js +87 -0
  69. package/src/wecom/policy-resolvers.js +93 -20
  70. package/src/wecom/quickstart-metadata.js +1247 -0
  71. package/src/wecom/reasoning-visibility.js +104 -0
  72. package/src/wecom/register-runtime.js +10 -0
  73. package/src/wecom/reliable-delivery-persistence.js +138 -0
  74. package/src/wecom/reliable-delivery.js +642 -0
  75. package/src/wecom/reply-output-policy.js +171 -0
  76. package/src/wecom/text-inbound-scheduler.js +6 -1
  77. package/src/wecom/workspace-auto-sender.js +2 -0
@@ -14,6 +14,7 @@ import { createWecomDefaultLimiters } from "./rate-limiter.js";
14
14
  import { createWecomMediaFetcher, normalizeOutboundMediaUrls, resolveWecomOutboundMediaTarget } from "./media-url-utils.js";
15
15
  import { createWecomOutboundSender } from "./outbound-sender.js";
16
16
  import { createWecomObservabilityMetricsStore } from "./observability-metrics.js";
17
+ import { createWecomReliableDeliveryStore } from "./reliable-delivery.js";
17
18
  import { createWecomRequestParsers } from "./request-parsers.js";
18
19
  import { createWecomTargetResolver } from "./target-utils.js";
19
20
  import { createDeliveredTranscriptReplyTracker } from "./transcript-utils.js";
@@ -45,6 +46,7 @@ export function createWecomPluginBaseServices({
45
46
  recordRuntimeErrorMetric,
46
47
  getWecomObservabilityMetrics,
47
48
  } = createWecomObservabilityMetricsStore();
49
+ const reliableDeliveryStore = createWecomReliableDeliveryStore();
48
50
  const { markTranscriptReplyDelivered, hasTranscriptReplyBeenDelivered } = createDeliveredTranscriptReplyTracker({
49
51
  ttlMs: TRANSCRIPT_REPLY_CACHE_TTL_MS,
50
52
  });
@@ -91,6 +93,7 @@ export function createWecomPluginBaseServices({
91
93
  getWecomAccessToken,
92
94
  buildWecomMessageSendRequest,
93
95
  sendWecomText,
96
+ sendWecomMarkdown,
94
97
  uploadWecomMedia,
95
98
  sendWecomImage,
96
99
  sendWecomVideo,
@@ -150,6 +153,56 @@ export function createWecomPluginBaseServices({
150
153
  resolveWecomTarget,
151
154
  });
152
155
 
156
+ function markWecomReliableInboundActivity({
157
+ mode = "agent",
158
+ accountId = "default",
159
+ sessionId = "",
160
+ fromUser = "",
161
+ at,
162
+ } = {}) {
163
+ return reliableDeliveryStore.markInboundActivity({
164
+ mode,
165
+ accountId,
166
+ sessionId,
167
+ fromUser,
168
+ at,
169
+ });
170
+ }
171
+
172
+ function recordReliableDeliveryOutcome({
173
+ mode = "agent",
174
+ accountId = "default",
175
+ sessionId = "",
176
+ fromUser = "",
177
+ deliveryStatus = "rejected_unknown",
178
+ layer = "",
179
+ reason = "",
180
+ at,
181
+ } = {}) {
182
+ return reliableDeliveryStore.recordDeliveryOutcome({
183
+ mode,
184
+ accountId,
185
+ sessionId,
186
+ fromUser,
187
+ deliveryStatus,
188
+ layer,
189
+ reason,
190
+ at,
191
+ });
192
+ }
193
+
194
+ function getWecomReliableDeliverySnapshot({
195
+ mode = "agent",
196
+ accountId = "default",
197
+ sessionId = "",
198
+ } = {}) {
199
+ return reliableDeliveryStore.getDeliverySnapshot({
200
+ mode,
201
+ accountId,
202
+ sessionId,
203
+ });
204
+ }
205
+
153
206
  return {
154
207
  markTranscriptReplyDelivered,
155
208
  hasTranscriptReplyBeenDelivered,
@@ -157,6 +210,10 @@ export function createWecomPluginBaseServices({
157
210
  recordDeliveryMetric,
158
211
  recordRuntimeErrorMetric,
159
212
  getWecomObservabilityMetrics,
213
+ markWecomReliableInboundActivity,
214
+ recordReliableDeliveryOutcome,
215
+ getWecomReliableDeliverySnapshot,
216
+ reliableDeliveryStore,
160
217
  scheduleTempFileCleanup,
161
218
  setBotStreamExpireMs,
162
219
  resolveBotActiveStream,
@@ -185,6 +242,7 @@ export function createWecomPluginBaseServices({
185
242
  getWecomAccessToken,
186
243
  buildWecomMessageSendRequest,
187
244
  sendWecomText,
245
+ sendWecomMarkdown,
188
246
  uploadWecomMedia,
189
247
  sendWecomImage,
190
248
  sendWecomVideo,
@@ -1,5 +1,5 @@
1
1
  export const MAX_REQUEST_BODY_SIZE = 1024 * 1024;
2
- export const PLUGIN_VERSION = "2.1.0";
2
+ export const PLUGIN_VERSION = "2.3.0";
3
3
  export const WECOM_TEMP_DIR_NAME = "openclaw-wechat";
4
4
  export const WECOM_TEMP_FILE_RETENTION_MS = 30 * 60 * 1000;
5
5
  export const WECOM_MIN_FILE_SIZE = 5;
@@ -3,6 +3,7 @@ import { WECOM_TEMP_DIR_NAME, BOOTSTRAP_TEMPLATE_FILES } from "./plugin-constant
3
3
  import { SEEDED_AGENT_WORKSPACES } from "./plugin-shared-state.js";
4
4
  import { WecomSessionTaskQueue } from "../core/stream-manager.js";
5
5
  import { createWecomBotReplyDeliverer } from "./outbound-delivery.js";
6
+ import { createWecomAgentReplyDeliverer } from "./outbound-agent-delivery.js";
6
7
  import { createWecomInboundContentBuilder } from "./inbound-content.js";
7
8
  import { createDynamicWorkspaceSeeder } from "./workspace-tools.js";
8
9
  import { createWecomSessionQueueManager } from "./session-queue.js";
@@ -13,6 +14,8 @@ export function createWecomPluginDeliveryInboundServices({
13
14
  setBotStreamExpireMs,
14
15
  attachWecomProxyDispatcher,
15
16
  resolveWecomDeliveryFallbackPolicy,
17
+ resolveWecomReasoningPolicy,
18
+ resolveWecomReplyFormatPolicy,
16
19
  resolveWecomWebhookBotDeliveryPolicy,
17
20
  resolveWecomObservabilityPolicy,
18
21
  resolveWecomBotProxyConfig,
@@ -28,10 +31,14 @@ export function createWecomPluginDeliveryInboundServices({
28
31
  drainBotStreamMedia,
29
32
  getWecomConfig,
30
33
  sendWecomText,
34
+ sendWecomMarkdown,
31
35
  fetchMediaFromUrl,
32
36
  extractWorkspacePathsFromText,
33
37
  resolveWorkspacePathToHost,
34
38
  recordDeliveryMetric,
39
+ recordReliableDeliveryOutcome,
40
+ enqueuePendingReply,
41
+ sendWecomOutboundMediaBatch,
35
42
  downloadWecomMedia,
36
43
  resolveWecomVoiceTranscriptionConfig,
37
44
  transcribeInboundVoice,
@@ -51,6 +58,8 @@ export function createWecomPluginDeliveryInboundServices({
51
58
  const { deliverBotReplyText } = createWecomBotReplyDeliverer({
52
59
  attachWecomProxyDispatcher,
53
60
  resolveWecomDeliveryFallbackPolicy,
61
+ resolveWecomReasoningPolicy,
62
+ resolveWecomReplyFormatPolicy,
54
63
  resolveWecomWebhookBotDeliveryPolicy,
55
64
  resolveWecomObservabilityPolicy,
56
65
  resolveWecomBotProxyConfig,
@@ -72,6 +81,21 @@ export function createWecomPluginDeliveryInboundServices({
72
81
  extractWorkspacePathsFromText,
73
82
  resolveWorkspacePathToHost,
74
83
  recordDeliveryMetric,
84
+ recordReliableDeliveryOutcome,
85
+ enqueuePendingReply,
86
+ });
87
+ const deliverAgentReply = createWecomAgentReplyDeliverer({
88
+ getWecomConfig,
89
+ sendWecomText,
90
+ sendWecomMarkdown,
91
+ sendWecomOutboundMediaBatch,
92
+ resolveWecomReasoningPolicy,
93
+ resolveWecomReplyFormatPolicy,
94
+ resolveWorkspacePathToHost,
95
+ createDeliveryTraceId,
96
+ recordDeliveryMetric,
97
+ recordReliableDeliveryOutcome,
98
+ enqueuePendingReply,
75
99
  });
76
100
 
77
101
  const { buildInboundContent } = createWecomInboundContentBuilder({
@@ -90,6 +114,7 @@ export function createWecomPluginDeliveryInboundServices({
90
114
  syncWecomSessionQueuePolicy,
91
115
  executeInboundTaskWithSessionQueue,
92
116
  deliverBotReplyText,
117
+ deliverAgentReply,
93
118
  buildInboundContent,
94
119
  };
95
120
  }
@@ -30,6 +30,7 @@ export function createPluginProcessingDeps(context = {}) {
30
30
  buildWecomBotStatusText: context.buildWecomBotStatusText,
31
31
  buildBotInboundContent: context.buildBotInboundContent,
32
32
  resolveWecomAgentRoute: context.resolveWecomAgentRoute,
33
+ resolveWecomReasoningPolicy: context.resolveWecomReasoningPolicy,
33
34
  seedDynamicAgentWorkspace: context.seedDynamicAgentWorkspace,
34
35
  resolveSessionTranscriptFilePath: context.resolveSessionTranscriptFilePath,
35
36
  readTranscriptAppendedChunk: context.readTranscriptAppendedChunk,
@@ -48,6 +49,8 @@ export function createPluginProcessingDeps(context = {}) {
48
49
  ACTIVE_LATE_REPLY_WATCHERS: context.ACTIVE_LATE_REPLY_WATCHERS,
49
50
  resetWecomConversationSession: context.resetWecomConversationSession,
50
51
  clearSessionStoreEntry: context.clearSessionStoreEntry,
52
+ markWecomReliableInboundActivity: context.markWecomReliableInboundActivity,
53
+ flushWecomSessionPendingReplies: context.flushWecomSessionPendingReplies,
51
54
  },
52
55
  agentInboundDeps: {
53
56
  getWecomConfig: context.getWecomConfig,
@@ -69,12 +72,14 @@ export function createPluginProcessingDeps(context = {}) {
69
72
  resolveWecomAgentRoute: context.resolveWecomAgentRoute,
70
73
  seedDynamicAgentWorkspace: context.seedDynamicAgentWorkspace,
71
74
  resolveWecomReplyStreamingPolicy: context.resolveWecomReplyStreamingPolicy,
75
+ resolveWecomReasoningPolicy: context.resolveWecomReasoningPolicy,
72
76
  asNumber: context.asNumber,
73
77
  requireEnv: context.requireEnv,
74
78
  getByteLength: context.getByteLength,
75
79
  markdownToWecomText: context.markdownToWecomText,
76
80
  autoSendWorkspaceFilesFromReplyText: context.autoSendWorkspaceFilesFromReplyText,
77
81
  sendWecomOutboundMediaBatch: context.sendWecomOutboundMediaBatch,
82
+ deliverAgentReply: context.deliverAgentReply,
78
83
  sleep: context.sleep,
79
84
  resolveSessionTranscriptFilePath: context.resolveSessionTranscriptFilePath,
80
85
  readTranscriptAppendedChunk: context.readTranscriptAppendedChunk,
@@ -88,6 +93,8 @@ export function createPluginProcessingDeps(context = {}) {
88
93
  ACTIVE_LATE_REPLY_WATCHERS: context.ACTIVE_LATE_REPLY_WATCHERS,
89
94
  resetWecomConversationSession: context.resetWecomConversationSession,
90
95
  clearSessionStoreEntry: context.clearSessionStoreEntry,
96
+ markWecomReliableInboundActivity: context.markWecomReliableInboundActivity,
97
+ flushWecomSessionPendingReplies: context.flushWecomSessionPendingReplies,
91
98
  },
92
99
  textSchedulerDeps: {
93
100
  resolveWecomGroupChatPolicy: context.resolveWecomGroupChatPolicy,
@@ -52,6 +52,7 @@ export function createPluginRouteRuntimeDeps(context = {}) {
52
52
  resolveWecomBotConfig: context.resolveWecomBotConfig,
53
53
  resolveWecomBotConfigs: context.resolveWecomBotConfigs,
54
54
  syncWecomBotLongConnections: context.syncWecomBotLongConnections,
55
+ initializeWecomReliableDeliveryPersistence: context.initializeWecomReliableDeliveryPersistence,
55
56
  listEnabledWecomAccounts: context.listEnabledWecomAccounts,
56
57
  getWecomConfig: context.getWecomConfig,
57
58
  wecomChannelPlugin: context.wecomChannelPlugin,
@@ -41,6 +41,8 @@ import {
41
41
  import { createWecomPluginBaseServices } from "./plugin-base-services.js";
42
42
  import { createWecomPluginAccountPolicyServices } from "./plugin-account-policy-services.js";
43
43
  import { createWecomPluginDeliveryInboundServices } from "./plugin-delivery-inbound-services.js";
44
+ import { createWecomPendingReplyManager } from "./pending-reply-manager.js";
45
+ import { createWecomReliableDeliveryPersistence } from "./reliable-delivery-persistence.js";
44
46
  import { createWecomBotInboundContentBuilder } from "./bot-inbound-content.js";
45
47
  import { createWecomBotLongConnectionManager } from "./bot-long-connection-manager.js";
46
48
  import { createWecomDocToolRegistrar } from "./doc-tool.js";
@@ -68,6 +70,7 @@ export function createWecomPluginServices({
68
70
  fetchImpl = fetch,
69
71
  proxyAgentCtor = ProxyAgent,
70
72
  } = {}) {
73
+ let pendingReplyApi = null;
71
74
  const base = createWecomPluginBaseServices({
72
75
  fetchImpl,
73
76
  proxyAgentCtor,
@@ -77,6 +80,7 @@ export function createWecomPluginServices({
77
80
  processEnv,
78
81
  getGatewayRuntime: base.getGatewayRuntime,
79
82
  getWecomObservabilityMetrics: base.getWecomObservabilityMetrics,
83
+ getWecomReliableDeliverySnapshot: base.getWecomReliableDeliverySnapshot,
80
84
  normalizeWecomResolvedTarget: base.normalizeWecomResolvedTarget,
81
85
  formatWecomTargetForLog: base.formatWecomTargetForLog,
82
86
  sendWecomWebhookText: base.sendWecomWebhookText,
@@ -85,11 +89,36 @@ export function createWecomPluginServices({
85
89
  sendWecomText: base.sendWecomText,
86
90
  });
87
91
 
92
+ const reliableDeliveryPersistence = createWecomReliableDeliveryPersistence({
93
+ reliableDeliveryStore: base.reliableDeliveryStore,
94
+ resolveWecomPendingReplyPolicy: accountPolicy.resolveWecomPendingReplyPolicy,
95
+ getGatewayRuntime: base.getGatewayRuntime,
96
+ processEnv,
97
+ logger: {
98
+ warn: (...args) => pendingReplyApi?.logger?.warn?.(...args),
99
+ debug: (...args) => pendingReplyApi?.logger?.debug?.(...args),
100
+ },
101
+ });
102
+
103
+ function markWecomReliableInboundActivity(payload = {}) {
104
+ const result = base.markWecomReliableInboundActivity(payload);
105
+ reliableDeliveryPersistence.schedulePersist("reliable-inbound");
106
+ return result;
107
+ }
108
+
109
+ function recordReliableDeliveryOutcome(payload = {}) {
110
+ const result = base.recordReliableDeliveryOutcome(payload);
111
+ reliableDeliveryPersistence.schedulePersist("reliable-delivery");
112
+ return result;
113
+ }
114
+
88
115
  const deliveryInbound = createWecomPluginDeliveryInboundServices({
89
116
  resolveWecomStreamManagerPolicy: accountPolicy.resolveWecomStreamManagerPolicy,
90
117
  setBotStreamExpireMs: base.setBotStreamExpireMs,
91
118
  attachWecomProxyDispatcher: base.attachWecomProxyDispatcher,
92
119
  resolveWecomDeliveryFallbackPolicy: accountPolicy.resolveWecomDeliveryFallbackPolicy,
120
+ resolveWecomReasoningPolicy: accountPolicy.resolveWecomReasoningPolicy,
121
+ resolveWecomReplyFormatPolicy: accountPolicy.resolveWecomReplyFormatPolicy,
93
122
  resolveWecomWebhookBotDeliveryPolicy: accountPolicy.resolveWecomWebhookBotDeliveryPolicy,
94
123
  resolveWecomObservabilityPolicy: accountPolicy.resolveWecomObservabilityPolicy,
95
124
  resolveWecomBotProxyConfig: accountPolicy.resolveWecomBotProxyConfig,
@@ -105,14 +134,60 @@ export function createWecomPluginServices({
105
134
  drainBotStreamMedia: base.drainBotStreamMedia,
106
135
  getWecomConfig: accountPolicy.getWecomConfig,
107
136
  sendWecomText: base.sendWecomText,
137
+ sendWecomMarkdown: base.sendWecomMarkdown,
108
138
  fetchMediaFromUrl: base.fetchMediaFromUrl,
109
139
  extractWorkspacePathsFromText: base.extractWorkspacePathsFromText,
110
140
  resolveWorkspacePathToHost: base.resolveWorkspacePathToHost,
111
141
  recordDeliveryMetric: base.recordDeliveryMetric,
142
+ recordReliableDeliveryOutcome,
143
+ enqueuePendingReply: (api, payload) => {
144
+ pendingReplyApi = api ?? pendingReplyApi;
145
+ return pendingReplyManager?.enqueuePendingReply?.(api, payload);
146
+ },
147
+ sendWecomOutboundMediaBatch: base.sendWecomOutboundMediaBatch,
112
148
  downloadWecomMedia: base.downloadWecomMedia,
113
149
  resolveWecomVoiceTranscriptionConfig: accountPolicy.resolveWecomVoiceTranscriptionConfig,
114
150
  transcribeInboundVoice: accountPolicy.transcribeInboundVoice,
115
151
  });
152
+ const pendingReplyManager = createWecomPendingReplyManager({
153
+ reliableDeliveryStore: base.reliableDeliveryStore,
154
+ resolveWecomPendingReplyPolicy: accountPolicy.resolveWecomPendingReplyPolicy,
155
+ ensurePersistenceLoaded: (api) => reliableDeliveryPersistence.ensureLoaded(api),
156
+ schedulePersistenceFlush: (reason, api) => reliableDeliveryPersistence.schedulePersist(reason, api),
157
+ deliverPendingReply: async (entry) => {
158
+ if (entry?.mode === "bot") {
159
+ return deliveryInbound.deliverBotReplyText({
160
+ api: pendingReplyApi,
161
+ fromUser: entry.fromUser,
162
+ accountId: entry.accountId,
163
+ sessionId: entry.sessionId,
164
+ text: entry.payload?.text,
165
+ thinkingContent: entry.payload?.thinkingContent,
166
+ mediaItems: entry.payload?.mediaItems,
167
+ mediaUrls: entry.payload?.mediaUrls,
168
+ mediaType: entry.payload?.mediaType,
169
+ reason: "pending-reply",
170
+ allowPendingEnqueue: false,
171
+ });
172
+ }
173
+ return deliveryInbound.deliverAgentReply({
174
+ api: pendingReplyApi,
175
+ fromUser: entry.fromUser,
176
+ accountId: entry.accountId,
177
+ sessionId: entry.sessionId,
178
+ text: entry.payload?.text,
179
+ thinkingContent: entry.payload?.thinkingContent,
180
+ mediaItems: entry.payload?.mediaItems,
181
+ mediaUrls: entry.payload?.mediaUrls,
182
+ mediaType: entry.payload?.mediaType,
183
+ reason: "pending-reply",
184
+ allowPendingEnqueue: false,
185
+ });
186
+ },
187
+ logger: {
188
+ warn: (...args) => pendingReplyApi?.logger?.warn?.(...args),
189
+ },
190
+ });
116
191
  const buildBotInboundContent = createWecomBotInboundContentBuilder({
117
192
  fetchMediaFromUrl: base.fetchMediaFromUrl,
118
193
  detectImageContentTypeFromBuffer,
@@ -157,6 +232,18 @@ export function createWecomPluginServices({
157
232
  ...base,
158
233
  ...accountPolicy,
159
234
  ...deliveryInbound,
235
+ markWecomReliableInboundActivity,
236
+ recordReliableDeliveryOutcome,
237
+ enqueueWecomPendingReply: pendingReplyManager.enqueuePendingReply,
238
+ flushDueWecomPendingReplies: pendingReplyManager.flushDuePendingReplies,
239
+ flushWecomSessionPendingReplies: pendingReplyManager.flushSessionPendingReplies,
240
+ initializeWecomReliableDeliveryPersistence: async (api) => {
241
+ pendingReplyApi = api ?? pendingReplyApi;
242
+ await reliableDeliveryPersistence.ensureLoaded(api);
243
+ await pendingReplyManager.initialize(api);
244
+ return true;
245
+ },
246
+ persistWecomReliableDeliveryState: (reason = "manual", api) => reliableDeliveryPersistence.persistNow(reason, api),
160
247
  buildBotInboundContent,
161
248
  registerWecomDocTools,
162
249
  resetWecomConversationSession,
@@ -12,6 +12,10 @@ export function createWecomPolicyResolvers({
12
12
  resolveWecomDebounceConfig,
13
13
  resolveWecomStreamingConfig,
14
14
  resolveWecomDeliveryFallbackConfig,
15
+ resolveWecomPendingReplyConfig,
16
+ resolveWecomQuotaTrackingConfig,
17
+ resolveWecomReasoningConfig,
18
+ resolveWecomReplyFormatConfig,
15
19
  resolveWecomWebhookBotDeliveryConfig,
16
20
  resolveWecomStreamManagerConfig,
17
21
  resolveWecomObservabilityConfig,
@@ -25,6 +29,44 @@ export function createWecomPolicyResolvers({
25
29
  throw new Error("createWecomPolicyResolvers: normalizeAccountId is required");
26
30
  }
27
31
 
32
+ function asObject(value) {
33
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
34
+ }
35
+
36
+ function resolveLegacyInlineAccountConfig(channelConfig, normalizedAccountId) {
37
+ const channel = asObject(channelConfig);
38
+ for (const [key, value] of Object.entries(channel)) {
39
+ if (normalizeAccountId(key) !== normalizedAccountId) continue;
40
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
41
+ return value;
42
+ }
43
+ return {};
44
+ }
45
+
46
+ function resolveRawWecomAccountConfig(channelConfig, normalizedAccountId) {
47
+ if (normalizedAccountId === "default") return asObject(channelConfig);
48
+ const rawChannel = asObject(channelConfig);
49
+ const accountConfig = asObject(rawChannel?.accounts)?.[normalizedAccountId];
50
+ if (accountConfig && typeof accountConfig === "object" && !Array.isArray(accountConfig)) {
51
+ return accountConfig;
52
+ }
53
+ return resolveLegacyInlineAccountConfig(rawChannel, normalizedAccountId);
54
+ }
55
+
56
+ function resolveWecomPolicyAccountInputs(api, accountId = "default", accountConfig = {}) {
57
+ const inputs = resolveWecomPolicyInputs(api);
58
+ const normalizedAccountId = normalizeAccountId(accountId ?? accountConfig?.accountId ?? "default");
59
+ const rawAccountConfig = resolveRawWecomAccountConfig(inputs.channelConfig, normalizedAccountId);
60
+ return {
61
+ ...inputs,
62
+ accountId: normalizedAccountId,
63
+ accountConfig: {
64
+ ...rawAccountConfig,
65
+ ...(accountConfig && typeof accountConfig === "object" ? accountConfig : {}),
66
+ },
67
+ };
68
+ }
69
+
28
70
  function resolveWecomPolicyInputs(api) {
29
71
  const cfg = api?.config ?? getGatewayRuntime()?.config ?? {};
30
72
  return {
@@ -95,28 +137,17 @@ export function createWecomPolicyResolvers({
95
137
  }
96
138
 
97
139
  function resolveWecomAllowFromPolicy(api, accountId, accountConfig = {}) {
98
- const inputs = resolveWecomPolicyInputs(api);
99
- return resolveWecomAllowFromPolicyConfig({
100
- ...inputs,
101
- accountId: normalizeAccountId(accountId ?? "default"),
102
- accountConfig: accountConfig ?? {},
103
- });
140
+ return resolveWecomAllowFromPolicyConfig(resolveWecomPolicyAccountInputs(api, accountId, accountConfig));
104
141
  }
105
142
 
106
143
  function resolveWecomDmPolicy(api, accountId, accountConfig = {}) {
107
- const inputs = resolveWecomPolicyInputs(api);
108
144
  if (typeof resolveWecomDmPolicyConfig !== "function") {
109
145
  return { mode: "open", allowFrom: [], rejectMessage: "当前私聊账号未授权,请联系管理员。", enabled: false };
110
146
  }
111
- return resolveWecomDmPolicyConfig({
112
- ...inputs,
113
- accountId: normalizeAccountId(accountId ?? "default"),
114
- accountConfig: accountConfig ?? {},
115
- });
147
+ return resolveWecomDmPolicyConfig(resolveWecomPolicyAccountInputs(api, accountId, accountConfig));
116
148
  }
117
149
 
118
150
  function resolveWecomEventPolicy(api, accountId, accountConfig = {}) {
119
- const inputs = resolveWecomPolicyInputs(api);
120
151
  if (typeof resolveWecomEventPolicyConfig !== "function") {
121
152
  return {
122
153
  enabled: true,
@@ -124,15 +155,14 @@ export function createWecomPolicyResolvers({
124
155
  enterAgentWelcomeText: "你好,我是 AI 助手,直接发消息即可开始对话。",
125
156
  };
126
157
  }
127
- return resolveWecomEventPolicyConfig({
128
- ...inputs,
129
- accountId: normalizeAccountId(accountId ?? "default"),
130
- accountConfig: accountConfig ?? {},
131
- });
158
+ return resolveWecomEventPolicyConfig(resolveWecomPolicyAccountInputs(api, accountId, accountConfig));
132
159
  }
133
160
 
134
- function resolveWecomGroupChatPolicy(api) {
135
- return resolveWecomGroupChatConfig(resolveWecomPolicyInputs(api));
161
+ function resolveWecomGroupChatPolicy(api, accountId = "default", accountConfig = {}, chatId = "") {
162
+ return resolveWecomGroupChatConfig({
163
+ ...resolveWecomPolicyAccountInputs(api, accountId, accountConfig),
164
+ chatId,
165
+ });
136
166
  }
137
167
 
138
168
  function resolveWecomTextDebouncePolicy(api) {
@@ -151,6 +181,45 @@ export function createWecomPolicyResolvers({
151
181
  return resolveWecomWebhookBotDeliveryConfig(resolveWecomPolicyInputs(api));
152
182
  }
153
183
 
184
+ function resolveWecomPendingReplyPolicy(api) {
185
+ if (typeof resolveWecomPendingReplyConfig !== "function") {
186
+ return {
187
+ enabled: true,
188
+ maxRetries: 3,
189
+ retryBackoffMs: 15000,
190
+ expireMs: 10 * 60 * 1000,
191
+ };
192
+ }
193
+ return resolveWecomPendingReplyConfig(resolveWecomPolicyInputs(api));
194
+ }
195
+
196
+ function resolveWecomQuotaTrackingPolicy(api) {
197
+ if (typeof resolveWecomQuotaTrackingConfig !== "function") {
198
+ return { enabled: true };
199
+ }
200
+ return resolveWecomQuotaTrackingConfig(resolveWecomPolicyInputs(api));
201
+ }
202
+
203
+ function resolveWecomReasoningPolicy(api) {
204
+ if (typeof resolveWecomReasoningConfig !== "function") {
205
+ return {
206
+ mode: "separate",
207
+ sendThinkingMessage: true,
208
+ includeInFinalAnswer: false,
209
+ title: "思考过程",
210
+ maxChars: 1200,
211
+ };
212
+ }
213
+ return resolveWecomReasoningConfig(resolveWecomPolicyInputs(api));
214
+ }
215
+
216
+ function resolveWecomReplyFormatPolicy(api) {
217
+ if (typeof resolveWecomReplyFormatConfig !== "function") {
218
+ return { mode: "auto" };
219
+ }
220
+ return resolveWecomReplyFormatConfig(resolveWecomPolicyInputs(api));
221
+ }
222
+
154
223
  function resolveWecomStreamManagerPolicy(api) {
155
224
  return resolveWecomStreamManagerConfig(resolveWecomPolicyInputs(api));
156
225
  }
@@ -176,6 +245,10 @@ export function createWecomPolicyResolvers({
176
245
  resolveWecomTextDebouncePolicy,
177
246
  resolveWecomReplyStreamingPolicy,
178
247
  resolveWecomDeliveryFallbackPolicy,
248
+ resolveWecomPendingReplyPolicy,
249
+ resolveWecomQuotaTrackingPolicy,
250
+ resolveWecomReasoningPolicy,
251
+ resolveWecomReplyFormatPolicy,
179
252
  resolveWecomWebhookBotDeliveryPolicy,
180
253
  resolveWecomStreamManagerPolicy,
181
254
  resolveWecomObservabilityPolicy,